Merge branch 'beta' into midturnab

This commit is contained in:
Dean 2025-02-17 01:15:37 -08:00 committed by GitHub
commit becde56248
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 4886 additions and 3510 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.6.0", "version": "1.7.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.5.4", "version": "1.7.0",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.6.0", "version": "1.7.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

View File

@ -0,0 +1,774 @@
{
"graphic": "terastallize",
"frames": [
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 150,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 225,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 70,
"zoomY": 70,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 70,
"zoomY": 70,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 90,
"zoomY": 90,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 90,
"zoomY": 90,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 200,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 100,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 100,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 60,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 60,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 60,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
100,
100,
100,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
255,
255,
255,
255
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
255,
255,
255,
0
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"tone": [
255,
255,
255,
255
],
"locked": true,
"priority": 1,
"focus": 2
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 1
}
]
],
"frameTimedEvents": {},
"position": 4,
"hue": 0
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "668-female.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 63,
"h": 72
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 63,
"h": 72
},
"frame": {
"x": 0,
"y": 0,
"w": 63,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:3f88e039152d4a967a218cb721938610:e6991ce9c3bad348cbc05ebf9b440302:d99ed0e84a0695b54e479aa98271aba1$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,158 @@
{ "frames": {
"unknown": {
"frame": { "x": 0, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"bug": {
"frame": { "x": 18, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"dark": {
"frame": { "x": 36, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"dragon": {
"frame": { "x": 54, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"electric": {
"frame": { "x": 72, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fairy": {
"frame": { "x": 0, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fighting": {
"frame": { "x": 18, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fire": {
"frame": { "x": 36, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"flying": {
"frame": { "x": 54, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ghost": {
"frame": { "x": 72, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"grass": {
"frame": { "x": 0, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ground": {
"frame": { "x": 18, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ice": {
"frame": { "x": 36, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"normal": {
"frame": { "x": 54, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"poison": {
"frame": { "x": 72, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"psychic": {
"frame": { "x": 0, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"rock": {
"frame": { "x": 18, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"steel": {
"frame": { "x": 36, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"water": {
"frame": { "x": 54, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"stellar": {
"frame": { "x": 72, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
}
},
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-dev",
"image": "button_tera.png",
"format": "RGBA8888",
"size": { "w": 90, "h": 84 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Sprite Sheet", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,158 @@
{ "frames": {
"unknown": {
"frame": { "x": 0, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"bug": {
"frame": { "x": 18, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"dark": {
"frame": { "x": 36, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"dragon": {
"frame": { "x": 54, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"electric": {
"frame": { "x": 72, "y": 0, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fairy": {
"frame": { "x": 0, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fighting": {
"frame": { "x": 18, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"fire": {
"frame": { "x": 36, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"flying": {
"frame": { "x": 54, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ghost": {
"frame": { "x": 72, "y": 21, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"grass": {
"frame": { "x": 0, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ground": {
"frame": { "x": 18, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"ice": {
"frame": { "x": 36, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"normal": {
"frame": { "x": 54, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"poison": {
"frame": { "x": 72, "y": 42, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"psychic": {
"frame": { "x": 0, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"rock": {
"frame": { "x": 18, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"steel": {
"frame": { "x": 36, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"water": {
"frame": { "x": 54, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
},
"stellar": {
"frame": { "x": 72, "y": 63, "w": 18, "h": 21 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 1, "w": 18, "h": 21 },
"sourceSize": { "w": 20, "h": 23 }
}
},
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-dev",
"image": "button_tera.png",
"format": "RGBA8888",
"size": { "w": 90, "h": 84 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Sprite Sheet", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

@ -1 +1 @@
Subproject commit f917baa1bb2fc5071587b7894ce7b4898cc64f36 Subproject commit 58dda14ee834204c4bd5ece47694a3c068df4b0e

View File

@ -9,7 +9,7 @@ import type { Constructor } from "#app/utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier"; import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, ModifierBar, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier } from "./modifier/modifier"; import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, ModifierBar, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier } from "./modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import type { Phase } from "#app/phase"; import type { Phase } from "#app/phase";
@ -1212,7 +1212,7 @@ export default class BattleScene extends SceneBase {
} }
getDoubleBattleChance(newWaveIndex: number, playerField: PlayerPokemon[]) { getDoubleBattleChance(newWaveIndex: number, playerField: PlayerPokemon[]) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); const doubleChance = new Utils.NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
return Math.max(doubleChance.value, 1); return Math.max(doubleChance.value, 1);
@ -1373,7 +1373,11 @@ export default class BattleScene extends SceneBase {
for (const pokemon of this.getPlayerParty()) { for (const pokemon of this.getPlayerParty()) {
pokemon.resetBattleData(); pokemon.resetBattleData();
pokemon.resetTera();
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
if (pokemon.hasSpecies(Species.TERAPAGOS) || (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)) {
this.arena.playerTerasUsed = 0;
}
} }
if (!this.trainer.visible) { if (!this.trainer.visible) {
@ -1658,7 +1662,7 @@ export default class BattleScene extends SceneBase {
} }
initPokemonSprite(sprite: Phaser.GameObjects.Sprite, pokemon?: Pokemon, hasShadow: boolean = false, ignoreOverride: boolean = false): Phaser.GameObjects.Sprite { initPokemonSprite(sprite: Phaser.GameObjects.Sprite, pokemon?: Pokemon, hasShadow: boolean = false, ignoreOverride: boolean = false): Phaser.GameObjects.Sprite {
sprite.setPipeline(this.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: hasShadow, ignoreOverride: ignoreOverride, teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined }); sprite.setPipeline(this.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: hasShadow, ignoreOverride: ignoreOverride, teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined, isTerastallized: pokemon ? pokemon.isTerastallized : false });
this.spriteSparkleHandler.add(sprite); this.spriteSparkleHandler.add(sprite);
return sprite; return sprite;
} }
@ -2589,11 +2593,8 @@ export default class BattleScene extends SceneBase {
const modifiersToRemove: PersistentModifier[] = []; const modifiersToRemove: PersistentModifier[] = [];
const modifierPromises: Promise<boolean>[] = []; const modifierPromises: Promise<boolean>[] = [];
if (modifier instanceof PersistentModifier) { if (modifier instanceof PersistentModifier) {
if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId)));
}
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual)) { if ((modifier as PersistentModifier).add(this.modifiers, !!virtual)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
success = modifier.apply(pokemon, true); success = modifier.apply(pokemon, true);
@ -2627,7 +2628,7 @@ export default class BattleScene extends SceneBase {
const args: unknown[] = []; const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) { if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) { if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1); const hpRestoreMultiplier = new Utils.NumberHolder(1);
this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier); this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier);
args.push(hpRestoreMultiplier.value); args.push(hpRestoreMultiplier.value);
} else { } else {
@ -2670,11 +2671,8 @@ export default class BattleScene extends SceneBase {
addEnemyModifier(modifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> { addEnemyModifier(modifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const modifiersToRemove: PersistentModifier[] = []; const modifiersToRemove: PersistentModifier[] = [];
if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false)));
}
if ((modifier as PersistentModifier).add(this.enemyModifiers, false)) { if ((modifier as PersistentModifier).add(this.enemyModifiers, false)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
modifier.apply(pokemon, true); modifier.apply(pokemon, true);
@ -2798,6 +2796,8 @@ export default class BattleScene extends SceneBase {
for (const modifier of modifiers) { for (const modifier of modifiers) {
this.addEnemyModifier(modifier, true, true); this.addEnemyModifier(modifier, true, true);
} }
this.currentBattle.trainer.genAI(party);
} }
party.forEach((enemyPokemon: EnemyPokemon, i: number) => { party.forEach((enemyPokemon: EnemyPokemon, i: number) => {
@ -2929,7 +2929,7 @@ export default class BattleScene extends SceneBase {
const modifierIndex = modifiers.indexOf(modifier); const modifierIndex = modifiers.indexOf(modifier);
if (modifierIndex > -1) { if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1); modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
modifier.apply(pokemon, false); modifier.apply(pokemon, false);
@ -3130,7 +3130,8 @@ export default class BattleScene extends SceneBase {
name: p.name, name: p.name,
form: p.getFormKey(), form: p.getFormKey(),
types: p.getTypes().map((type) => Type[type]), types: p.getTypes().map((type) => Type[type]),
teraType: p.getTeraType() !== Type.UNKNOWN ? Type[p.getTeraType()] : "", teraType: Type[p.getTeraType()],
isTerastallized: p.isTerastallized,
level: p.level, level: p.level,
currentHP: p.hp, currentHP: p.hp,
maxHP: p.getMaxHp(), maxHP: p.getMaxHp(),

View File

@ -92,6 +92,7 @@ export default class Battle {
public started: boolean = false; public started: boolean = false;
public enemySwitchCounter: number = 0; public enemySwitchCounter: number = 0;
public turn: number = 0; public turn: number = 0;
public preTurnCommands: TurnCommands;
public turnCommands: TurnCommands; public turnCommands: TurnCommands;
public playerParticipantIds: Set<number> = new Set<number>(); public playerParticipantIds: Set<number> = new Set<number>();
public battleScore: number = 0; public battleScore: number = 0;
@ -180,6 +181,7 @@ export default class Battle {
incrementTurn(): void { incrementTurn(): void {
this.turn++; this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ])); this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.preTurnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.battleSeedState = null; this.battleSeedState = null;
} }
@ -201,7 +203,7 @@ export default class Battle {
} }
pickUpScatteredMoney(): void { pickUpScatteredMoney(): void {
const moneyAmount = new Utils.IntegerHolder(globalScene.currentBattle.moneyScattered); const moneyAmount = new Utils.NumberHolder(globalScene.currentBattle.moneyScattered);
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {

View File

@ -239,37 +239,25 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr {
} }
} }
export class PostBattleInitStatStageChangeAbAttr extends PostBattleInitAbAttr { export class PostTeraFormChangeStatChangeAbAttr extends AbAttr {
private stats: BattleStat[]; private stats: BattleStat[];
private stages: number; private stages: number;
private selfTarget: boolean;
constructor(stats: BattleStat[], stages: number, selfTarget?: boolean) { constructor(stats: BattleStat[], stages: number) {
super(); super();
this.stats = stats; this.stats = stats;
this.stages = stages; this.stages = stages;
this.selfTarget = !!selfTarget;
} }
applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise<boolean> {
const statStageChangePhases: StatStageChangePhase[] = []; const statStageChangePhases: StatStageChangePhase[] = [];
if (!simulated) { if (!simulated) {
if (this.selfTarget) { statStageChangePhases.push(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages));
statStageChangePhases.push(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages));
} else {
for (const opponent of pokemon.getOpponents()) {
statStageChangePhases.push(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages));
}
}
for (const statStageChangePhase of statStageChangePhases) { for (const statStageChangePhase of statStageChangePhases) {
if (!this.selfTarget && !statStageChangePhase.getPokemon()?.summonData) { globalScene.unshiftPhase(statStageChangePhase);
globalScene.pushPhase(statStageChangePhase);
} else { // TODO: This causes the ability bar to be shown at the wrong time
globalScene.unshiftPhase(statStageChangePhase);
}
} }
} }
@ -1307,7 +1295,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
if ( if (
!pokemon.isTerastallized() && !pokemon.isTerastallized &&
move.id !== Moves.STRUGGLE && move.id !== Moves.STRUGGLE &&
/** /**
* Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute
@ -3143,7 +3131,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr {
return false; return false;
} }
(args[1] as Utils.IntegerHolder).value += this.changeAmount; (args[1] as Utils.NumberHolder).value += this.changeAmount;
return true; return true;
} }
} }
@ -3985,7 +3973,7 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr {
} }
override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value *= this.multiplier; (args[0] as Utils.NumberHolder).value *= this.multiplier;
return true; return true;
} }
@ -4086,7 +4074,7 @@ export class HealFromBerryUseAbAttr extends AbAttr {
export class RunSuccessAbAttr extends AbAttr { export class RunSuccessAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = 256; (args[0] as Utils.NumberHolder).value = 256;
return true; return true;
} }
@ -4156,7 +4144,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
export class MaxMultiHitAbAttr extends AbAttr { export class MaxMultiHitAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = 0; (args[0] as Utils.NumberHolder).value = 0;
return true; return true;
} }
@ -4287,7 +4275,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
export class RedirectMoveAbAttr extends AbAttr { export class RedirectMoveAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.canRedirect(args[0] as Moves)) { if (this.canRedirect(args[0] as Moves)) {
const target = args[1] as Utils.IntegerHolder; const target = args[1] as Utils.NumberHolder;
const newTarget = pokemon.getBattlerIndex(); const newTarget = pokemon.getBattlerIndex();
if (target.value !== newTarget) { if (target.value !== newTarget) {
target.value = newTarget; target.value = newTarget;
@ -4804,7 +4792,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr {
} }
override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, _args: any[]): boolean { override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, _args: any[]): boolean {
if (pokemon.isTerastallized()) { if (pokemon.isTerastallized) {
return false; return false;
} }
const currentTerrain = globalScene.arena.getTerrainType(); const currentTerrain = globalScene.arena.getTerrainType();
@ -6234,7 +6222,7 @@ export function initAbilities() {
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.condition((pokemon) => !pokemon.isTerastallized()), .condition((pokemon) => !pokemon.isTerastallized),
new Ability(Abilities.QUICK_DRAW, 8) new Ability(Abilities.QUICK_DRAW, 8)
.attr(BypassSpeedChanceAbAttr, 30), .attr(BypassSpeedChanceAbAttr, 30),
new Ability(Abilities.UNSEEN_FIST, 8) new Ability(Abilities.UNSEEN_FIST, 8)
@ -6386,29 +6374,25 @@ export function initAbilities() {
new Ability(Abilities.TOXIC_CHAIN, 9) new Ability(Abilities.TOXIC_CHAIN, 9)
.attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC), .attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC),
new Ability(Abilities.EMBODY_ASPECT_TEAL, 9) new Ability(Abilities.EMBODY_ASPECT_TEAL, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPD ], 1, true) .attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.SPD ], 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr),
.partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9) new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true) .attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.SPDEF ], 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr),
.partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9) new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.ATK ], 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr),
.partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9) new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.DEF ], 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr),
.partial(), // Ogerpon tera interactions
new Ability(Abilities.TERA_SHIFT, 9) new Ability(Abilities.TERA_SHIFT, 9)
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1) .attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)

View File

@ -56,6 +56,7 @@ export enum ChargeAnim {
export enum CommonAnim { export enum CommonAnim {
USE_ITEM = 2000, USE_ITEM = 2000,
HEALTH_UP, HEALTH_UP,
TERASTALLIZE,
POISON = 2010, POISON = 2010,
TOXIC, TOXIC,
PARALYSIS, PARALYSIS,

View File

@ -2485,7 +2485,7 @@ export class TarShotTag extends BattlerTag {
* @returns whether the tag is applied * @returns whether the tag is applied
*/ */
override canAdd(pokemon: Pokemon): boolean { override canAdd(pokemon: Pokemon): boolean {
return !pokemon.isTerastallized(); return !pokemon.isTerastallized;
} }
override onAdd(pokemon: Pokemon): void { override onAdd(pokemon: Pokemon): void {

View File

@ -349,23 +349,23 @@ export abstract class Challenge {
/** /**
* An apply function for AI_LEVEL challenges. Derived classes should alter this. * An apply function for AI_LEVEL challenges. Derived classes should alter this.
* @param level {@link Utils.IntegerHolder} The generated level. * @param level {@link Utils.NumberHolder} The generated level.
* @param levelCap {@link Number} The current level cap. * @param levelCap {@link Number} The current level cap.
* @param isTrainer {@link Boolean} Whether this is a trainer pokemon. * @param isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @returns {@link boolean} Whether this function did anything. * @returns {@link boolean} Whether this function did anything.
*/ */
applyLevelChange(level: Utils.IntegerHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean { applyLevelChange(level: Utils.NumberHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean {
return false; return false;
} }
/** /**
* An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this. * An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this.
* @param pokemon {@link Pokemon} The pokemon that is being considered. * @param pokemon {@link Pokemon} The pokemon that is being considered.
* @param moveSlots {@link Utils.IntegerHolder} The amount of move slots. * @param moveSlots {@link Utils.NumberHolder} The amount of move slots.
* @returns {@link boolean} Whether this function did anything. * @returns {@link boolean} Whether this function did anything.
*/ */
applyMoveSlot(pokemon: Pokemon, moveSlots: Utils.IntegerHolder): boolean { applyMoveSlot(pokemon: Pokemon, moveSlots: Utils.NumberHolder): boolean {
return false; return false;
} }
@ -393,10 +393,10 @@ export abstract class Challenge {
* @param pokemon {@link Pokemon} What pokemon would learn the move. * @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question. * @param move {@link Moves} The move in question.
* @param level {@link Utils.IntegerHolder} The level threshold for access. * @param level {@link Utils.NumberHolder} The level threshold for access.
* @returns {@link boolean} Whether this function did anything. * @returns {@link boolean} Whether this function did anything.
*/ */
applyMoveAccessLevel(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean { applyMoveAccessLevel(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.NumberHolder): boolean {
return false; return false;
} }
@ -405,10 +405,10 @@ export abstract class Challenge {
* @param pokemon {@link Pokemon} What pokemon would learn the move. * @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question. * @param move {@link Moves} The move in question.
* @param weight {@link Utils.IntegerHolder} The base weight of the move * @param weight {@link Utils.NumberHolder} The base weight of the move
* @returns {@link boolean} Whether this function did anything. * @returns {@link boolean} Whether this function did anything.
*/ */
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean { applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.NumberHolder): boolean {
return false; return false;
} }
@ -456,8 +456,8 @@ export class SingleGenerationChallenge extends Challenge {
} }
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies?.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; // TODO: is the bang on fusionSpecies correct? const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; // TODO: is the bang on fusionSpecies correct?
if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) { if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) {
valid.value = false; valid.value = false;
return true; return true;
@ -913,22 +913,22 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
* Apply all challenges that modify what level AI are. * Apply all challenges that modify what level AI are.
* @param gameMode {@link GameMode} The current gameMode * @param gameMode {@link GameMode} The current gameMode
* @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL * @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL
* @param level {@link Utils.IntegerHolder} The generated level of the pokemon. * @param level {@link Utils.NumberHolder} The generated level of the pokemon.
* @param levelCap {@link Number} The maximum level cap for the current wave. * @param levelCap {@link Number} The maximum level cap for the current wave.
* @param isTrainer {@link Boolean} Whether this is a trainer pokemon. * @param isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @returns True if any challenge was successfully applied. * @returns True if any challenge was successfully applied.
*/ */
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_LEVEL, level: Utils.IntegerHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_LEVEL, level: Utils.NumberHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean;
/** /**
* Apply all challenges that modify how many move slots the AI has. * Apply all challenges that modify how many move slots the AI has.
* @param gameMode {@link GameMode} The current gameMode * @param gameMode {@link GameMode} The current gameMode
* @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS * @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS
* @param pokemon {@link Pokemon} The pokemon being considered. * @param pokemon {@link Pokemon} The pokemon being considered.
* @param moveSlots {@link Utils.IntegerHolder} The amount of move slots. * @param moveSlots {@link Utils.NumberHolder} The amount of move slots.
* @returns True if any challenge was successfully applied. * @returns True if any challenge was successfully applied.
*/ */
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_MOVE_SLOTS, pokemon: Pokemon, moveSlots: Utils.IntegerHolder): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_MOVE_SLOTS, pokemon: Pokemon, moveSlots: Utils.NumberHolder): boolean;
/** /**
* Apply all challenges that modify whether a pokemon has its passive. * Apply all challenges that modify whether a pokemon has its passive.
* @param gameMode {@link GameMode} The current gameMode * @param gameMode {@link GameMode} The current gameMode
@ -952,10 +952,10 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
* @param pokemon {@link Pokemon} What pokemon would learn the move. * @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question. * @param move {@link Moves} The move in question.
* @param level {@link Utils.IntegerHolder} The level threshold for access. * @param level {@link Utils.NumberHolder} The level threshold for access.
* @returns True if any challenge was successfully applied. * @returns True if any challenge was successfully applied.
*/ */
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_ACCESS, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_ACCESS, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.NumberHolder): boolean;
/** /**
* Apply all challenges that modify what weight a pokemon gives to move generation * Apply all challenges that modify what weight a pokemon gives to move generation
* @param gameMode {@link GameMode} The current gameMode * @param gameMode {@link GameMode} The current gameMode
@ -963,10 +963,10 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
* @param pokemon {@link Pokemon} What pokemon would learn the move. * @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question. * @param move {@link Moves} The move in question.
* @param weight {@link Utils.IntegerHolder} The weight of the move. * @param weight {@link Utils.NumberHolder} The weight of the move.
* @returns True if any challenge was successfully applied. * @returns True if any challenge was successfully applied.
*/ */
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.NumberHolder): boolean;
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;

View File

@ -791,7 +791,7 @@ export default class Move implements Localizable {
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, null, typeChangeMovePowerMultiplier); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, null, typeChangeMovePowerMultiplier);
const sourceTeraType = source.getTeraType(); const sourceTeraType = source.getTeraType();
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60; power.value = 60;
} }
@ -908,7 +908,7 @@ export class AttackMove extends Move {
attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2; attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2;
if (attackScore) { if (attackScore) {
if (this.category === MoveCategory.PHYSICAL) { if (this.category === MoveCategory.PHYSICAL) {
const atk = new Utils.IntegerHolder(user.getEffectiveStat(Stat.ATK, target)); const atk = new Utils.NumberHolder(user.getEffectiveStat(Stat.ATK, target));
applyMoveAttrs(VariableAtkAttr, user, target, move, atk); applyMoveAttrs(VariableAtkAttr, user, target, move, atk);
if (atk.value > user.getEffectiveStat(Stat.SPATK, target)) { if (atk.value > user.getEffectiveStat(Stat.SPATK, target)) {
const statRatio = user.getEffectiveStat(Stat.SPATK, target) / atk.value; const statRatio = user.getEffectiveStat(Stat.SPATK, target) / atk.value;
@ -919,7 +919,7 @@ export class AttackMove extends Move {
} }
} }
} else { } else {
const spAtk = new Utils.IntegerHolder(user.getEffectiveStat(Stat.SPATK, target)); const spAtk = new Utils.NumberHolder(user.getEffectiveStat(Stat.SPATK, target));
applyMoveAttrs(VariableAtkAttr, user, target, move, spAtk); applyMoveAttrs(VariableAtkAttr, user, target, move, spAtk);
if (spAtk.value > user.getEffectiveStat(Stat.ATK, target)) { if (spAtk.value > user.getEffectiveStat(Stat.ATK, target)) {
const statRatio = user.getEffectiveStat(Stat.ATK, target) / spAtk.value; const statRatio = user.getEffectiveStat(Stat.ATK, target) / spAtk.value;
@ -1337,7 +1337,7 @@ export class IgnoreOpponentStatStagesAttr extends MoveAttr {
export class HighCritAttr extends MoveAttr { export class HighCritAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value++; (args[0] as Utils.NumberHolder).value++;
return true; return true;
} }
@ -1369,7 +1369,7 @@ export class FixedDamageAttr extends MoveAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = this.getDamage(user, target, move); (args[0] as Utils.NumberHolder).value = this.getDamage(user, target, move);
return true; return true;
} }
@ -1385,7 +1385,7 @@ export class UserHpDamageAttr extends FixedDamageAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = user.hp; (args[0] as Utils.NumberHolder).value = user.hp;
return true; return true;
} }
@ -1437,7 +1437,7 @@ export class MatchHpAttr extends FixedDamageAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = target.hp - user.hp; (args[0] as Utils.NumberHolder).value = target.hp - user.hp;
return true; return true;
} }
@ -1467,7 +1467,7 @@ export class CounterDamageAttr extends FixedDamageAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: number, ar: AttackMoveResult) => total + ar.damage, 0); const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: number, ar: AttackMoveResult) => total + ar.damage, 0);
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier); (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(damage * this.multiplier);
return true; return true;
} }
@ -1499,7 +1499,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
export class ModifiedDamageAttr extends MoveAttr { export class ModifiedDamageAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const initialDamage = args[0] as Utils.IntegerHolder; const initialDamage = args[0] as Utils.NumberHolder;
initialDamage.value = this.getModifiedDamage(user, target, move, initialDamage.value); initialDamage.value = this.getModifiedDamage(user, target, move, initialDamage.value);
return true; return true;
@ -2164,7 +2164,7 @@ export class IncrementMovePriorityAttr extends MoveAttr {
* @param user {@linkcode Pokemon} using this move * @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move * @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used * @param move {@linkcode Move} being used
* @param args [0] {@linkcode Utils.IntegerHolder} for move priority. * @param args [0] {@linkcode Utils.NumberHolder} for move priority.
* @returns true if function succeeds * @returns true if function succeeds
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2172,7 +2172,7 @@ export class IncrementMovePriorityAttr extends MoveAttr {
return false; return false;
} }
(args[0] as Utils.IntegerHolder).value += this.increaseAmount; (args[0] as Utils.NumberHolder).value += this.increaseAmount;
return true; return true;
} }
} }
@ -2210,7 +2210,7 @@ export class MultiHitAttr extends MoveAttr {
* @param user {@linkcode Pokemon} that used the attack * @param user {@linkcode Pokemon} that used the attack
* @param target {@linkcode Pokemon} targeted by the attack * @param target {@linkcode Pokemon} targeted by the attack
* @param move {@linkcode Move} being used * @param move {@linkcode Move} being used
* @param args [0] {@linkcode Utils.IntegerHolder} storing the hit count of the attack * @param args [0] {@linkcode Utils.NumberHolder} storing the hit count of the attack
* @returns True * @returns True
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2277,7 +2277,7 @@ export class ChangeMultiHitTypeAttr extends MoveAttr {
export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr { export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) { if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) {
(args[0] as Utils.IntegerHolder).value = MultiHitType._3; (args[0] as Utils.NumberHolder).value = MultiHitType._3;
return true; return true;
} }
return false; return false;
@ -4118,7 +4118,7 @@ export class PresentPowerAttr extends VariablePowerAttr {
export class WaterShurikenPowerAttr extends VariablePowerAttr { export class WaterShurikenPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) { if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) {
(args[0] as Utils.IntegerHolder).value = 20; (args[0] as Utils.NumberHolder).value = 20;
return true; return true;
} }
return false; return false;
@ -4140,7 +4140,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr {
const stockpilingTag = user.getTag(StockpilingTag); const stockpilingTag = user.getTag(StockpilingTag);
if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { if (stockpilingTag && stockpilingTag.stockpiledCount > 0) {
const power = args[0] as Utils.IntegerHolder; const power = args[0] as Utils.NumberHolder;
power.value = this.multiplier * stockpilingTag.stockpiledCount; power.value = this.multiplier * stockpilingTag.stockpiledCount;
return true; return true;
} }
@ -4449,7 +4449,7 @@ export class VariableAtkAttr extends MoveAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
//const atk = args[0] as Utils.IntegerHolder; //const atk = args[0] as Utils.NumberHolder;
return false; return false;
} }
} }
@ -4459,7 +4459,7 @@ export class TargetAtkUserAtkAttr extends VariableAtkAttr {
super(); super();
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = target.getEffectiveStat(Stat.ATK, target); (args[0] as Utils.NumberHolder).value = target.getEffectiveStat(Stat.ATK, target);
return true; return true;
} }
} }
@ -4470,7 +4470,7 @@ export class DefAtkAttr extends VariableAtkAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = user.getEffectiveStat(Stat.DEF, target); (args[0] as Utils.NumberHolder).value = user.getEffectiveStat(Stat.DEF, target);
return true; return true;
} }
} }
@ -4481,7 +4481,7 @@ export class VariableDefAttr extends MoveAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
//const def = args[0] as Utils.IntegerHolder; //const def = args[0] as Utils.NumberHolder;
return false; return false;
} }
} }
@ -4492,7 +4492,7 @@ export class DefDefAttr extends VariableDefAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = target.getEffectiveStat(Stat.DEF, user); (args[0] as Utils.NumberHolder).value = target.getEffectiveStat(Stat.DEF, user);
return true; return true;
} }
} }
@ -4634,7 +4634,7 @@ export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const category = (args[0] as Utils.NumberHolder); const category = (args[0] as Utils.NumberHolder);
if (user.isTerastallized() && user.getEffectiveStat(Stat.ATK, target, move, true, true, false, false, true) > if (user.isTerastallized && user.getEffectiveStat(Stat.ATK, target, move, true, true, false, false, true) >
user.getEffectiveStat(Stat.SPATK, target, move, true, true, false, false, true)) { user.getEffectiveStat(Stat.SPATK, target, move, true, true, false, false, true)) {
category.value = MoveCategory.PHYSICAL; category.value = MoveCategory.PHYSICAL;
return true; return true;
@ -4662,7 +4662,7 @@ export class TeraBlastPowerAttr extends VariablePowerAttr {
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0] as Utils.NumberHolder; const power = args[0] as Utils.NumberHolder;
if (user.isTerastallized() && user.getTeraType() === Type.STELLAR) { if (user.isTerastallized && user.getTeraType() === Type.STELLAR) {
power.value = 100; power.value = 100;
return true; return true;
} }
@ -4720,30 +4720,6 @@ export class VariableMoveTypeAttr extends MoveAttr {
} }
} }
/**
* Attribute used for Tera Starstorm that changes the move type to Stellar
* @extends VariableMoveTypeAttr
*/
export class TeraStarstormTypeAttr extends VariableMoveTypeAttr {
/**
*
* @param user the {@linkcode Pokemon} using the move
* @param target n/a
* @param move n/a
* @param args[0] {@linkcode Utils.NumberHolder} the move type
* @returns `true` if the move type is changed to {@linkcode Type.STELLAR}, `false` otherwise
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.isTerastallized() && (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS)) {
const moveType = args[0] as Utils.NumberHolder;
moveType.value = Type.STELLAR;
return true;
}
return false;
}
}
export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0]; const moveType = args[0];
@ -5009,7 +4985,7 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
return false; return false;
} }
if (user.isTerastallized()) { if (user.isTerastallized) {
moveType.value = user.getTeraType(); // changes move type to tera type moveType.value = user.getTeraType(); // changes move type to tera type
return true; return true;
} }
@ -5018,6 +4994,30 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
} }
} }
/**
* Attribute used for Tera Starstorm that changes the move type to Stellar
* @extends VariableMoveTypeAttr
*/
export class TeraStarstormTypeAttr extends VariableMoveTypeAttr {
/**
*
* @param user the {@linkcode Pokemon} using the move
* @param target n/a
* @param move n/a
* @param args[0] {@linkcode Utils.NumberHolder} the move type
* @returns `true` if the move type is changed to {@linkcode Type.STELLAR}, `false` otherwise
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.isTerastallized && user.hasSpecies(Species.TERAPAGOS)) {
const moveType = args[0] as Utils.NumberHolder;
moveType.value = Type.STELLAR;
return true;
}
return false;
}
}
export class MatchUserTypeAttr extends VariableMoveTypeAttr { export class MatchUserTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0]; const moveType = args[0];
@ -6345,7 +6345,7 @@ export class RemoveTypeAttr extends MoveEffectAttr {
return false; return false;
} }
if (user.isTerastallized() && user.getTeraType() === this.removedType) { // active tera types cannot be removed if (user.isTerastallized && user.getTeraType() === this.removedType) { // active tera types cannot be removed
return false; return false;
} }
@ -6525,7 +6525,7 @@ export class ChangeTypeAttr extends MoveEffectAttr {
} }
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => !target.isTerastallized() && !target.hasAbility(Abilities.MULTITYPE) && !target.hasAbility(Abilities.RKS_SYSTEM) && !(target.getTypes().length === 1 && target.getTypes()[0] === this.type); return (user, target, move) => !target.isTerastallized && !target.hasAbility(Abilities.MULTITYPE) && !target.hasAbility(Abilities.RKS_SYSTEM) && !(target.getTypes().length === 1 && target.getTypes()[0] === this.type);
} }
} }
@ -6548,7 +6548,7 @@ export class AddTypeAttr extends MoveEffectAttr {
} }
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => !target.isTerastallized() && !target.getTypes().includes(this.type); return (user, target, move) => !target.isTerastallized && !target.getTypes().includes(this.type);
} }
} }
@ -11076,7 +11076,7 @@ export function initMoves() {
.attr(TeraMoveCategoryAttr) .attr(TeraMoveCategoryAttr)
.attr(TeraBlastTypeAttr) .attr(TeraBlastTypeAttr)
.attr(TeraBlastPowerAttr) .attr(TeraBlastPowerAttr)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, { condition: (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR) }), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, { condition: (user, target, move) => user.isTerastallized && user.isOfType(Type.STELLAR) }),
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9) new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
.attr(ProtectAttr, BattlerTagType.SILK_TRAP) .attr(ProtectAttr, BattlerTagType.SILK_TRAP)
.condition(failIfLastCondition), .condition(failIfLastCondition),
@ -11271,7 +11271,7 @@ export function initMoves() {
new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
.attr(TeraMoveCategoryAttr) .attr(TeraMoveCategoryAttr)
.attr(TeraStarstormTypeAttr) .attr(TeraStarstormTypeAttr)
.attr(VariableTargetAttr, (user, target, move) => (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS) && user.isTerastallized() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER) .attr(VariableTargetAttr, (user, target, move) => user.hasSpecies(Species.TERAPAGOS) && user.isTerastallized ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER)
.partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */ .partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */
new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9) new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9)
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc) .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)

View File

@ -594,7 +594,7 @@ function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: P
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()), isTerastallized: tradedPokemon.isTerastallized });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", tradedPokemon.shiny); sprite.setPipelineData("shiny", tradedPokemon.shiny);
@ -615,7 +615,7 @@ function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: P
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()), isTerastallized: tradedPokemon.isTerastallized });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", receivedPokemon.shiny); sprite.setPipelineData("shiny", receivedPokemon.shiny);

View File

@ -10,7 +10,7 @@ import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifi
import type { EnemyPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { IntegerHolder, randSeedInt } from "#app/utils"; import { NumberHolder, randSeedInt } from "#app/utils";
import type PokemonSpecies from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
@ -279,7 +279,7 @@ async function summonSafariPokemon() {
if (pokemon.species.abilityHidden) { if (pokemon.species.abilityHidden) {
const hiddenIndex = pokemon.species.ability2 ? 2 : 1; const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
if (pokemon.abilityIndex < hiddenIndex) { if (pokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256); const hiddenAbilityChance = new NumberHolder(256);
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);

View File

@ -1,5 +1,5 @@
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { generateModifierType, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs } from "#app/data/trainer-config"; import { trainerConfigs } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
@ -23,7 +23,6 @@ import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { getPokeballTintColor } from "#app/data/pokeball"; import { getPokeballTintColor } from "#app/data/pokeball";
@ -387,11 +386,7 @@ function getPartyConfig(): EnemyPartyConfig {
nature: Nature.ADAMANT, nature: Nature.ADAMANT,
moveSet: [ Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH ], moveSet: [ Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH ],
ivs: [ 31, 31, 31, 31, 31, 31 ], ivs: [ 31, 31, 31, 31, 31, 31 ],
modifierConfigs: [ tera: Type.STEEL,
{
modifier: generateModifierType(modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType,
}
]
} }
] ]
}; };

View File

@ -12,7 +12,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
import type { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon";
import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
import type PokemonSpecies from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
@ -452,7 +452,7 @@ async function postProcessTransformedPokemon(previousPokemon: PlayerPokemon, new
if (newPokemon.species.abilityHidden) { if (newPokemon.species.abilityHidden) {
const hiddenIndex = newPokemon.species.ability2 ? 2 : 1; const hiddenIndex = newPokemon.species.ability2 ? 2 : 1;
if (newPokemon.abilityIndex < hiddenIndex) { if (newPokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256); const hiddenAbilityChance = new NumberHolder(256);
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);

View File

@ -46,6 +46,7 @@ import type { Variant } from "#app/data/variant";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Type } from "#app/enums/type";
/** /**
* Animates exclamation sprite over trainer's head at start of encounter * Animates exclamation sprite over trainer's head at start of encounter
@ -98,6 +99,7 @@ export interface EnemyPokemonConfig {
modifierConfigs?: HeldModifierConfig[]; modifierConfigs?: HeldModifierConfig[];
tags?: BattlerTagType[]; tags?: BattlerTagType[];
dataSource?: PokemonData; dataSource?: PokemonData;
tera?: Type;
aiType?: AiType; aiType?: AiType;
} }
@ -329,6 +331,14 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
tags.forEach(tag => enemyPokemon.addTag(tag)); tags.forEach(tag => enemyPokemon.addTag(tag));
} }
// Set tera
if (config.tera && config.tera !== Type.UNKNOWN) {
enemyPokemon.teraType = config.tera;
if (battle.trainer) {
battle.trainer.config.setInstantTera(e);
}
}
// mysteryEncounterBattleEffects will only be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied // mysteryEncounterBattleEffects will only be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied
if (config.mysteryEncounterBattleEffects) { if (config.mysteryEncounterBattleEffects) {
enemyPokemon.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects; enemyPokemon.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects;

View File

@ -61,7 +61,7 @@ export function doPokemonTransformationSequence(previousPokemon: PlayerPokemon,
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(previousPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(previousPokemon.getTeraType()), isTerastallized: previousPokemon.isTerastallized });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", previousPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", previousPokemon.getSpriteKey());
sprite.setPipelineData("shiny", previousPokemon.shiny); sprite.setPipelineData("shiny", previousPokemon.shiny);

View File

@ -1,8 +1,7 @@
import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier"; import { PokemonFormChangeItemModifier } from "../modifier/modifier";
import type Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { MoveCategory, allMoves } from "./move"; import { MoveCategory, allMoves } from "./move";
import { Type } from "#enums/type";
import type { Constructor, nil } from "#app/utils"; import type { Constructor, nil } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -399,23 +398,7 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
* @extends SpeciesFormChangeTrigger * @extends SpeciesFormChangeTrigger
*/ */
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger { export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
/** The Tera type that triggers the form change */ description = i18next.t("pokemonEvolutions:Forms.tera" );
private teraType: Type;
constructor(teraType: Type) {
super();
this.teraType = teraType;
this.description = i18next.t("pokemonEvolutions:Forms.tera", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
}
/**
* Checks if the associated Pokémon has the required Tera Shard that matches with the associated Tera type.
* @param {Pokemon} pokemon the Pokémon that is trying to do the form change
* @returns `true` if the Pokémon can change forms, `false` otherwise
*/
canChange(pokemon: Pokemon): boolean {
return !!globalScene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id && m.teraType === this.teraType);
}
} }
/** /**
@ -425,10 +408,6 @@ export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
*/ */
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger { export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
description = i18next.t("pokemonEvolutions:Forms.teraLapse"); description = i18next.t("pokemonEvolutions:Forms.teraLapse");
canChange(pokemon: Pokemon): boolean {
return !!globalScene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id);
}
} }
/** /**
@ -992,19 +971,19 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.OGERPON, "teal-mask", "wellspring-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.WELLSPRING_MASK)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "wellspring-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.WELLSPRING_MASK)),
new SpeciesFormChange(Species.OGERPON, "teal-mask", "hearthflame-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.HEARTHFLAME_MASK)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "hearthflame-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.HEARTHFLAME_MASK)),
new SpeciesFormChange(Species.OGERPON, "teal-mask", "cornerstone-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "cornerstone-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)),
new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeTeraTrigger(Type.GRASS)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.GRASS)), new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeLapseTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(Type.WATER)), new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.WATER)), new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeTeraTrigger(Type.FIRE)), new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.FIRE)), new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeLapseTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(Type.ROCK)), new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.ROCK)) new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true)
], ],
[Species.TERAPAGOS]: [ [Species.TERAPAGOS]: [
new SpeciesFormChange(Species.TERAPAGOS, "", "terastal", new SpeciesFormChangeAbilityTrigger(), true), new SpeciesFormChange(Species.TERAPAGOS, "", "terastal", new SpeciesFormChangeAbilityTrigger(), true),
new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(Type.STELLAR)), new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.STELLAR)) new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true)
], ],
[Species.GALAR_DARMANITAN]: [ [Species.GALAR_DARMANITAN]: [
new SpeciesFormChange(Species.GALAR_DARMANITAN, "", "zen", new SpeciesFormChangeAbilityTrigger(), true), new SpeciesFormChange(Species.GALAR_DARMANITAN, "", "zen", new SpeciesFormChangeAbilityTrigger(), true),

View File

@ -1051,7 +1051,11 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
caughtAttr += DexAttr.VARIANT_2; caughtAttr += DexAttr.VARIANT_2;
caughtAttr += DexAttr.VARIANT_3; caughtAttr += DexAttr.VARIANT_3;
} }
caughtAttr += DexAttr.DEFAULT_FORM;
// Summing successive bigints for each obtainable form
caughtAttr += this?.forms?.length > 1 ?
this.forms.map((f, index) => f.isUnobtainable ? 0n : 128n * 2n ** BigInt(index)).reduce((acc, val) => acc + val, 0n) :
DexAttr.DEFAULT_FORM;
return caughtAttr; return caughtAttr;
} }

View File

@ -175,11 +175,51 @@ export const trainerPartyTemplates = {
type PartyTemplateFunc = () => TrainerPartyTemplate; type PartyTemplateFunc = () => TrainerPartyTemplate;
type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon; type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[]; type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
type GenAIFunc = (party: EnemyPokemon[]) => void;
export interface PartyMemberFuncs { export interface PartyMemberFuncs {
[key: number]: PartyMemberFunc [key: number]: PartyMemberFunc
} }
export enum TeraAIMode {
NO_TERA,
INSTANT_TERA,
SMART_TERA
}
/**
* Stores data and helper functions about a trainers AI options.
*/
export class TrainerAI {
public teraMode: TeraAIMode = TeraAIMode.NO_TERA;
public instantTeras: number[];
/**
* @param canTerastallize Whether this trainer is allowed to tera
*/
constructor(teraMode: TeraAIMode = TeraAIMode.NO_TERA) {
this.teraMode = teraMode;
this.instantTeras = [];
}
/**
* Checks if a trainer can tera
* @returns Whether this trainer can currently tera
*/
public canTerastallize() {
return this.teraMode !== TeraAIMode.NO_TERA;
}
/**
* Sets a pokemon on this AI to just instantly tera on first move used
* @param index The index of the pokemon to instantly tera
*/
public setInstantTera(index: number) {
this.teraMode = TeraAIMode.INSTANT_TERA;
this.instantTeras.push(index);
}
}
export class TrainerConfig { export class TrainerConfig {
public trainerType: TrainerType; public trainerType: TrainerType;
public trainerTypeDouble: TrainerType; public trainerTypeDouble: TrainerType;
@ -203,6 +243,7 @@ export class TrainerConfig {
public doubleEncounterBgm: string; public doubleEncounterBgm: string;
public victoryBgm: string; public victoryBgm: string;
public genModifiersFunc: GenModifiersFunc; public genModifiersFunc: GenModifiersFunc;
public genAIFuncs: GenAIFunc[] = [];
public modifierRewardFuncs: ModifierTypeFunc[] = []; public modifierRewardFuncs: ModifierTypeFunc[] = [];
public partyTemplates: TrainerPartyTemplate[]; public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc; public partyTemplateFunc: PartyTemplateFunc;
@ -212,6 +253,7 @@ export class TrainerConfig {
public speciesFilter: PokemonSpeciesFilter; public speciesFilter: PokemonSpeciesFilter;
public specialtyTypes: Type[] = []; public specialtyTypes: Type[] = [];
public hasVoucher: boolean = false; public hasVoucher: boolean = false;
public trainerAI: TrainerAI;
public encounterMessages: string[] = []; public encounterMessages: string[] = [];
public victoryMessages: string[] = []; public victoryMessages: string[] = [];
@ -227,6 +269,7 @@ export class TrainerConfig {
constructor(trainerType: TrainerType, allowLegendaries?: boolean) { constructor(trainerType: TrainerType, allowLegendaries?: boolean) {
this.trainerType = trainerType; this.trainerType = trainerType;
this.trainerAI = new TrainerAI();
this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]); this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]);
this.battleBgm = "battle_trainer"; this.battleBgm = "battle_trainer";
this.mixedBattleBgm = "battle_trainer"; this.mixedBattleBgm = "battle_trainer";
@ -550,6 +593,47 @@ export class TrainerConfig {
return this; return this;
} }
/**
* Sets random pokemon from the trainers team to instant tera. Uses their specialty types is they have one.
* @param count The amount of pokemon to have instant tera
* @returns this
*/
setRandomTeraModifiers(count: () => integer): TrainerConfig {
this.genAIFuncs.push((party: EnemyPokemon[]) => {
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i);
for (let t = 0; t < Math.min(count(), party.length); t++) {
const randomIndex = Utils.randSeedItem(partyMemberIndexes);
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
if (this.specialtyTypes?.length) {
party[randomIndex].teraType = Utils.randSeedItem(this.specialtyTypes);
}
this.trainerAI.setInstantTera(randomIndex);
}
});
return this;
}
/**
* Sets a specific pokemon to instant tera
* @param index The index within the team to have instant tera
* @returns this
*/
setInstantTera(index: number): TrainerConfig {
this.trainerAI.setInstantTera(index);
return this;
}
// function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: Type[]): PersistentModifier[] {
// const ret: PersistentModifier[] = [];
// const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i);
// for (let t = 0; t < Math.min(count, party.length); t++) {
// const randomIndex = Utils.randSeedItem(partyMemberIndexes);
// partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
// ret.push(modifierTypes.TERA_SHARD().generateType([], [ Utils.randSeedItem(types ? types : party[randomIndex].getTypes()) ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct?
// }
// return ret;
// }
setEventModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig { setEventModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig {
this.eventRewardFuncs = modifierTypeFuncs.map(func => () => { this.eventRewardFuncs = modifierTypeFuncs.map(func => () => {
const modifierTypeFunc = func(); const modifierTypeFunc = func();
@ -851,10 +935,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_gym"); this.setBattleBgm("battle_unova_gym");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => { this.setRandomTeraModifiers(() => globalScene.currentBattle.waveIndex >= 100 ? 1 : 0);
const waveIndex = globalScene.currentBattle.waveIndex;
return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined);
});
return this; return this;
} }
@ -910,7 +991,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_elite"); this.setBattleBgm("battle_unova_elite");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 1, specialtyTypes.length ? specialtyTypes : undefined)); this.setRandomTeraModifiers(() => 1);
return this; return this;
} }
@ -1197,16 +1278,6 @@ function getSpeciesFilterRandomPartyMemberFunc(
}; };
} }
function getRandomTeraModifiers(party: EnemyPokemon[], count: number, types?: Type[]): PersistentModifier[] {
const ret: PersistentModifier[] = [];
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i);
for (let t = 0; t < Math.min(count, party.length); t++) {
const randomIndex = Utils.randSeedItem(partyMemberIndexes);
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
ret.push(modifierTypes.TERA_SHARD().generateType([], [ Utils.randSeedItem(types ? types : party[randomIndex].getTypes()) ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct?
}
return ret;
}
type SignatureSpecies = { type SignatureSpecies = {
[key in string]: (Species | Species[])[]; [key in string]: (Species | Species[])[];
@ -1747,10 +1818,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.RED]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double") [TrainerType.RED]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PIKACHU ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PIKACHU ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 8; // G-Max Pikachu p.formIndex = 8; // G-Max Pikachu
@ -1774,10 +1842,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion") [TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GYARADOS, Species.KINGDRA ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GYARADOS, Species.KINGDRA ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AERODACTYL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AERODACTYL ]))
@ -1787,16 +1852,15 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CHARIZARD ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CHARIZARD ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TYRANITAR, Species.GARCHOMP, Species.KOMMO_O ])) .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TYRANITAR, Species.GARCHOMP, Species.KOMMO_O ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.type1;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DRAGONITE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DRAGONITE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double") [TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SKARMORY ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SKARMORY ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CRADILY, Species.ARMALDO ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CRADILY, Species.ARMALDO ]))
@ -1814,10 +1878,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.generateName(); p.generateName();
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double") [TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PELIPPER ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PELIPPER ], TrainerSlot.TRAINER, true, p => {
p.abilityIndex = 1; // Drizzle p.abilityIndex = 1; // Drizzle
@ -1840,10 +1901,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion") [TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SPIRITOMB ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SPIRITOMB ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1853,7 +1911,9 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MILOTIC, Species.ROSERADE, Species.HISUI_ARCANINE ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MILOTIC, Species.ROSERADE, Species.HISUI_ARCANINE ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.type1;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TOGEKISS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TOGEKISS ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.setBoss(true, 2); p.setBoss(true, 2);
@ -1864,10 +1924,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder") [TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BOUFFALANT, Species.BRAVIARY ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BOUFFALANT, Species.BRAVIARY ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_LILLIGANT, Species.HISUI_ZOROARK, Species.BASCULEGION ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_LILLIGANT, Species.HISUI_ZOROARK, Species.BASCULEGION ], TrainerSlot.TRAINER, true, p => {
@ -1882,16 +1939,15 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.CHANDELURE, Species.KROOKODILE, Species.REUNICLUS, Species.CONKELDURR ])) .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.CHANDELURE, Species.KROOKODILE, Species.REUNICLUS, Species.CONKELDURR ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.speciesId === Species.KROOKODILE ? Type.DARK : p.species.type1;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.VOLCARONA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.VOLCARONA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const pokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ pokemon.species.speciesId === Species.KROOKODILE ? pokemon.species.type2 : pokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(pokemon) as PersistentModifier ];
}),
[TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double") [TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.DRUDDIGON ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.DRUDDIGON ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ARCHEOPS ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ARCHEOPS ]))
@ -1899,7 +1955,9 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.SALAMENCE, Species.HYDREIGON, Species.ARCHALUDON ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.SALAMENCE, Species.HYDREIGON, Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => {
p.teraType = Type.DRAGON;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.LAPRAS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.LAPRAS ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // G-Max Lapras p.formIndex = 1; // G-Max Lapras
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1911,10 +1969,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ Type.DRAGON ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_kalos_champion") [TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_kalos_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.HAWLUCHA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.HAWLUCHA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1927,6 +1982,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TYRANTRUM, Species.AURORUS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TYRANTRUM, Species.AURORUS ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.abilityIndex = 2; // Rock Head Tyrantrum, Snow Warning Aurorus p.abilityIndex = 2; // Rock Head Tyrantrum, Snow Warning Aurorus
p.teraType = p.species.speciesId === Species.TYRANTRUM ? Type.DRAGON : Type.ICE;
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GOODRA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GOODRA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1938,10 +1994,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.KUKUI]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kukui") [TrainerType.KUKUI]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kukui")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1965,11 +2018,9 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.HISUI_DECIDUEYE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.HISUI_DECIDUEYE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.teraType = p.species.speciesId === Species.INCINEROAR ? Type.DARK : Type.FIGHTING;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(5),
const teraPokemon = party[5];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.HAU]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_alola_champion") [TrainerType.HAU]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_alola_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALOLA_RAICHU ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALOLA_RAICHU ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1982,6 +2033,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TAPU_LELE, Species.TAPU_BULU ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TAPU_LELE, Species.TAPU_BULU ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
p.teraType = p.species.type1;
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ZYGARDE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ZYGARDE ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // Zygarde 10% forme, Aura Break p.formIndex = 1; // Zygarde 10% forme, Aura Break
@ -1993,10 +2045,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2); p.setBoss(true, 2);
p.gender = p.species.speciesId === Species.PRIMARINA ? Gender.FEMALE : Gender.MALE; p.gender = p.species.speciesId === Species.PRIMARINA ? Gender.FEMALE : Gender.MALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.LEON]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_galar_champion") [TrainerType.LEON]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_galar_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AEGISLASH ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AEGISLASH ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2017,10 +2066,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(3),
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.MUSTARD]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_mustard") [TrainerType.MUSTARD]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_mustard")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2033,6 +2079,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.GALAR_SLOWBRO, Species.GALAR_SLOWKING ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.GALAR_SLOWBRO, Species.GALAR_SLOWKING ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
p.teraType = Type.PSYCHIC;
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALAR_DARMANITAN ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALAR_DARMANITAN ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2050,10 +2097,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(2),
const teraPokemon = party[2];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_geeta") [TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_geeta")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GLIMMORA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GLIMMORA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2070,11 +2114,9 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KINGAMBIT ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KINGAMBIT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.abilityIndex = 1; // Supreme Overlord p.abilityIndex = 1; // Supreme Overlord
p.teraType = Type.FLYING;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(5),
const teraPokemon = party[5];
return [ modifierTypes.TERA_SHARD().generateType([], [ Type.FLYING ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_nemona") [TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_nemona")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 0; // Midday form p.formIndex = 0; // Midday form
@ -2086,16 +2128,15 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GHOLDENGO ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GHOLDENGO ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ARMAROUGE, Species.CERULEDGE ])) .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ARMAROUGE, Species.CERULEDGE ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.speciesId === Species.ARMAROUGE ? Type.PSYCHIC : Type.GHOST;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
[TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kieran") [TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kieran")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.POLIWRATH, Species.POLITOED ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.POLIWRATH, Species.POLITOED ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2117,7 +2158,7 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.OGERPON ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.OGERPON ], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(4, 4); // Random Ogerpon Tera Mask p.formIndex = Utils.randSeedInt(4); // Random Ogerpon Tera Mask
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) { // Check if Ivy Cudgel is in the moveset, if not, replace the first move with Ivy Cudgel. if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) { // Check if Ivy Cudgel is in the moveset, if not, replace the first move with Ivy Cudgel.
@ -2129,17 +2170,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const starter = party[4];
let teraShardType: Type;
const pokemonType2 = starter.species.forms[starter.formIndex].type2;
if (starter.formIndex === 4 || Utils.isNullOrUndefined(pokemonType2)) {
teraShardType = starter.species.type1;
} else {
teraShardType = pokemonType2;
}
return [ modifierTypes.TERA_SHARD().generateType([], [ teraShardType ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ];
}),
[TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL) [TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL)
.setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE) .setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE)
@ -2161,20 +2192,22 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540), .setSpeciesFilter(species => species.baseTotal >= 540),
[TrainerType.RIVAL_4]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(1.75).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_2").setMixedBattleBgm("battle_rival_2").setPartyTemplates(trainerPartyTemplates.RIVAL_4) [TrainerType.RIVAL_4]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(1.75).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_2").setMixedBattleBgm("battle_rival_2").setPartyTemplates(trainerPartyTemplates.RIVAL_4)
.setModifierRewardFuncs(() => modifierTypes.TERA_ORB)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true,
(p => p.abilityIndex = 0))) (p => {
p.abilityIndex = 0;
p.teraType = p.species.type1;
})))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540) .setSpeciesFilter(species => species.baseTotal >= 540)
.setGenModifiersFunc(party => { .setInstantTera(0),
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType([], [ starter.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ]; // TODO: is the bang correct?
}),
[TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5) [TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true,
p => { p => {
p.setBoss(true, 2); p.setBoss(true, 2);
p.abilityIndex = 0; p.abilityIndex = 0;
p.teraType = p.species.type1;
})) }))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
@ -2185,15 +2218,13 @@ export const trainerConfigs: TrainerConfigs = {
p.shiny = true; p.shiny = true;
p.variant = 1; p.variant = 1;
})) }))
.setGenModifiersFunc(party => { .setInstantTera(0),
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType([], [ starter.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ]; //TODO: is the bang correct?
}),
[TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6) [TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true,
p => { p => {
p.setBoss(true, 3); p.setBoss(true, 3);
p.abilityIndex = 0; p.abilityIndex = 0;
p.teraType = p.species.type1;
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
})) }))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true,
@ -2212,10 +2243,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1; // Mega Rayquaza p.formIndex = 1; // Mega Rayquaza
p.generateName(); p.generateName();
})) }))
.setGenModifiersFunc(party => { .setInstantTera(0),
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType([], [ starter.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ]; // TODO: is the bang correct?
}),
[TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN ], TrainerSlot.TRAINER, true, p => {
@ -2713,10 +2741,7 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
p.generateName(); p.generateName();
})) }))
.setGenModifiersFunc(party => { .setInstantTera(4),
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ]; //TODO: is the bang correct?
}),
[TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") [TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2); p.setBoss(true, 2);
@ -2749,10 +2774,12 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setGenModifiersFunc(party => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.ZAMAZENTA ], TrainerSlot.TRAINER, true, p => {
const teraPokemon = party[0]; p.setBoss(true, 2);
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ]; //TODO: is the bang correct? p.generateAndPopulateMoveset();
}), p.pokeball = PokeballType.MASTER_BALL;
}))
.setInstantTera(0),
[TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true) [TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 3); p.setBoss(true, 3);

