Merge branch 'pagefaultgames:beta' into codespace-cuddly-system-x5pq7j974vwjc4pq

This commit is contained in:
Xhazn 2025-02-17 09:10:53 -06:00 committed by GitHub
commit be5e20ceeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
88 changed files with 5190 additions and 3666 deletions

View File

@ -164,13 +164,13 @@ input:-internal-autofill-selected {
}
/* Show cycle buttons only on STARTER_SELECT and on touch configuration panel */
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadOpenFilters,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='RUN_INFO']) #apadCycleForm,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='RUN_INFO']) #apadCycleShiny,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX'], [data-ui-mode='POKEDEX_PAGE']) #apadOpenFilters,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX'], [data-ui-mode='POKEDEX_PAGE'], [data-ui-mode='RUN_INFO']) #apadCycleForm,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX'], [data-ui-mode='POKEDEX_PAGE'], [data-ui-mode='RUN_INFO']) #apadCycleShiny,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadCycleNature,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='RUN_INFO']) #apadCycleAbility,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadCycleGender,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadCycleVariant {
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE'], [data-ui-mode='RUN_INFO']) #apadCycleAbility,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE']) #apadCycleGender,
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX']) #apadCycleVariant {
display: none;
}

4
package-lock.json generated
View File

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

View File

@ -1,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.5.4",
"version": "1.7.0",
"type": "module",
"scripts": {
"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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 24 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 bfcd7f91c39630f155839872c8f66fd0a89e12ac
Subproject commit 58dda14ee834204c4bd5ece47694a3c068df4b0e

View File

@ -9,7 +9,7 @@ import type { Constructor } from "#app/utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils";
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 { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import type { Phase } from "#app/phase";
@ -1212,7 +1212,7 @@ export default class BattleScene extends SceneBase {
}
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);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
return Math.max(doubleChance.value, 1);
@ -1373,7 +1373,11 @@ export default class BattleScene extends SceneBase {
for (const pokemon of this.getPlayerParty()) {
pokemon.resetBattleData();
pokemon.resetTera();
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) {
@ -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 {
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);
return sprite;
}
@ -2589,11 +2593,8 @@ export default class BattleScene extends SceneBase {
const modifiersToRemove: PersistentModifier[] = [];
const modifierPromises: Promise<boolean>[] = [];
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 instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
success = modifier.apply(pokemon, true);
@ -2627,7 +2628,7 @@ export default class BattleScene extends SceneBase {
const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
const hpRestoreMultiplier = new Utils.NumberHolder(1);
this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier);
args.push(hpRestoreMultiplier.value);
} else {
@ -2670,11 +2671,8 @@ export default class BattleScene extends SceneBase {
addEnemyModifier(modifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> {
return new Promise(resolve => {
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 instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, true);
@ -2798,6 +2796,8 @@ export default class BattleScene extends SceneBase {
for (const modifier of modifiers) {
this.addEnemyModifier(modifier, true, true);
}
this.currentBattle.trainer.genAI(party);
}
party.forEach((enemyPokemon: EnemyPokemon, i: number) => {
@ -2929,7 +2929,7 @@ export default class BattleScene extends SceneBase {
const modifierIndex = modifiers.indexOf(modifier);
if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
if (modifier instanceof PokemonFormChangeItemModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, false);
@ -3130,7 +3130,8 @@ export default class BattleScene extends SceneBase {
name: p.name,
form: p.getFormKey(),
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,
currentHP: p.hp,
maxHP: p.getMaxHp(),

View File

@ -92,6 +92,7 @@ export default class Battle {
public started: boolean = false;
public enemySwitchCounter: number = 0;
public turn: number = 0;
public preTurnCommands: TurnCommands;
public turnCommands: TurnCommands;
public playerParticipantIds: Set<number> = new Set<number>();
public battleScore: number = 0;
@ -180,6 +181,7 @@ export default class Battle {
incrementTurn(): void {
this.turn++;
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;
}
@ -201,7 +203,7 @@ export default class Battle {
}
pickUpScatteredMoney(): void {
const moneyAmount = new Utils.IntegerHolder(globalScene.currentBattle.moneyScattered);
const moneyAmount = new Utils.NumberHolder(globalScene.currentBattle.moneyScattered);
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
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 stages: number;
private selfTarget: boolean;
constructor(stats: BattleStat[], stages: number, selfTarget?: boolean) {
constructor(stats: BattleStat[], stages: number) {
super();
this.stats = stats;
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[] = [];
if (!simulated) {
if (this.selfTarget) {
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));
}
}
statStageChangePhases.push(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages));
for (const statStageChangePhase of statStageChangePhases) {
if (!this.selfTarget && !statStageChangePhase.getPokemon()?.summonData) {
globalScene.pushPhase(statStageChangePhase);
} else { // TODO: This causes the ability bar to be shown at the wrong time
globalScene.unshiftPhase(statStageChangePhase);
}
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 {
if (
!pokemon.isTerastallized() &&
!pokemon.isTerastallized &&
move.id !== Moves.STRUGGLE &&
/**
* Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute
@ -3127,7 +3115,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr {
return false;
}
(args[1] as Utils.IntegerHolder).value += this.changeAmount;
(args[1] as Utils.NumberHolder).value += this.changeAmount;
return true;
}
}
@ -3969,7 +3957,7 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr {
}
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;
}
@ -4070,7 +4058,7 @@ export class HealFromBerryUseAbAttr extends AbAttr {
export class RunSuccessAbAttr extends AbAttr {
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;
}
@ -4140,7 +4128,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
export class MaxMultiHitAbAttr extends AbAttr {
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;
}
@ -4271,7 +4259,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
export class RedirectMoveAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
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();
if (target.value !== newTarget) {
target.value = newTarget;
@ -4788,7 +4776,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr {
}
override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, _args: any[]): boolean {
if (pokemon.isTerastallized()) {
if (pokemon.isTerastallized) {
return false;
}
const currentTerrain = globalScene.arena.getTerrainType();
@ -6201,7 +6189,7 @@ export function initAbilities() {
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr)
.condition((pokemon) => !pokemon.isTerastallized()),
.condition((pokemon) => !pokemon.isTerastallized),
new Ability(Abilities.QUICK_DRAW, 8)
.attr(BypassSpeedChanceAbAttr, 30),
new Ability(Abilities.UNSEEN_FIST, 8)
@ -6285,15 +6273,13 @@ export function initAbilities() {
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.PROTOSYNTHESIS, 0, WeatherType.SUNNY, WeatherType.HARSH_SUN)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // While setting the tag, the getbattlestat should ignore all modifiers to stats except stat stages
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.QUARK_DRIVE, 9)
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), PostSummonAddBattlerTagAbAttr, BattlerTagType.QUARK_DRIVE, 0, true)
.attr(PostTerrainChangeAddBattlerTagAttr, BattlerTagType.QUARK_DRIVE, 0, TerrainType.ELECTRIC)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // While setting the tag, the getbattlestat should ignore all modifiers to stats except stat stages
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.GOOD_AS_GOLD, 9)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS && ![ MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES, MoveTarget.USER_SIDE ].includes(move.moveTarget))
.ignorable(),
@ -6355,29 +6341,25 @@ export function initAbilities() {
new Ability(Abilities.TOXIC_CHAIN, 9)
.attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC),
new Ability(Abilities.EMBODY_ASPECT_TEAL, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPD ], 1, true)
.attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.SPD ], 1)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // Ogerpon tera interactions
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true)
.attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.SPDEF ], 1)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // Ogerpon tera interactions
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.ATK ], 1)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // Ogerpon tera interactions
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true)
.attr(PostTeraFormChangeStatChangeAbAttr, [ Stat.DEF ], 1)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.partial(), // Ogerpon tera interactions
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.TERA_SHIFT, 9)
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(UncopiableAbilityAbAttr)

View File

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

View File

@ -2485,7 +2485,7 @@ export class TarShotTag extends BattlerTag {
* @returns whether the tag is applied
*/
override canAdd(pokemon: Pokemon): boolean {
return !pokemon.isTerastallized();
return !pokemon.isTerastallized;
}
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.
* @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 isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @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;
}
/**
* An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this.
* @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.
*/
applyMoveSlot(pokemon: Pokemon, moveSlots: Utils.IntegerHolder): boolean {
applyMoveSlot(pokemon: Pokemon, moveSlots: Utils.NumberHolder): boolean {
return false;
}
@ -393,10 +393,10 @@ export abstract class Challenge {
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @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.
*/
applyMoveAccessLevel(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
applyMoveAccessLevel(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.NumberHolder): boolean {
return false;
}
@ -405,10 +405,10 @@ export abstract class Challenge {
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @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.
*/
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.NumberHolder): boolean {
return false;
}
@ -456,8 +456,8 @@ export class SingleGenerationChallenge extends Challenge {
}
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : 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 baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
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))) {
valid.value = false;
return true;
@ -913,22 +913,22 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
* Apply all challenges that modify what level AI are.
* @param gameMode {@link GameMode} The current gameMode
* @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 isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @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.
* @param gameMode {@link GameMode} The current gameMode
* @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS
* @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.
*/
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.
* @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 moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @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.
*/
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
* @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 moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @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.
*/
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;

View File

@ -791,7 +791,7 @@ export default class Move implements Localizable {
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, null, typeChangeMovePowerMultiplier);
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;
}
@ -908,7 +908,7 @@ export class AttackMove extends Move {
attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2;
if (attackScore) {
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);
if (atk.value > user.getEffectiveStat(Stat.SPATK, target)) {
const statRatio = user.getEffectiveStat(Stat.SPATK, target) / atk.value;
@ -919,7 +919,7 @@ export class AttackMove extends Move {
}
}
} 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);
if (spAtk.value > user.getEffectiveStat(Stat.ATK, target)) {
const statRatio = user.getEffectiveStat(Stat.ATK, target) / spAtk.value;
@ -1337,7 +1337,7 @@ export class IgnoreOpponentStatStagesAttr extends MoveAttr {
export class HighCritAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value++;
(args[0] as Utils.NumberHolder).value++;
return true;
}
@ -1369,7 +1369,7 @@ export class FixedDamageAttr extends MoveAttr {
}
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;
}
@ -1385,7 +1385,7 @@ export class UserHpDamageAttr extends FixedDamageAttr {
}
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;
}
@ -1437,7 +1437,7 @@ export class MatchHpAttr extends FixedDamageAttr {
}
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;
}
@ -1467,7 +1467,7 @@ export class CounterDamageAttr extends FixedDamageAttr {
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);
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier);
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(damage * this.multiplier);
return true;
}
@ -1499,7 +1499,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
export class ModifiedDamageAttr extends MoveAttr {
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);
return true;
@ -2164,7 +2164,7 @@ export class IncrementMovePriorityAttr extends MoveAttr {
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @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
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2172,7 +2172,7 @@ export class IncrementMovePriorityAttr extends MoveAttr {
return false;
}
(args[0] as Utils.IntegerHolder).value += this.increaseAmount;
(args[0] as Utils.NumberHolder).value += this.increaseAmount;
return true;
}
}
@ -2210,7 +2210,7 @@ export class MultiHitAttr extends MoveAttr {
* @param user {@linkcode Pokemon} that used the attack
* @param target {@linkcode Pokemon} targeted by the attack
* @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
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2277,7 +2277,7 @@ export class ChangeMultiHitTypeAttr extends MoveAttr {
export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
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 false;
@ -4118,7 +4118,7 @@ export class PresentPowerAttr extends VariablePowerAttr {
export class WaterShurikenPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
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 false;
@ -4140,7 +4140,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr {
const stockpilingTag = user.getTag(StockpilingTag);
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;
return true;
}
@ -4449,7 +4449,7 @@ export class VariableAtkAttr extends MoveAttr {
}
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;
}
}
@ -4459,7 +4459,7 @@ export class TargetAtkUserAtkAttr extends VariableAtkAttr {
super();
}
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;
}
}
@ -4470,7 +4470,7 @@ export class DefAtkAttr extends VariableAtkAttr {
}
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;
}
}
@ -4481,7 +4481,7 @@ export class VariableDefAttr extends MoveAttr {
}
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;
}
}
@ -4492,7 +4492,7 @@ export class DefDefAttr extends VariableDefAttr {
}
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;
}
}
@ -4634,7 +4634,7 @@ export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
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)) {
category.value = MoveCategory.PHYSICAL;
return true;
@ -4662,7 +4662,7 @@ export class TeraBlastPowerAttr extends VariablePowerAttr {
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0] as Utils.NumberHolder;
if (user.isTerastallized() && user.getTeraType() === Type.STELLAR) {
if (user.isTerastallized && user.getTeraType() === Type.STELLAR) {
power.value = 100;
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 {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0];
@ -5009,7 +4985,7 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
return false;
}
if (user.isTerastallized()) {
if (user.isTerastallized) {
moveType.value = user.getTeraType(); // changes move type to tera type
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 {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0];
@ -6345,7 +6345,7 @@ export class RemoveTypeAttr extends MoveEffectAttr {
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;
}
@ -6525,7 +6525,7 @@ export class ChangeTypeAttr extends MoveEffectAttr {
}
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 {
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(TeraBlastTypeAttr)
.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)
.attr(ProtectAttr, BattlerTagType.SILK_TRAP)
.condition(failIfLastCondition),
@ -11271,7 +11271,7 @@ export function initMoves() {
new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
.attr(TeraMoveCategoryAttr)
.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} */
new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9)
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)

View File

@ -594,7 +594,7 @@ function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: P
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("spriteKey", tradedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", tradedPokemon.shiny);
@ -615,7 +615,7 @@ function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: P
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("spriteKey", receivedPokemon.getSpriteKey());
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 { PokeballType } from "#enums/pokeball";
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 { getPokemonSpecies } from "#app/data/pokemon-species";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
@ -279,7 +279,7 @@ async function summonSafariPokemon() {
if (pokemon.species.abilityHidden) {
const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
if (pokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256);
const hiddenAbilityChance = new NumberHolder(256);
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);

View File

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

View File

@ -12,7 +12,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon 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 { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
@ -452,7 +452,7 @@ async function postProcessTransformedPokemon(previousPokemon: PlayerPokemon, new
if (newPokemon.species.abilityHidden) {
const hiddenIndex = newPokemon.species.ability2 ? 2 : 1;
if (newPokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256);
const hiddenAbilityChance = new NumberHolder(256);
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
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 { globalScene } from "#app/global-scene";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Type } from "#app/enums/type";
/**
* Animates exclamation sprite over trainer's head at start of encounter
@ -98,6 +99,7 @@ export interface EnemyPokemonConfig {
modifierConfigs?: HeldModifierConfig[];
tags?: BattlerTagType[];
dataSource?: PokemonData;
tera?: Type;
aiType?: AiType;
}
@ -329,6 +331,14 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
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
if (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);
}
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("spriteKey", previousPokemon.getSpriteKey());
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 { StatusEffect } from "#enums/status-effect";
import { MoveCategory, allMoves } from "./move";
import { Type } from "#enums/type";
import type { Constructor, nil } from "#app/utils";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
@ -399,23 +398,7 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
* @extends SpeciesFormChangeTrigger
*/
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
/** The Tera type that triggers the form change */
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);
}
description = i18next.t("pokemonEvolutions:Forms.tera" );
}
/**
@ -425,10 +408,6 @@ export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
*/
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
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", "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", "teal-mask-tera", new SpeciesFormChangeTeraTrigger(Type.GRASS)),
new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.GRASS)),
new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(Type.WATER)),
new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.WATER)),
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeTeraTrigger(Type.FIRE)),
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.FIRE)),
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(Type.ROCK)),
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.ROCK))
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 SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true),
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 SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true)
],
[Species.TERAPAGOS]: [
new SpeciesFormChange(Species.TERAPAGOS, "", "terastal", new SpeciesFormChangeAbilityTrigger(), true),
new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(Type.STELLAR)),
new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.STELLAR))
new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(), true),
new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true)
],
[Species.GALAR_DARMANITAN]: [
new SpeciesFormChange(Species.GALAR_DARMANITAN, "", "zen", new SpeciesFormChangeAbilityTrigger(), true),

View File

@ -7,7 +7,7 @@ import i18next from "i18next";
import type { AnySound } from "#app/battle-scene";
import { globalScene } from "#app/global-scene";
import type { GameMode } from "#app/game-mode";
import type { StarterMoveset } from "#app/system/game-data";
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
import * as Utils from "#app/utils";
import { uncatchableSpecies } from "#app/data/balance/biomes";
import { speciesEggMoves } from "#app/data/balance/egg-moves";
@ -32,6 +32,37 @@ export enum Region {
PALDEA
}
// TODO: this is horrible and will need to be removed once a refactor/cleanup of forms is executed.
export const normalForm: Species[] = [
Species.PIKACHU,
Species.RAICHU,
Species.EEVEE,
Species.JOLTEON,
Species.FLAREON,
Species.VAPOREON,
Species.ESPEON,
Species.UMBREON,
Species.LEAFEON,
Species.GLACEON,
Species.SYLVEON,
Species.PICHU,
Species.ROTOM,
Species.DIALGA,
Species.PALKIA,
Species.KYUREM,
Species.GENESECT,
Species.FROAKIE,
Species.FROGADIER,
Species.GRENINJA,
Species.ROCKRUFF,
Species.NECROZMA,
Species.MAGEARNA,
Species.MARSHADOW,
Species.CRAMORANT,
Species.ZARUDE,
Species.CALYREX
];
/**
* Gets the {@linkcode PokemonSpecies} object associated with the {@linkcode Species} enum given
* @param species The species to fetch
@ -997,25 +1028,59 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
? this.forms[formIndex || 0].getFormSpriteKey()
: "";
}
/**
* Generates a {@linkcode bigint} corresponding to the maximum unlocks possible for this species,
* taking into account if the species has a male/female gender, and which variants are implemented.
* @returns {@linkcode bigint} Maximum unlocks, can be compared with {@linkcode DexEntry.caughtAttr}.
*/
getFullUnlocksData(): bigint {
let caughtAttr: bigint = 0n;
caughtAttr += DexAttr.NON_SHINY;
caughtAttr += DexAttr.SHINY;
if (this.malePercent !== null) {
if (this.malePercent > 0) {
caughtAttr += DexAttr.MALE;
}
if (this.malePercent < 100) {
caughtAttr += DexAttr.FEMALE;
}
}
caughtAttr += DexAttr.DEFAULT_VARIANT;
if (this.hasVariants()) {
caughtAttr += DexAttr.VARIANT_2;
caughtAttr += DexAttr.VARIANT_3;
}
// 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;
}
}
export class PokemonForm extends PokemonSpeciesForm {
public formName: string;
public formKey: string;
public formSpriteKey: string | null;
public isUnobtainable: boolean;
// This is a collection of form keys that have in-run form changes, but should still be separately selectable from the start screen
private starterSelectableKeys: string[] = [ "10", "50", "10-pc", "50-pc", "red", "orange", "yellow", "green", "blue", "indigo", "violet" ];
constructor(formName: string, formKey: string, type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
baseTotal: number, baseHp: number, baseAtk: number, baseDef: number, baseSpatk: number, baseSpdef: number, baseSpd: number,
catchRate: number, baseFriendship: number, baseExp: number, genderDiffs: boolean = false, formSpriteKey: string | null = null, isStarterSelectable: boolean = false
catchRate: number, baseFriendship: number, baseExp: number, genderDiffs: boolean = false, formSpriteKey: string | null = null, isStarterSelectable: boolean = false,
isUnobtainable: boolean = false
) {
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
catchRate, baseFriendship, baseExp, genderDiffs, (isStarterSelectable || !formKey));
this.formName = formName;
this.formKey = formKey;
this.formSpriteKey = formSpriteKey;
this.isUnobtainable = isUnobtainable;
}
getFormSpriteKey(_formIndex?: number) {
@ -1812,7 +1877,7 @@ export function initSpecies() {
new PokemonForm("Dragon", "dragon", Type.DRAGON, null, 3.2, 320, Abilities.MULTITYPE, Abilities.NONE, Abilities.NONE, 720, 120, 120, 120, 120, 120, 120, 3, 0, 360),
new PokemonForm("Dark", "dark", Type.DARK, null, 3.2, 320, Abilities.MULTITYPE, Abilities.NONE, Abilities.NONE, 720, 120, 120, 120, 120, 120, 120, 3, 0, 360),
new PokemonForm("Fairy", "fairy", Type.FAIRY, null, 3.2, 320, Abilities.MULTITYPE, Abilities.NONE, Abilities.NONE, 720, 120, 120, 120, 120, 120, 120, 3, 0, 360),
new PokemonForm("???", "unknown", Type.UNKNOWN, null, 3.2, 320, Abilities.MULTITYPE, Abilities.NONE, Abilities.NONE, 720, 120, 120, 120, 120, 120, 120, 3, 0, 360),
new PokemonForm("???", "unknown", Type.UNKNOWN, null, 3.2, 320, Abilities.MULTITYPE, Abilities.NONE, Abilities.NONE, 720, 120, 120, 120, 120, 120, 120, 3, 0, 360, false, null, false, true),
),
new PokemonSpecies(Species.VICTINI, 5, false, false, true, "Victory Pokémon", Type.PSYCHIC, Type.FIRE, 0.4, 4, Abilities.VICTORY_STAR, Abilities.NONE, Abilities.NONE, 600, 100, 100, 100, 100, 100, 100, 3, 100, 300, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.SNIVY, 5, false, false, false, "Grass Snake Pokémon", Type.GRASS, null, 0.6, 8.1, Abilities.OVERGROW, Abilities.NONE, Abilities.CONTRARY, 308, 45, 45, 55, 45, 55, 63, 45, 70, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
@ -2366,7 +2431,7 @@ export function initSpecies() {
),
new PokemonSpecies(Species.MARSHADOW, 7, false, false, true, "Gloomdweller Pokémon", Type.FIGHTING, Type.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, GrowthRate.SLOW, null, false, true,
new PokemonForm("Normal", "", Type.FIGHTING, Type.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, false, null, true),
new PokemonForm("Zenith", "zenith", Type.FIGHTING, Type.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300)
new PokemonForm("Zenith", "zenith", Type.FIGHTING, Type.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, false, null, false, true)
),
new PokemonSpecies(Species.POIPOLE, 7, true, false, false, "Poison Pin Pokémon", Type.POISON, null, 0.6, 1.8, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 420, 67, 73, 67, 73, 67, 73, 45, 0, 210, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.NAGANADEL, 7, true, false, false, "Poison Pin Pokémon", Type.POISON, Type.DRAGON, 3.6, 150, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 540, 73, 73, 73, 127, 73, 121, 45, 0, 270, GrowthRate.SLOW, null, false),
@ -2465,11 +2530,11 @@ export function initSpecies() {
new PokemonSpecies(Species.GRAPPLOCT, 8, false, false, false, "Jujitsu Pokémon", Type.FIGHTING, null, 1.6, 39, Abilities.LIMBER, Abilities.NONE, Abilities.TECHNICIAN, 480, 80, 118, 90, 70, 80, 42, 45, 50, 168, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.SINISTEA, 8, false, false, false, "Black Tea Pokémon", Type.GHOST, null, 0.1, 0.2, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.MEDIUM_FAST, null, false, false,
new PokemonForm("Phony Form", "phony", Type.GHOST, null, 0.1, 0.2, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true),
new PokemonForm("Antique Form", "antique", Type.GHOST, null, 0.1, 0.2, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true),
new PokemonForm("Antique Form", "antique", Type.GHOST, null, 0.1, 0.2, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true, true),
),
new PokemonSpecies(Species.POLTEAGEIST, 8, false, false, false, "Black Tea Pokémon", Type.GHOST, null, 0.2, 0.4, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, GrowthRate.MEDIUM_FAST, null, false, false,
new PokemonForm("Phony Form", "phony", Type.GHOST, null, 0.2, 0.4, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true),
new PokemonForm("Antique Form", "antique", Type.GHOST, null, 0.2, 0.4, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true),
new PokemonForm("Antique Form", "antique", Type.GHOST, null, 0.2, 0.4, Abilities.WEAK_ARMOR, Abilities.NONE, Abilities.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true, true),
),
new PokemonSpecies(Species.HATENNA, 8, false, false, false, "Calm Pokémon", Type.PSYCHIC, null, 0.4, 3.4, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 265, 42, 30, 45, 56, 53, 39, 235, 50, 53, GrowthRate.SLOW, 0, false),
new PokemonSpecies(Species.HATTREM, 8, false, false, false, "Serene Pokémon", Type.PSYCHIC, null, 0.6, 4.8, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 370, 57, 40, 65, 86, 73, 49, 120, 50, 130, GrowthRate.SLOW, 0, false),

View File

@ -175,11 +175,51 @@ export const trainerPartyTemplates = {
type PartyTemplateFunc = () => TrainerPartyTemplate;
type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
type GenAIFunc = (party: EnemyPokemon[]) => void;
export interface PartyMemberFuncs {
[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 {
public trainerType: TrainerType;
public trainerTypeDouble: TrainerType;
@ -203,6 +243,7 @@ export class TrainerConfig {
public doubleEncounterBgm: string;
public victoryBgm: string;
public genModifiersFunc: GenModifiersFunc;
public genAIFuncs: GenAIFunc[] = [];
public modifierRewardFuncs: ModifierTypeFunc[] = [];
public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc;
@ -212,6 +253,7 @@ export class TrainerConfig {
public speciesFilter: PokemonSpeciesFilter;
public specialtyTypes: Type[] = [];
public hasVoucher: boolean = false;
public trainerAI: TrainerAI;
public encounterMessages: string[] = [];
public victoryMessages: string[] = [];
@ -227,6 +269,7 @@ export class TrainerConfig {
constructor(trainerType: TrainerType, allowLegendaries?: boolean) {
this.trainerType = trainerType;
this.trainerAI = new TrainerAI();
this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]);
this.battleBgm = "battle_trainer";
this.mixedBattleBgm = "battle_trainer";
@ -550,6 +593,47 @@ export class TrainerConfig {
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 {
this.eventRewardFuncs = modifierTypeFuncs.map(func => () => {
const modifierTypeFunc = func();
@ -581,107 +665,107 @@ export class TrainerConfig {
switch (team) {
case "rocket": {
return {
[TrainerPoolTier.COMMON]: [ Species.RATTATA, Species.KOFFING, Species.EKANS, Species.ZUBAT, Species.MAGIKARP, Species.HOUNDOUR, Species.ONIX, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.DROWZEE, Species.VILEPLUME ],
[TrainerPoolTier.UNCOMMON]: [ Species.PORYGON, Species.MANKEY, Species.MAGNEMITE, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO, Species.MAGBY, Species.ELEKID ],
[TrainerPoolTier.RARE]: [ Species.DRATINI, Species.LARVITAR ]
[TrainerPoolTier.COMMON]: [ Species.RATICATE, Species.ARBOK, Species.VILEPLUME, Species.ARCANINE, Species.GENGAR, Species.HYPNO, Species.ELECTRODE, Species.EXEGGUTOR, Species.CUBONE, Species.KOFFING, Species.GYARADOS, Species.CROBAT, Species.STEELIX, Species.HOUNDOOM, Species.HONCHKROW ],
[TrainerPoolTier.UNCOMMON]: [ Species.OMASTAR, Species.KABUTOPS, Species.MAGNEZONE, Species.ELECTIVIRE, Species.MAGMORTAR, Species.PORYGON_Z, Species.ANNIHILAPE, Species.ALOLA_SANDSLASH, Species.ALOLA_PERSIAN, Species.ALOLA_GOLEM, Species.ALOLA_MUK, Species.PALDEA_TAUROS ],
[TrainerPoolTier.RARE]: [ Species.DRAGONITE, Species.TYRANITAR ]
};
}
case "magma": {
return {
[TrainerPoolTier.COMMON]: [ Species.GROWLITHE, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.BALTOY, Species.ROLYCOLY, Species.GLIGAR, Species.TORKOAL, Species.HOUNDOUR, Species.MAGBY ],
[TrainerPoolTier.UNCOMMON]: [ Species.TRAPINCH, Species.SILICOBRA, Species.RHYHORN, Species.ANORITH, Species.LILEEP, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ],
[TrainerPoolTier.RARE]: [ Species.CAPSAKID, Species.CHARCADET ]
[TrainerPoolTier.COMMON]: [ Species.ARCANINE, Species.MAGCARGO, Species.HOUNDOOM, Species.TORKOAL, Species.SOLROCK, Species.CLAYDOL, Species.HIPPOWDON, Species.MAGMORTAR, Species.GLISCOR, Species.COALOSSAL ],
[TrainerPoolTier.UNCOMMON]: [ Species.AGGRON, Species.FLYGON, Species.CRADILY, Species.ARMALDO, Species.RHYPERIOR, Species.TURTONATOR, Species.SANDACONDA, Species.TOEDSCRUEL, Species.HISUI_ARCANINE ],
[TrainerPoolTier.RARE]: [ Species.CHARCADET, Species.SCOVILLAIN ]
};
}
case "aqua": {
return {
[TrainerPoolTier.COMMON]: [ Species.CORPHISH, Species.SPHEAL, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.LOTAD, Species.WAILMER, Species.REMORAID, Species.BARBOACH ],
[TrainerPoolTier.UNCOMMON]: [ Species.MANTYKE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.DHELMISE, Species.CLOBBOPUS, Species.FEEBAS, Species.PALDEA_WOOPER, Species.HORSEA, Species.SKRELP ],
[TrainerPoolTier.RARE]: [ Species.DONDOZO, Species.BASCULEGION ]
[TrainerPoolTier.COMMON]: [ Species.TENTACRUEL, Species.LANTURN, Species.AZUMARILL, Species.QUAGSIRE, Species.OCTILLERY, Species.LUDICOLO, Species.PELIPPER, Species.WAILORD, Species.WHISCASH, Species.CRAWDAUNT, Species.WALREIN, Species.CLAMPERL ],
[TrainerPoolTier.UNCOMMON]: [ Species.QUAGSIRE, Species.MANTINE, Species.KINGDRA, Species.MILOTIC, Species.DRAGALGE, Species.DHELMISE, Species.BARRASKEWDA, Species.GRAPPLOCT, Species.OVERQWIL ],
[TrainerPoolTier.RARE]: [ Species.BASCULEGION, Species.DONDOZO ]
};
}
case "galactic": {
return {
[TrainerPoolTier.COMMON]: [ Species.BRONZOR, Species.SWINUB, Species.YANMA, Species.LICKITUNG, Species.TANGELA, Species.MAGBY, Species.ELEKID, Species.SKORUPI, Species.ZUBAT, Species.MURKROW, Species.MAGIKARP, Species.VOLTORB ],
[TrainerPoolTier.UNCOMMON]: [ Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.DUSKULL, Species.ROTOM, Species.HISUI_VOLTORB, Species.GLIGAR, Species.ABRA ],
[TrainerPoolTier.RARE]: [ Species.URSALUNA, Species.HISUI_LILLIGANT, Species.SPIRITOMB, Species.HISUI_SNEASEL ]
[TrainerPoolTier.COMMON]: [ Species.ELECTRODE, Species.GYARADOS, Species.CROBAT, Species.HONCHKROW, Species.BRONZONG, Species.DRAPION, Species.LICKILICKY, Species.TANGROWTH, Species.ELECTIVIRE, Species.MAGMORTAR, Species.YANMEGA, Species.MAMOSWINE ],
[TrainerPoolTier.UNCOMMON]: [ Species.ALAKAZAM, Species.WEAVILE, Species.GLISCOR, Species.DUSKNOIR, Species.ROTOM, Species.OVERQWIL, Species.HISUI_ARCANINE, Species.HISUI_ELECTRODE ],
[TrainerPoolTier.RARE]: [ Species.SPIRITOMB, Species.URSALUNA, Species.SNEASLER, Species.HISUI_LILLIGANT ]
};
}
case "plasma": {
return {
[TrainerPoolTier.COMMON]: [ Species.YAMASK, Species.ROGGENROLA, Species.JOLTIK, Species.TYMPOLE, Species.FRILLISH, Species.FERROSEED, Species.SANDILE, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.CUBCHOO, Species.VANILLITE ],
[TrainerPoolTier.UNCOMMON]: [ Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK, Species.TYNAMO, Species.GALAR_DARUMAKA, Species.GOLETT, Species.MIENFOO, Species.DURANT, Species.SIGILYPH ],
[TrainerPoolTier.RARE]: [ Species.HISUI_ZORUA, Species.AXEW, Species.DEINO, Species.HISUI_BRAVIARY ]
[TrainerPoolTier.COMMON]: [ Species.GIGALITH, Species.CONKELDURR, Species.SEISMITOAD, Species.KROOKODILE, Species.DARMANITAN, Species.COFAGRIGUS, Species.VANILLUXE, Species.AMOONGUSS, Species.JELLICENT, Species.GALVANTULA, Species.FERROTHORN, Species.BEARTIC ],
[TrainerPoolTier.UNCOMMON]: [ Species.EXCADRILL, Species.SIGILYPH, Species.ZOROARK, Species.KLINKLANG, Species.EELEKTROSS, Species.MIENSHAO, Species.GOLURK, Species.BISHARP, Species.MANDIBUZZ, Species.DURANT, Species.GALAR_DARMANITAN ],
[TrainerPoolTier.RARE]: [ Species.HAXORUS, Species.HYDREIGON, Species.HISUI_ZOROARK, Species.HISUI_BRAVIARY ]
};
}
case "plasma_2": {
return {
[TrainerPoolTier.COMMON]: [ Species.MAGNEMITE, Species.GRIMER, Species.VOLTORB, Species.PORYGON, Species.BRONZOR, Species.MUNNA, Species.SOLOSIS, Species.JOLTIK, Species.FERROSEED, Species.TYNAMO, Species.ELGYEM ],
[TrainerPoolTier.UNCOMMON]: [ Species.BELDUM, Species.ROTOM, Species.TIRTOUGA, Species.ARCHEN, Species.GOLETT, Species.DURANT, Species.GRUBBIN, Species.BLIPBUG, Species.VAROOM, Species.ALOLA_GRIMER, Species.HISUI_VOLTORB ],
[TrainerPoolTier.RARE]: [ Species.ELEKID, Species.MAGBY, Species.PAWNIARD, Species.DURALUDON ]
[TrainerPoolTier.COMMON]: [ Species.MUK, Species.ELECTRODE, Species.BRONZONG, Species.MAGNEZONE, Species.PORYGON_Z, Species.MUSHARNA, Species.REUNICLUS, Species.GALVANTULA, Species.FERROTHORN, Species.EELEKTROSS, Species.BEHEEYEM ],
[TrainerPoolTier.UNCOMMON]: [ Species.METAGROSS, Species.ROTOM, Species.CARRACOSTA, Species.ARCHEOPS, Species.GOLURK, Species.DURANT, Species.VIKAVOLT, Species.ORBEETLE, Species.REVAVROOM, Species.ALOLA_MUK, Species.HISUI_ELECTRODE ],
[TrainerPoolTier.RARE]: [ Species.ELECTIVIRE, Species.MAGMORTAR, Species.BISHARP, Species.ARCHALUDON ]
};
}
case "flare": {
return {
[TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.FOONGUS, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY ],
[TrainerPoolTier.UNCOMMON]: [ Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO ],
[TrainerPoolTier.RARE]: [ Species.NOIBAT, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO ]
[TrainerPoolTier.COMMON]: [ Species.MANECTRIC, Species.DRAPION, Species.LIEPARD, Species.AMOONGUSS, Species.DIGGERSBY, Species.TALONFLAME, Species.PYROAR, Species.PANGORO, Species.MEOWSTIC, Species.MALAMAR, Species.CLAWITZER, Species.HELIOLISK ],
[TrainerPoolTier.UNCOMMON]: [ Species.HOUNDOOM, Species.WEAVILE, Species.CHANDELURE, Species.AEGISLASH, Species.BARBARACLE, Species.DRAGALGE, Species.GOODRA, Species.TREVENANT, Species.GOURGEIST ],
[TrainerPoolTier.RARE]: [ Species.NOIVERN, Species.HISUI_GOODRA, Species.HISUI_AVALUGG ]
};
}
case "aether": {
return {
[TrainerPoolTier.COMMON]: [ Species.BRUXISH, Species.SLOWPOKE, Species.BALTOY, Species.EXEGGCUTE, Species.ABRA, Species.ALOLA_RAICHU, Species.ELGYEM, Species.NATU, Species.BLIPBUG, Species.GIRAFARIG, Species.ORANGURU ],
[TrainerPoolTier.UNCOMMON]: [ Species.GALAR_SLOWPOKE, Species.MEDITITE, Species.BELDUM, Species.HATENNA, Species.INKAY, Species.RALTS, Species.GALAR_MR_MIME ],
[TrainerPoolTier.RARE]: [ Species.ARMAROUGE, Species.HISUI_BRAVIARY, Species.PORYGON ]
[TrainerPoolTier.COMMON]: [ Species.ALAKAZAM, Species.SLOWBRO, Species.EXEGGUTOR, Species.XATU, Species.CLAYDOL, Species.BEHEEYEM, Species.ORANGURU, Species.BRUXISH, Species.ORBEETLE, Species.FARIGIRAF, Species.ALOLA_RAICHU ],
[TrainerPoolTier.UNCOMMON]: [ Species.KIRLIA, Species.MEDICHAM, Species.METAGROSS, Species.MALAMAR, Species.HATTERENE, Species.MR_RIME, Species.GALAR_SLOWKING ],
[TrainerPoolTier.RARE]: [ Species.PORYGON_Z, Species.ARMAROUGE, Species.HISUI_BRAVIARY ]
};
}
case "skull": {
return {
[TrainerPoolTier.COMMON]: [ Species.MAREANIE, Species.ALOLA_GRIMER, Species.GASTLY, Species.ZUBAT, Species.FOMANTIS, Species.VENIPEDE, Species.BUDEW, Species.KOFFING, Species.STUNKY, Species.CROAGUNK, Species.NIDORAN_F ],
[TrainerPoolTier.UNCOMMON]: [ Species.GALAR_SLOWPOKE, Species.SKORUPI, Species.PALDEA_WOOPER, Species.VULLABY, Species.HISUI_QWILFISH, Species.GLIMMET ],
[TrainerPoolTier.RARE]: [ Species.SKRELP, Species.HISUI_SNEASEL ]
[TrainerPoolTier.COMMON]: [ Species.NIDOQUEEN, Species.GENGAR, Species.KOFFING, Species.CROBAT, Species.ROSERADE, Species.SKUNTANK, Species.TOXICROAK, Species.SCOLIPEDE, Species.TOXAPEX, Species.LURANTIS, Species.ALOLA_MUK ],
[TrainerPoolTier.UNCOMMON]: [ Species.DRAPION, Species.MANDIBUZZ, Species.OVERQWIL, Species.GLIMMORA, Species.CLODSIRE, Species.GALAR_SLOWBRO ],
[TrainerPoolTier.RARE]: [ Species.DRAGALGE, Species.SNEASLER ]
};
}
case "macro": {
return {
[TrainerPoolTier.COMMON]: [ Species.HATENNA, Species.FEEBAS, Species.BOUNSWEET, Species.SALANDIT, Species.GALAR_PONYTA, Species.GOTHITA, Species.FROSLASS, Species.VULPIX, Species.FRILLISH, Species.ODDISH, Species.SINISTEA ],
[TrainerPoolTier.UNCOMMON]: [ Species.VULLABY, Species.MAREANIE, Species.ALOLA_VULPIX, Species.TOGEPI, Species.GALAR_CORSOLA, Species.APPLIN ],
[TrainerPoolTier.RARE]: [ Species.TINKATINK, Species.HISUI_LILLIGANT ]
[TrainerPoolTier.COMMON]: [ Species.NINETALES, Species.BELLOSSOM, Species.MILOTIC, Species.FROSLASS, Species.GOTHITELLE, Species.JELLICENT, Species.SALAZZLE, Species.TSAREENA, Species.POLTEAGEIST, Species.HATTERENE, Species.GALAR_RAPIDASH ],
[TrainerPoolTier.UNCOMMON]: [ Species.TOGEKISS, Species.MANDIBUZZ, Species.TOXAPEX, Species.APPLETUN, Species.CURSOLA, Species.ALOLA_NINETALES ],
[TrainerPoolTier.RARE]: [ Species.TINKATON, Species.HISUI_LILLIGANT ]
};
}
case "star_1": {
return {
[TrainerPoolTier.COMMON]: [ Species.MURKROW, Species.SEEDOT, Species.CACNEA, Species.STUNKY, Species.SANDILE, Species.NYMBLE, Species.MASCHIFF, Species.GALAR_ZIGZAGOON ],
[TrainerPoolTier.UNCOMMON]: [ Species.UMBREON, Species.SNEASEL, Species.CORPHISH, Species.ZORUA, Species.INKAY, Species.BOMBIRDIER ],
[TrainerPoolTier.RARE]: [ Species.DEINO, Species.SPRIGATITO ]
[TrainerPoolTier.COMMON]: [ Species.SHIFTRY, Species.CACTURNE, Species.HONCHKROW, Species.SKUNTANK, Species.KROOKODILE, Species.OBSTAGOON, Species.LOKIX, Species.MABOSSTIFF ],
[TrainerPoolTier.UNCOMMON]: [ Species.UMBREON, Species.CRAWDAUNT, Species.WEAVILE, Species.ZOROARK, Species.MALAMAR, Species.BOMBIRDIER ],
[TrainerPoolTier.RARE]: [ Species.HYDREIGON, Species.MEOWSCARADA ]
};
}
case "star_2": {
return {
[TrainerPoolTier.COMMON]: [ Species.GROWLITHE, Species.HOUNDOUR, Species.NUMEL, Species.LITWICK, Species.FLETCHLING, Species.LITLEO, Species.ROLYCOLY, Species.CAPSAKID ],
[TrainerPoolTier.UNCOMMON]: [ Species.PONYTA, Species.FLAREON, Species.MAGBY, Species.TORKOAL, Species.SALANDIT, Species.TURTONATOR ],
[TrainerPoolTier.RARE]: [ Species.LARVESTA, Species.FUECOCO ]
[TrainerPoolTier.COMMON]: [ Species.ARCANINE, Species.HOUNDOOM, Species.CAMERUPT, Species.CHANDELURE, Species.TALONFLAME, Species.PYROAR, Species.COALOSSAL, Species.SCOVILLAIN ],
[TrainerPoolTier.UNCOMMON]: [ Species.RAPIDASH, Species.FLAREON, Species.TORKOAL, Species.MAGMORTAR, Species.SALAZZLE, Species.TURTONATOR ],
[TrainerPoolTier.RARE]: [ Species.VOLCARONA, Species.SKELEDIRGE ]
};
}
case "star_3": {
return {
[TrainerPoolTier.COMMON]: [ Species.ZUBAT, Species.GRIMER, Species.STUNKY, Species.FOONGUS, Species.MAREANIE, Species.TOXEL, Species.SHROODLE, Species.PALDEA_WOOPER ],
[TrainerPoolTier.UNCOMMON]: [ Species.GASTLY, Species.SEVIPER, Species.SKRELP, Species.ALOLA_GRIMER, Species.GALAR_SLOWPOKE, Species.HISUI_QWILFISH ],
[TrainerPoolTier.RARE]: [ Species.GLIMMET, Species.BULBASAUR ]
[TrainerPoolTier.COMMON]: [ Species.MUK, Species.CROBAT, Species.SKUNTANK, Species.AMOONGUSS, Species.TOXAPEX, Species.TOXTRICITY, Species.GRAFAIAI, Species.CLODSIRE ],
[TrainerPoolTier.UNCOMMON]: [ Species.GENGAR, Species.SEVIPER, Species.DRAGALGE, Species.OVERQWIL, Species.ALOLA_MUK, Species.GALAR_SLOWBRO ],
[TrainerPoolTier.RARE]: [ Species.GLIMMORA, Species.VENUSAUR ]
};
}
case "star_4": {
return {
[TrainerPoolTier.COMMON]: [ Species.CLEFFA, Species.IGGLYBUFF, Species.AZURILL, Species.COTTONEE, Species.FLABEBE, Species.HATENNA, Species.IMPIDIMP, Species.TINKATINK ],
[TrainerPoolTier.UNCOMMON]: [ Species.TOGEPI, Species.GARDEVOIR, Species.SYLVEON, Species.KLEFKI, Species.MIMIKYU, Species.ALOLA_VULPIX ],
[TrainerPoolTier.RARE]: [ Species.GALAR_PONYTA, Species.POPPLIO ]
[TrainerPoolTier.COMMON]: [ Species.CLEFABLE, Species.WIGGLYTUFF, Species.AZUMARILL, Species.WHIMSICOTT, Species.FLORGES, Species.HATTERENE, Species.GRIMMSNARL, Species.TINKATON ],
[TrainerPoolTier.UNCOMMON]: [ Species.TOGEKISS, Species.GARDEVOIR, Species.SYLVEON, Species.KLEFKI, Species.MIMIKYU, Species.ALOLA_NINETALES ],
[TrainerPoolTier.RARE]: [ Species.GALAR_RAPIDASH, Species.PRIMARINA ]
};
}
case "star_5": {
return {
[TrainerPoolTier.COMMON]: [ Species.SHROOMISH, Species.MAKUHITA, Species.MEDITITE, Species.CROAGUNK, Species.SCRAGGY, Species.MIENFOO, Species.PAWMI, Species.PALDEA_TAUROS ],
[TrainerPoolTier.UNCOMMON]: [ Species.RIOLU, Species.TIMBURR, Species.HAWLUCHA, Species.PASSIMIAN, Species.FALINKS, Species.FLAMIGO ],
[TrainerPoolTier.RARE]: [ Species.JANGMO_O, Species.QUAXLY ]
[TrainerPoolTier.COMMON]: [ Species.BRELOOM, Species.HARIYAMA, Species.MEDICHAM, Species.TOXICROAK, Species.SCRAFTY, Species.MIENSHAO, Species.PAWMOT, Species.PALDEA_TAUROS ],
[TrainerPoolTier.UNCOMMON]: [ Species.LUCARIO, Species.CONKELDURR, Species.HAWLUCHA, Species.PASSIMIAN, Species.FALINKS, Species.FLAMIGO ],
[TrainerPoolTier.RARE]: [ Species.KOMMO_O, Species.QUAQUAVAL ]
};
}
}
@ -851,10 +935,7 @@ export class TrainerConfig {
this.setHasVoucher(true);
this.setBattleBgm("battle_unova_gym");
this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => {
const waveIndex = globalScene.currentBattle.waveIndex;
return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined);
});
this.setRandomTeraModifiers(() => globalScene.currentBattle.waveIndex >= 100 ? 1 : 0);
return this;
}
@ -910,7 +991,7 @@ export class TrainerConfig {
this.setHasVoucher(true);
this.setBattleBgm("battle_unova_elite");
this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 1, specialtyTypes.length ? specialtyTypes : undefined));
this.setRandomTeraModifiers(() => 1);
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 = {
[key in string]: (Species | Species[])[];
@ -1500,9 +1571,9 @@ export const trainerConfigs: TrainerConfigs = {
),
[TrainerType.ROCKET_GRUNT]: new TrainerConfig(++t).setHasGenders("Rocket Grunt Female").setHasDouble("Rocket Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH, Species.SLOWPOKE ],
[TrainerPoolTier.UNCOMMON]: [ Species.GYARADOS, Species.LICKITUNG, Species.TAUROS, Species.MANKEY, Species.SCYTHER, Species.ELEKID, Species.MAGBY, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.MAGNEMITE ],
[TrainerPoolTier.RARE]: [ Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO ],
[TrainerPoolTier.COMMON]: [ Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.ODDISH, Species.GEODUDE, Species.SLOWPOKE, Species.GRIMER, Species.KOFFING ],
[TrainerPoolTier.UNCOMMON]: [ Species.MANKEY, Species.GROWLITHE, Species.MAGNEMITE, Species.GASTLY, Species.VOLTORB, Species.EXEGGCUTE, Species.CUBONE, Species.LICKITUNG, Species.SCYTHER, Species.TAUROS, Species.GYARADOS, Species.MURKROW, Species.ELEKID, Species.MAGBY ],
[TrainerPoolTier.RARE]: [ Species.PORYGON, Species.OMANYTE, Species.KABUTO, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GEODUDE, Species.ALOLA_GRIMER, Species.PALDEA_TAUROS ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DRATINI, Species.LARVITAR ]
}),
[TrainerType.ARCHER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.HOUNDOOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
@ -1511,71 +1582,71 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.PETREL]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.WEEZING ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MAGMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Magma Grunt Female").setHasDouble("Magma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.GROWLITHE, Species.BALTOY ],
[TrainerPoolTier.UNCOMMON]: [ Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR, Species.RHYHORN, Species.HEATMOR ],
[TrainerPoolTier.RARE]: [ Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ],
[TrainerPoolTier.SUPER_RARE]: [ Species.CAPSAKID, Species.CHARCADET ]
[TrainerPoolTier.COMMON]: [ Species.DIGLETT, Species.GROWLITHE, Species.SLUGMA, Species.MAGBY, Species.POOCHYENA, Species.ZIGZAGOON, Species.NUMEL, Species.TORKOAL, Species.BALTOY ],
[TrainerPoolTier.UNCOMMON]: [ Species.RHYHORN, Species.GLIGAR, Species.PHANPY, Species.SOLROCK, Species.HIPPOPOTAS, Species.HEATMOR, Species.ROLYCOLY, Species.SILICOBRA ],
[TrainerPoolTier.RARE]: [ Species.ARON, Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.TURTONATOR, Species.TOEDSCOOL, Species.HISUI_GROWLITHE ],
[TrainerPoolTier.SUPER_RARE]: [ Species.CHARCADET, Species.CAPSAKID ]
}),
[TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.COURTNEY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin_female", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.AQUA_GRUNT]: new TrainerConfig(++t).setHasGenders("Aqua Grunt Female").setHasDouble("Aqua Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH ],
[TrainerPoolTier.UNCOMMON]: [ Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA ],
[TrainerPoolTier.RARE]: [ Species.MANTYKE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.BASCULEGION ]
[TrainerPoolTier.COMMON]: [ Species.QWILFISH, Species.REMORAID, Species.ZIGZAGOON, Species.LOTAD, Species.CARVANHA, Species.WAILMER, Species.BARBOACH, Species.CORPHISH, Species.SPHEAL ],
[TrainerPoolTier.UNCOMMON]: [ Species.TENTACOOL, Species.HORSEA, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.AZURILL, Species.CLAMPERL, Species.CLOBBOPUS ],
[TrainerPoolTier.RARE]: [ Species.MANTYKE, Species.SKRELP, Species.DHELMISE, Species.ARROKUDA, Species.HISUI_QWILFISH, Species.PALDEA_WOOPER ],
[TrainerPoolTier.SUPER_RARE]: [ Species.BASCULEGION, Species.DONDOZO ]
}),
[TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SHELLY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin_female", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.GALACTIC_GRUNT]: new TrainerConfig(++t).setHasGenders("Galactic Grunt Female").setHasDouble("Galactic Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY, Species.CARNIVINE ],
[TrainerPoolTier.UNCOMMON]: [ Species.LICKITUNG, Species.RHYHORN, Species.TANGELA, Species.ZUBAT, Species.YANMA, Species.SKORUPI, Species.GLIGAR, Species.SWINUB ],
[TrainerPoolTier.RARE]: [ Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.ELEKID, Species.MAGBY, Species.DUSKULL ],
[TrainerPoolTier.SUPER_RARE]: [ Species.ROTOM, Species.SPIRITOMB, Species.HISUI_SNEASEL ]
[TrainerPoolTier.COMMON]: [ Species.WURMPLE, Species.SHINX, Species.BURMY, Species.DRIFLOON, Species.GLAMEOW, Species.STUNKY, Species.BRONZOR, Species.CROAGUNK, Species.CARNIVINE ],
[TrainerPoolTier.UNCOMMON]: [ Species.ZUBAT, Species.LICKITUNG, Species.RHYHORN, Species.TANGELA, Species.YANMA, Species.GLIGAR, Species.SWINUB, Species.SKORUPI ],
[TrainerPoolTier.RARE]: [ Species.SNEASEL, Species.ELEKID, Species.MAGBY, Species.DUSKULL, Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH ],
[TrainerPoolTier.SUPER_RARE]: [ Species.SPIRITOMB, Species.ROTOM, Species.HISUI_SNEASEL ]
}),
[TrainerType.JUPITER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.SKUNTANK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MARS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.PURUGLY ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SATURN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander", "galactic", [ Species.TOXICROAK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE ],
[TrainerPoolTier.UNCOMMON]: [ Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK, Species.CUBCHOO, Species.KLINK ],
[TrainerPoolTier.RARE]: [ Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO ]
[TrainerPoolTier.COMMON]: [ Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.WOOBAT, Species.TYMPOLE, Species.SANDILE, Species.SCRAGGY, Species.TRUBBISH, Species.VANILLITE ],
[TrainerPoolTier.UNCOMMON]: [ Species.TIMBURR, Species.VENIPEDE, Species.DARUMAKA, Species.FOONGUS, Species.FRILLISH, Species.JOLTIK, Species.KLINK, Species.CUBCHOO, Species.GOLETT ],
[TrainerPoolTier.RARE]: [ Species.DRILBUR, Species.ZORUA, Species.MIENFOO, Species.PAWNIARD, Species.BOUFFALANT, Species.RUFFLET, Species.VULLABY, Species.DURANT ],
[TrainerPoolTier.SUPER_RARE]: [ Species.AXEW, Species.DRUDDIGON, Species.DEINO, Species.HISUI_ZORUA ]
}),
[TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [ Species.CRYOGONAL ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.COLRESS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_boss", "plasma_2", [ Species.KLINKLANG ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_colress").setMixedBattleBgm("battle_colress").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR ],
[TrainerPoolTier.UNCOMMON]: [ Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP, Species.FOONGUS ],
[TrainerPoolTier.RARE]: [ Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO ],
[TrainerPoolTier.COMMON]: [ Species.PONYTA, Species.HOUNDOUR, Species.SKORUPI, Species.CROAGUNK, Species.SCRAGGY, Species.FLETCHLING, Species.SCATTERBUG, Species.LITLEO, Species.ESPURR, Species.INKAY ],
[TrainerPoolTier.UNCOMMON]: [ Species.POOCHYENA, Species.ELECTRIKE, Species.PURRLOIN, Species.FOONGUS, Species.PANCHAM, Species.BINACLE, Species.SKRELP, Species.CLAUNCHER, Species.HELIOPTILE, Species.PHANTUMP, Species.PUMPKABOO ],
[TrainerPoolTier.RARE]: [ Species.SNEASEL, Species.LITWICK, Species.PAWNIARD, Species.SLIGGOO ],
[TrainerPoolTier.SUPER_RARE]: [ Species.NOIBAT, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG ]
}),
[TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [ Species.LIEPARD ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [ Species.MALAMAR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU ],
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK ],
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.TURTONATOR, Species.MIMIKYU, Species.MAGNEMITE, Species.DRAMPA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.JANGMO_O, Species.PORYGON ]
[TrainerPoolTier.COMMON]: [ Species.CORSOLA, Species.LILLIPUP, Species.PIKIPEK, Species.YUNGOOS, Species.ROCKRUFF, Species.MORELULL, Species.BOUNSWEET, Species.COMFEY, Species.KOMALA, Species.TOGEDEMARU, Species.ALOLA_RAICHU, Species.ALOLA_DIGLETT, Species.ALOLA_GEODUDE, Species.ALOLA_EXEGGUTOR ],
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.CRABRAWLER, Species.ORICORIO, Species.CUTIEFLY, Species.WISHIWASHI, Species.MUDBRAY, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.PYUKUMUKU, Species.MINIOR, Species.BRUXISH, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.ALOLA_MAROWAK ],
[TrainerPoolTier.RARE]: [ Species.MAGNEMITE, Species.TURTONATOR, Species.MIMIKYU, Species.DRAMPA, Species.GALAR_CORSOLA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.PORYGON, Species.JANGMO_O ]
}),
[TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [ Species.HYPNO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH, Species.DROWZEE ],
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY ],
[TrainerPoolTier.RARE]: [ Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE ],
[TrainerPoolTier.COMMON]: [ Species.EKANS, Species.DROWZEE, Species.KOFFING, Species.SPINARAK, Species.SCRAGGY, Species.TRUBBISH, Species.MAREANIE, Species.SALANDIT, Species.ALOLA_RATTATA, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER ],
[TrainerPoolTier.UNCOMMON]: [ Species.ZUBAT, Species.GASTLY, Species.HOUNDOUR, Species.SABLEYE, Species.VENIPEDE, Species.SANDILE, Species.VULLABY, Species.PANCHAM, Species.FOMANTIS, Species.ALOLA_MAROWAK ],
[TrainerPoolTier.RARE]: [ Species.PAWNIARD, Species.WISHIWASHI, Species.SANDYGAST, Species.MIMIKYU, Species.DHELMISE, Species.NYMBLE ],
[TrainerPoolTier.SUPER_RARE]: [ Species.GRUBBIN, Species.DEWPIDER ]
}),
[TrainerType.PLUMERIA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("skull_admin", "skull", [ Species.SALAZZLE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MACRO_GRUNT]: new TrainerConfig(++t).setHasGenders("Macro Grunt Female").setHasDouble("Macro Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_macro_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.CUFANT, Species.GALAR_MEOWTH, Species.KLINK, Species.ROOKIDEE, Species.CRAMORANT, Species.GALAR_ZIGZAGOON, Species.SKWOVET, Species.STEELIX, Species.MAWILE, Species.FERROSEED ],
[TrainerPoolTier.UNCOMMON]: [ Species.DRILBUR, Species.MAGNEMITE, Species.HATENNA, Species.ARROKUDA, Species.APPLIN, Species.GALAR_PONYTA, Species.GALAR_YAMASK, Species.SINISTEA, Species.RIOLU ],
[TrainerPoolTier.RARE]: [ Species.FALINKS, Species.BELDUM, Species.GALAR_FARFETCHD, Species.GALAR_MR_MIME, Species.HONEDGE, Species.SCIZOR, Species.GALAR_DARUMAKA ],
[TrainerPoolTier.COMMON]: [ Species.STEELIX, Species.MAWILE, Species.FERROSEED, Species.KLINK, Species.SKWOVET, Species.ROOKIDEE, Species.CRAMORANT, Species.CUFANT, Species.GALAR_MEOWTH, Species.GALAR_ZIGZAGOON ],
[TrainerPoolTier.UNCOMMON]: [ Species.MAGNEMITE, Species.RIOLU, Species.DRILBUR, Species.APPLIN, Species.ARROKUDA, Species.SINISTEA, Species.HATENNA, Species.GALAR_PONYTA, Species.GALAR_YAMASK ],
[TrainerPoolTier.RARE]: [ Species.SCIZOR, Species.BELDUM, Species.HONEDGE, Species.FALINKS, Species.GALAR_FARFETCHD, Species.GALAR_MR_MIME, Species.GALAR_DARUMAKA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DURALUDON, Species.DREEPY ]
}),
[TrainerType.OLEANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("macro_admin", "macro", [ Species.GARBODOR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_oleana").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
@ -1747,10 +1818,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.gender = Gender.MALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[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 => {
p.formIndex = 8; // G-Max Pikachu
@ -1774,10 +1842,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.gender = Gender.MALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[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(1, getRandomPartyMemberFunc([ Species.AERODACTYL ]))
@ -1787,16 +1852,15 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
}))
.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 => {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(4),
[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(1, getRandomPartyMemberFunc([ Species.CRADILY, Species.ARMALDO ]))
@ -1814,10 +1878,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
p.generateName();
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(4),
[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 => {
p.abilityIndex = 1; // Drizzle
@ -1840,10 +1901,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(4),
[TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SPIRITOMB ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -1853,7 +1911,9 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
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 => {
p.generateAndPopulateMoveset();
p.setBoss(true, 2);
@ -1864,10 +1924,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.gender = Gender.FEMALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[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(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.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 => {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
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 ];
}),
.setInstantTera(4),
[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(1, getRandomPartyMemberFunc([ Species.ARCHEOPS ]))
@ -1899,7 +1955,9 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
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 => {
p.formIndex = 1; // G-Max Lapras
p.generateAndPopulateMoveset();
@ -1911,10 +1969,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ Type.DRAGON ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_kalos_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.HAWLUCHA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -1927,6 +1982,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TYRANTRUM, Species.AURORUS ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
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 => {
p.generateAndPopulateMoveset();
@ -1938,10 +1994,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.gender = Gender.FEMALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[TrainerType.KUKUI]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kukui")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -1965,11 +2018,9 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.HISUI_DECIDUEYE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
p.teraType = p.species.speciesId === Species.INCINEROAR ? Type.DARK : Type.FIGHTING;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[5];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(5),
[TrainerType.HAU]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_alola_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALOLA_RAICHU ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -1982,6 +2033,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TAPU_LELE, Species.TAPU_BULU ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.teraType = p.species.type1;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ZYGARDE ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // Zygarde 10% forme, Aura Break
@ -1993,10 +2045,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2);
p.gender = p.species.speciesId === Species.PRIMARINA ? Gender.FEMALE : Gender.MALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[TrainerType.LEON]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_galar_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AEGISLASH ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2017,10 +2066,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.gender = Gender.MALE;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[3];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(3),
[TrainerType.MUSTARD]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_mustard")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2033,6 +2079,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.GALAR_SLOWBRO, Species.GALAR_SLOWKING ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.teraType = Type.PSYCHIC;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALAR_DARMANITAN ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2050,10 +2097,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE;
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[2];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(2),
[TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_geeta")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GLIMMORA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2070,11 +2114,9 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KINGAMBIT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.abilityIndex = 1; // Supreme Overlord
p.teraType = Type.FLYING;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[5];
return [ modifierTypes.TERA_SHARD().generateType([], [ Type.FLYING ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(5),
[TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_nemona")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 0; // Midday form
@ -2086,16 +2128,15 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.MASTER_BALL;
}))
.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 => {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[4];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type2 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ];
}),
.setInstantTera(4),
[TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kieran")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.POLIWRATH, Species.POLITOED ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2117,7 +2158,7 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL;
}))
.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.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.
@ -2129,17 +2170,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE;
p.setBoss(true, 2);
}))
.setGenModifiersFunc(party => {
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 ];
}),
.setInstantTera(4),
[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)
@ -2161,20 +2192,22 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.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)
.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,
(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(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540)
.setGenModifiersFunc(party => {
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?
}),
.setInstantTera(0),
[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,
p => {
p.setBoss(true, 2);
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(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.variant = 1;
}))
.setGenModifiersFunc(party => {
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?
}),
.setInstantTera(0),
[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,
p => {
p.setBoss(true, 3);
p.abilityIndex = 0;
p.teraType = p.species.type1;
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,
@ -2212,10 +2243,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1; // Mega Rayquaza
p.generateName();
}))
.setGenModifiersFunc(party => {
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?
}),
.setInstantTera(0),
[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 => {
@ -2225,7 +2253,11 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.DUGTRIO, Species.ALOLA_DUGTRIO ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.HONCHKROW ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.NIDOQUEEN, Species.NIDOKING ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.RHYPERIOR ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.RHYPERIOR ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.abilityIndex = 1; // Solid Rock
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KANGASKHAN ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2234,20 +2266,32 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
})),
[TrainerType.ROCKET_BOSS_GIOVANNI_2]: new TrainerConfig(++t).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", [], true).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.TYRANITAR, Species.IRON_THORNS ], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.TYRANITAR ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HIPPOWDON ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.GARCHOMP, Species.EXCADRILL ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GASTRODON, Species.SEISMITOAD ], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.GASTRODON, Species.SEISMITOAD ], TrainerSlot.TRAINER, true, p => {
if (p.species.speciesId === Species.GASTRODON) {
p.abilityIndex = 0; // Storm Drain
} else if (p.species.speciesId === Species.SEISMITOAD) {
p.abilityIndex = 2; // Water Absorb
}
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.GARCHOMP, Species.EXCADRILL ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
if (p.species.speciesId === Species.GARCHOMP) {
p.abilityIndex = 2; // Rough Skin
} else if (p.species.speciesId === Species.EXCADRILL) {
p.abilityIndex = 0; // Sand Rush
}
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.RHYPERIOR ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.abilityIndex = 1; // Solid Rock
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.KANGASKHAN ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2295,7 +2339,10 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
p.abilityIndex = 0; // Chlorophyll
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GREAT_TUSK ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GREAT_TUSK ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.CAMERUPT ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2384,7 +2431,10 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MAGNEZONE ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.UXIE, Species.MESPRIT, Species.AZELF ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.UXIE, Species.MESPRIT, Species.AZELF ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.HOUNDOOM ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
@ -2425,7 +2475,10 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 0;
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.KINGAMBIT ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.VOLCARONA, Species.IRON_MOTH ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.VOLCARONA, Species.IRON_MOTH ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.HYDREIGON, Species.IRON_JUGULIS ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2448,7 +2501,14 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.DRAGALGE, Species.CLAWITZER ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.DRAGALGE, Species.CLAWITZER ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
if (p.species.speciesId === Species.DRAGALGE) {
p.abilityIndex = 2; // Adaptability
} else if (p.species.speciesId === Species.CLAWITZER) {
p.abilityIndex = 0; // Mega Launcher
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GALLADE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.abilityIndex = 1; // Sharpness
@ -2467,7 +2527,14 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
p.gender = Gender.MALE;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.DRAGALGE, Species.CLAWITZER ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.DRAGALGE, Species.CLAWITZER ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
if (p.species.speciesId === Species.DRAGALGE) {
p.abilityIndex = 2; // Adaptability
} else if (p.species.speciesId === Species.CLAWITZER) {
p.abilityIndex = 0; // Mega Launcher
}
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.AEGISLASH, Species.HISUI_GOODRA ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.IRON_VALIANT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2477,7 +2544,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.formIndex = 1; // Mega Gyardos
p.formIndex = 1; // Mega Gyarados
p.generateName();
p.gender = Gender.MALE;
}))
@ -2597,11 +2664,26 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL;
})),
[TrainerType.ROSE]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", []).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ESCAVALIER, Species.FERROTHORN ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SIRFETCHD, Species.MR_RIME ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.KLINKLANG, Species.PERRSERKER ]))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ESCAVALIER, Species.FERROTHORN ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SIRFETCHD, Species.MR_RIME ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.KLINKLANG, Species.PERRSERKER ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.COPPERAJAH ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2614,13 +2696,21 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AEGISLASH, Species.GHOLDENGO ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AEGISLASH, Species.GHOLDENGO ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.DRACOZOLT, Species.DRACOVISH ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.abilityIndex = 1; //Strong Jaw Dracovish, Hustle Dracozolt
p.pokeball = PokeballType.ULTRA_BALL;
p.abilityIndex = 1; // Strong Jaw Dracovish, Hustle Dracozolt
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MELMETAL ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MELMETAL ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GALAR_ARTICUNO, Species.GALAR_ZAPDOS, Species.GALAR_MOLTRES ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2651,10 +2741,7 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL;
p.generateName();
}))
.setGenModifiersFunc(party => {
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?
}),
.setInstantTera(4),
[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 => {
p.setBoss(true, 2);
@ -2666,7 +2753,10 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset();
p.formIndex = Utils.randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.RAIKOU, Species.ENTEI, Species.SUICUNE ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.RAIKOU, Species.ENTEI, Species.SUICUNE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(5, 1); // Random Starmobile form
p.generateAndPopulateMoveset();
@ -2684,10 +2774,12 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setGenModifiersFunc(party => {
const teraPokemon = party[0];
return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ]; //TODO: is the bang correct?
}),
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.ZAMAZENTA ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL;
}))
.setInstantTera(0),
[TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 3);

View File

@ -44,6 +44,7 @@ export class Arena {
public bgm: string;
public ignoreAbilities: boolean;
public ignoringEffectSource: BattlerIndex | null;
public playerTerasUsed: number;
/**
* 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).
@ -63,6 +64,7 @@ export class Arena {
this.bgm = bgm;
this.trainerPool = biomeTrainerPools[biome];
this.updatePoolsForTimeOfDay();
this.playerTerasUsed = 0;
this.playerFaints = playerFaints;
}

View File

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

View File

@ -52,7 +52,7 @@ import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
import { Type } from "#enums/type";
import { getLevelTotalExp } from "#app/data/exp";
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 { Gender } from "#app/data/gender";
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 { getNatureStatMultiplier } from "#app/data/nature";
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 type { TrainerSlot } from "#app/data/trainer-config";
import Overrides from "#app/overrides";
@ -163,6 +163,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public pokerus: boolean;
public switchOutStatus: boolean;
public evoCounter: number;
public teraType: Type;
public isTerastallized: boolean;
public stellarTypesBoosted: Type[];
public fusionSpecies: PokemonSpecies | null;
public fusionFormIndex: number;
@ -172,6 +175,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public fusionGender: Gender;
public fusionLuck: number;
public fusionCustomPokemonData: CustomPokemonData | null;
public fusionTeraType: Type;
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)}'`;
}
const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE);
const hiddenAbilityChance = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
if (!this.hasTrainer()) {
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
}
@ -269,8 +273,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionGender = dataSource.fusionGender;
this.fusionLuck = dataSource.fusionLuck;
this.fusionCustomPokemonData = dataSource.fusionCustomPokemonData;
this.fusionTeraType = dataSource.fusionTeraType;
this.usedTMs = dataSource.usedTMs ?? [];
this.customPokemonData = new CustomPokemonData(dataSource.customPokemonData);
this.teraType = dataSource.teraType;
this.isTerastallized = dataSource.isTerastallized;
this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? [];
} else {
this.id = Utils.randSeedInt(4294967296);
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.fusionLuck = this.luck;
this.teraType = Utils.randSeedItem(this.getTypes(false, false, true));
this.isTerastallized = false;
this.stellarTypesBoosted = [];
}
this.generateName();
@ -355,7 +367,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const getSprite = (hasShadow?: boolean) => {
const ret = globalScene.addPokemonSprite(this, 0, 0, `pkmn__${this.isPlayer() ? "back__" : ""}sub`, undefined, true);
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;
};
@ -723,7 +735,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
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);
}
@ -945,7 +960,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns the final critical-hit stage value
*/
getCritStage(source: Pokemon, move: Move): number {
const critStage = new Utils.IntegerHolder(0);
const critStage = new Utils.NumberHolder(0);
applyMoveAttrs(HighCritAttr, source, this, move, critStage);
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
@ -1059,7 +1074,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const baseStats = this.calculateBaseStats();
// Using base stats, calculate and store stats one by one
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) {
statHolder.value = statHolder.value + this.level + 10;
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;
}
/**
* 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;
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[] {
const types: Type[] = [];
if (includeTeraType) {
if (includeTeraType && this.isTerastallized) {
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);
if (forDefend) {
return types;
@ -1322,6 +1346,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} else if (fusionType1 !== types[0]) {
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 {
// 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)
@ -1584,23 +1614,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 {
// I don't think this should be possible anymore, please report if you encounter this. --NightKev
if (globalScene === undefined) {
console.warn("Pokemon.getTeraType(): Global scene is not defined!");
return Type.UNKNOWN;
getTeraType(): Type {
if (this.hasSpecies(Species.TERAPAGOS)) {
return Type.STELLAR;
} else if (this.hasSpecies(Species.OGERPON)) {
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 =>
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;
return this.teraType;
}
public isGrounded(): boolean {
@ -1742,7 +1780,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/
getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true, move?: Move): TypeDamageMultiplier {
if (moveType === Type.STELLAR) {
return this.isTerastallized() ? 2 : 1;
return this.isTerastallized ? 2 : 1;
}
const types = this.getTypes(true, true);
const arena = globalScene.arena;
@ -2529,7 +2567,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @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 {
const statStage = new Utils.IntegerHolder(this.getStatStage(stat));
const statStage = new Utils.NumberHolder(this.getStatStage(stat));
const ignoreStatStage = new Utils.BooleanHolder(false);
if (opponent) {
@ -2579,8 +2617,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return 1;
}
const userAccStage = new Utils.IntegerHolder(this.getStatStage(Stat.ACC));
const targetEvaStage = new Utils.IntegerHolder(target.getStatStage(Stat.EVA));
const userAccStage = new Utils.NumberHolder(this.getStatStage(Stat.ACC));
const targetEvaStage = new Utils.NumberHolder(target.getStatStage(Stat.EVA));
const ignoreAccStatStage = new Utils.BooleanHolder(false);
const ignoreEvaStatStage = new Utils.BooleanHolder(false);
@ -2759,7 +2797,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
/** 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)) {
glaiveRushMultiplier.value = 2;
}
@ -2779,11 +2817,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const matchesSourceType = sourceTypes.includes(moveType);
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
const stabMultiplier = new Utils.NumberHolder(1);
if (matchesSourceType) {
stabMultiplier.value += 0.5;
}
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) {
if (matchesSourceType && moveType !== Type.STELLAR) {
stabMultiplier.value += 0.5;
}
@ -2791,6 +2825,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
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);
/** Halves damage if the attacker is using a physical attack while burned */
@ -3831,6 +3879,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 {
this.turnData = new PokemonTurnData();
}
@ -4036,6 +4094,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 fusionPaletteColors: Map<number, number>;
@ -4049,8 +4112,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
Math.random = originalRandom;
paletteColors = paletteColors!; // tell TS compiler that paletteColors is defined!
fusionPaletteColors = fusionPaletteColors!; // TS compiler that fusionPaletteColors is defined!
paletteColors = paletteColors!; // erroneously tell TS compiler that paletteColors is defined!
fusionPaletteColors = fusionPaletteColors!; // mischievously misinform TS compiler that fusionPaletteColors is defined!
const [ palette, fusionPalette ] = [ paletteColors, fusionPaletteColors ]
.map(paletteColors => {
let keys = Array.from(paletteColors.keys()).sort((a: number, b: number) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1);
@ -4589,6 +4652,7 @@ export class PlayerPokemon extends Pokemon {
newPokemon.fusionVariant = this.fusionVariant;
newPokemon.fusionGender = this.fusionGender;
newPokemon.fusionLuck = this.fusionLuck;
newPokemon.fusionTeraType = this.teraType;
newPokemon.usedTMs = this.usedTMs;
globalScene.getPlayerParty().push(newPokemon);
@ -4740,6 +4804,7 @@ export class EnemyPokemon extends Pokemon {
public aiType: AiType;
public bossSegments: number;
public bossSegmentIndex: number;
public initialTeamIndex: number;
/** To indicate if the instance was populated with a dataSource -> e.g. loaded & populated from session data */
public readonly isPopulatedFromDataSource: boolean;
@ -4749,6 +4814,7 @@ export class EnemyPokemon extends Pokemon {
undefined, dataSource ? dataSource.nature : undefined, dataSource);
this.trainerSlot = trainerSlot;
this.initialTeamIndex = globalScene.currentBattle?.enemyParty.length ?? 0;
this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource
if (boss) {
this.setBoss(boss, dataSource?.bossSegments);

View File

@ -11,7 +11,8 @@ import {
TrainerSlot,
trainerConfigs,
trainerPartyTemplates,
signatureSpecies
signatureSpecies,
TeraAIMode
} from "#app/data/trainer-config";
import type { EnemyPokemon } from "#app/field/pokemon";
import * as Utils from "#app/utils";
@ -36,6 +37,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
public partyTemplateIndex: number;
public name: string;
public partnerName: string;
public originalIndexes: { [key: number]: number } = {};
constructor(trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: number, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) {
super(globalScene, -72, 80);
@ -546,6 +548,13 @@ export default class Trainer extends Phaser.GameObjects.Container {
return [];
}
genAI(party: EnemyPokemon[]) {
if (this.config.genAIFuncs) {
this.config.genAIFuncs.forEach(f => f(party));
}
console.log("Generated AI funcs");
}
loadAssets(): Promise<void> {
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("type_tera", "ui");
this.loadAtlas("type_bgs", "ui");
this.loadAtlas("button_tera", "ui");
this.loadImage("mystery_egg", "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 Pokemon from "#app/field/pokemon";
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 Overrides from "#app/overrides";
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 PartyUiHandler from "#app/ui/party-ui-handler";
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 { BattlerTagType } from "#enums/battler-tag-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 {
protected restorePoints: number;
protected restorePercent: number;
@ -1156,11 +1186,11 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
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...
let foundULTRA_Z = false,
foundN_LUNA = false,
foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, _i) => {
console.log("Checking ", fc.item);
switch (fc.item) {
case FormChangeItem.ULTRANECROZIUM_Z:
foundULTRA_Z = true;
@ -1176,6 +1206,8 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
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.
formChangeItemTriggers = formChangeItemTriggers.filter(fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z);
} else {
console.log("DID NOT FIND ");
}
}
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 {
private chancePercent: number;
@ -1469,14 +1479,21 @@ export const modifierTypes = {
if (!globalScene.getModifiers(TerastallizeAccessModifier).length) {
return null;
}
let type: Type;
if (!randSeedInt(3)) {
const partyMemberTypes = party.map(p => p.getTypes(false, false, true)).flat();
type = randSeedItem(partyMemberTypes);
} else {
type = randSeedInt(64) ? randSeedInt(18) as Type : Type.STELLAR;
const teraTypes: Type[] = [];
party.forEach(p => {
if (!(p.hasSpecies(Species.TERAPAGOS) || p.hasSpecies(Species.OGERPON) || p.hasSpecies(Species.SHEDINJA))) {
teraTypes.push(p.teraType);
}
});
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[]) => {
@ -1719,7 +1736,7 @@ const modifierPool: ModifierPool = {
return Math.min(Math.ceil(highestPartyLevel / 20), 4);
}, 4),
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[]) => {
if (party.filter(p => !p.fusionSpecies).length > 1) {
if (globalScene.gameMode.isSplicedOnly) {
@ -1842,7 +1859,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)),
new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
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.WIDE_LENS, 7),
].map(m => {

View File

@ -3,16 +3,16 @@ import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
import { getLevelTotalExp } from "#app/data/exp";
import { allMoves } from "#app/data/move";
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 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 Overrides from "#app/overrides";
import { EvolutionPhase } from "#app/phases/evolution-phase";
import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase";
import { LevelUpPhase } from "#app/phases/level-up-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { achvs } from "#app/system/achv";
import type { VoucherType } from "#app/system/voucher";
import { Command } from "#app/ui/command-ui-handler";
import { addTextObject, TextStyle } from "#app/ui/text";
@ -25,7 +25,7 @@ import type { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species";
import { type PermanentStat, type TempBattleStat, BATTLE_STATS, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { Type } from "#enums/type";
import type { Type } from "#enums/type";
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 { 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
* 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 {
private restorePoints: number;
private restorePercent: number;

View File

@ -118,6 +118,7 @@ export class CommandPhase extends FieldPhase {
let success: boolean = false;
switch (command) {
case Command.TERA:
case Command.FIGHT:
let useStruggle = false;
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 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 };
if (!moveId) {
turnCommand.targets = [ this.fieldIndex ];
@ -152,6 +154,7 @@ export class CommandPhase extends FieldPhase {
} else {
globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
}
globalScene.currentBattle.preTurnCommands[this.fieldIndex] = preTurnCommand;
globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
success = true;
} 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) */
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] =
{ 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);
}
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("spriteKey", this.pokemon.getSpriteKey());
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.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
pokemon.resetTera();
if (pokemon.turnData?.attacksReceived?.length) {
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?

View File

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

View File

@ -450,6 +450,12 @@ export class MoveEffectPhase extends PokemonPhase {
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();
});
});

View File

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

View File

@ -36,5 +36,6 @@ export class PartyHealPhase extends BattlePhase {
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;
return super.end();
} else if (healOrDamage) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
const hpRestoreMultiplier = new Utils.NumberHolder(1);
if (!this.revive) {
globalScene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
}

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { SemiInvulnerableTag } from "#app/data/battler-tags";
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 { BattleSpec } from "#app/enums/battle-spec";
import { BattlerTagType } from "#app/enums/battler-tag-type";
@ -11,6 +11,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase";
import { MovePhase } from "./move-phase";
import { PokemonHealPhase } from "./pokemon-heal-phase";
import { applyAbAttrs, PostTeraFormChangeStatChangeAbAttr } from "#app/data/ability";
export class QuietFormChangePhase extends BattlePhase {
protected pokemon: Pokemon;
@ -51,7 +52,7 @@ export class QuietFormChangePhase extends BattlePhase {
} catch (err: unknown) {
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 => {
if (this.pokemon.summonData?.speciesForm) {
k += "Base";
@ -145,6 +146,9 @@ export class QuietFormChangePhase extends BattlePhase {
movePhase.cancel();
}
}
if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) {
applyAbAttrs(PostTeraFormChangeStatChangeAbAttr, this.pokemon, null);
}
super.end();
}

View File

@ -45,7 +45,7 @@ export class SelectModifierPhase extends BattlePhase {
if (!this.isCopy) {
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
}
const modifierCount = new Utils.IntegerHolder(3);
const modifierCount = new Utils.NumberHolder(3);
if (this.isPlayer()) {
globalScene.applyModifiers(ExtraModifierModifier, 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 { SwitchType } from "#enums/switch-type";
import { globalScene } from "#app/global-scene";
import { TeraPhase } from "./tera-phase";
export class TurnStartPhase extends FieldPhase {
constructor() {
@ -139,6 +140,20 @@ export class TurnStartPhase extends FieldPhase {
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) {
const pokemon = field[o];

View File

@ -351,7 +351,7 @@ export default class SpritePipeline extends FieldSpritePipeline {
const data = sprite.pipelineData;
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 yShadowOffset = data["yShadowOffset"] as number;
const ignoreFieldPos = data["ignoreFieldPos"] as boolean;

View File

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

View File

@ -1087,6 +1087,8 @@ export class GameData {
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.playerTerasUsed = sessionData.arena.playerTerasUsed;
globalScene.arena.tags = sessionData.arena.tags;
if (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 type { Species } from "#enums/species";
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import type { Type } from "#app/enums/type";
export default class PokemonData {
public id: number;
@ -45,6 +46,9 @@ export default class PokemonData {
public pokerus: boolean;
public usedTMs: Moves[];
public evoCounter: number;
public teraType: Type;
public isTerastallized: boolean;
public stellarTypesBoosted: Type[];
public fusionSpecies: Species;
public fusionFormIndex: number;
@ -53,6 +57,7 @@ export default class PokemonData {
public fusionVariant: Variant;
public fusionGender: Gender;
public fusionLuck: number;
public fusionTeraType: Type;
public boss: boolean;
public bossSegments?: number;
@ -103,6 +108,9 @@ export default class PokemonData {
this.evoCounter = source.evoCounter ?? 0;
}
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.fusionFormIndex = source.fusionFormIndex;
@ -112,6 +120,7 @@ export default class PokemonData {
this.fusionGender = source.fusionGender;
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
this.fusionCustomPokemonData = new CustomPokemonData(source.fusionCustomPokemonData);
this.fusionTeraType = (source.fusionTeraType ?? 0) as Type;
this.usedTMs = source.usedTMs ?? [];
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 --- //
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));
/**
@ -138,6 +141,10 @@ class SessionVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 session data migration!");
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}!`);
@ -164,6 +171,10 @@ class SystemVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 system data migraton!");
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}!`);
@ -190,8 +201,12 @@ class SettingsVersionConverter extends VersionConverter {
console.log("Applying v1.1.0 settings data migraton!");
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 GameManager from "#test/utils/gameManager";
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", () => {
@ -258,7 +258,7 @@ describe("Abilities - Libero", () => {
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true);
leadPokemon.isTerastallized = true;
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);

View File

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

View File

@ -1,6 +1,6 @@
import { TurnHeldItemTransferModifier } from "#app/modifier/modifier";
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 Phaser from "phaser";
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", () => {
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);

View File

@ -150,6 +150,17 @@ describe("Spec - Pokemon", () => {
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 () => {
game.override.starterSpecies(Species.NATU);
game.override.starterFusionSpecies(Species.HOUNDOUR);

View File

@ -6,7 +6,6 @@ import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import * as Messages from "#app/messages";
import { TerastallizeModifier, overrideHeldItems } from "#app/modifier/modifier";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
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 {
// Suppress getPokemonNameWithAffix because it calls on a null battle spec
vi.spyOn(Messages, "getPokemonNameWithAffix").mockReturnValue("");
game.override
.enemyAbility(targetAbility)
.enemyHeldItems([{ name:"TERA_SHARD", type: teraType }]);
game.override.enemyAbility(targetAbility);
const user = game.scene.addPlayerPokemon(getPokemonSpecies(Species.SNORLAX), 5);
const target = game.scene.addEnemyPokemon(getPokemonSpecies(targetSpecies), 5, TrainerSlot.NONE);
if (teraType !== undefined) {
overrideHeldItems(target, false);
target.teraType = teraType;
target.isTerastallized = true;
}
expect(target.getMoveEffectiveness(user, allMoves[move])).toBe(expected);
@ -40,7 +38,6 @@ describe("Moves - Type Effectiveness", () => {
type: Phaser.HEADLESS,
});
game = new GameManager(phaserGame);
TerastallizeModifier.prototype.apply = (args) => true;
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 () => {
game.override.enemySpecies(Species.SKARMORY)
.enemyHeldItems([{ name: "TERA_SHARD", type: Type.WATER }]);
game.override.enemySpecies(Species.SKARMORY);
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.WATER;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness");
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 () => {
game.override.enemySpecies(Species.PELIPPER)
.enemyHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]);
game.override.enemySpecies(Species.PELIPPER);
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.FIRE;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness");
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 () => {
game.override.enemyHeldItems([{ name: "TERA_SHARD", type: Type.GRASS }]).enemySpecies(Species.SPRIGATITO);
game.override.enemySpecies(Species.SPRIGATITO);
await game.classicMode.startBattle([ Species.PIKACHU ]);
const enemy = game.scene.getEnemyPokemon()!;
enemy.teraType = Type.GRASS;
enemy.isTerastallized = true;
vi.spyOn(enemy, "getMoveEffectiveness");
@ -119,7 +121,8 @@ describe("Moves - Tar Shot", () => {
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);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);

View File

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

View File

@ -29,8 +29,7 @@ describe("Moves - Tera Starstorm", () => {
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(30)
.enemySpecies(Species.MAGIKARP)
.startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]);
.enemySpecies(Species.MAGIKARP);
});
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 ]);
const terapagos = game.scene.getPlayerPokemon()!;
terapagos.isTerastallized = true;
vi.spyOn(terapagos, "getMoveType");
game.move.select(Moves.TERA_STARSTORM);
await game.phaseInterceptor.to("TurnEndPhase");
expect(terapagos.isTerastallized()).toBe(true);
expect(terapagos.getMoveType).toHaveReturnedWith(Type.STELLAR);
});
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 ]);
const terapagos = game.scene.getPlayerParty()[1];
terapagos.isTerastallized = true;
game.move.select(Moves.TERA_STARSTORM, 0, BattlerIndex.ENEMY);
game.move.select(Moves.TERA_STARSTORM, 1);
@ -82,6 +84,8 @@ describe("Moves - Tera Starstorm", () => {
fusionedMon.fusionGender = magikarp.gender;
fusionedMon.fusionLuck = magikarp.luck;
fusionedMon.isTerastallized = true;
vi.spyOn(fusionedMon, "getMoveType");
game.move.select(Moves.TERA_STARSTORM, 0);
@ -90,7 +94,6 @@ describe("Moves - Tera Starstorm", () => {
// Fusion and terastallized
expect(fusionedMon.isFusion()).toBe(true);
expect(fusionedMon.isTerastallized()).toBe(true);
// Move effects should be applied
expect(fusionedMon.getMoveType).toHaveReturnedWith(Type.STELLAR);
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.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", () => {
if (this.lastTeraType !== Type.UNKNOWN) {
if (pokemon.isTerastallized) {
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);
}
const teraType = pokemon.getTeraType();
const teraType = pokemon.isTerastallized ? pokemon.getTeraType() : Type.UNKNOWN;
const teraTypeUpdated = this.lastTeraType !== teraType;
if (teraTypeUpdated) {

View File

@ -7,18 +7,24 @@ import { Button } from "#enums/buttons";
import { getPokemonNameWithAffix } from "#app/messages";
import { CommandPhase } from "#app/phases/command-phase";
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 {
FIGHT = 0,
BALL,
POKEMON,
RUN
RUN,
TERA
}
export default class CommandUiHandler extends UiHandler {
private commandsContainer: Phaser.GameObjects.Container;
private cursorObj: Phaser.GameObjects.Image | null;
private teraButton: Phaser.GameObjects.Sprite;
protected fieldIndex: number = 0;
protected cursor2: number = 0;
@ -40,6 +46,13 @@ export default class CommandUiHandler extends UiHandler {
this.commandsContainer.setVisible(false);
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++) {
const commandText = addTextObject(c % 2 === 0 ? 0 : 55.8, c < 2 ? 0 : 16, commands[c], TextStyle.WINDOW);
commandText.setName(commands[c]);
@ -62,11 +75,22 @@ export default class CommandUiHandler extends UiHandler {
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();
messageHandler.bg.setVisible(true);
messageHandler.commandWindow.setVisible(true);
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);
if (this.getCursor() === Command.POKEMON) {
this.setCursor(Command.FIGHT);
@ -108,6 +132,10 @@ export default class CommandUiHandler extends UiHandler {
(globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0);
success = true;
break;
case Command.TERA:
ui.setMode(Mode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA);
success = true;
break;
}
} else {
(globalScene.getCurrentPhase() as CommandPhase).cancel();
@ -115,23 +143,29 @@ export default class CommandUiHandler extends UiHandler {
} else {
switch (button) {
case Button.UP:
if (cursor >= 2) {
if (cursor === Command.POKEMON || cursor === Command.RUN) {
success = this.setCursor(cursor - 2);
}
break;
case Button.DOWN:
if (cursor < 2) {
if (cursor === Command.FIGHT || cursor === Command.BALL) {
success = this.setCursor(cursor + 2);
}
break;
case Button.LEFT:
if (cursor % 2 === 1) {
if (cursor === Command.BALL || cursor === Command.RUN) {
success = this.setCursor(cursor - 1);
} else if ((cursor === Command.FIGHT || cursor === Command.POKEMON) && this.canTera()) {
success = this.setCursor(Command.TERA);
this.toggleTeraButton();
}
break;
case Button.RIGHT:
if (cursor % 2 === 0) {
if (cursor === Command.FIGHT || cursor === Command.POKEMON) {
success = this.setCursor(cursor + 1);
} else if (cursor === Command.TERA) {
success = this.setCursor(Command.FIGHT);
this.toggleTeraButton();
}
break;
}
@ -144,6 +178,17 @@ export default class CommandUiHandler extends UiHandler {
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 {
return !this.fieldIndex ? this.cursor : this.cursor2;
}
@ -163,7 +208,12 @@ export default class CommandUiHandler extends UiHandler {
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;
}

View File

@ -33,6 +33,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
private moveInfoOverlay : MoveInfoOverlay;
protected fieldIndex: number = 0;
protected fromCommand: Command = Command.FIGHT;
protected cursor2: number = 0;
constructor() {
@ -114,6 +115,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
super.show(args);
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();
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.ACTION) {
if ((globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, cursor, false)) {
if ((globalScene.getCurrentPhase() as CommandPhase).handleCommand(this.fromCommand, cursor, false)) {
success = true;
} else {
ui.playError();

View File

@ -133,7 +133,7 @@ export class FilterText extends Phaser.GameObjects.Container {
const handler = ui.getHandler() as AwaitableUiHandler;
handler.tutorialActive = true;
// Switch to the dialog test window
this.selections[index].setText(String(i18next.t(dialogueName)));
this.selections[index].setText( dialogueName === "" ? this.defaultText : String(i18next.t(dialogueName)));
ui.revertMode();
this.onChange();
},

View File

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

View File

@ -16,7 +16,7 @@ import { pokemonFormChanges } from "#app/data/pokemon-forms";
import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpeciesForm, normalForm } from "#app/data/pokemon-species";
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
import { starterPassiveAbilities } from "#app/data/balance/passives";
import { Type } from "#enums/type";
@ -383,7 +383,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.pokemonHatchedIcon.setScale(0.8);
this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedIcon);
this.pokemonShinyIcon = globalScene.add.sprite(14, 76, "shiny_icons");
this.pokemonShinyIcon = globalScene.add.sprite(14, 117, "shiny_icons");
this.pokemonShinyIcon.setOrigin(0.15, 0.2);
this.pokemonShinyIcon.setScale(1);
this.pokemonCaughtHatchedContainer.add(this.pokemonShinyIcon);
@ -601,7 +601,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
const form = species.forms[formIndex];
// If this form has a specific set of moves, we get them.
this.levelMoves = (formIndex > 0 && pokemonFormLevelMoves.hasOwnProperty(formIndex)) ? pokemonFormLevelMoves[species.speciesId][formIndex] : pokemonSpeciesLevelMoves[species.speciesId];
this.levelMoves = (formIndex > 0 && pokemonFormLevelMoves.hasOwnProperty(species.speciesId) && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)) ? pokemonFormLevelMoves[species.speciesId][formIndex] : pokemonSpeciesLevelMoves[species.speciesId];
this.ability1 = form.ability1;
this.ability2 = (form.ability2 === form.ability1) ? undefined : form.ability2;
this.abilityHidden = (form.abilityHidden === form.ability1) ? undefined : form.abilityHidden;
@ -741,14 +741,16 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
return biomes;
}
isCaught(otherSpeciesDexEntry?: DexEntry): bigint {
isCaught(otherSpecies?: PokemonSpecies): bigint {
if (globalScene.dexForDevs) {
return 255n;
}
const dexEntry = otherSpeciesDexEntry ? otherSpeciesDexEntry : this.speciesStarterDexEntry;
const species = otherSpecies ? otherSpecies : this.species;
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const starterDexEntry = globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)];
return dexEntry?.caughtAttr ?? 0n;
return (dexEntry?.caughtAttr ?? 0n) & (starterDexEntry?.caughtAttr ?? 0n) & species.getFullUnlocksData();
}
/**
* Check whether a given form is caught for a given species.
@ -765,11 +767,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
}
const species = otherSpecies ? otherSpecies : this.species;
const formIndex = otherFormIndex !== undefined ? otherFormIndex : this.formIndex;
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const caughtAttr = this.isCaught(species);
const isFormCaught = dexEntry ?
(dexEntry.caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n
: false;
const isFormCaught = (caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n;
return isFormCaught;
}
@ -783,8 +783,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
*/
initStarterPrefs(): StarterAttributes {
const starterAttributes : StarterAttributes | null = this.species ? { ...this.savedStarterAttributes } : null;
const dexEntry = globalScene.gameData.dexData[this.species.speciesId];
const caughtAttr = this.isCaught(dexEntry);
const caughtAttr = this.isCaught();
// no preferences or Pokemon wasn't caught, return empty attribute
if (!starterAttributes || !caughtAttr) {
@ -1235,7 +1234,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
case MenuOptions.BIOMES:
if (!(this.isCaught() || this.speciesStarterDexEntry?.seenAttr)) {
if (!(isCaught || this.speciesStarterDexEntry?.seenAttr)) {
error = true;
} else {
this.blockInput = true;
@ -1372,8 +1371,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
});
this.evolutions.map(evo => {
const evoSpecies = allSpecies.find(species => species.speciesId === evo.speciesId);
const evoSpeciesStarterDexEntry = evoSpecies ? globalScene.gameData.dexData[evoSpecies.speciesId] : undefined;
const isCaughtEvo = this.isCaught(evoSpeciesStarterDexEntry) ? true : false;
const isCaughtEvo = this.isCaught(evoSpecies) ? true : false;
// Attempts to find the formIndex of the evolved species
const newFormKey = evo.evoFormKey ? evo.evoFormKey : (this.species.forms.length > 0 ? this.species.forms[this.formIndex].formKey : "");
const matchingForm = evoSpecies?.forms.find(form => form.formKey === newFormKey);
@ -1535,6 +1533,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.pokemonShinyIcon.setVisible(true);
starterAttributes.shiny = true;
this.savedStarterAttributes.shiny = starterAttributes.shiny;
} else {
let newVariant = props.variant;
do {
@ -1583,7 +1582,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (this.species.forms[newFormIndex].isStarterSelectable || globalScene.dexForDevs) { // TODO: are those bangs correct?
break;
}
} while (newFormIndex !== props.formIndex);
} while (newFormIndex !== props.formIndex || this.species.forms[newFormIndex].isUnobtainable);
starterAttributes.form = newFormIndex; // store the selected form
this.savedStarterAttributes.form = starterAttributes.form;
this.formIndex = newFormIndex;
@ -1688,7 +1687,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
}
this.pokemonCandyCountText.setText(`x${starterData.candyCount}`);
const egg = new Egg({ scene: globalScene, species: this.species.speciesId, sourceType: EggSourceType.SAME_SPECIES_EGG });
const egg = new Egg({ scene: globalScene, species: this.starterId, sourceType: EggSourceType.SAME_SPECIES_EGG });
egg.addEggToGameData();
globalScene.gameData.saveSystem().then(success => {
@ -1856,6 +1855,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (this.canCycleGender) {
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Gender, gamepadType, this.genderIconElement, this.genderLabel);
}
} else {
// Making space for "Uncaught" text
this.instructionRowY += 8;
}
if (this.canCycleForm) {
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Form, gamepadType, this.formIconElement, this.formLabel);
@ -2093,7 +2095,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (species) {
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const caughtAttr = this.isCaught(dexEntry);
const caughtAttr = this.isCaught(species);
if (!caughtAttr) {
const props = this.starterAttributes;
@ -2240,13 +2242,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.pokemonCandyContainer.setVisible(true);
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
this.pokemonShinyIcon.setY(135);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
this.pokemonHatchedIcon.setVisible(false);
this.pokemonHatchedCountText.setVisible(false);
this.pokemonFormText.setY(36);
} else {
this.pokemonShinyIcon.setY(117);
this.pokemonHatchedIcon.setVisible(true);
this.pokemonHatchedCountText.setVisible(true);
this.pokemonFormText.setY(42);
@ -2276,7 +2276,12 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (isFormCaught || isFormSeen) {
const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex!); // TODO: is the bang correct?
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
this.pokemonFormText.setText(species.getFormNameToDisplay(formIndex));
// TODO: change this once forms are refactored
if (normalForm.includes(species.speciesId) && !formIndex) {
this.pokemonFormText.setText("");
} else {
this.pokemonFormText.setText(species.getFormNameToDisplay(formIndex));
}
this.pokemonFormText.setVisible(true);
if (!isFormCaught) {
this.pokemonFormText.setY(18);
@ -2321,7 +2326,8 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
*/
getCurrentDexProps(speciesId: number): bigint {
let props = 0n;
const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr;
const species = allSpecies.find(sp => sp.speciesId === speciesId);
const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr & (species?.getFullUnlocksData() ?? 0n);
/* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props
* It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props

View File

@ -115,6 +115,10 @@ export default class PokedexScanUiHandler extends FormModalUiHandler {
this.reduceKeys();
setTimeout(() => {
input.setFocus(); // Focus after a short delay to avoid unwanted input
}, 50);
input.on("keydown", (inputObject, evt: KeyboardEvent) => {
if ([ "escape", "space" ].some((v) => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && ui.getMode() === Mode.AUTO_COMPLETE) {
// Delete autocomplete list and recovery focus.
@ -169,7 +173,8 @@ export default class PokedexScanUiHandler extends FormModalUiHandler {
this.submitAction = (_) => {
if (ui.getMode() === Mode.POKEDEX_SCAN) {
this.sanitizeInputs();
const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text)));
const outputName = this.reducedKeys.includes(this.inputs[0].text) ? this.inputs[0].text : "";
const sanitizedName = btoa(unescape(encodeURIComponent(outputName)));
config.buttonActions[0](sanitizedName);
return true;
}

View File

@ -7,7 +7,7 @@ import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
import type { PokemonForm } from "#app/data/pokemon-species";
import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpeciesForm, getPokerusStarters, normalForm } from "#app/data/pokemon-species";
import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
import { catchableSpecies } from "#app/data/balance/biomes";
import { Type } from "#enums/type";
@ -336,6 +336,8 @@ export default class PokedexUiHandler extends MessageUiHandler {
const costReductionLabels = [
new DropDownLabel(i18next.t("filterBar:costReduction"), undefined, DropDownState.OFF),
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:costReductionLocked"), undefined, DropDownState.EXCLUDE),
];
@ -411,15 +413,15 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.iconAnimHandler = new PokemonIconAnimHandler();
this.iconAnimHandler.setup();
this.pokemonNumberText = addTextObject(6, 140, "", TextStyle.SUMMARY);
this.pokemonNumberText = addTextObject(6, 141, "", TextStyle.SUMMARY);
this.pokemonNumberText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonNumberText);
this.pokemonNameText = addTextObject(6, 127, "", TextStyle.SUMMARY);
this.pokemonNameText = addTextObject(6, 128, "", TextStyle.SUMMARY);
this.pokemonNameText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonNameText);
this.pokemonFormText = addTextObject(6, 122, "", TextStyle.PARTY, { fontSize: textSettings.instructionTextSize });
this.pokemonFormText = addTextObject(6, 121, "", TextStyle.PARTY, { fontSize: textSettings.instructionTextSize });
this.pokemonFormText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonFormText);
@ -537,6 +539,8 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.starterSelectContainer.bringToTop(this.filterBarContainer);
this.initTutorialOverlay(this.starterSelectContainer);
this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer);
this.starterSelectContainer.bringToTop(this.pokemonNameText);
this.starterSelectContainer.bringToTop(this.pokemonFormText);
}
show(args: any[]): boolean {
@ -565,7 +569,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.starterPreferences[species.speciesId] = this.initStarterPrefs(species);
if (dexEntry.caughtAttr || globalScene.dexForDevs) {
if ((dexEntry.caughtAttr & species.getFullUnlocksData()) || globalScene.dexForDevs) {
icon.clearTint();
} else if (dexEntry.seenAttr) {
icon.setTint(0x808080);
@ -606,7 +610,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
return {};
}
const caughtAttr = dexEntry.caughtAttr;
const caughtAttr = dexEntry.caughtAttr & species.getFullUnlocksData();
const hasShiny = caughtAttr & DexAttr.SHINY;
const hasNonShiny = caughtAttr & DexAttr.NON_SHINY;
@ -1255,7 +1259,9 @@ export default class PokedexUiHandler extends MessageUiHandler {
// First, ensure you have the caught attributes for the species else default to bigint 0
// TODO: This might be removed depending on how accessible we want the pokedex function to be
const caughtAttr = globalScene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0);
const caughtAttr = (globalScene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0)) &
(globalScene.gameData.dexData[this.getStarterSpeciesId(container.species.speciesId)]?.caughtAttr || BigInt(0)) &
container.species.getFullUnlocksData();
const starterData = globalScene.gameData.starterData[starterId];
const isStarterProgressable = speciesEggMoves.hasOwnProperty(starterId);
@ -1292,7 +1298,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (fitsEggMove1 && !fitsLevelMove1) {
container.eggMove1Icon.setVisible(true);
const em1 = eggMoves.findIndex(name => name === selectedMove1);
if ((starterData[starterId].eggMoves & (1 << em1)) === 0) {
if ((starterData.eggMoves & (1 << em1)) === 0) {
container.eggMove1Icon.setTint(0x808080);
} else {
container.eggMove1Icon.clearTint();
@ -1303,7 +1309,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (fitsEggMove2 && !fitsLevelMove2) {
container.eggMove2Icon.setVisible(true);
const em2 = eggMoves.findIndex(name => name === selectedMove2);
if ((starterData[starterId].eggMoves & (1 << em2)) === 0) {
if ((starterData.eggMoves & (1 << em2)) === 0) {
container.eggMove2Icon.setTint(0x808080);
} else {
container.eggMove2Icon.clearTint();
@ -1580,11 +1586,12 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
const speciesId = container.species.speciesId;
const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr & container.species.getFullUnlocksData();
this.updateStarterValueLabel(container);
container.label.setVisible(true);
const speciesVariants = speciesId && globalScene.gameData.dexData[speciesId].caughtAttr & DexAttr.SHINY
? [ DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3 ].filter(v => !!(globalScene.gameData.dexData[speciesId].caughtAttr & v))
const speciesVariants = speciesId && caughtAttr & DexAttr.SHINY
? [ DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3 ].filter(v => !!(caughtAttr & v))
: [];
for (let v = 0; v < 3; v++) {
const hasVariant = speciesVariants.length > v;
@ -1595,7 +1602,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
container.starterPassiveBgs.setVisible(!!globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].passiveAttr);
container.hiddenAbilityIcon.setVisible(!!globalScene.gameData.dexData[speciesId].caughtAttr && !!(globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].abilityAttr & 4));
container.hiddenAbilityIcon.setVisible(!!caughtAttr && !!(globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].abilityAttr & 4));
container.classicWinIcon.setVisible(globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].classicWinCount > 0);
container.favoriteIcon.setVisible(this.starterPreferences[speciesId]?.favorite ?? false);
@ -1698,7 +1705,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
openFormTray(species: PokemonSpecies): boolean {
this.trayForms = species.forms;
this.trayForms = species.forms.filter(f => !f.isUnobtainable);
this.trayNumIcons = this.trayForms.length;
this.trayRows = Math.floor(this.trayNumIcons / 9) + (this.trayNumIcons % 9 === 0 ? 0 : 1);
@ -1729,7 +1736,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.trayContainers = [];
this.trayForms.map((f, index) => {
const isFormCaught = dexEntry ? (dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f.formIndex ?? 0)) > 0n : false;
const isFormCaught = dexEntry ? (dexEntry.caughtAttr & species.getFullUnlocksData() & globalScene.gameData.getFormAttr(f.formIndex ?? 0)) > 0n : false;
const isFormSeen = dexEntry ? (dexEntry.seenAttr & globalScene.gameData.getFormAttr(f.formIndex ?? 0)) > 0n : false;
const formContainer = new PokedexMonContainer(species, { formIndex: f.formIndex, female: props.female, shiny: props.shiny, variant: props.variant });
this.iconAnimHandler.addOrUpdate(formContainer.icon, PokemonIconAnimMode.NONE);
@ -1866,6 +1873,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
} else {
this.pokemonNumberText.setText(species ? i18next.t("pokedexUiHandler:pokemonNumber") + padInt(species.speciesId, 4) : "");
this.pokemonNameText.setText(species ? "???" : "");
this.pokemonFormText.setText("");
this.type1Icon.setVisible(false);
this.type2Icon.setVisible(false);
if (species) {
@ -1899,8 +1907,9 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (species) {
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const caughtAttr = dexEntry.caughtAttr & globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)].caughtAttr & species.getFullUnlocksData();
if (!dexEntry.caughtAttr) {
if (!caughtAttr) {
const props = this.getSanitizedProps(globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)));
if (shiny === undefined) {
@ -1917,7 +1926,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
}
const isFormCaught = dexEntry ? (dexEntry.caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
const isFormCaught = dexEntry ? (caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
const isFormSeen = dexEntry ? (dexEntry.seenAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
const assetLoadCancelled = new BooleanHolder(false);
@ -1949,19 +1958,24 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
if (isFormCaught || isFormSeen || globalScene.dexForDevs) {
this.pokemonFormText.setText("Form Text");
// TODO: change this once forms are refactored
if (normalForm.includes(species.speciesId) && !formIndex) {
this.pokemonFormText.setText("");
} else {
this.pokemonFormText.setText(species.getFormNameToDisplay(formIndex, false));
}
} else {
this.pokemonFormText.setText("");
}
if (isFormCaught || isFormSeen || globalScene.dexForDevs) {
const speciesForm = getPokemonSpeciesForm(species.speciesId, 0); // TODO: always selecting the first form
const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex ?? 0); // TODO: always selecting the first form
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
} else {
this.setTypeIcons(null, null);
}
if (species?.forms?.length > 1) {
if (species?.forms?.filter(f => !f.isUnobtainable).length > 1) {
if (!this.showingTray) {
this.showFormTrayIconElement.setVisible(true);
this.showFormTrayLabel.setVisible(true);
@ -2054,7 +2068,8 @@ export default class PokedexUiHandler extends MessageUiHandler {
*/
getCurrentDexProps(speciesId: number): bigint {
let props = 0n;
const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr;
const species = allSpecies.find(sp => sp.speciesId === speciesId);
const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr & (species?.getFullUnlocksData() ?? 0n);
/* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props
* It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props

View File

@ -420,17 +420,6 @@ export default class RunInfoUiHandler extends UiHandler {
private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) {
// Loads and adds trainer sprites to the UI
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
// 2 Rows x 3 Columns
@ -444,18 +433,6 @@ export default class RunInfoUiHandler extends UiHandler {
enemyData["player"] = true;
const enemy = enemyData.toPokemon();
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));
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);

View File

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

View File

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