View File

@ -44,6 +44,7 @@ export class Arena {
public bgm: string; public bgm: string;
public ignoreAbilities: boolean; public ignoreAbilities: boolean;
public ignoringEffectSource: BattlerIndex | null; public ignoringEffectSource: BattlerIndex | null;
public playerTerasUsed: number;
/** /**
* Saves the number of times a party pokemon faints during a arena encounter. * Saves the number of times a party pokemon faints during a arena encounter.
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave). * {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
@ -63,6 +64,7 @@ export class Arena {
this.bgm = bgm; this.bgm = bgm;
this.trainerPool = biomeTrainerPools[biome]; this.trainerPool = biomeTrainerPools[biome];
this.updatePoolsForTimeOfDay(); this.updatePoolsForTimeOfDay();
this.playerTerasUsed = 0;
this.playerFaints = playerFaints; this.playerFaints = playerFaints;
} }

View File

@ -27,6 +27,9 @@ export default class PokemonSpriteSparkleHandler {
if (!s.visible || (s.parentContainer instanceof Pokemon && !s.parentContainer.parentContainer)) { if (!s.visible || (s.parentContainer instanceof Pokemon && !s.parentContainer.parentContainer)) {
continue; continue;
} }
if (!(s.parentContainer instanceof Pokemon) || !(s.parentContainer as Pokemon).isTerastallized) {
continue;
}
const pokemon = s.parentContainer instanceof Pokemon ? s.parentContainer as Pokemon : null; const pokemon = s.parentContainer instanceof Pokemon ? s.parentContainer as Pokemon : null;
const parent = (pokemon || s).parentContainer; const parent = (pokemon || s).parentContainer;
const texture = s.texture; const texture = s.texture;

View File

@ -52,7 +52,7 @@ import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { getLevelTotalExp } from "#app/data/exp"; import { getLevelTotalExp } from "#app/data/exp";
import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier, PokemonMultiHitModifier } from "#app/modifier/modifier"; import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier, PokemonMultiHitModifier } from "#app/modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
@ -79,7 +79,7 @@ import { DexAttr } from "#app/system/game-data";
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
import { getNatureStatMultiplier } from "#app/data/nature"; import { getNatureStatMultiplier } from "#app/data/nature";
import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import type { SpeciesFormChange } from "#app/data/pokemon-forms";
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms"; import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms";
import { TerrainType } from "#app/data/terrain"; import { TerrainType } from "#app/data/terrain";
import type { TrainerSlot } from "#app/data/trainer-config"; import type { TrainerSlot } from "#app/data/trainer-config";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
@ -163,6 +163,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public pokerus: boolean; public pokerus: boolean;
public switchOutStatus: boolean; public switchOutStatus: boolean;
public evoCounter: number; public evoCounter: number;
public teraType: Type;
public isTerastallized: boolean;
public stellarTypesBoosted: Type[];
public fusionSpecies: PokemonSpecies | null; public fusionSpecies: PokemonSpecies | null;
public fusionFormIndex: number; public fusionFormIndex: number;
@ -172,6 +175,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public fusionGender: Gender; public fusionGender: Gender;
public fusionLuck: number; public fusionLuck: number;
public fusionCustomPokemonData: CustomPokemonData | null; public fusionCustomPokemonData: CustomPokemonData | null;
public fusionTeraType: Type;
private summonDataPrimer: PokemonSummonData | null; private summonDataPrimer: PokemonSummonData | null;
@ -200,7 +204,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
throw `Cannot create a player Pokemon for species '${species.getName(formIndex)}'`; throw `Cannot create a player Pokemon for species '${species.getName(formIndex)}'`;
} }
const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE); const hiddenAbilityChance = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
if (!this.hasTrainer()) { if (!this.hasTrainer()) {
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
} }
@ -269,8 +273,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionGender = dataSource.fusionGender; this.fusionGender = dataSource.fusionGender;
this.fusionLuck = dataSource.fusionLuck; this.fusionLuck = dataSource.fusionLuck;
this.fusionCustomPokemonData = dataSource.fusionCustomPokemonData; this.fusionCustomPokemonData = dataSource.fusionCustomPokemonData;
this.fusionTeraType = dataSource.fusionTeraType;
this.usedTMs = dataSource.usedTMs ?? []; this.usedTMs = dataSource.usedTMs ?? [];
this.customPokemonData = new CustomPokemonData(dataSource.customPokemonData); this.customPokemonData = new CustomPokemonData(dataSource.customPokemonData);
this.teraType = dataSource.teraType;
this.isTerastallized = dataSource.isTerastallized;
this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? [];
} else { } else {
this.id = Utils.randSeedInt(4294967296); this.id = Utils.randSeedInt(4294967296);
this.ivs = ivs || Utils.getIvsFromId(this.id); this.ivs = ivs || Utils.getIvsFromId(this.id);
@ -319,6 +327,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0); this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0);
this.fusionLuck = this.luck; this.fusionLuck = this.luck;
this.teraType = Utils.randSeedItem(this.getTypes(false, false, true));
this.isTerastallized = false;
this.stellarTypesBoosted = [];
} }
this.generateName(); this.generateName();
@ -355,7 +367,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const getSprite = (hasShadow?: boolean) => { const getSprite = (hasShadow?: boolean) => {
const ret = globalScene.addPokemonSprite(this, 0, 0, `pkmn__${this.isPlayer() ? "back__" : ""}sub`, undefined, true); const ret = globalScene.addPokemonSprite(this, 0, 0, `pkmn__${this.isPlayer() ? "back__" : ""}sub`, undefined, true);
ret.setOrigin(0.5, 1); ret.setOrigin(0.5, 1);
ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, teraColor: getTypeRgb(this.getTeraType()) }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, teraColor: getTypeRgb(this.getTeraType()), isTerastallized: this.isTerastallized });
return ret; return ret;
}; };
@ -723,7 +735,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
updateSpritePipelineData(): void { updateSpritePipelineData(): void {
[ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType())); [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => {
s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType());
s.pipelineData["isTerastallized"] = this.isTerastallized;
});
this.updateInfo(true); this.updateInfo(true);
} }
@ -945,7 +960,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns the final critical-hit stage value * @returns the final critical-hit stage value
*/ */
getCritStage(source: Pokemon, move: Move): number { getCritStage(source: Pokemon, move: Move): number {
const critStage = new Utils.IntegerHolder(0); const critStage = new Utils.NumberHolder(0);
applyMoveAttrs(HighCritAttr, source, this, move, critStage); applyMoveAttrs(HighCritAttr, source, this, move, critStage);
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage); globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage); globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
@ -1059,7 +1074,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const baseStats = this.calculateBaseStats(); const baseStats = this.calculateBaseStats();
// Using base stats, calculate and store stats one by one // Using base stats, calculate and store stats one by one
for (const s of PERMANENT_STATS) { for (const s of PERMANENT_STATS) {
const statHolder = new Utils.IntegerHolder(Math.floor(((2 * baseStats[s] + this.ivs[s]) * this.level) * 0.01)); const statHolder = new Utils.NumberHolder(Math.floor(((2 * baseStats[s] + this.ivs[s]) * this.level) * 0.01));
if (s === Stat.HP) { if (s === Stat.HP) {
statHolder.value = statHolder.value + this.level + 10; statHolder.value = statHolder.value + this.level + 10;
globalScene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); globalScene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder);
@ -1208,6 +1223,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.fusionSpecies?.speciesId === species; return this.fusionSpecies?.speciesId === species;
} }
/**
* Checks if the {@linkcode Pokemon} has is the specified {@linkcode Species} or is fused with it.
* @param species the pokemon {@linkcode Species} to check
* @returns `true` if the pokemon is the species or is fused with it, `false` otherwise
*/
hasSpecies(species: Species): boolean {
return this.species.speciesId === species || this.fusionSpecies?.speciesId === species;
}
abstract isBoss(): boolean; abstract isBoss(): boolean;
getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] { getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] {
@ -1284,9 +1308,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride: boolean = false): Type[] { public getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride: boolean = false): Type[] {
const types: Type[] = []; const types: Type[] = [];
if (includeTeraType) { if (includeTeraType && this.isTerastallized) {
const teraType = this.getTeraType(); const teraType = this.getTeraType();
if (teraType !== Type.UNKNOWN) { if (this.isTerastallized && !(forDefend && teraType === Type.STELLAR)) { // Stellar tera uses its original types defensively
types.push(teraType); types.push(teraType);
if (forDefend) { if (forDefend) {
return types; return types;
@ -1322,6 +1346,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} else if (fusionType1 !== types[0]) { } else if (fusionType1 !== types[0]) {
secondType = fusionType1; secondType = fusionType1;
} }
if (secondType === Type.UNKNOWN && Utils.isNullOrUndefined(fusionType2)) { // If second pokemon was monotype and shared its primary type
secondType = (customTypes && this.customPokemonData.types.length > 1 && this.customPokemonData.types[1] !== Type.UNKNOWN)
? this.customPokemonData.types[1] : (speciesForm.type2 ?? Type.UNKNOWN);
}
} else { } else {
// If not a fusion, just get the second type from the species, checking for permanent changes from ME // If not a fusion, just get the second type from the species, checking for permanent changes from ME
secondType = (customTypes && this.customPokemonData.types.length > 1 && this.customPokemonData.types[1] !== Type.UNKNOWN) secondType = (customTypes && this.customPokemonData.types.length > 1 && this.customPokemonData.types[1] !== Type.UNKNOWN)
@ -1596,23 +1626,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** /**
* @returns the pokemon's current tera {@linkcode Type}, or `Type.UNKNOWN` if the pokemon is not terastallized * @returns the pokemon's current tera {@linkcode Type}
*/ */
public getTeraType(): Type { getTeraType(): Type {
// I don't think this should be possible anymore, please report if you encounter this. --NightKev if (this.hasSpecies(Species.TERAPAGOS)) {
if (globalScene === undefined) { return Type.STELLAR;
console.warn("Pokemon.getTeraType(): Global scene is not defined!"); } else if (this.hasSpecies(Species.OGERPON)) {
return Type.UNKNOWN; const ogerponForm = this.species.speciesId === Species.OGERPON ? this.formIndex : this.fusionFormIndex;
switch (ogerponForm) {
case 0:
case 4:
return Type.GRASS;
case 1:
case 5:
return Type.WATER;
case 2:
case 6:
return Type.FIRE;
case 3:
case 7:
return Type.ROCK;
}
} else if (this.hasSpecies(Species.SHEDINJA)) {
return Type.BUG;
} }
const teraModifier = globalScene.findModifier(m => return this.teraType;
m instanceof TerastallizeModifier
&& m.pokemonId === this.id
&& m.getBattlesLeft() > 0, this.isPlayer()) as TerastallizeModifier;
return teraModifier?.teraType ?? Type.UNKNOWN;
}
public isTerastallized(): boolean {
return this.getTeraType() !== Type.UNKNOWN;
} }
public isGrounded(): boolean { public isGrounded(): boolean {
@ -1754,7 +1792,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/ */
getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true, move?: Move): TypeDamageMultiplier { getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true, move?: Move): TypeDamageMultiplier {
if (moveType === Type.STELLAR) { if (moveType === Type.STELLAR) {
return this.isTerastallized() ? 2 : 1; return this.isTerastallized ? 2 : 1;
} }
const types = this.getTypes(true, true); const types = this.getTypes(true, true);
const arena = globalScene.arena; const arena = globalScene.arena;
@ -2541,7 +2579,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @return the stat stage multiplier to be used for effective stat calculation * @return the stat stage multiplier to be used for effective stat calculation
*/ */
getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true, ignoreHeldItems: boolean = false): number { getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true, ignoreHeldItems: boolean = false): number {
const statStage = new Utils.IntegerHolder(this.getStatStage(stat)); const statStage = new Utils.NumberHolder(this.getStatStage(stat));
const ignoreStatStage = new Utils.BooleanHolder(false); const ignoreStatStage = new Utils.BooleanHolder(false);
if (opponent) { if (opponent) {
@ -2591,8 +2629,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return 1; return 1;
} }
const userAccStage = new Utils.IntegerHolder(this.getStatStage(Stat.ACC)); const userAccStage = new Utils.NumberHolder(this.getStatStage(Stat.ACC));
const targetEvaStage = new Utils.IntegerHolder(target.getStatStage(Stat.EVA)); const targetEvaStage = new Utils.NumberHolder(target.getStatStage(Stat.EVA));
const ignoreAccStatStage = new Utils.BooleanHolder(false); const ignoreAccStatStage = new Utils.BooleanHolder(false);
const ignoreEvaStatStage = new Utils.BooleanHolder(false); const ignoreEvaStatStage = new Utils.BooleanHolder(false);
@ -2771,7 +2809,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** Doubles damage if this Pokemon's last move was Glaive Rush */ /** Doubles damage if this Pokemon's last move was Glaive Rush */
const glaiveRushMultiplier = new Utils.IntegerHolder(1); const glaiveRushMultiplier = new Utils.NumberHolder(1);
if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) { if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) {
glaiveRushMultiplier.value = 2; glaiveRushMultiplier.value = 2;
} }
@ -2791,11 +2829,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const matchesSourceType = sourceTypes.includes(moveType); const matchesSourceType = sourceTypes.includes(moveType);
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
const stabMultiplier = new Utils.NumberHolder(1); const stabMultiplier = new Utils.NumberHolder(1);
if (matchesSourceType) { if (matchesSourceType && moveType !== Type.STELLAR) {
stabMultiplier.value += 0.5;
}
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) {
stabMultiplier.value += 0.5; stabMultiplier.value += 0.5;
} }
@ -2803,6 +2837,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier); applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
} }
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
if (source.isTerastallized && sourceTeraType === moveType && moveType !== Type.STELLAR) {
stabMultiplier.value += 0.5;
}
if (source.isTerastallized && source.teraType === Type.STELLAR && (!source.stellarTypesBoosted.includes(moveType) || source.hasSpecies(Species.TERAPAGOS))) {
if (matchesSourceType) {
stabMultiplier.value += 0.5;
} else {
stabMultiplier.value += 0.2;
}
}
stabMultiplier.value = Math.min(stabMultiplier.value, 2.25); stabMultiplier.value = Math.min(stabMultiplier.value, 2.25);
/** Halves damage if the attacker is using a physical attack while burned */ /** Halves damage if the attacker is using a physical attack while burned */
@ -3843,6 +3891,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
resetTera(): void {
const wasTerastallized = this.isTerastallized;
this.isTerastallized = false;
this.stellarTypesBoosted = [];
if (wasTerastallized) {
this.updateSpritePipelineData();
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeLapseTeraTrigger);
}
}
resetTurnData(): void { resetTurnData(): void {
this.turnData = new PokemonTurnData(); this.turnData = new PokemonTurnData();
} }
@ -4048,6 +4106,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
if (fusionPixelColors.length === 0) { // ERROR HANDLING IS NOT OPTIONAL BUDDY
console.log("Failed to create fusion palette");
return;
}
let paletteColors: Map<number, number>; let paletteColors: Map<number, number>;
let fusionPaletteColors: Map<number, number>; let fusionPaletteColors: Map<number, number>;
@ -4061,8 +4124,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
Math.random = originalRandom; Math.random = originalRandom;
paletteColors = paletteColors!; // tell TS compiler that paletteColors is defined! paletteColors = paletteColors!; // erroneously tell TS compiler that paletteColors is defined!
fusionPaletteColors = fusionPaletteColors!; // TS compiler that fusionPaletteColors is defined! fusionPaletteColors = fusionPaletteColors!; // mischievously misinform TS compiler that fusionPaletteColors is defined!
const [ palette, fusionPalette ] = [ paletteColors, fusionPaletteColors ] const [ palette, fusionPalette ] = [ paletteColors, fusionPaletteColors ]
.map(paletteColors => { .map(paletteColors => {
let keys = Array.from(paletteColors.keys()).sort((a: number, b: number) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1); let keys = Array.from(paletteColors.keys()).sort((a: number, b: number) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1);
@ -4601,6 +4664,7 @@ export class PlayerPokemon extends Pokemon {
newPokemon.fusionVariant = this.fusionVariant; newPokemon.fusionVariant = this.fusionVariant;
newPokemon.fusionGender = this.fusionGender; newPokemon.fusionGender = this.fusionGender;
newPokemon.fusionLuck = this.fusionLuck; newPokemon.fusionLuck = this.fusionLuck;
newPokemon.fusionTeraType = this.teraType;
newPokemon.usedTMs = this.usedTMs; newPokemon.usedTMs = this.usedTMs;
globalScene.getPlayerParty().push(newPokemon); globalScene.getPlayerParty().push(newPokemon);
@ -4752,6 +4816,7 @@ export class EnemyPokemon extends Pokemon {
public aiType: AiType; public aiType: AiType;
public bossSegments: number; public bossSegments: number;
public bossSegmentIndex: number; public bossSegmentIndex: number;
public initialTeamIndex: number;
/** To indicate if the instance was populated with a dataSource -> e.g. loaded & populated from session data */ /** To indicate if the instance was populated with a dataSource -> e.g. loaded & populated from session data */
public readonly isPopulatedFromDataSource: boolean; public readonly isPopulatedFromDataSource: boolean;
@ -4761,6 +4826,7 @@ export class EnemyPokemon extends Pokemon {
undefined, dataSource ? dataSource.nature : undefined, dataSource); undefined, dataSource ? dataSource.nature : undefined, dataSource);
this.trainerSlot = trainerSlot; this.trainerSlot = trainerSlot;
this.initialTeamIndex = globalScene.currentBattle?.enemyParty.length ?? 0;
this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource
if (boss) { if (boss) {
this.setBoss(boss, dataSource?.bossSegments); this.setBoss(boss, dataSource?.bossSegments);

View File

@ -11,7 +11,8 @@ import {
TrainerSlot, TrainerSlot,
trainerConfigs, trainerConfigs,
trainerPartyTemplates, trainerPartyTemplates,
signatureSpecies signatureSpecies,
TeraAIMode
} from "#app/data/trainer-config"; } from "#app/data/trainer-config";
import type { EnemyPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
@ -36,6 +37,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
public partyTemplateIndex: number; public partyTemplateIndex: number;
public name: string; public name: string;
public partnerName: string; public partnerName: string;
public originalIndexes: { [key: number]: number } = {};
constructor(trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: number, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) { constructor(trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: number, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) {
super(globalScene, -72, 80); super(globalScene, -72, 80);
@ -546,6 +548,13 @@ export default class Trainer extends Phaser.GameObjects.Container {
return []; return [];
} }
genAI(party: EnemyPokemon[]) {
if (this.config.genAIFuncs) {
this.config.genAIFuncs.forEach(f => f(party));
}
console.log("Generated AI funcs");
}
loadAssets(): Promise<void> { loadAssets(): Promise<void> {
return this.config.loadAssets(this.variant); return this.config.loadAssets(this.variant);
} }
@ -667,4 +676,13 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
}); });
} }
shouldTera(pokemon: EnemyPokemon): boolean {
if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA) {
if (!pokemon.isTerastallized && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)) {
return true;
}
}
return false;
}
} }

View File

@ -103,6 +103,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("icon_tera", "ui"); this.loadImage("icon_tera", "ui");
this.loadImage("type_tera", "ui"); this.loadImage("type_tera", "ui");
this.loadAtlas("type_bgs", "ui"); this.loadAtlas("type_bgs", "ui");
this.loadAtlas("button_tera", "ui");
this.loadImage("mystery_egg", "ui"); this.loadImage("mystery_egg", "ui");
this.loadImage("normal_memory", "ui"); this.loadImage("normal_memory", "ui");

View File

@ -11,7 +11,7 @@ import { Type } from "#enums/type";
import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { AddPokeballModifier, AddVoucherModifier, AttackTypeBoosterModifier, BaseStatModifier, BerryModifier, BoostBugSpawnModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, EvolutionStatBoosterModifier, EvoTrackerModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, HealingBoosterModifier, HealShopCostModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, IvScannerModifier, LevelIncrementBoosterModifier, LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, PokemonInstantReviveModifier, PokemonLevelIncrementModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PokemonNatureChangeModifier, PokemonNatureWeightModifier, PokemonPpRestoreModifier, PokemonPpUpModifier, PokemonStatusHealModifier, PreserveBerryModifier, RememberMoveModifier, ResetNegativeStatStageModifier, ShinyRateBoosterModifier, SpeciesCritBoosterModifier, SpeciesStatBoosterModifier, SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerastallizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, type EnemyPersistentModifier, type Modifier, type PersistentModifier, TempExtraModifierModifier, CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier"; import { AddPokeballModifier, AddVoucherModifier, AttackTypeBoosterModifier, BaseStatModifier, BerryModifier, BoostBugSpawnModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, EvolutionStatBoosterModifier, EvoTrackerModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, HealingBoosterModifier, HealShopCostModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, IvScannerModifier, LevelIncrementBoosterModifier, LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, PokemonInstantReviveModifier, PokemonLevelIncrementModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PokemonNatureChangeModifier, PokemonNatureWeightModifier, PokemonPpRestoreModifier, PokemonPpUpModifier, PokemonStatusHealModifier, PreserveBerryModifier, RememberMoveModifier, ResetNegativeStatStageModifier, ShinyRateBoosterModifier, SpeciesCritBoosterModifier, SpeciesStatBoosterModifier, SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerrastalizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, type EnemyPersistentModifier, type Modifier, type PersistentModifier, TempExtraModifierModifier, CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { Unlockables } from "#app/system/unlockables"; import { Unlockables } from "#app/system/unlockables";
@ -19,7 +19,7 @@ import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system
import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
import PartyUiHandler from "#app/ui/party-ui-handler"; import PartyUiHandler from "#app/ui/party-ui-handler";
import { getModifierTierTextTint } from "#app/ui/text"; import { getModifierTierTextTint } from "#app/ui/text";
import { formatMoney, getEnumKeys, getEnumValues, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils"; import { formatMoney, getEnumKeys, getEnumValues, isNullOrUndefined, NumberHolder, padInt, randSeedInt } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
@ -275,6 +275,36 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
} }
} }
export class TerastallizeModifierType extends PokemonModifierType {
private teraType: Type;
constructor(teraType: Type) {
super("", `${Type[teraType].toLowerCase()}_tera_shard`, (type, args) => new TerrastalizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType),
(pokemon: PlayerPokemon) => {
if ([ pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId ].filter(s => s === Species.TERAPAGOS || s === Species.OGERPON || s === Species.SHEDINJA).length > 0) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"tera_shard");
this.teraType = teraType;
}
get name(): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
}
getDescription(): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
}
getPregenArgs(): any[] {
return [ this.teraType ];
}
}
export class PokemonHpRestoreModifierType extends PokemonModifierType { export class PokemonHpRestoreModifierType extends PokemonModifierType {
protected restorePoints: number; protected restorePoints: number;
protected restorePercent: number; protected restorePercent: number;
@ -1156,11 +1186,11 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
if (p.species.speciesId === Species.NECROZMA) { if (p.species.speciesId === Species.NECROZMA) {
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break... // technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
let foundULTRA_Z = false, let foundULTRA_Z = false,
foundN_LUNA = false, foundN_LUNA = false,
foundN_SOLAR = false; foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, _i) => { formChangeItemTriggers.forEach((fc, _i) => {
console.log("Checking ", fc.item);
switch (fc.item) { switch (fc.item) {
case FormChangeItem.ULTRANECROZIUM_Z: case FormChangeItem.ULTRANECROZIUM_Z:
foundULTRA_Z = true; foundULTRA_Z = true;
@ -1176,6 +1206,8 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) { if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) {
// all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition. // all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition.
formChangeItemTriggers = formChangeItemTriggers.filter(fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z); formChangeItemTriggers = formChangeItemTriggers.filter(fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z);
} else {
console.log("DID NOT FIND ");
} }
} }
return formChangeItemTriggers; return formChangeItemTriggers;
@ -1192,28 +1224,6 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
} }
} }
export class TerastallizeModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
private teraType: Type;
constructor(teraType: Type) {
super("", `${Type[teraType].toLowerCase()}_tera_shard`, (type, args) => new TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), "tera_shard");
this.teraType = teraType;
}
get name(): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
}
getDescription(): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
}
getPregenArgs(): any[] {
return [ this.teraType ];
}
}
export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemModifierType { export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemModifierType {
private chancePercent: number; private chancePercent: number;
@ -1469,14 +1479,21 @@ export const modifierTypes = {
if (!globalScene.getModifiers(TerastallizeAccessModifier).length) { if (!globalScene.getModifiers(TerastallizeAccessModifier).length) {
return null; return null;
} }
let type: Type; const teraTypes: Type[] = [];
if (!randSeedInt(3)) { party.forEach(p => {
const partyMemberTypes = party.map(p => p.getTypes(false, false, true)).flat(); if (!(p.hasSpecies(Species.TERAPAGOS) || p.hasSpecies(Species.OGERPON) || p.hasSpecies(Species.SHEDINJA))) {
type = randSeedItem(partyMemberTypes); teraTypes.push(p.teraType);
} else { }
type = randSeedInt(64) ? randSeedInt(18) as Type : Type.STELLAR; });
let excludedType = Type.UNKNOWN;
if (teraTypes.length > 0 && teraTypes.filter(t => t === teraTypes[0]).length === teraTypes.length) {
excludedType = teraTypes[0];
} }
return new TerastallizeModifierType(type); let shardType = randSeedInt(64) ? randSeedInt(18) as Type : Type.STELLAR;
while (shardType === excludedType) {
shardType = randSeedInt(64) ? randSeedInt(18) as Type : Type.STELLAR;
}
return new TerastallizeModifierType(shardType);
}), }),
BERRY: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => { BERRY: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
@ -1719,7 +1736,7 @@ const modifierPool: ModifierPool = {
return Math.min(Math.ceil(highestPartyLevel / 20), 4); return Math.min(Math.ceil(highestPartyLevel / 20), 4);
}, 4), }, 4),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.TERA_SHARD, 1), new WeightedModifierType(modifierTypes.TERA_SHARD, (party: Pokemon[]) => party.filter(p => !(p.hasSpecies(Species.TERAPAGOS) || p.hasSpecies(Species.OGERPON) || p.hasSpecies(Species.SHEDINJA))).length > 0 ? 1 : 0),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => {
if (party.filter(p => !p.fusionSpecies).length > 1) { if (party.filter(p => !p.fusionSpecies).length > 1) {
if (globalScene.gameMode.isSplicedOnly) { if (globalScene.gameMode.isSplicedOnly) {
@ -1842,7 +1859,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)),
new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)),
new WeightedModifierType(modifierTypes.TERA_ORB, () => Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4), 4), new WeightedModifierType(modifierTypes.TERA_ORB, () => !globalScene.gameMode.isClassic ? Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4) : 0, 4),
new WeightedModifierType(modifierTypes.QUICK_CLAW, 3), new WeightedModifierType(modifierTypes.QUICK_CLAW, 3),
new WeightedModifierType(modifierTypes.WIDE_LENS, 7), new WeightedModifierType(modifierTypes.WIDE_LENS, 7),
].map(m => { ].map(m => {

View File

@ -3,16 +3,16 @@ import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
import { getLevelTotalExp } from "#app/data/exp"; import { getLevelTotalExp } from "#app/data/exp";
import { allMoves } from "#app/data/move"; import { allMoves } from "#app/data/move";
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
import { type FormChangeItem, SpeciesFormChangeItemTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms"; import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
import { getStatusEffectHealText } from "#app/data/status-effect"; import { getStatusEffectHealText } from "#app/data/status-effect";
import Pokemon, { type PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { EvolutionPhase } from "#app/phases/evolution-phase"; import { EvolutionPhase } from "#app/phases/evolution-phase";
import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase"; import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase";
import { LevelUpPhase } from "#app/phases/level-up-phase"; import { LevelUpPhase } from "#app/phases/level-up-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { achvs } from "#app/system/achv";
import type { VoucherType } from "#app/system/voucher"; import type { VoucherType } from "#app/system/voucher";
import { Command } from "#app/ui/command-ui-handler"; import { Command } from "#app/ui/command-ui-handler";
import { addTextObject, TextStyle } from "#app/ui/text"; import { addTextObject, TextStyle } from "#app/ui/text";
@ -25,7 +25,7 @@ import type { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { type PermanentStat, type TempBattleStat, BATTLE_STATS, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; import { type PermanentStat, type TempBattleStat, BATTLE_STATS, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import i18next from "i18next"; import i18next from "i18next";
import { type DoubleBattleChanceBoosterModifierType, type EvolutionItemModifierType, type FormChangeItemModifierType, type ModifierOverride, type ModifierType, type PokemonBaseStatTotalModifierType, type PokemonExpBoosterModifierType, type PokemonFriendshipBoosterModifierType, type PokemonMoveAccuracyBoosterModifierType, type PokemonMultiHitModifierType, type TerastallizeModifierType, type TmModifierType, getModifierType, ModifierPoolType, ModifierTypeGenerator, modifierTypes, PokemonHeldItemModifierType } from "./modifier-type"; import { type DoubleBattleChanceBoosterModifierType, type EvolutionItemModifierType, type FormChangeItemModifierType, type ModifierOverride, type ModifierType, type PokemonBaseStatTotalModifierType, type PokemonExpBoosterModifierType, type PokemonFriendshipBoosterModifierType, type PokemonMoveAccuracyBoosterModifierType, type PokemonMultiHitModifierType, type TerastallizeModifierType, type TmModifierType, getModifierType, ModifierPoolType, ModifierTypeGenerator, modifierTypes, PokemonHeldItemModifierType } from "./modifier-type";
import { Color, ShadowColor } from "#enums/color"; import { Color, ShadowColor } from "#enums/color";
@ -786,72 +786,6 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi
} }
} }
export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
public override type: TerastallizeModifierType;
public teraType: Type;
public isTransferable: boolean = false;
constructor(type: TerastallizeModifierType, pokemonId: number, teraType: Type, battlesLeft?: number, stackCount?: number) {
super(type, pokemonId, battlesLeft || 10, stackCount);
this.teraType = teraType;
}
matchType(modifier: Modifier): boolean {
if (modifier instanceof TerastallizeModifier && modifier.teraType === this.teraType) {
return true;
}
return false;
}
clone(): TerastallizeModifier {
return new TerastallizeModifier(this.type, this.pokemonId, this.teraType, this.battlesLeft, this.stackCount);
}
getArgs(): any[] {
return [ this.pokemonId, this.teraType, this.battlesLeft ];
}
/**
* Applies the {@linkcode TerastallizeModifier} to the specified {@linkcode Pokemon}.
* @param pokemon the {@linkcode Pokemon} to be terastallized
* @returns always `true`
*/
override apply(pokemon: Pokemon): boolean {
if (pokemon.isPlayer()) {
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeTeraTrigger);
globalScene.validateAchv(achvs.TERASTALLIZE);
if (this.teraType === Type.STELLAR) {
globalScene.validateAchv(achvs.STELLAR_TERASTALLIZE);
}
}
pokemon.updateSpritePipelineData();
return true;
}
/**
* Triggers {@linkcode LapsingPokemonHeldItemModifier.lapse} and if it returns `0` a form change is triggered.
* @param pokemon THe {@linkcode Pokemon} to be terastallized
* @returns the result of {@linkcode LapsingPokemonHeldItemModifier.lapse}
*/
public override lapse(pokemon: Pokemon): boolean {
const ret = super.lapse(pokemon);
if (!ret) {
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeLapseTeraTrigger);
pokemon.updateSpritePipelineData();
}
return ret;
}
getScoreMultiplier(): number {
return 1.25;
}
getMaxHeldItemCount(pokemon: Pokemon): number {
return 1;
}
}
/** /**
* Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that * Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that
* increase the value of a given {@linkcode PermanentStat}. * increase the value of a given {@linkcode PermanentStat}.
@ -2022,6 +1956,36 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier {
} }
} }
export class TerrastalizeModifier extends ConsumablePokemonModifier {
public override type: TerastallizeModifierType;
public teraType: Type;
constructor(type: TerastallizeModifierType, pokemonId: number, teraType: Type) {
super(type, pokemonId);
this.teraType = teraType;
}
/**
* Checks if {@linkcode TerrastalizeModifier} should be applied
* @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if the {@linkcode TerrastalizeModifier} should be applied
*/
override shouldApply(playerPokemon?: PlayerPokemon): boolean {
return super.shouldApply(playerPokemon) && [ playerPokemon?.species.speciesId, playerPokemon?.fusionSpecies?.speciesId ].filter(s => s === Species.TERAPAGOS || s === Species.OGERPON || s === Species.SHEDINJA).length === 0;
}
/**
* Applies {@linkcode TerrastalizeModifier}
* @param pokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if hp was restored
*/
override apply(pokemon: Pokemon): boolean {
pokemon.teraType = this.teraType;
return true;
}
}
export class PokemonHpRestoreModifier extends ConsumablePokemonModifier { export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
private restorePoints: number; private restorePoints: number;
private restorePercent: number; private restorePercent: number;

View File

@ -118,6 +118,7 @@ export class CommandPhase extends FieldPhase {
let success: boolean = false; let success: boolean = false;
switch (command) { switch (command) {
case Command.TERA:
case Command.FIGHT: case Command.FIGHT:
let useStruggle = false; let useStruggle = false;
const turnMove: TurnMove | undefined = (args.length === 2 ? (args[1] as TurnMove) : undefined); const turnMove: TurnMove | undefined = (args.length === 2 ? (args[1] as TurnMove) : undefined);
@ -137,6 +138,7 @@ export class CommandPhase extends FieldPhase {
} }
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
const preTurnCommand: TurnCommand = { command: command, targets: [ this.fieldIndex ], skip: command === Command.FIGHT };
const moveTargets: MoveTargetSet = turnMove === undefined ? getMoveTargets(playerPokemon, moveId) : { targets: turnMove.targets, multiple: turnMove.targets.length > 1 }; const moveTargets: MoveTargetSet = turnMove === undefined ? getMoveTargets(playerPokemon, moveId) : { targets: turnMove.targets, multiple: turnMove.targets.length > 1 };
if (!moveId) { if (!moveId) {
turnCommand.targets = [ this.fieldIndex ]; turnCommand.targets = [ this.fieldIndex ];
@ -152,6 +154,7 @@ export class CommandPhase extends FieldPhase {
} else { } else {
globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
} }
globalScene.currentBattle.preTurnCommands[this.fieldIndex] = preTurnCommand;
globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
success = true; success = true;
} else if (cursor < playerPokemon.getMoveset().length) { } else if (cursor < playerPokemon.getMoveset().length) {

View File

@ -81,6 +81,10 @@ export class EnemyCommandPhase extends FieldPhase {
/** Select a move to use (and a target to use it against, if applicable) */ /** Select a move to use (and a target to use it against, if applicable) */
const nextMove = enemyPokemon.getNextMove(); const nextMove = enemyPokemon.getNextMove();
if (trainer && trainer.shouldTera(enemyPokemon)) {
globalScene.currentBattle.preTurnCommands[this.fieldIndex + BattlerIndex.ENEMY] = { command: Command.TERA };
}
globalScene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = globalScene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] =
{ command: Command.FIGHT, move: nextMove, skip: this.skipTurn }; { command: Command.FIGHT, move: nextMove, skip: this.skipTurn };

View File

@ -116,7 +116,7 @@ export class EvolutionPhase extends Phase {
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()), isTerastallized: this.pokemon.isTerastallized });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey());
sprite.setPipelineData("shiny", this.pokemon.shiny); sprite.setPipelineData("shiny", this.pokemon.shiny);

View File

@ -108,6 +108,8 @@ export class FaintPhase extends PokemonPhase {
globalScene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true); globalScene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true);
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
pokemon.resetTera();
if (pokemon.turnData?.attacksReceived?.length) { if (pokemon.turnData?.attacksReceived?.length) {
const lastAttack = pokemon.turnData.attacksReceived[0]; const lastAttack = pokemon.turnData.attacksReceived[0];
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, globalScene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, globalScene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct?

View File

@ -15,7 +15,7 @@ export class MoneyRewardPhase extends BattlePhase {
} }
start() { start() {
const moneyAmount = new Utils.IntegerHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); const moneyAmount = new Utils.NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);

View File

@ -450,6 +450,12 @@ export class MoveEffectPhase extends PokemonPhase {
target.lapseTag(BattlerTagType.SUBSTITUTE); target.lapseTag(BattlerTagType.SUBSTITUTE);
} }
}); });
const moveType = user.getMoveType(move, true);
if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
user.stellarTypesBoosted.push(moveType);
}
this.end(); this.end();
}); });
}); });

View File

@ -354,7 +354,7 @@ export class MovePhase extends BattlePhase {
if (failureMessage) { if (failureMessage) {
failedText = failureMessage; failedText = failureMessage;
} else if (failedDueToTerrain) { } else if (failedDueToTerrain) {
failedText = getTerrainBlockMessage(this.pokemon, globalScene.arena.getTerrainType()); failedText = getTerrainBlockMessage(targets[0], globalScene.arena.getTerrainType());
} }
this.showFailedText(failedText); this.showFailedText(failedText);

View File

@ -36,5 +36,6 @@ export class PartyHealPhase extends BattlePhase {
globalScene.ui.fadeIn(500).then(() => this.end()); globalScene.ui.fadeIn(500).then(() => this.end());
}); });
}); });
globalScene.arena.playerTerasUsed = 0;
} }
} }

View File

@ -62,7 +62,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
this.message = null; this.message = null;
return super.end(); return super.end();
} else if (healOrDamage) { } else if (healOrDamage) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1); const hpRestoreMultiplier = new Utils.NumberHolder(1);
if (!this.revive) { if (!this.revive) {
globalScene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); globalScene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
} }

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { SemiInvulnerableTag } from "#app/data/battler-tags"; import { SemiInvulnerableTag } from "#app/data/battler-tags";
import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import type { SpeciesFormChange } from "#app/data/pokemon-forms";
import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms"; import { getSpeciesFormChangeMessage, SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { BattleSpec } from "#app/enums/battle-spec"; import { BattleSpec } from "#app/enums/battle-spec";
import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type";
@ -11,6 +11,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import { MovePhase } from "./move-phase"; import { MovePhase } from "./move-phase";
import { PokemonHealPhase } from "./pokemon-heal-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase";
import { applyAbAttrs, PostTeraFormChangeStatChangeAbAttr } from "#app/data/ability";
export class QuietFormChangePhase extends BattlePhase { export class QuietFormChangePhase extends BattlePhase {
protected pokemon: Pokemon; protected pokemon: Pokemon;
@ -51,7 +52,7 @@ export class QuietFormChangePhase extends BattlePhase {
} catch (err: unknown) { } catch (err: unknown) {
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()), isTerastallized: this.pokemon.isTerastallized });
[ "spriteColors", "fusionSpriteColors" ].map(k => { [ "spriteColors", "fusionSpriteColors" ].map(k => {
if (this.pokemon.summonData?.speciesForm) { if (this.pokemon.summonData?.speciesForm) {
k += "Base"; k += "Base";
@ -145,6 +146,9 @@ export class QuietFormChangePhase extends BattlePhase {
movePhase.cancel(); movePhase.cancel();
} }
} }
if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) {
applyAbAttrs(PostTeraFormChangeStatChangeAbAttr, this.pokemon, null);
}
super.end(); super.end();
} }

View File

@ -45,7 +45,7 @@ export class SelectModifierPhase extends BattlePhase {
if (!this.isCopy) { if (!this.isCopy) {
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount); regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
} }
const modifierCount = new Utils.IntegerHolder(3); const modifierCount = new Utils.NumberHolder(3);
if (this.isPlayer()) { if (this.isPlayer()) {
globalScene.applyModifiers(ExtraModifierModifier, true, modifierCount); globalScene.applyModifiers(ExtraModifierModifier, true, modifierCount);
globalScene.applyModifiers(TempExtraModifierModifier, true, modifierCount); globalScene.applyModifiers(TempExtraModifierModifier, true, modifierCount);

51
src/phases/tera-phase.ts Normal file
View File

@ -0,0 +1,51 @@
import type Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase";
import i18next from "i18next";
import { globalScene } from "#app/global-scene";
import { Type } from "#app/enums/type";
import { achvs } from "#app/system/achv";
import { SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
export class TeraPhase extends BattlePhase {
public pokemon: Pokemon;
constructor(pokemon: Pokemon) {
super();
this.pokemon = pokemon;
}
start() {
super.start();
console.log(this.pokemon.name, "terastallized to", Type[this.pokemon.teraType].toString());
globalScene.queueMessage(i18next.t("battle:pokemonTerastallized", { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), type: i18next.t(`pokemonInfo:Type.${Type[this.pokemon.teraType]}`) }));
new CommonBattleAnim(CommonAnim.TERASTALLIZE, this.pokemon).play(false, () => {
this.end();
});
}
end() {
this.pokemon.isTerastallized = true;
this.pokemon.updateSpritePipelineData();
if (this.pokemon.isPlayer()) {
globalScene.arena.playerTerasUsed += 1;
}
globalScene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangeTeraTrigger);
if (this.pokemon.isPlayer()) {
globalScene.validateAchv(achvs.TERASTALLIZE);
if (this.pokemon.teraType === Type.STELLAR) {
globalScene.validateAchv(achvs.STELLAR_TERASTALLIZE);
}
}
super.end();
}
}

View File

@ -21,6 +21,7 @@ import { BattlerIndex } from "#app/battle";
import { TrickRoomTag } from "#app/data/arena-tag"; import { TrickRoomTag } from "#app/data/arena-tag";
import { SwitchType } from "#enums/switch-type"; import { SwitchType } from "#enums/switch-type";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { TeraPhase } from "./tera-phase";
export class TurnStartPhase extends FieldPhase { export class TurnStartPhase extends FieldPhase {
constructor() { constructor() {
@ -139,6 +140,20 @@ export class TurnStartPhase extends FieldPhase {
let orderIndex = 0; let orderIndex = 0;
for (const o of this.getSpeedOrder()) {
const pokemon = field[o];
const preTurnCommand = globalScene.currentBattle.preTurnCommands[o];
if (preTurnCommand?.skip) {
continue;
}
switch (preTurnCommand?.command) {
case Command.TERA:
globalScene.pushPhase(new TeraPhase(pokemon));
}
}
for (const o of moveOrder) { for (const o of moveOrder) {
const pokemon = field[o]; const pokemon = field[o];

View File

@ -351,7 +351,7 @@ export default class SpritePipeline extends FieldSpritePipeline {
const data = sprite.pipelineData; const data = sprite.pipelineData;
const tone = data["tone"] as number[]; const tone = data["tone"] as number[];
const teraColor = data["teraColor"] as number[] ?? [ 0, 0, 0 ]; const teraColor = (data["isTerastallized"] as boolean) ? (data["teraColor"] as number[] ?? [ 0, 0, 0 ]) : [ 0, 0, 0 ];
const hasShadow = data["hasShadow"] as boolean; const hasShadow = data["hasShadow"] as boolean;
const yShadowOffset = data["yShadowOffset"] as number; const yShadowOffset = data["yShadowOffset"] as number;
const ignoreFieldPos = data["ignoreFieldPos"] as boolean; const ignoreFieldPos = data["ignoreFieldPos"] as boolean;

View File

@ -10,12 +10,14 @@ export default class ArenaData {
public weather: Weather | null; public weather: Weather | null;
public terrain: Terrain | null; public terrain: Terrain | null;
public tags: ArenaTag[]; public tags: ArenaTag[];
public playerTerasUsed: number;
constructor(source: Arena | any) { constructor(source: Arena | any) {
const sourceArena = source instanceof Arena ? source as Arena : null; const sourceArena = source instanceof Arena ? source as Arena : null;
this.biome = sourceArena ? sourceArena.biomeType : source.biome; this.biome = sourceArena ? sourceArena.biomeType : source.biome;
this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null;
this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null;
this.playerTerasUsed = (sourceArena ? sourceArena.playerTerasUsed : source.playerTerasUsed) ?? 0;
this.tags = []; this.tags = [];
if (source.tags) { if (source.tags) {

View File

@ -1087,6 +1087,8 @@ export class GameData {
globalScene.arena.terrain = sessionData.arena.terrain; globalScene.arena.terrain = sessionData.arena.terrain;
globalScene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, globalScene.arena.terrain?.terrainType!, globalScene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct? globalScene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, globalScene.arena.terrain?.terrainType!, globalScene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct?
globalScene.arena.playerTerasUsed = sessionData.arena.playerTerasUsed;
globalScene.arena.tags = sessionData.arena.tags; globalScene.arena.tags = sessionData.arena.tags;
if (globalScene.arena.tags) { if (globalScene.arena.tags) {
for (const tag of globalScene.arena.tags) { for (const tag of globalScene.arena.tags) {

View File

@ -13,6 +13,7 @@ import type { Biome } from "#enums/biome";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import type { Species } from "#enums/species"; import type { Species } from "#enums/species";
import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import type { Type } from "#app/enums/type";
export default class PokemonData { export default class PokemonData {
public id: number; public id: number;
@ -45,6 +46,9 @@ export default class PokemonData {
public pokerus: boolean; public pokerus: boolean;
public usedTMs: Moves[]; public usedTMs: Moves[];
public evoCounter: number; public evoCounter: number;
public teraType: Type;
public isTerastallized: boolean;
public stellarTypesBoosted: Type[];
public fusionSpecies: Species; public fusionSpecies: Species;
public fusionFormIndex: number; public fusionFormIndex: number;
@ -53,6 +57,7 @@ export default class PokemonData {
public fusionVariant: Variant; public fusionVariant: Variant;
public fusionGender: Gender; public fusionGender: Gender;
public fusionLuck: number; public fusionLuck: number;
public fusionTeraType: Type;
public boss: boolean; public boss: boolean;
public bossSegments?: number; public bossSegments?: number;
@ -103,6 +108,9 @@ export default class PokemonData {
this.evoCounter = source.evoCounter ?? 0; this.evoCounter = source.evoCounter ?? 0;
} }
this.pokerus = !!source.pokerus; this.pokerus = !!source.pokerus;
this.teraType = source.teraType as Type;
this.isTerastallized = source.isTerastallized || false;
this.stellarTypesBoosted = source.stellarTypesBoosted || [];
this.fusionSpecies = sourcePokemon ? sourcePokemon.fusionSpecies?.speciesId : source.fusionSpecies; this.fusionSpecies = sourcePokemon ? sourcePokemon.fusionSpecies?.speciesId : source.fusionSpecies;
this.fusionFormIndex = source.fusionFormIndex; this.fusionFormIndex = source.fusionFormIndex;
@ -112,6 +120,7 @@ export default class PokemonData {
this.fusionGender = source.fusionGender; this.fusionGender = source.fusionGender;
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0); this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
this.fusionCustomPokemonData = new CustomPokemonData(source.fusionCustomPokemonData); this.fusionCustomPokemonData = new CustomPokemonData(source.fusionCustomPokemonData);
this.fusionTeraType = (source.fusionTeraType ?? 0) as Type;
this.usedTMs = source.usedTMs ?? []; this.usedTMs = source.usedTMs ?? [];
this.customPokemonData = new CustomPokemonData(source.customPokemonData); this.customPokemonData = new CustomPokemonData(source.customPokemonData);

View File

@ -7,6 +7,9 @@ import * as v1_0_4 from "./versions/v1_0_4";
// --- v1.1.0 PATCHES --- // // --- v1.1.0 PATCHES --- //
import * as v1_1_0 from "./versions/v1_1_0"; import * as v1_1_0 from "./versions/v1_1_0";
// --- v1.7.0 PATCHES --- //
import * as v1_7_0 from "./versions/v1_7_0";
const LATEST_VERSION = version.split(".").map(value => parseInt(value)); const LATEST_VERSION = version.split(".").map(value => parseInt(value));
/** /**
@ -138,6 +141,10 @@ class SessionVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 session data migration!"); console.log("Applying v1.1.0 session data migration!");
this.callMigrators(data, v1_1_0.sessionMigrators); this.callMigrators(data, v1_1_0.sessionMigrators);
} }
if (curMinor < 7) {
console.log("Applying v1.7.0 session data migration!");
this.callMigrators(data, v1_7_0.sessionMigrators);
}
} }
console.log(`Session data successfully migrated to v${version}!`); console.log(`Session data successfully migrated to v${version}!`);
@ -164,6 +171,10 @@ class SystemVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 system data migraton!"); console.log("Applying v1.1.0 system data migraton!");
this.callMigrators(data, v1_1_0.systemMigrators); this.callMigrators(data, v1_1_0.systemMigrators);
} }
if (curMinor < 7) {
console.log("Applying v1.7.0 system data migration!");
this.callMigrators(data, v1_7_0.systemMigrators);
}
} }
console.log(`System data successfully migrated to v${version}!`); console.log(`System data successfully migrated to v${version}!`);
@ -190,8 +201,12 @@ class SettingsVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 settings data migraton!"); console.log("Applying v1.1.0 settings data migraton!");
this.callMigrators(data, v1_1_0.settingsMigrators); this.callMigrators(data, v1_1_0.settingsMigrators);
} }
if (curMinor < 7) {
console.log("Applying v1.7.0 settings data migration!");
this.callMigrators(data, v1_7_0.settingsMigrators);
}
} }
console.log(`System data successfully migrated to v${version}!`); console.log(`Settings data successfully migrated to v${version}!`);
} }
} }

View File

@ -0,0 +1,49 @@
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
import type { SessionSaveData } from "#app/system/game-data";
import * as Utils from "#app/utils";
export const systemMigrators = [] as const;
export const settingsMigrators = [] as const;
export const sessionMigrators = [
function migrateTera(data: SessionSaveData) {
for (let i = 0; i < data.modifiers.length;) {
if (data.modifiers[i].className === "TerastallizeModifier") {
data.party.forEach((p) => {
if (p.id === data.modifiers[i].args[0]) {
p.teraType = data.modifiers[i].args[1];
}
});
data.modifiers.splice(i, 1);
} else {
i++;
}
}
for (let i = 0; i < data.enemyModifiers.length;) {
if (data.enemyModifiers[i].className === "TerastallizeModifier") {
data.enemyParty.forEach((p) => {
if (p.id === data.enemyModifiers[i].args[0]) {
p.teraType = data.enemyModifiers[i].args[1];
}
});
data.enemyModifiers.splice(i, 1);
} else {
i++;
}
}
data.party.forEach(p => {
if (Utils.isNullOrUndefined(p.teraType)) {
p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1;
}
});
data.enemyParty.forEach(p => {
if (Utils.isNullOrUndefined(p.teraType)) {
p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1;
}
});
}
] as const;

View File

@ -11,7 +11,7 @@ import { Species } from "#enums/species";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
describe("Abilities - Libero", () => { describe("Abilities - Libero", () => {
@ -258,7 +258,7 @@ describe("Abilities - Libero", () => {
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); leadPokemon.isTerastallized = true;
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -11,7 +11,7 @@ import { Species } from "#enums/species";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
describe("Abilities - Protean", () => { describe("Abilities - Protean", () => {
@ -258,7 +258,7 @@ describe("Abilities - Protean", () => {
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); leadPokemon.isTerastallized = true;
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -1,6 +1,6 @@
import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { TurnHeldItemTransferModifier } from "#app/modifier/modifier";
import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, achvs } from "#app/system/achv"; import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, achvs } from "#app/system/achv";
import { IntegerHolder, NumberHolder } from "#app/utils"; import { NumberHolder } from "#app/utils";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -174,7 +174,7 @@ describe("LevelAchv", () => {
it("should validate the achievement based on the level", () => { it("should validate the achievement based on the level", () => {
const levelAchv = new LevelAchv("", "Test Level Achievement", 100, "level_icon", 10); const levelAchv = new LevelAchv("", "Test Level Achievement", 100, "level_icon", 10);
const integerHolder = new IntegerHolder(50); const integerHolder = new NumberHolder(50);
expect(levelAchv.validate([ integerHolder ])).toBe(false); expect(levelAchv.validate([ integerHolder ])).toBe(false);

View File

@ -150,6 +150,17 @@ describe("Spec - Pokemon", () => {
expect(types[1]).toBe(Type.DARK); expect(types[1]).toBe(Type.DARK);
}); });
it("Fusing mons with two and one types", async () => {
game.override.starterSpecies(Species.NUMEL);
game.override.starterFusionSpecies(Species.CHARMANDER);
await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0];
const types = pokemon.getTypes();
expect(types[0]).toBe(Type.FIRE);
expect(types[1]).toBe(Type.GROUND);
});
it("Fusing two mons with two types", async () => { it("Fusing two mons with two types", async () => {
game.override.starterSpecies(Species.NATU); game.override.starterSpecies(Species.NATU);
game.override.starterFusionSpecies(Species.HOUNDOUR); game.override.starterFusionSpecies(Species.HOUNDOUR);

View File

@ -6,7 +6,6 @@ import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species"; import { Species } from "#app/enums/species";
import * as Messages from "#app/messages"; import * as Messages from "#app/messages";
import { TerastallizeModifier, overrideHeldItems } from "#app/modifier/modifier";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
@ -15,15 +14,14 @@ function testMoveEffectiveness(game: GameManager, move: Moves, targetSpecies: Sp
expected: number, targetAbility: Abilities = Abilities.BALL_FETCH, teraType?: Type): void { expected: number, targetAbility: Abilities = Abilities.BALL_FETCH, teraType?: Type): void {
// Suppress getPokemonNameWithAffix because it calls on a null battle spec // Suppress getPokemonNameWithAffix because it calls on a null battle spec
vi.spyOn(Messages, "getPokemonNameWithAffix").mockReturnValue(""); vi.spyOn(Messages, "getPokemonNameWithAffix").mockReturnValue("");
game.override game.override.enemyAbility(targetAbility);
.enemyAbility(targetAbility)
.enemyHeldItems([{ name:"TERA_SHARD", type: teraType }]);
const user = game.scene.addPlayerPokemon(getPokemonSpecies(Species.SNORLAX), 5); const user = game.scene.addPlayerPokemon(getPokemonSpecies(Species.SNORLAX), 5);
const target = game.scene.addEnemyPokemon(getPokemonSpecies(targetSpecies), 5, TrainerSlot.NONE); const target = game.scene.addEnemyPokemon(getPokemonSpecies(targetSpecies), 5, TrainerSlot.NONE);
if (teraType !== undefined) { if (teraType !== undefined) {
overrideHeldItems(target, false); target.teraType = teraType;
target.isTerastallized = true;
} }
expect(target.getMoveEffectiveness(user, allMoves[move])).toBe(expected); expect(target.getMoveEffectiveness(user, allMoves[move])).toBe(expected);
@ -40,7 +38,6 @@ describe("Moves - Type Effectiveness", () => {
type: Phaser.HEADLESS, type: Phaser.HEADLESS,
}); });
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
TerastallizeModifier.prototype.apply = (args) => true;
game.override.ability(Abilities.BALL_FETCH); game.override.ability(Abilities.BALL_FETCH);
}); });

View File

@ -117,11 +117,12 @@ describe("Moves - Freeze-Dry", () => {
}); });
it("should deal 2x damage to steel type terastallized into water", async () => { it("should deal 2x damage to steel type terastallized into water", async () => {
game.override.enemySpecies(Species.SKARMORY) game.override.enemySpecies(Species.SKARMORY);
.enemyHeldItems([{ name: "TERA_SHARD", type: Type.WATER }]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.WATER;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness"); vi.spyOn(enemy, "getMoveEffectiveness");
game.move.select(Moves.FREEZE_DRY); game.move.select(Moves.FREEZE_DRY);
@ -132,11 +133,12 @@ describe("Moves - Freeze-Dry", () => {
}); });
it("should deal 0.5x damage to water type terastallized into fire", async () => { it("should deal 0.5x damage to water type terastallized into fire", async () => {
game.override.enemySpecies(Species.PELIPPER) game.override.enemySpecies(Species.PELIPPER);
.enemyHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.FIRE;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness"); vi.spyOn(enemy, "getMoveEffectiveness");
game.move.select(Moves.FREEZE_DRY); game.move.select(Moves.FREEZE_DRY);

View File

@ -83,10 +83,12 @@ describe("Moves - Tar Shot", () => {
}); });
it("does not double the effectiveness of Fire-type moves against a Pokémon that is Terastallized", async () => { it("does not double the effectiveness of Fire-type moves against a Pokémon that is Terastallized", async () => {
game.override.enemyHeldItems([{ name: "TERA_SHARD", type: Type.GRASS }]).enemySpecies(Species.SPRIGATITO); game.override.enemySpecies(Species.SPRIGATITO);
await game.classicMode.startBattle([ Species.PIKACHU ]); await game.classicMode.startBattle([ Species.PIKACHU ]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.GRASS;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness"); vi.spyOn(enemy, "getMoveEffectiveness");
@ -119,7 +121,8 @@ describe("Moves - Tar Shot", () => {
await game.toNextTurn(); await game.toNextTurn();
game.override.enemyHeldItems([{ name: "TERA_SHARD", type: Type.GRASS }]); enemy.teraType = Type.GRASS;
enemy.isTerastallized = true;
game.move.select(Moves.FIRE_PUNCH); game.move.select(Moves.FIRE_PUNCH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);

View File

@ -35,7 +35,6 @@ describe("Moves - Tera Blast", () => {
.starterSpecies(Species.FEEBAS) .starterSpecies(Species.FEEBAS)
.moveset([ Moves.TERA_BLAST ]) .moveset([ Moves.TERA_BLAST ])
.ability(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH)
.startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }])
.enemySpecies(Species.MAGIKARP) .enemySpecies(Species.MAGIKARP)
.enemyMoveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.STURDY) .enemyAbility(Abilities.STURDY)
@ -45,13 +44,15 @@ describe("Moves - Tera Blast", () => {
}); });
it("changes type to match user's tera type", async () => { it("changes type to match user's tera type", async () => {
game.override game.override.enemySpecies(Species.FURRET);
.enemySpecies(Species.FURRET)
.startingHeldItems([{ name: "TERA_SHARD", type: Type.FIGHTING }]);
await game.startBattle(); await game.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
vi.spyOn(enemyPokemon, "apply"); vi.spyOn(enemyPokemon, "apply");
const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.teraType = Type.FIGHTING;
playerPokemon.isTerastallized = true;
game.move.select(Moves.TERA_BLAST); game.move.select(Moves.TERA_BLAST);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase"); await game.phaseInterceptor.to("MoveEffectPhase");
@ -60,10 +61,12 @@ describe("Moves - Tera Blast", () => {
}, 20000); }, 20000);
it("increases power if user is Stellar tera type", async () => { it("increases power if user is Stellar tera type", async () => {
game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]);
await game.startBattle(); await game.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.teraType = Type.STELLAR;
playerPokemon.isTerastallized = true;
game.move.select(Moves.TERA_BLAST); game.move.select(Moves.TERA_BLAST);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase"); await game.phaseInterceptor.to("MoveEffectPhase");
@ -72,13 +75,15 @@ describe("Moves - Tera Blast", () => {
}, 20000); }, 20000);
it("is super effective against terastallized targets if user is Stellar tera type", async () => { it("is super effective against terastallized targets if user is Stellar tera type", async () => {
game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]);
await game.startBattle(); await game.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.teraType = Type.STELLAR;
playerPokemon.isTerastallized = true;
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
vi.spyOn(enemyPokemon, "apply"); vi.spyOn(enemyPokemon, "apply");
vi.spyOn(enemyPokemon, "isTerastallized").mockReturnValue(true); enemyPokemon.isTerastallized = true;
game.move.select(Moves.TERA_BLAST); game.move.select(Moves.TERA_BLAST);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
@ -93,6 +98,7 @@ describe("Moves - Tera Blast", () => {
const playerPokemon = game.scene.getPlayerPokemon()!; const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.stats[Stat.ATK] = 100; playerPokemon.stats[Stat.ATK] = 100;
playerPokemon.stats[Stat.SPATK] = 1; playerPokemon.stats[Stat.SPATK] = 1;
playerPokemon.isTerastallized = true;
vi.spyOn(teraBlastAttr, "apply"); vi.spyOn(teraBlastAttr, "apply");
@ -169,10 +175,11 @@ describe("Moves - Tera Blast", () => {
it("causes stat drops if user is Stellar tera type", async () => { it("causes stat drops if user is Stellar tera type", async () => {
game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]);
await game.startBattle(); await game.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!; const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.teraType = Type.STELLAR;
playerPokemon.isTerastallized = true;
game.move.select(Moves.TERA_BLAST); game.move.select(Moves.TERA_BLAST);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);

View File

@ -29,8 +29,7 @@ describe("Moves - Tera Starstorm", () => {
.enemyAbility(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH)
.enemyLevel(30) .enemyLevel(30)
.enemySpecies(Species.MAGIKARP) .enemySpecies(Species.MAGIKARP);
.startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]);
}); });
it("changes type to Stellar when used by Terapagos in its Stellar Form", async () => { it("changes type to Stellar when used by Terapagos in its Stellar Form", async () => {
@ -38,19 +37,22 @@ describe("Moves - Tera Starstorm", () => {
await game.classicMode.startBattle([ Species.TERAPAGOS ]); await game.classicMode.startBattle([ Species.TERAPAGOS ]);
const terapagos = game.scene.getPlayerPokemon()!; const terapagos = game.scene.getPlayerPokemon()!;
terapagos.isTerastallized = true;
vi.spyOn(terapagos, "getMoveType"); vi.spyOn(terapagos, "getMoveType");
game.move.select(Moves.TERA_STARSTORM); game.move.select(Moves.TERA_STARSTORM);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
expect(terapagos.isTerastallized()).toBe(true);
expect(terapagos.getMoveType).toHaveReturnedWith(Type.STELLAR); expect(terapagos.getMoveType).toHaveReturnedWith(Type.STELLAR);
}); });
it("targets both opponents in a double battle when used by Terapagos in its Stellar Form", async () => { it("targets both opponents in a double battle when used by Terapagos in its Stellar Form", async () => {
await game.classicMode.startBattle([ Species.MAGIKARP, Species.TERAPAGOS ]); await game.classicMode.startBattle([ Species.MAGIKARP, Species.TERAPAGOS ]);
const terapagos = game.scene.getPlayerParty()[1];
terapagos.isTerastallized = true;
game.move.select(Moves.TERA_STARSTORM, 0, BattlerIndex.ENEMY); game.move.select(Moves.TERA_STARSTORM, 0, BattlerIndex.ENEMY);
game.move.select(Moves.TERA_STARSTORM, 1); game.move.select(Moves.TERA_STARSTORM, 1);
@ -82,6 +84,8 @@ describe("Moves - Tera Starstorm", () => {
fusionedMon.fusionGender = magikarp.gender; fusionedMon.fusionGender = magikarp.gender;
fusionedMon.fusionLuck = magikarp.luck; fusionedMon.fusionLuck = magikarp.luck;
fusionedMon.isTerastallized = true;
vi.spyOn(fusionedMon, "getMoveType"); vi.spyOn(fusionedMon, "getMoveType");
game.move.select(Moves.TERA_STARSTORM, 0); game.move.select(Moves.TERA_STARSTORM, 0);
@ -90,7 +94,6 @@ describe("Moves - Tera Starstorm", () => {
// Fusion and terastallized // Fusion and terastallized
expect(fusionedMon.isFusion()).toBe(true); expect(fusionedMon.isFusion()).toBe(true);
expect(fusionedMon.isTerastallized()).toBe(true);
// Move effects should be applied // Move effects should be applied
expect(fusionedMon.getMoveType).toHaveReturnedWith(Type.STELLAR); expect(fusionedMon.getMoveType).toHaveReturnedWith(Type.STELLAR);
expect(game.scene.getEnemyField().every(pokemon => pokemon.isFullHp())).toBe(false); expect(game.scene.getEnemyField().every(pokemon => pokemon.isFullHp())).toBe(false);

View File

@ -324,9 +324,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.lastTeraType = pokemon.getTeraType(); this.lastTeraType = pokemon.getTeraType();
this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2); this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN); this.teraIcon.setVisible(pokemon.isTerastallized);
this.teraIcon.on("pointerover", () => { this.teraIcon.on("pointerover", () => {
if (this.lastTeraType !== Type.UNKNOWN) { if (pokemon.isTerastallized) {
globalScene.ui.showTooltip("", i18next.t("fightUiHandler:teraHover", { type: i18next.t(`pokemonInfo:Type.${Type[this.lastTeraType]}`) })); globalScene.ui.showTooltip("", i18next.t("fightUiHandler:teraHover", { type: i18next.t(`pokemonInfo:Type.${Type[this.lastTeraType]}`) }));
} }
}); });
@ -542,7 +542,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0); this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
} }
const teraType = pokemon.getTeraType(); const teraType = pokemon.isTerastallized ? pokemon.getTeraType() : Type.UNKNOWN;
const teraTypeUpdated = this.lastTeraType !== teraType; const teraTypeUpdated = this.lastTeraType !== teraType;
if (teraTypeUpdated) { if (teraTypeUpdated) {

View File

@ -7,18 +7,24 @@ import { Button } from "#enums/buttons";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { CommandPhase } from "#app/phases/command-phase"; import { CommandPhase } from "#app/phases/command-phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { TerastallizeAccessModifier } from "#app/modifier/modifier";
import { Type } from "#app/enums/type";
import { getTypeRgb } from "#app/data/type";
export enum Command { export enum Command {
FIGHT = 0, FIGHT = 0,
BALL, BALL,
POKEMON, POKEMON,
RUN RUN,
TERA
} }
export default class CommandUiHandler extends UiHandler { export default class CommandUiHandler extends UiHandler {
private commandsContainer: Phaser.GameObjects.Container; private commandsContainer: Phaser.GameObjects.Container;
private cursorObj: Phaser.GameObjects.Image | null; private cursorObj: Phaser.GameObjects.Image | null;
private teraButton: Phaser.GameObjects.Sprite;
protected fieldIndex: number = 0; protected fieldIndex: number = 0;
protected cursor2: number = 0; protected cursor2: number = 0;
@ -40,6 +46,13 @@ export default class CommandUiHandler extends UiHandler {
this.commandsContainer.setVisible(false); this.commandsContainer.setVisible(false);
ui.add(this.commandsContainer); ui.add(this.commandsContainer);
this.teraButton = globalScene.add.sprite(-32, 15, "button_tera");
this.teraButton.setName("terrastallize-button");
this.teraButton.setScale(1.3);
this.teraButton.setFrame("fire");
this.teraButton.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true, teraColor: getTypeRgb(Type.FIRE), isTerastallized: false });
this.commandsContainer.add(this.teraButton);
for (let c = 0; c < commands.length; c++) { for (let c = 0; c < commands.length; c++) {
const commandText = addTextObject(c % 2 === 0 ? 0 : 55.8, c < 2 ? 0 : 16, commands[c], TextStyle.WINDOW); const commandText = addTextObject(c % 2 === 0 ? 0 : 55.8, c < 2 ? 0 : 16, commands[c], TextStyle.WINDOW);
commandText.setName(commands[c]); commandText.setName(commands[c]);
@ -62,11 +75,22 @@ export default class CommandUiHandler extends UiHandler {
commandPhase = globalScene.getStandbyPhase() as CommandPhase; commandPhase = globalScene.getStandbyPhase() as CommandPhase;
} }
if (this.canTera()) {
this.teraButton.setVisible(true);
this.teraButton.setFrame(Type[globalScene.getField()[this.fieldIndex].getTeraType()].toLowerCase());
} else {
this.teraButton.setVisible(false);
if (this.cursor === Command.TERA) {
this.setCursor(Command.FIGHT);
}
}
this.toggleTeraButton();
const messageHandler = this.getUi().getMessageHandler(); const messageHandler = this.getUi().getMessageHandler();
messageHandler.bg.setVisible(true); messageHandler.bg.setVisible(true);
messageHandler.commandWindow.setVisible(true); messageHandler.commandWindow.setVisible(true);
messageHandler.movesWindowContainer.setVisible(false); messageHandler.movesWindowContainer.setVisible(false);
messageHandler.message.setWordWrapWidth(1110); messageHandler.message.setWordWrapWidth(this.canTera() ? 910 : 1110);
messageHandler.showText(i18next.t("commandUiHandler:actionMessage", { pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon()) }), 0); messageHandler.showText(i18next.t("commandUiHandler:actionMessage", { pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon()) }), 0);
if (this.getCursor() === Command.POKEMON) { if (this.getCursor() === Command.POKEMON) {
this.setCursor(Command.FIGHT); this.setCursor(Command.FIGHT);
@ -108,6 +132,10 @@ export default class CommandUiHandler extends UiHandler {
(globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0); (globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0);
success = true; success = true;
break; break;
case Command.TERA:
ui.setMode(Mode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA);
success = true;
break;
} }
} else { } else {
(globalScene.getCurrentPhase() as CommandPhase).cancel(); (globalScene.getCurrentPhase() as CommandPhase).cancel();
@ -115,23 +143,29 @@ export default class CommandUiHandler extends UiHandler {
} else { } else {
switch (button) { switch (button) {
case Button.UP: case Button.UP:
if (cursor >= 2) { if (cursor === Command.POKEMON || cursor === Command.RUN) {
success = this.setCursor(cursor - 2); success = this.setCursor(cursor - 2);
} }
break; break;
case Button.DOWN: case Button.DOWN:
if (cursor < 2) { if (cursor === Command.FIGHT || cursor === Command.BALL) {
success = this.setCursor(cursor + 2); success = this.setCursor(cursor + 2);
} }
break; break;
case Button.LEFT: case Button.LEFT:
if (cursor % 2 === 1) { if (cursor === Command.BALL || cursor === Command.RUN) {
success = this.setCursor(cursor - 1); success = this.setCursor(cursor - 1);
} else if ((cursor === Command.FIGHT || cursor === Command.POKEMON) && this.canTera()) {
success = this.setCursor(Command.TERA);
this.toggleTeraButton();
} }
break; break;
case Button.RIGHT: case Button.RIGHT:
if (cursor % 2 === 0) { if (cursor === Command.FIGHT || cursor === Command.POKEMON) {
success = this.setCursor(cursor + 1); success = this.setCursor(cursor + 1);
} else if (cursor === Command.TERA) {
success = this.setCursor(Command.FIGHT);
this.toggleTeraButton();
} }
break; break;
} }
@ -144,6 +178,17 @@ export default class CommandUiHandler extends UiHandler {
return success; return success;
} }
canTera(): boolean {
const hasTeraMod = !!globalScene.getModifiers(TerastallizeAccessModifier).length;
const currentTeras = globalScene.arena.playerTerasUsed;
const plannedTera = globalScene.currentBattle.preTurnCommands[0]?.command === Command.TERA && this.fieldIndex > 0 ? 1 : 0;
return hasTeraMod && (currentTeras + plannedTera) < 1;
}
toggleTeraButton() {
this.teraButton.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true, teraColor: getTypeRgb(globalScene.getField()[this.fieldIndex].getTeraType()), isTerastallized: this.getCursor() === Command.TERA });
}
getCursor(): number { getCursor(): number {
return !this.fieldIndex ? this.cursor : this.cursor2; return !this.fieldIndex ? this.cursor : this.cursor2;
} }
@ -163,7 +208,12 @@ export default class CommandUiHandler extends UiHandler {
this.commandsContainer.add(this.cursorObj); this.commandsContainer.add(this.cursorObj);
} }
this.cursorObj.setPosition(-5 + (cursor % 2 === 1 ? 56 : 0), 8 + (cursor >= 2 ? 16 : 0)); if (cursor === Command.TERA) {
this.cursorObj.setVisible(false);
} else {
this.cursorObj.setPosition(-5 + (cursor % 2 === 1 ? 56 : 0), 8 + (cursor >= 2 ? 16 : 0));
this.cursorObj.setVisible(true);
}
return changed; return changed;
} }

View File

@ -33,6 +33,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
private moveInfoOverlay : MoveInfoOverlay; private moveInfoOverlay : MoveInfoOverlay;
protected fieldIndex: number = 0; protected fieldIndex: number = 0;
protected fromCommand: Command = Command.FIGHT;
protected cursor2: number = 0; protected cursor2: number = 0;
constructor() { constructor() {
@ -114,6 +115,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
super.show(args); super.show(args);
this.fieldIndex = args.length ? args[0] as number : 0; this.fieldIndex = args.length ? args[0] as number : 0;
this.fromCommand = args.length > 1 ? args[1] as Command : Command.FIGHT;
const messageHandler = this.getUi().getMessageHandler(); const messageHandler = this.getUi().getMessageHandler();
messageHandler.bg.setVisible(false); messageHandler.bg.setVisible(false);
@ -140,7 +142,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
if (button === Button.CANCEL || button === Button.ACTION) { if (button === Button.CANCEL || button === Button.ACTION) {
if (button === Button.ACTION) { if (button === Button.ACTION) {
if ((globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, cursor, false)) { if ((globalScene.getCurrentPhase() as CommandPhase).handleCommand(this.fromCommand, cursor, false)) {
success = true; success = true;
} else { } else {
ui.playError(); ui.playError();

View File

@ -14,7 +14,7 @@ import * as Utils from "./../utils";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import i18next from "i18next"; import i18next from "i18next";
import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
import { IntegerHolder } from "./../utils"; import { NumberHolder } from "./../utils";
import Phaser from "phaser"; import Phaser from "phaser";
import type { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
@ -191,7 +191,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
const typeOptions = args[1] as ModifierTypeOption[]; const typeOptions = args[1] as ModifierTypeOption[];
const removeHealShop = globalScene.gameMode.hasNoShop; const removeHealShop = globalScene.gameMode.hasNoShop;
const baseShopCost = new IntegerHolder(globalScene.getWaveMoneyAmount(1)); const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1));
globalScene.applyModifier(HealShopCostModifier, true, baseShopCost); globalScene.applyModifier(HealShopCostModifier, true, baseShopCost);
const shopTypeOptions = !removeHealShop const shopTypeOptions = !removeHealShop
? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value) ? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value)

View File

@ -336,6 +336,8 @@ export default class PokedexUiHandler extends MessageUiHandler {
const costReductionLabels = [ const costReductionLabels = [
new DropDownLabel(i18next.t("filterBar:costReduction"), undefined, DropDownState.OFF), new DropDownLabel(i18next.t("filterBar:costReduction"), undefined, DropDownState.OFF),
new DropDownLabel(i18next.t("filterBar:costReductionUnlocked"), undefined, DropDownState.ON), new DropDownLabel(i18next.t("filterBar:costReductionUnlocked"), undefined, DropDownState.ON),
new DropDownLabel(i18next.t("filterBar:costReductionUnlockedOne"), undefined, DropDownState.ONE),
new DropDownLabel(i18next.t("filterBar:costReductionUnlockedTwo"), undefined, DropDownState.TWO),
new DropDownLabel(i18next.t("filterBar:costReductionUnlockable"), undefined, DropDownState.UNLOCKABLE), new DropDownLabel(i18next.t("filterBar:costReductionUnlockable"), undefined, DropDownState.UNLOCKABLE),
new DropDownLabel(i18next.t("filterBar:costReductionLocked"), undefined, DropDownState.EXCLUDE), new DropDownLabel(i18next.t("filterBar:costReductionLocked"), undefined, DropDownState.EXCLUDE),
]; ];

View File

@ -420,17 +420,6 @@ export default class RunInfoUiHandler extends UiHandler {
private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) { private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) {
// Loads and adds trainer sprites to the UI // Loads and adds trainer sprites to the UI
this.showTrainerSprites(enemyContainer); this.showTrainerSprites(enemyContainer);
// Determining which Terastallize Modifier belongs to which Pokemon
// Creates a dictionary {PokemonId: TeraShardType}
const teraPokemon = {};
this.runInfo.enemyModifiers.forEach((m) => {
const modifier = m.toModifier(this.modifiersModule[m.className]);
if (modifier instanceof Modifier.TerastallizeModifier) {
const teraDetails = modifier?.getArgs();
const pkmnId = teraDetails[0];
teraPokemon[pkmnId] = teraDetails[1];
}
});
// Creates the Pokemon icons + level information and adds it to enemyContainer // Creates the Pokemon icons + level information and adds it to enemyContainer
// 2 Rows x 3 Columns // 2 Rows x 3 Columns
@ -444,18 +433,6 @@ export default class RunInfoUiHandler extends UiHandler {
enemyData["player"] = true; enemyData["player"] = true;
const enemy = enemyData.toPokemon(); const enemy = enemyData.toPokemon();
const enemyIcon = globalScene.addPokemonIcon(enemy, 0, 0, 0, 0); const enemyIcon = globalScene.addPokemonIcon(enemy, 0, 0, 0, 0);
// Applying Terastallizing Type tint to Pokemon icon
// If the Pokemon is a fusion, it has two sprites and so, the tint has to be applied to each icon separately
const enemySprite1 = enemyIcon.list[0] as Phaser.GameObjects.Sprite;
const enemySprite2 = (enemyIcon.list.length > 1) ? enemyIcon.list[1] as Phaser.GameObjects.Sprite : undefined;
if (teraPokemon[enemyData.id]) {
const teraTint = getTypeRgb(teraPokemon[enemyData.id]);
const teraColor = new Phaser.Display.Color(teraTint[0], teraTint[1], teraTint[2]);
enemySprite1.setTint(teraColor.color);
if (enemySprite2) {
enemySprite2.setTint(teraColor.color);
}
}
enemyIcon.setPosition(39 * (e % 3) + 5, (35 * pokemonRowHeight)); enemyIcon.setPosition(39 * (e % 3) + 5, (35 * pokemonRowHeight));
const enemyLevel = addTextObject(43 * (e % 3), (27 * (pokemonRowHeight + 1)), `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, isBoss ? TextStyle.PARTY_RED : TextStyle.PARTY, { fontSize: "54px" }); const enemyLevel = addTextObject(43 * (e % 3), (27 * (pokemonRowHeight + 1)), `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, isBoss ? TextStyle.PARTY_RED : TextStyle.PARTY, { fontSize: "54px" });
enemyLevel.setShadow(0, 0, undefined); enemyLevel.setShadow(0, 0, undefined);

View File

@ -88,7 +88,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
starterInfoXPos: 33, starterInfoXPos: 33,
}, },
"es-ES":{ "es-ES":{
starterInfoTextSize: "56px", starterInfoTextSize: "52px",
instructionTextSize: "35px", instructionTextSize: "35px",
}, },
"fr":{ "fr":{

View File

@ -334,6 +334,7 @@ export default class SummaryUiHandler extends UiHandler {
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType())); this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType()));
this.pokemonSprite.setPipelineData("isTerastallized", this.pokemon.isTerastallized);
this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("ignoreTimeTint", true);
this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey());
this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny); this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny);
@ -782,7 +783,7 @@ export default class SummaryUiHandler extends UiHandler {
if (types.length > 1) { if (types.length > 1) {
profileContainer.add(getTypeIcon(1, types[1])); profileContainer.add(getTypeIcon(1, types[1]));
} }
if (this.pokemon?.isTerastallized()) { if (this.pokemon?.isTerastallized) {
profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true)); profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true));
} }

View File

@ -342,13 +342,6 @@ export class NumberHolder {
} }
} }
/** @deprecated Use {@linkcode NumberHolder} */
export class IntegerHolder extends NumberHolder {
constructor(value: number) {
super(value);
}
}
export class FixedInt { export class FixedInt {
public readonly value: number; public readonly value: number;