This commit is contained in:
AJ Fontaine 2025-06-03 15:19:20 -04:00
commit a5af779af1
229 changed files with 5689 additions and 2437 deletions

View File

@ -1,7 +1,8 @@
all:
- "src/**"
- "test/**"
- "public/**"
# Negations syntax from https://github.com/dorny/paths-filter/issues/184#issuecomment-2786521554
- "src/**/!(*.{md,py,sh,gitkeep,gitignore})"
- "test/**/!(*.{md,py,sh,gitkeep,gitignore})"
- "public/**/!(*.{md,py,sh,gitkeep,gitignore})"
# Workflows that can impact tests
- ".github/workflows/test*.yml"
- ".github/test-filters.yml"
@ -11,9 +12,4 @@ all:
- "vite*" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts
- "tsconfig*.json" # tsconfig.json tweaking can impact compilation
- "global.d.ts"
- ".env*"
# Blanket negations for files that cannot impact tests
- "!**/*.py" # No .py files
- "!**/*.sh" # No .sh files
- "!**/*.md" # No .md files
- "!**/.git*" # .gitkeep and family
- ".env*"

View File

@ -19,19 +19,20 @@ on:
jobs:
test:
name: Shard ${{ inputs.shard }} of ${{ inputs.totalShards }}
# We can't use dynmically named jobs until https://github.com/orgs/community/discussions/13261 is implemented
name: Shard
runs-on: ubuntu-latest
if: ${{ !inputs.skip }}
steps:
- name: Check out Git repository
uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
submodules: "recursive"
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
node-version-file: ".nvmrc"
cache: "npm"
- name: Install Node.js dependencies
run: npm ci
- name: Run tests

View File

@ -25,6 +25,7 @@ jobs:
- name: checkout
uses: actions/checkout@v4
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36
id: filter
with:
filters: .github/test-filters.yml
@ -33,10 +34,10 @@ jobs:
needs: check-path-change-filter
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shard: [1, 2, 3, 4, 5]
uses: ./.github/workflows/test-shard-template.yml
with:
project: main
shard: ${{ matrix.shard }}
totalShards: 10
skip: ${{ needs.check-path-change-filter.outputs.all == 'false'}}
totalShards: 5
skip: ${{ needs.check-path-change-filter.outputs.all != 'true'}}

4
package-lock.json generated
View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,34 +1,36 @@
{
"1": {
"319452": "831a1f",
"4a7310": "982443",
"7ba563": "b44040",
"bdef84": "ec8c8c",
"8cbd63": "c54b4b",
"215200": "710f2e",
"a5d670": "df5252",
"4aa552": "9f2f2c",
"a5d674": "e16363",
"196b21": "891222",
"7aa953": "c54b4b",
"7ba563": "b44040",
"215200": "710f2e",
"f7ce00": "7aa1df",
"525252": "123a5a",
"63b56b": "b2332f",
"a5d673": "df5252",
"8c6b3a": "448bc3",
"4aa552": "9f2f2c"
"bdef84": "ec8c8c",
"63b56b": "b2332f",
"319452": "831a1f",
"196b21": "891222",
"4a7310": "982443"
},
"2": {
"319452": "b08d72",
"4a7310": "4f3956",
"7ba563": "704e7e",
"bdef84": "a779ba",
"8cbd63": "e3d7a6",
"215200": "583823",
"a5d670": "d7cda7",
"4aa552": "c5a77f",
"a5d674": "8c669b",
"196b21": "78582c",
"7aa953": "704e7e",
"7ba563": "704e7e",
"215200": "583823",
"f7ce00": "f2aacd",
"525252": "a53b6f",
"63b56b": "cfc191",
"a5d673": "d7cda7",
"8c6b3a": "df87bb",
"4aa552": "c5a77f"
"bdef84": "a779ba",
"63b56b": "cfc191",
"319452": "b08d72",
"196b21": "78582c",
"4a7310": "4f3956"
}
}

View File

@ -1,28 +1,28 @@
{
"1": {
"196b21": "831a1f",
"7ba563": "b44040",
"215201": "630d28",
"215200": "710f2f",
"a5d674": "df5252",
"8cbd63": "c54b4b",
"63b56b": "b2332f",
"a5d670": "e16363",
"319452": "831a1f",
"4aa552": "9f2f2c",
"7ba563": "b44040",
"8cbd63": "c54b4b",
"215200": "710f2f",
"196b21": "831a1f",
"a5d674": "df5252",
"4a7310": "982443",
"a5d673": "e16363",
"63b56b": "b2332f",
"215201": "630d28"
"4a7310": "982443"
},
"2": {
"196b21": "b08d72",
"7ba563": "704e7e",
"215201": "583823",
"215200": "3f3249",
"a5d674": "d7cda7",
"8cbd63": "e3d7a6",
"63b56b": "cfc191",
"a5d670": "8c669b",
"319452": "b08d72",
"4aa552": "c5a77f",
"7ba563": "704e7e",
"8cbd63": "e3d7a6",
"215200": "3f3249",
"196b21": "b08d72",
"a5d674": "d7cda7",
"4a7310": "4f3956",
"a5d673": "8c669b",
"63b56b": "cfc191",
"215201": "583823"
"4a7310": "4f3956"
}
}

View File

@ -1,28 +1,28 @@
{
"1": {
"196b21": "780d4a",
"7ba563": "b44040",
"215201": "710f2e",
"215200": "710f2f",
"a5d674": "de5b6f",
"8cbd63": "bf3d64",
"63b56b": "9e2056",
"a5d670": "e16363",
"319452": "780d4a",
"4aa552": "8a1652",
"7ba563": "b44040",
"8cbd63": "bf3d64",
"215200": "710f2f",
"196b21": "780d4a",
"a5d674": "de5b6f",
"4a7310": "982443",
"a5d673": "e16363",
"63b56b": "9e2056",
"215201": "710f2e"
"4a7310": "982443"
},
"2": {
"196b21": "b59c72",
"7ba563": "805a9c",
"215201": "694d37",
"215200": "41334d",
"a5d674": "f6f7df",
"8cbd63": "ebe9ca",
"63b56b": "e3ddb8",
"a5d670": "a473ba",
"319452": "b59c72",
"4aa552": "c9b991",
"7ba563": "805a9c",
"8cbd63": "ebe9ca",
"215200": "41334d",
"196b21": "b59c72",
"a5d674": "f6f7df",
"4a7310": "4f3956",
"a5d673": "a473ba",
"63b56b": "e3ddb8",
"215201": "694d37"
"4a7310": "4f3956"
}
}

View File

@ -1,34 +1,36 @@
{
"1": {
"319452": "780d4a",
"4a7310": "982443",
"7ba563": "b44040",
"bdef84": "ec8c8c",
"8cbd63": "bf3d64",
"215200": "710f2e",
"a5d670": "de5b6f",
"4aa552": "8a1652",
"a5d674": "e16363",
"196b21": "7d1157",
"7aa953": "bf3d64",
"7ba563": "b44040",
"215200": "710f2e",
"f7ce00": "5bcfc3",
"525252": "20668c",
"63b56b": "9e2056",
"a5d673": "de5b6f",
"8c6b3a": "33a3b0",
"4aa552": "8a1652"
"bdef84": "ec8c8c",
"63b56b": "9e2056",
"319452": "780d4a",
"196b21": "7d1157",
"4a7310": "982443"
},
"2": {
"319452": "b59c72",
"4a7310": "4f3956",
"7ba563": "805a9c",
"bdef84": "c193cf",
"8cbd63": "f6f7df",
"215200": "694d37",
"a5d670": "ebe9ca",
"4aa552": "c9b991",
"a5d674": "a473ba",
"196b21": "9c805f",
"7aa953": "805a9c",
"7ba563": "805a9c",
"215200": "694d37",
"f7ce00": "f2aab6",
"525252": "983364",
"63b56b": "e3ddb8",
"a5d673": "ebe9ca",
"8c6b3a": "df879f",
"4aa552": "c9b991"
"bdef84": "c193cf",
"63b56b": "e3ddb8",
"319452": "b59c72",
"196b21": "9c805f",
"4a7310": "4f3956"
}
}

View File

@ -1,7 +1,7 @@
{
"textures": [
{
"image": "statuses_ca_ES.png",
"image": "statuses_ca.png",
"format": "RGBA8888",
"size": {
"w": 22,

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,188 @@
{
"textures": [
{
"image": "statuses_da.png",
"format": "RGBA8888",
"size": {
"w": 22,
"h": 64
},
"scale": 1,
"frames": [
{
"filename": "pokerus",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 22,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
},
"frame": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
}
},
{
"filename": "burn",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 8,
"w": 20,
"h": 8
}
},
{
"filename": "faint",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 16,
"w": 20,
"h": 8
}
},
{
"filename": "freeze",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 24,
"w": 20,
"h": 8
}
},
{
"filename": "paralysis",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 32,
"w": 20,
"h": 8
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 40,
"w": 20,
"h": 8
}
},
{
"filename": "sleep",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 48,
"w": 20,
"h": 8
}
},
{
"filename": "toxic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 56,
"w": 20,
"h": 8
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,188 @@
{
"textures": [
{
"image": "statuses_ro.png",
"format": "RGBA8888",
"size": {
"w": 22,
"h": 64
},
"scale": 1,
"frames": [
{
"filename": "pokerus",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 22,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
},
"frame": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
}
},
{
"filename": "burn",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 8,
"w": 20,
"h": 8
}
},
{
"filename": "faint",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 16,
"w": 20,
"h": 8
}
},
{
"filename": "freeze",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 24,
"w": 20,
"h": 8
}
},
{
"filename": "paralysis",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 32,
"w": 20,
"h": 8
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 40,
"w": 20,
"h": 8
}
},
{
"filename": "sleep",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 48,
"w": 20,
"h": 8
}
},
{
"filename": "toxic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 56,
"w": 20,
"h": 8
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,188 @@
{
"textures": [
{
"image": "statuses_ru.png",
"format": "RGBA8888",
"size": {
"w": 22,
"h": 64
},
"scale": 1,
"frames": [
{
"filename": "pokerus",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 22,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
},
"frame": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
}
},
{
"filename": "burn",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 8,
"w": 20,
"h": 8
}
},
{
"filename": "faint",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 16,
"w": 20,
"h": 8
}
},
{
"filename": "freeze",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 24,
"w": 20,
"h": 8
}
},
{
"filename": "paralysis",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 32,
"w": 20,
"h": 8
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 40,
"w": 20,
"h": 8
}
},
{
"filename": "sleep",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 48,
"w": 20,
"h": 8
}
},
{
"filename": "toxic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 56,
"w": 20,
"h": 8
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,188 @@
{
"textures": [
{
"image": "statuses_tr.png",
"format": "RGBA8888",
"size": {
"w": 22,
"h": 64
},
"scale": 1,
"frames": [
{
"filename": "pokerus",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 22,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
},
"frame": {
"x": 0,
"y": 0,
"w": 22,
"h": 8
}
},
{
"filename": "burn",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 8,
"w": 20,
"h": 8
}
},
{
"filename": "faint",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 16,
"w": 20,
"h": 8
}
},
{
"filename": "freeze",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 24,
"w": 20,
"h": 8
}
},
{
"filename": "paralysis",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 32,
"w": 20,
"h": 8
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 40,
"w": 20,
"h": 8
}
},
{
"filename": "sleep",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 48,
"w": 20,
"h": 8
}
},
{
"filename": "toxic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 20,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 20,
"h": 8
},
"frame": {
"x": 0,
"y": 56,
"w": 20,
"h": 8
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

View File

@ -1,7 +1,7 @@
{
"textures": [
{
"image": "types_ca-ES.png",
"image": "types_ca.png",
"format": "RGBA8888",
"size": {
"w": 32,

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

440
public/images/types_da.json Normal file
View File

@ -0,0 +1,440 @@
{
"textures": [
{
"image": "types_da.png",
"format": "RGBA8888",
"size": {
"w": 32,
"h": 280
},
"scale": 1,
"frames": [
{
"filename": "unknown",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
}
},
{
"filename": "bug",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 14,
"w": 32,
"h": 14
}
},
{
"filename": "dark",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 28,
"w": 32,
"h": 14
}
},
{
"filename": "dragon",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 42,
"w": 32,
"h": 14
}
},
{
"filename": "electric",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 56,
"w": 32,
"h": 14
}
},
{
"filename": "fairy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 70,
"w": 32,
"h": 14
}
},
{
"filename": "fighting",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 84,
"w": 32,
"h": 14
}
},
{
"filename": "fire",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 98,
"w": 32,
"h": 14
}
},
{
"filename": "flying",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 112,
"w": 32,
"h": 14
}
},
{
"filename": "ghost",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 126,
"w": 32,
"h": 14
}
},
{
"filename": "grass",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 140,
"w": 32,
"h": 14
}
},
{
"filename": "ground",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 154,
"w": 32,
"h": 14
}
},
{
"filename": "ice",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 168,
"w": 32,
"h": 14
}
},
{
"filename": "normal",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 182,
"w": 32,
"h": 14
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 196,
"w": 32,
"h": 14
}
},
{
"filename": "psychic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 210,
"w": 32,
"h": 14
}
},
{
"filename": "rock",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 224,
"w": 32,
"h": 14
}
},
{
"filename": "steel",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 238,
"w": 32,
"h": 14
}
},
{
"filename": "water",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 252,
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

BIN
public/images/types_da.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

440
public/images/types_ro.json Normal file
View File

@ -0,0 +1,440 @@
{
"textures": [
{
"image": "types_ro.png",
"format": "RGBA8888",
"size": {
"w": 32,
"h": 280
},
"scale": 1,
"frames": [
{
"filename": "unknown",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
}
},
{
"filename": "bug",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 14,
"w": 32,
"h": 14
}
},
{
"filename": "dark",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 28,
"w": 32,
"h": 14
}
},
{
"filename": "dragon",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 42,
"w": 32,
"h": 14
}
},
{
"filename": "electric",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 56,
"w": 32,
"h": 14
}
},
{
"filename": "fairy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 70,
"w": 32,
"h": 14
}
},
{
"filename": "fighting",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 84,
"w": 32,
"h": 14
}
},
{
"filename": "fire",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 98,
"w": 32,
"h": 14
}
},
{
"filename": "flying",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 112,
"w": 32,
"h": 14
}
},
{
"filename": "ghost",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 126,
"w": 32,
"h": 14
}
},
{
"filename": "grass",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 140,
"w": 32,
"h": 14
}
},
{
"filename": "ground",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 154,
"w": 32,
"h": 14
}
},
{
"filename": "ice",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 168,
"w": 32,
"h": 14
}
},
{
"filename": "normal",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 182,
"w": 32,
"h": 14
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 196,
"w": 32,
"h": 14
}
},
{
"filename": "psychic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 210,
"w": 32,
"h": 14
}
},
{
"filename": "rock",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 224,
"w": 32,
"h": 14
}
},
{
"filename": "steel",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 238,
"w": 32,
"h": 14
}
},
{
"filename": "water",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 252,
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

BIN
public/images/types_ro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

440
public/images/types_ru.json Normal file
View File

@ -0,0 +1,440 @@
{
"textures": [
{
"image": "types_ru.png",
"format": "RGBA8888",
"size": {
"w": 32,
"h": 280
},
"scale": 1,
"frames": [
{
"filename": "unknown",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
}
},
{
"filename": "bug",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 14,
"w": 32,
"h": 14
}
},
{
"filename": "dark",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 28,
"w": 32,
"h": 14
}
},
{
"filename": "dragon",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 42,
"w": 32,
"h": 14
}
},
{
"filename": "electric",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 56,
"w": 32,
"h": 14
}
},
{
"filename": "fairy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 70,
"w": 32,
"h": 14
}
},
{
"filename": "fighting",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 84,
"w": 32,
"h": 14
}
},
{
"filename": "fire",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 98,
"w": 32,
"h": 14
}
},
{
"filename": "flying",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 112,
"w": 32,
"h": 14
}
},
{
"filename": "ghost",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 126,
"w": 32,
"h": 14
}
},
{
"filename": "grass",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 140,
"w": 32,
"h": 14
}
},
{
"filename": "ground",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 154,
"w": 32,
"h": 14
}
},
{
"filename": "ice",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 168,
"w": 32,
"h": 14
}
},
{
"filename": "normal",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 182,
"w": 32,
"h": 14
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 196,
"w": 32,
"h": 14
}
},
{
"filename": "psychic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 210,
"w": 32,
"h": 14
}
},
{
"filename": "rock",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 224,
"w": 32,
"h": 14
}
},
{
"filename": "steel",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 238,
"w": 32,
"h": 14
}
},
{
"filename": "water",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 252,
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

BIN
public/images/types_ru.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

440
public/images/types_tr.json Normal file
View File

@ -0,0 +1,440 @@
{
"textures": [
{
"image": "types_tr.png",
"format": "RGBA8888",
"size": {
"w": 32,
"h": 280
},
"scale": 1,
"frames": [
{
"filename": "unknown",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
}
},
{
"filename": "bug",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 14,
"w": 32,
"h": 14
}
},
{
"filename": "dark",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 28,
"w": 32,
"h": 14
}
},
{
"filename": "dragon",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 42,
"w": 32,
"h": 14
}
},
{
"filename": "electric",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 56,
"w": 32,
"h": 14
}
},
{
"filename": "fairy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 70,
"w": 32,
"h": 14
}
},
{
"filename": "fighting",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 84,
"w": 32,
"h": 14
}
},
{
"filename": "fire",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 98,
"w": 32,
"h": 14
}
},
{
"filename": "flying",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 112,
"w": 32,
"h": 14
}
},
{
"filename": "ghost",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 126,
"w": 32,
"h": 14
}
},
{
"filename": "grass",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 140,
"w": 32,
"h": 14
}
},
{
"filename": "ground",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 154,
"w": 32,
"h": 14
}
},
{
"filename": "ice",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 168,
"w": 32,
"h": 14
}
},
{
"filename": "normal",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 182,
"w": 32,
"h": 14
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 196,
"w": 32,
"h": 14
}
},
{
"filename": "psychic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 210,
"w": 32,
"h": 14
}
},
{
"filename": "rock",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 224,
"w": 32,
"h": 14
}
},
{
"filename": "steel",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 238,
"w": 32,
"h": 14
}
},
{
"filename": "water",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 252,
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

BIN
public/images/types_tr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -1 +1 @@
Subproject commit 42cd5cf577f475c22bc82d55e7ca358eb4f3184f
Subproject commit 4dab23d6a78b6cf32db43c9953e3c2000f448007

149
scripts/decrypt-save.js Normal file
View File

@ -0,0 +1,149 @@
import pkg from "crypto-js";
const { AES, enc } = pkg;
// biome-ignore lint: This is how you import fs from node
import * as fs from "node:fs";
const SAVE_KEY = "x0i2O7WRiANTqPmZ";
/**
* A map of condensed keynames to their associated full names
* NOTE: Update this if `src/system/game-data#systemShortKeys` ever changes!
*/
const systemShortKeys = {
seenAttr: "$sa",
caughtAttr: "$ca",
natureAttr: "$na",
seenCount: "$s",
caughtCount: "$c",
hatchedCount: "$hc",
ivs: "$i",
moveset: "$m",
eggMoves: "$em",
candyCount: "$x",
friendship: "$f",
abilityAttr: "$a",
passiveAttr: "$pa",
valueReduction: "$vr",
classicWinCount: "$wc",
};
/**
* Replace the shortened key names with their full names
* @param {string} dataStr - The string to convert
* @returns {string} The string with shortened keynames replaced with full names
*/
function convertSystemDataStr(dataStr) {
const fromKeys = Object.values(systemShortKeys);
const toKeys = Object.keys(systemShortKeys);
for (const k in fromKeys) {
dataStr = dataStr.replace(new RegExp(`${fromKeys[k].replace("$", "\\$")}`, "g"), toKeys[k]);
}
return dataStr;
}
/**
* Decrypt a save
* @param {string} path - The path to the encrypted save file
* @returns {string} The decrypted save data
*/
function decryptSave(path) {
// Check if the file exists
if (!fs.existsSync(path)) {
console.error(`File not found: ${path}`);
process.exit(1);
}
let fileData;
try {
fileData = fs.readFileSync(path, "utf8");
} catch (e) {
switch (e.code) {
case "ENOENT":
console.error(`File not found: ${path}`);
break;
case "EACCES":
console.error(`Could not open ${path}: Permission denied`);
break;
case "EISDIR":
console.error(`Unable to read ${path} as it is a directory`);
break;
default:
console.error(`Error reading file: ${e.message}`);
}
process.exit(1);
}
return convertSystemDataStr(AES.decrypt(fileData, SAVE_KEY).toString(enc.Utf8));
}
/* Print the usage message and exits */
function printUsage() {
console.log(`
Usage: node decrypt-save.js <encrypted-file> [save-file]
Arguments:
file-path Path to the encrypted save file to decrypt.
save-file Path to where the decrypted data should be written. If not provided, the decrypted data will be printed to the console.
Options:
-h, --help Show this help message and exit.
Description:
This script decrypts an encrypted pokerogue save file
`);
}
/**
* Write `data` to `filePath`, gracefully communicating errors that arise
* @param {string} filePath
* @param {string} data
*/
function writeToFile(filePath, data) {
try {
fs.writeFileSync(filePath, data);
} catch (e) {
switch (e.code) {
case "EACCES":
console.error(`Could not open ${filePath}: Permission denied`);
break;
case "EISDIR":
console.error(`Unable to write to ${filePath} as it is a directory`);
break;
default:
console.error(`Error writing file: ${e.message}`);
}
process.exit(1);
}
}
function main() {
let args = process.argv.slice(2);
// Get options
const options = args.filter(arg => arg.startsWith("-"));
// get args
args = args.filter(arg => !arg.startsWith("-"));
if (args.length === 0 || options.includes("-h") || options.includes("--help") || args.length > 2) {
printUsage();
process.exit(0);
}
// If the user provided a second argument, check if the file exists already and refuse to write to it.
if (args.length === 2) {
const destPath = args[1];
if (fs.existsSync(destPath)) {
console.error(`Refusing to overwrite ${destPath}`);
process.exit(1);
}
}
// Otherwise, commence decryption.
const decrypt = decryptSave(args[0]);
if (args.length === 1) {
process.stdout.write(decrypt);
process.exit(0);
}
writeToFile(destPath, decrypt);
}
main();

View File

@ -7425,7 +7425,12 @@ export function initAbilities() {
.uncopiable()
.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))
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) =>
pokemon !== attacker
&& move.category === MoveCategory.STATUS
&& ![ MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES, MoveTarget.USER_SIDE ].includes(move.moveTarget)
)
.edgeCase() // Heal Bell should not cure the status of a Pokemon with Good As Gold
.ignorable(),
new Ability(Abilities.VESSEL_OF_RUIN, 9)
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)

View File

@ -7528,7 +7528,7 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr {
/** Causes the effect to fail when the target's ability is unsupressable or already suppressed. */
getCondition(): MoveConditionFunc {
return (user, target, move) => target.getAbility().isSuppressable && !target.summonData.abilitySuppressed;
return (_user, target, _move) => !target.summonData.abilitySuppressed && (target.getAbility().isSuppressable || (target.hasPassive() && target.getPassiveAbility().isSuppressable));
}
}

View File

@ -581,7 +581,7 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
}
function getEggOptions(commonEggs: number, rareEggs: number) {
const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(trainerNameKey);
const eggDescription = i18next.t(`${namespace}:title`);
const eggOptions: IEggOptions[] = [];
if (commonEggs > 0) {

View File

@ -1697,8 +1697,8 @@ export function initSpecies() {
new PokemonSpecies(Species.CHINCHOU, 2, false, false, false, "Angler Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 0.5, 12, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 330, 75, 38, 38, 56, 56, 67, 190, 50, 66, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.LANTURN, 2, false, false, false, "Light Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 1.2, 22.5, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 460, 125, 58, 58, 76, 76, 67, 75, 50, 161, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.PICHU, 2, false, false, false, "Tiny Mouse Pokémon", PokemonType.ELECTRIC, null, 0.3, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, GrowthRate.MEDIUM_FAST, 50, false, false,
new PokemonForm("Normal", "", PokemonType.ELECTRIC, null, 1.4, 61.5, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
new PokemonForm("Spiky-Eared", "spiky", PokemonType.ELECTRIC, null, 1.4, 61.5, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
new PokemonForm("Normal", "", PokemonType.ELECTRIC, null, 1.4, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
new PokemonForm("Spiky-Eared", "spiky", PokemonType.ELECTRIC, null, 1.4, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
),
new PokemonSpecies(Species.CLEFFA, 2, false, false, false, "Star Shape Pokémon", PokemonType.FAIRY, null, 0.3, 3, Abilities.CUTE_CHARM, Abilities.MAGIC_GUARD, Abilities.FRIEND_GUARD, 218, 50, 25, 28, 45, 55, 15, 150, 140, 44, GrowthRate.FAST, 25, false),
new PokemonSpecies(Species.IGGLYBUFF, 2, false, false, false, "Balloon Pokémon", PokemonType.NORMAL, PokemonType.FAIRY, 0.3, 1, Abilities.CUTE_CHARM, Abilities.COMPETITIVE, Abilities.FRIEND_GUARD, 210, 90, 30, 15, 40, 20, 15, 170, 50, 42, GrowthRate.FAST, 25, false),
@ -3121,7 +3121,7 @@ export function initSpecies() {
),
new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", PokemonType.WATER, PokemonType.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Gouging Fire and Raging Bolt
new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", PokemonType.GRASS, PokemonType.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Iron Boulder and Iron Crown
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 4.4, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, false, true),

View File

@ -0,0 +1,9 @@
export enum DropDownColumn {
GEN,
TYPES,
BIOME,
CAUGHT,
UNLOCKS,
MISC,
SORT
}

View File

@ -5,7 +5,9 @@ import { globalScene } from "#app/global-scene";
import type { Variant } from "#app/sprites/variant";
import { populateVariantColors, variantColorCache } from "#app/sprites/variant";
import { variantData } from "#app/sprites/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info";
import BattleInfo from "#app/ui/battle-info/battle-info";
import { EnemyBattleInfo } from "#app/ui/battle-info/enemy-battle-info";
import { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info";
import type Move from "#app/data/moves/move";
import {
HighCritAttr,
@ -3337,22 +3339,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.battleInfo.updateInfo(this, instant);
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.battleInfo.updateEffectiveness(effectiveness);
}
toggleStats(visible: boolean): void {
this.battleInfo.toggleStats(visible);
}
toggleFlyout(visible: boolean): void {
this.battleInfo.toggleFlyout(visible);
}
/**
* Adds experience to this PlayerPokemon, subject to wave based level caps.
* @param exp The amount of experience to add
@ -5508,6 +5498,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
export class PlayerPokemon extends Pokemon {
protected battleInfo: PlayerBattleInfo;
public compatibleTms: Moves[];
constructor(
@ -6026,6 +6017,7 @@ export class PlayerPokemon extends Pokemon {
}
export class EnemyPokemon extends Pokemon {
protected battleInfo: EnemyBattleInfo;
public trainerSlot: TrainerSlot;
public aiType: AiType;
public bossSegments: number;
@ -6694,6 +6686,19 @@ export class EnemyPokemon extends Pokemon {
return ret;
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.battleInfo.updateEffectiveness(effectiveness);
}
toggleFlyout(visible: boolean): void {
this.battleInfo.toggleFlyout(visible);
}
}
/**

View File

@ -7,7 +7,7 @@ import type PokemonSpecies from "./data/pokemon-species";
import { allSpecies } from "./data/pokemon-species";
import type { Arena } from "./field/arena";
import Overrides from "#app/overrides";
import { randSeedInt, randSeedItem } from "#app/utils/common";
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common";
import { Biome } from "#enums/biome";
import { Species } from "#enums/species";
import { Challenges } from "./enums/challenges";
@ -124,16 +124,20 @@ export class GameMode implements GameModeConfig {
/**
* @returns either:
* - random biome for Daily mode
* - override from overrides.ts
* - random biome for Daily mode
* - Town
*/
getStartingBiome(): Biome {
if (!isNullOrUndefined(Overrides.STARTING_BIOME_OVERRIDE)) {
return Overrides.STARTING_BIOME_OVERRIDE;
}
switch (this.modeId) {
case GameModes.DAILY:
return getDailyStartingBiome();
default:
return Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN;
return Biome.TOWN;
}
}

View File

@ -29,7 +29,7 @@ window.addEventListener("unhandledrejection", event => {
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
return this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
};
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;

View File

@ -73,7 +73,7 @@ class DefaultOverrides {
*/
readonly BATTLE_STYLE_OVERRIDE: BattleStyle | null = null;
readonly STARTING_WAVE_OVERRIDE: number = 0;
readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
readonly STARTING_BIOME_OVERRIDE: Biome | null = null;
readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null;
/** Multiplies XP gained by this value including 0. Set to null to ignore the override. */
readonly XP_MULTIPLIER_OVERRIDE: number | null = null;

View File

@ -125,6 +125,12 @@ export class SwitchSummonPhase extends SummonPhase {
const switchedInPokemon: Pokemon | undefined = party[this.slotIndex];
this.lastPokemon = this.getPokemon();
// Defensive programming: Overcome the bug where the summon data has somehow not been reset
// prior to switching in a new Pokemon.
// Force the switch to occur and load the assets for the new pokemon, ignoring override.
switchedInPokemon.resetSummonData();
switchedInPokemon.loadAssets(true);
applyPreSummonAbAttrs(PreSummonAbAttr, switchedInPokemon);
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
if (!switchedInPokemon) {
@ -132,6 +138,7 @@ export class SwitchSummonPhase extends SummonPhase {
return;
}
if (this.switchType === SwitchType.BATON_PASS) {
// If switching via baton pass, update opposing tags coming from the prior pokemon
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>

View File

@ -65,14 +65,14 @@ const fonts: Array<LoadingFontFaceProperty> = [
unicodeRange: rangesByLanguage.chinese,
}),
extraOptions: { sizeAdjust: "70%", format: "woff2" },
only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca"],
only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca", "da", "tr", "ro", "ru"],
},
{
face: new FontFace("pkmnems", "url(./fonts/unifont-15.1.05.subset.woff2)", {
unicodeRange: rangesByLanguage.chinese,
}),
extraOptions: { format: "woff2" },
only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca"],
only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca", "da", "tr", "ro", "ru"],
},
// japanese
{
@ -174,7 +174,7 @@ export async function initI18n(): Promise<void> {
"es-MX": ["es-ES", "en"],
default: ["en"],
},
supportedLngs: ["en", "es-ES", "es-MX", "fr", "it", "de", "zh-CN", "zh-TW", "pt-BR", "ko", "ja", "ca-ES"],
supportedLngs: ["en", "es-ES", "es-MX", "fr", "it", "de", "zh-CN", "zh-TW", "pt-BR", "ko", "ja", "ca", "da", "tr", "ro", "ru"],
backend: {
loadPath(lng: string, [ns]: string[]) {
let fileName: string;

View File

@ -193,35 +193,35 @@ export const Setting: Array<Setting> = [
options: [
{
value: "1",
label: "1x",
label: i18next.t("settings:gameSpeed1x"),
},
{
value: "1.25",
label: "1.25x",
label: i18next.t("settings:gameSpeed1_25x"),
},
{
value: "1.5",
label: "1.5x",
label: i18next.t("settings:gameSpeed1_5x"),
},
{
value: "2",
label: "2x",
label: i18next.t("settings:gameSpeed2x"),
},
{
value: "2.5",
label: "2.5x",
label: i18next.t("settings:gameSpeed2_5x"),
},
{
value: "3",
label: "3x",
label: i18next.t("settings:gameSpeed3x"),
},
{
value: "4",
label: "4x",
label: i18next.t("settings:gameSpeed4x"),
},
{
value: "5",
label: "5x",
label: i18next.t("settings:gameSpeed5x"),
},
],
default: 3,
@ -921,10 +921,6 @@ export function setSetting(setting: string, value: number): boolean {
label: "Español (LATAM)",
handler: () => changeLocaleHandler("es-MX"),
},
{
label: "Italiano",
handler: () => changeLocaleHandler("it"),
},
{
label: "Français",
handler: () => changeLocaleHandler("fr"),
@ -933,18 +929,14 @@ export function setSetting(setting: string, value: number): boolean {
label: "Deutsch",
handler: () => changeLocaleHandler("de"),
},
{
label: "Italiano",
handler: () => changeLocaleHandler("it"),
},
{
label: "Português (BR)",
handler: () => changeLocaleHandler("pt-BR"),
},
{
label: "简体中文",
handler: () => changeLocaleHandler("zh-CN"),
},
{
label: "繁體中文",
handler: () => changeLocaleHandler("zh-TW"),
},
{
label: "한국어",
handler: () => changeLocaleHandler("ko"),
@ -954,8 +946,32 @@ export function setSetting(setting: string, value: number): boolean {
handler: () => changeLocaleHandler("ja"),
},
{
label: "Català",
handler: () => changeLocaleHandler("ca-ES"),
label: "简体中文",
handler: () => changeLocaleHandler("zh-CN"),
},
{
label: "繁體中文",
handler: () => changeLocaleHandler("zh-TW"),
},
{
label: "Català (Needs Help)",
handler: () => changeLocaleHandler("ca"),
},
{
label: "Türkçe (Needs Help)",
handler: () => changeLocaleHandler("tr")
},
{
label: "Русский (Needs Help)",
handler: () => changeLocaleHandler("ru"),
},
{
label: "Dansk (Needs Help)",
handler: () => changeLocaleHandler("da")
},
{
label: "Română (Needs Help)",
handler: () => changeLocaleHandler("ro")
},
{
label: i18next.t("settings:back"),

View File

@ -20,37 +20,37 @@ declare module "phaser" {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
interface Sprite {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
interface Image {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
interface NineSlice {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
interface Text {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
interface Rectangle {
/**
* Sets this object's position relative to another object with a given offset
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
setPositionRelative(guideObject: any, x: number, y: number): this;
}
}

View File

@ -161,7 +161,7 @@ export class UiInputs {
buttonInfo(pressed = true): void {
if (globalScene.showMovesetFlyout) {
for (const p of globalScene.getField().filter(p => p?.isActive(true))) {
for (const p of globalScene.getEnemyField().filter(p => p?.isActive(true))) {
p.toggleFlyout(pressed);
}
}

View File

@ -1,4 +1,4 @@
import type { default as Pokemon } from "../field/pokemon";
import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
import { addTextObject, TextStyle } from "./text";
import { fixedInt } from "#app/utils/common";
import { globalScene } from "#app/global-scene";
@ -126,7 +126,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
* Links the given {@linkcode Pokemon} and subscribes to the {@linkcode BattleSceneEventType.MOVE_USED} event
* @param pokemon {@linkcode Pokemon} to link to this flyout
*/
initInfo(pokemon: Pokemon) {
initInfo(pokemon: EnemyPokemon) {
this.pokemon = pokemon;
this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`;

View File

@ -1,986 +0,0 @@
import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
import { getLevelTotalExp, getLevelRelExp } from "../data/exp";
import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common";
import { addTextObject, TextStyle } from "./text";
import { getGenderSymbol, getGenderColor, Gender } from "../data/gender";
import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
import { getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type";
import { getVariantTint } from "#app/sprites/variant";
import { Stat } from "#enums/stat";
import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
export default class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
private baseY: number;
private player: boolean;
private mini: boolean;
private boss: boolean;
private bossSegments: number;
private offset: boolean;
private lastName: string | null;
private lastTeraType: PokemonType;
private lastStatus: StatusEffect;
private lastHp: number;
private lastMaxHp: number;
private lastHpFrame: string | null;
private lastExp: number;
private lastLevelExp: number;
private lastLevel: number;
private lastLevelCapped: boolean;
private lastStats: string;
private box: Phaser.GameObjects.Sprite;
private nameText: Phaser.GameObjects.Text;
private genderText: Phaser.GameObjects.Text;
private ownedIcon: Phaser.GameObjects.Sprite;
private championRibbon: Phaser.GameObjects.Sprite;
private teraIcon: Phaser.GameObjects.Sprite;
private shinyIcon: Phaser.GameObjects.Sprite;
private fusionShinyIcon: Phaser.GameObjects.Sprite;
private splicedIcon: Phaser.GameObjects.Sprite;
private statusIndicator: Phaser.GameObjects.Sprite;
private levelContainer: Phaser.GameObjects.Container;
private hpBar: Phaser.GameObjects.Image;
private hpBarSegmentDividers: Phaser.GameObjects.Rectangle[];
private levelNumbersContainer: Phaser.GameObjects.Container;
private hpNumbersContainer: Phaser.GameObjects.Container;
private type1Icon: Phaser.GameObjects.Sprite;
private type2Icon: Phaser.GameObjects.Sprite;
private type3Icon: Phaser.GameObjects.Sprite;
private expBar: Phaser.GameObjects.Image;
// #region Type effectiveness hint objects
private effectivenessContainer: Phaser.GameObjects.Container;
private effectivenessWindow: Phaser.GameObjects.NineSlice;
private effectivenessText: Phaser.GameObjects.Text;
private currentEffectiveness?: string;
// #endregion
public expMaskRect: Phaser.GameObjects.Graphics;
private statsContainer: Phaser.GameObjects.Container;
private statsBox: Phaser.GameObjects.Sprite;
private statValuesContainer: Phaser.GameObjects.Container;
private statNumbers: Phaser.GameObjects.Sprite[];
public flyoutMenu?: BattleFlyout;
private statOrder: Stat[];
private readonly statOrderPlayer = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
private readonly statOrderEnemy = [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
constructor(x: number, y: number, player: boolean) {
super(globalScene, x, y);
this.baseY = y;
this.player = player;
this.mini = !player;
this.boss = false;
this.offset = false;
this.lastName = null;
this.lastTeraType = PokemonType.UNKNOWN;
this.lastStatus = StatusEffect.NONE;
this.lastHp = -1;
this.lastMaxHp = -1;
this.lastHpFrame = null;
this.lastExp = -1;
this.lastLevelExp = -1;
this.lastLevel = -1;
// Initially invisible and shown via Pokemon.showInfo
this.setVisible(false);
this.box = globalScene.add.sprite(0, 0, this.getTextureName());
this.box.setName("box");
this.box.setOrigin(1, 0.5);
this.add(this.box);
this.nameText = addTextObject(player ? -115 : -124, player ? -15.2 : -11.2, "", TextStyle.BATTLE_INFO);
this.nameText.setName("text_name");
this.nameText.setOrigin(0, 0);
this.add(this.nameText);
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO);
this.genderText.setName("text_gender");
this.genderText.setOrigin(0, 0);
this.genderText.setPositionRelative(this.nameText, 0, 2);
this.add(this.genderText);
if (!this.player) {
this.ownedIcon = globalScene.add.sprite(0, 0, "icon_owned");
this.ownedIcon.setName("icon_owned");
this.ownedIcon.setVisible(false);
this.ownedIcon.setOrigin(0, 0);
this.ownedIcon.setPositionRelative(this.nameText, 0, 11.75);
this.add(this.ownedIcon);
this.championRibbon = globalScene.add.sprite(0, 0, "champion_ribbon");
this.championRibbon.setName("icon_champion_ribbon");
this.championRibbon.setVisible(false);
this.championRibbon.setOrigin(0, 0);
this.championRibbon.setPositionRelative(this.nameText, 8, 11.75);
this.add(this.championRibbon);
}
this.teraIcon = globalScene.add.sprite(0, 0, "icon_tera");
this.teraIcon.setName("icon_tera");
this.teraIcon.setVisible(false);
this.teraIcon.setOrigin(0, 0);
this.teraIcon.setScale(0.5);
this.teraIcon.setPositionRelative(this.nameText, 0, 2);
this.teraIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.teraIcon);
this.shinyIcon = globalScene.add.sprite(0, 0, "shiny_star");
this.shinyIcon.setName("icon_shiny");
this.shinyIcon.setVisible(false);
this.shinyIcon.setOrigin(0, 0);
this.shinyIcon.setScale(0.5);
this.shinyIcon.setPositionRelative(this.nameText, 0, 2);
this.shinyIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.shinyIcon);
this.fusionShinyIcon = globalScene.add.sprite(0, 0, "shiny_star_2");
this.fusionShinyIcon.setName("icon_fusion_shiny");
this.fusionShinyIcon.setVisible(false);
this.fusionShinyIcon.setOrigin(0, 0);
this.fusionShinyIcon.setScale(0.5);
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
this.add(this.fusionShinyIcon);
this.splicedIcon = globalScene.add.sprite(0, 0, "icon_spliced");
this.splicedIcon.setName("icon_spliced");
this.splicedIcon.setVisible(false);
this.splicedIcon.setOrigin(0, 0);
this.splicedIcon.setScale(0.5);
this.splicedIcon.setPositionRelative(this.nameText, 0, 2);
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.splicedIcon);
this.statusIndicator = globalScene.add.sprite(0, 0, getLocalizedSpriteKey("statuses"));
this.statusIndicator.setName("icon_status");
this.statusIndicator.setVisible(false);
this.statusIndicator.setOrigin(0, 0);
this.statusIndicator.setPositionRelative(this.nameText, 0, 11.5);
this.add(this.statusIndicator);
this.levelContainer = globalScene.add.container(player ? -41 : -50, player ? -10 : -5);
this.levelContainer.setName("container_level");
this.add(this.levelContainer);
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
this.levelContainer.add(levelOverlay);
this.hpBar = globalScene.add.image(player ? -61 : -71, player ? -1 : 4.5, "overlay_hp");
this.hpBar.setName("hp_bar");
this.hpBar.setOrigin(0);
this.add(this.hpBar);
this.hpBarSegmentDividers = [];
this.levelNumbersContainer = globalScene.add.container(9.5, globalScene.uiTheme ? 0 : -0.5);
this.levelNumbersContainer.setName("container_level");
this.levelContainer.add(this.levelNumbersContainer);
if (this.player) {
this.hpNumbersContainer = globalScene.add.container(-15, 10);
this.hpNumbersContainer.setName("container_hp");
this.add(this.hpNumbersContainer);
const expBar = globalScene.add.image(-98, 18, "overlay_exp");
expBar.setName("overlay_exp");
expBar.setOrigin(0);
this.add(expBar);
const expMaskRect = globalScene.make.graphics({});
expMaskRect.setScale(6);
expMaskRect.fillStyle(0xffffff);
expMaskRect.beginPath();
expMaskRect.fillRect(127, 126, 85, 2);
const expMask = expMaskRect.createGeometryMask();
expBar.setMask(expMask);
this.expBar = expBar;
this.expMaskRect = expMaskRect;
}
this.statsContainer = globalScene.add.container(0, 0);
this.statsContainer.setName("container_stats");
this.statsContainer.setAlpha(0);
this.add(this.statsContainer);
this.statsBox = globalScene.add.sprite(0, 0, `${this.getTextureName()}_stats`);
this.statsBox.setName("box_stats");
this.statsBox.setOrigin(1, 0.5);
this.statsContainer.add(this.statsBox);
const statLabels: Phaser.GameObjects.Sprite[] = [];
this.statNumbers = [];
this.statValuesContainer = globalScene.add.container(0, 0);
this.statsContainer.add(this.statValuesContainer);
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
// since the player won't have HP to show, it doesn't need to change from the current version
const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5;
const paddingX = this.player ? 4 : 2;
const statOverflow = this.player ? 1 : 0;
this.statOrder = this.player ? this.statOrderPlayer : this.statOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
this.statOrder.map((s, i) => {
// we do a check for i > statOverflow to see when the stat labels go onto the next column
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
const statX =
i > statOverflow
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
let statY: number; // this will be the y-axis placement for the labels
if (this.statOrder[i] === Stat.SPD || this.statOrder[i] === Stat.HP) {
statY = baseY + 5;
} else {
statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
}
const statLabel = globalScene.add.sprite(statX, statY, "pbinfo_stat", Stat[s]);
statLabel.setName("icon_stat_label_" + i.toString());
statLabel.setOrigin(0, 0);
statLabels.push(statLabel);
this.statValuesContainer.add(statLabel);
const statNumber = globalScene.add.sprite(
statX + statLabel.width,
statY,
"pbinfo_stat_numbers",
this.statOrder[i] !== Stat.HP ? "3" : "empty",
);
statNumber.setName("icon_stat_number_" + i.toString());
statNumber.setOrigin(0, 0);
this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber);
if (this.statOrder[i] === Stat.HP) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
});
if (!this.player) {
this.flyoutMenu = new BattleFlyout(this.player);
this.add(this.flyoutMenu);
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
}
this.type1Icon = globalScene.add.sprite(
player ? -139 : -15,
player ? -17 : -15.5,
`pbinfo_${player ? "player" : "enemy"}_type1`,
);
this.type1Icon.setName("icon_type_1");
this.type1Icon.setOrigin(0, 0);
this.add(this.type1Icon);
this.type2Icon = globalScene.add.sprite(
player ? -139 : -15,
player ? -1 : -2.5,
`pbinfo_${player ? "player" : "enemy"}_type2`,
);
this.type2Icon.setName("icon_type_2");
this.type2Icon.setOrigin(0, 0);
this.add(this.type2Icon);
this.type3Icon = globalScene.add.sprite(
player ? -154 : 0,
player ? -17 : -15.5,
`pbinfo_${player ? "player" : "enemy"}_type`,
);
this.type3Icon.setName("icon_type_3");
this.type3Icon.setOrigin(0, 0);
this.add(this.type3Icon);
if (!this.player) {
this.effectivenessContainer = globalScene.add.container(0, 0);
this.effectivenessContainer.setPositionRelative(this.type1Icon, 22, 4);
this.effectivenessContainer.setVisible(false);
this.add(this.effectivenessContainer);
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
this.effectivenessContainer.add(this.effectivenessWindow);
this.effectivenessContainer.add(this.effectivenessText);
}
}
getStatsValueContainer(): Phaser.GameObjects.Container {
return this.statValuesContainer;
}
initInfo(pokemon: Pokemon) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
this.name = pokemon.getNameToRender();
this.box.name = pokemon.getNameToRender();
this.flyoutMenu?.initInfo(pokemon);
this.genderText.setText(getGenderSymbol(pokemon.gender));
this.genderText.setColor(getGenderColor(pokemon.gender));
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.lastTeraType = pokemon.getTeraType();
this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
this.teraIcon.setVisible(pokemon.isTerastallized);
this.teraIcon.on("pointerover", () => {
if (pokemon.isTerastallized) {
globalScene.ui.showTooltip(
"",
i18next.t("fightUiHandler:teraHover", {
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
}),
);
}
});
this.teraIcon.on("pointerout", () => globalScene.ui.hideTooltip());
const isFusion = pokemon.isFusion(true);
this.splicedIcon.setPositionRelative(
this.nameText,
nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
2.5,
);
this.splicedIcon.setVisible(isFusion);
if (this.splicedIcon.visible) {
this.splicedIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
),
);
this.splicedIcon.on("pointerout", () => globalScene.ui.hideTooltip());
}
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setPositionRelative(
this.nameText,
nameTextWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`);
this.shinyIcon.setVisible(pokemon.isShiny());
this.shinyIcon.setTint(getVariantTint(baseVariant));
if (this.shinyIcon.visible) {
const shinyDescriptor =
doubleShiny || baseVariant
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
: "";
this.shinyIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
),
);
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
}
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
this.fusionShinyIcon.setVisible(doubleShiny);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
if (!this.player) {
if (this.nameText.visible) {
this.nameText.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
i18next.t("battleInfo:generation", {
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
}),
),
);
this.nameText.on("pointerout", () => globalScene.ui.hideTooltip());
}
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
const opponentPokemonDexAttr = pokemon.getDexAttr();
if (globalScene.gameMode.isClassic) {
if (
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
this.championRibbon.setVisible(true);
}
}
// Check if Player owns all genders and forms of the Pokemon
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
// Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
if (missingDexAttrs || !playerOwnsThisAbility) {
this.ownedIcon.setTint(0x808080);
}
if (this.boss) {
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
}
}
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
this.hpBar.setFrame(this.lastHpFrame);
if (this.player) {
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
}
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
this.shinyIcon.setVisible(pokemon.isShiny());
const types = pokemon.getTypes(true, false, undefined, true);
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
if (this.player) {
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
this.statValuesContainer.setPosition(8, 7);
}
const stats = this.statOrder.map(() => 0);
this.lastStats = stats.join("");
this.updateStats(stats);
}
getTextureName(): string {
return `pbinfo_${this.player ? "player" : "enemy"}${!this.player && this.boss ? "_boss" : this.mini ? "_mini" : ""}`;
}
setMini(mini: boolean): void {
if (this.mini === mini) {
return;
}
this.mini = mini;
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
if (this.player) {
this.y -= 12 * (mini ? 1 : -1);
this.baseY = this.y;
}
const offsetElements = [
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.statusIndicator,
this.levelContainer,
];
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
el.x += 4 * (mini ? 1 : -1);
el.y += -8 * (mini ? 1 : -1);
});
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
const toggledElements = [this.hpNumbersContainer, this.expBar];
toggledElements.forEach(el => el.setVisible(!mini));
}
toggleStats(visible: boolean): void {
globalScene.tweens.add({
targets: this.statsContainer,
duration: fixedInt(125),
ease: "Sine.easeInOut",
alpha: visible ? 1 : 0,
});
}
updateBossSegments(pokemon: EnemyPokemon): void {
const boss = !!pokemon.bossSegments;
if (boss !== this.boss) {
this.boss = boss;
[
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.ownedIcon,
this.championRibbon,
this.statusIndicator,
this.statValuesContainer,
].map(e => (e.x += 48 * (boss ? -1 : 1)));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.levelContainer.x += 2 * (boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
}
this.bossSegments = boss ? pokemon.bossSegments : 0;
this.updateBossSegmentDividers(pokemon);
}
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
while (this.hpBarSegmentDividers.length) {
this.hpBarSegmentDividers.pop()?.destroy();
}
if (this.boss && this.bossSegments > 1) {
const uiTheme = globalScene.uiTheme;
const maxHp = pokemon.getMaxHp();
for (let s = 1; s < this.bossSegments; s++) {
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
const divider = globalScene.add.rectangle(
0,
0,
1,
this.hpBar.height - (uiTheme ? 0 : 1),
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
);
divider.setOrigin(0.5, 0);
divider.setName("hpBar_divider_" + s.toString());
this.add(divider);
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
this.hpBarSegmentDividers.push(divider);
}
}
}
setOffset(offset: boolean): void {
if (this.offset === offset) {
return;
}
this.offset = offset;
this.x += 10 * (this.offset === this.player ? 1 : -1);
this.y += 27 * (this.offset ? 1 : -1);
this.baseY = this.y;
}
updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
return new Promise(resolve => {
if (!globalScene) {
return resolve();
}
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
this.genderText.setText(getGenderSymbol(gender));
this.genderText.setColor(getGenderColor(gender));
const nameUpdated = this.lastName !== pokemon.getNameToRender();
if (nameUpdated) {
this.updateNameText(pokemon);
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
}
const teraType = pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN;
const teraTypeUpdated = this.lastTeraType !== teraType;
if (teraTypeUpdated) {
this.teraIcon.setVisible(teraType !== PokemonType.UNKNOWN);
this.teraIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth + this.genderText.displayWidth + 1,
2,
);
this.teraIcon.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(teraType)));
this.lastTeraType = teraType;
}
const isFusion = pokemon.isFusion(true);
if (nameUpdated || teraTypeUpdated) {
this.splicedIcon.setVisible(isFusion);
this.teraIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth + this.genderText.displayWidth + 1,
2,
);
this.splicedIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
1.5,
);
this.shinyIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
}
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
if (this.lastStatus !== StatusEffect.NONE) {
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
}
const offsetX = !this.player ? (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0) : 0;
this.statusIndicator.setPositionRelative(this.nameText, offsetX, 11.5);
this.statusIndicator.setVisible(!!this.lastStatus);
}
const types = pokemon.getTypes(true, false, undefined, true);
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
const updateHpFrame = () => {
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
};
const updatePokemonHp = () => {
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
const speed = globalScene.hpBarSpeed;
if (speed) {
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
}
globalScene.tweens.add({
targets: this.hpBar,
ease: "Sine.easeOut",
scaleX: pokemon.getHpRatio(true),
duration: duration,
onUpdate: () => {
if (this.player && this.lastHp !== pokemon.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
this.lastHp = tweenHp;
}
updateHpFrame();
},
onComplete: () => {
updateHpFrame();
// If, after tweening, the hp is different from the original (due to rounding), force the hp number display
// to update to the correct value.
if (this.player && this.lastHp !== pokemon.hp) {
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
this.lastHp = pokemon.hp;
}
resolve();
},
});
if (!this.player) {
this.lastHp = pokemon.hp;
}
this.lastMaxHp = pokemon.getMaxHp();
};
if (this.player) {
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
const originalResolve = resolve;
const durationMultipler = Math.max(
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(
1 - Math.min(pokemon.level - this.lastLevel, 10) / 10,
),
0.1,
);
resolve = () => this.updatePokemonExp(pokemon, false, durationMultipler).then(() => originalResolve());
} else if (isLevelCapped !== this.lastLevelCapped) {
this.setLevel(pokemon.level);
}
this.lastLevelCapped = isLevelCapped;
}
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
return updatePokemonHp();
}
if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
}
const stats = pokemon.getStatStages();
const statsStr = stats.join("");
if (this.lastStats !== statsStr) {
this.updateStats(stats);
this.lastStats = statsStr;
}
this.shinyIcon.setVisible(pokemon.isShiny(true));
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setTint(getVariantTint(baseVariant));
this.fusionShinyIcon.setVisible(doubleShiny);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
resolve();
});
}
updateNameText(pokemon: Pokemon): void {
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
let nameTextWidth: number;
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
nameTextWidth = nameSizeTest.displayWidth;
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
nameTextWidth >
(this.player || !this.boss ? 60 : 98) -
((gender !== Gender.GENDERLESS ? 6 : 0) +
(pokemon.fusionSpecies ? 8 : 0) +
(pokemon.isShiny() ? 8 : 0) +
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
) {
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
nameSizeTest.setText(displayName);
nameTextWidth = nameSizeTest.displayWidth;
}
nameSizeTest.destroy();
this.nameText.setText(displayName);
this.lastName = pokemon.getNameToRender();
if (this.nameText.visible) {
this.nameText.setInteractive(
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
Phaser.Geom.Rectangle.Contains,
);
}
}
updatePokemonExp(pokemon: Pokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
return new Promise(resolve => {
const levelUp = this.lastLevel < pokemon.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
if (levelUp) {
ratio = 1;
} else {
ratio = 0;
}
instant = true;
}
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
1 - Math.max(this.lastLevel - 100, 0) / 150,
);
let duration =
this.visible && !instant
? ((levelExp - this.lastLevelExp) / relLevelExp) *
BattleInfo.EXP_GAINS_DURATION_BASE *
durationMultiplier *
levelDurationMultiplier
: 0;
const speed = globalScene.expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
} else {
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
}
if (duration) {
globalScene.playSound("se/exp");
}
globalScene.tweens.add({
targets: this.expMaskRect,
ease: "Sine.easeIn",
x: ratio * 510,
duration: duration,
onComplete: () => {
if (!globalScene) {
return resolve();
}
if (duration) {
globalScene.sound.stopByKey("se/exp");
}
if (ratio === 1) {
globalScene.playSound("se/level_up");
this.setLevel(this.lastLevel);
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
this.expMaskRect.x = 0;
this.updateInfo(pokemon, instant).then(() => resolve());
});
return;
}
resolve();
},
});
});
}
setLevel(level: number): void {
const isCapped = level >= globalScene.getMaxExpLevel();
this.levelNumbersContainer.removeAll(true);
const levelStr = level.toString();
for (let i = 0; i < levelStr.length; i++) {
this.levelNumbersContainer.add(
globalScene.add.image(i * 8, 0, `numbers${isCapped && this.player ? "_red" : ""}`, levelStr[i]),
);
}
this.levelContainer.setX((this.player ? -41 : -50) - 8 * Math.max(levelStr.length - 3, 0));
}
setHpNumbers(hp: number, maxHp: number): void {
if (!this.player || !globalScene) {
return;
}
this.hpNumbersContainer.removeAll(true);
const hpStr = hp.toString();
const maxHpStr = maxHp.toString();
let offset = 0;
for (let i = maxHpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
}
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
for (let i = hpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
}
}
updateStats(stats: number[]): void {
this.statOrder.map((s, i) => {
if (s !== Stat.HP) {
this.statNumbers[i].setFrame(stats[s - 1].toString());
}
});
}
/**
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
*/
toggleFlyout(visible: boolean): void {
this.flyoutMenu?.toggleFlyout(visible);
if (visible) {
this.effectivenessContainer?.setVisible(false);
} else {
this.updateEffectiveness(this.currentEffectiveness);
}
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
if (this.player) {
return;
}
this.currentEffectiveness = effectiveness;
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) {
this.effectivenessContainer.setVisible(false);
return;
}
this.effectivenessText.setText(effectiveness);
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
this.effectivenessContainer.setVisible(true);
}
getBaseY(): number {
return this.baseY;
}
resetY(): void {
this.y = this.baseY;
}
}
export class PlayerBattleInfo extends BattleInfo {
constructor() {
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true);
}
}
export class EnemyBattleInfo extends BattleInfo {
constructor() {
super(140, -141, false);
}
setMini(_mini: boolean): void {} // Always mini
}

View File

@ -0,0 +1,692 @@
import type { default as Pokemon } from "../../field/pokemon";
import { getLocalizedSpriteKey, fixedInt, getShinyDescriptor } from "#app/utils/common";
import { addTextObject, TextStyle } from "../text";
import { getGenderSymbol, getGenderColor, Gender } from "../../data/gender";
import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
import { getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type";
import { getVariantTint } from "#app/sprites/variant";
import { Stat } from "#enums/stat";
import i18next from "i18next";
/**
* Parameters influencing the position of elements within the battle info container
*/
export type BattleInfoParamList = {
/** X offset for the name text*/
nameTextX: number;
/** Y offset for the name text */
nameTextY: number;
/** X offset for the level container */
levelContainerX: number;
/** Y offset for the level container */
levelContainerY: number;
/** X offset for the hp bar */
hpBarX: number;
/** Y offset for the hp bar */
hpBarY: number;
/** Parameters for the stat box container */
statBox: {
/** The starting offset from the left of the label for the entries in the stat box */
xOffset: number;
/** The X padding between each number column */
paddingX: number;
/** The index of the stat entries at which paddingX is used instead of startingX */
statOverflow: number;
};
};
export default abstract class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
protected baseY: number;
protected baseLvContainerX: number;
protected player: boolean;
protected mini: boolean;
protected boss: boolean;
protected bossSegments: number;
protected offset: boolean;
protected lastName: string | null;
protected lastTeraType: PokemonType;
protected lastStatus: StatusEffect;
protected lastHp: number;
protected lastMaxHp: number;
protected lastHpFrame: string | null;
protected lastExp: number;
protected lastLevelExp: number;
protected lastLevel: number;
protected lastLevelCapped: boolean;
protected lastStats: string;
protected box: Phaser.GameObjects.Sprite;
protected nameText: Phaser.GameObjects.Text;
protected genderText: Phaser.GameObjects.Text;
protected teraIcon: Phaser.GameObjects.Sprite;
protected shinyIcon: Phaser.GameObjects.Sprite;
protected fusionShinyIcon: Phaser.GameObjects.Sprite;
protected splicedIcon: Phaser.GameObjects.Sprite;
protected statusIndicator: Phaser.GameObjects.Sprite;
protected levelContainer: Phaser.GameObjects.Container;
protected hpBar: Phaser.GameObjects.Image;
protected levelNumbersContainer: Phaser.GameObjects.Container;
protected type1Icon: Phaser.GameObjects.Sprite;
protected type2Icon: Phaser.GameObjects.Sprite;
protected type3Icon: Phaser.GameObjects.Sprite;
protected expBar: Phaser.GameObjects.Image;
public expMaskRect: Phaser.GameObjects.Graphics;
protected statsContainer: Phaser.GameObjects.Container;
protected statsBox: Phaser.GameObjects.Sprite;
protected statValuesContainer: Phaser.GameObjects.Container;
protected statNumbers: Phaser.GameObjects.Sprite[];
get statOrder(): Stat[] {
return [];
}
/** Helper method used by the constructor to create the tera and shiny icons next to the name */
private constructIcons() {
const hitArea = new Phaser.Geom.Rectangle(0, 0, 12, 15);
const hitCallback = Phaser.Geom.Rectangle.Contains;
this.teraIcon = globalScene.add
.sprite(0, 0, "icon_tera")
.setName("icon_tera")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.shinyIcon = globalScene.add
.sprite(0, 0, "shiny_star")
.setName("icon_shiny")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.fusionShinyIcon = globalScene.add
.sprite(0, 0, "shiny_star_2")
.setName("icon_fusion_shiny")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.copyPosition(this.shinyIcon);
this.splicedIcon = globalScene.add
.sprite(0, 0, "icon_spliced")
.setName("icon_spliced")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.add([this.teraIcon, this.shinyIcon, this.fusionShinyIcon, this.splicedIcon]);
}
/**
* Submethod of the constructor that creates and adds the stats container to the battle info
*/
protected constructStatContainer({ xOffset, paddingX, statOverflow }: BattleInfoParamList["statBox"]): void {
this.statsContainer = globalScene.add.container(0, 0).setName("container_stats").setAlpha(0);
this.add(this.statsContainer);
this.statsBox = globalScene.add
.sprite(0, 0, `${this.getTextureName()}_stats`)
.setName("box_stats")
.setOrigin(1, 0.5);
this.statsContainer.add(this.statsBox);
const statLabels: Phaser.GameObjects.Sprite[] = [];
this.statNumbers = [];
this.statValuesContainer = globalScene.add.container();
this.statsContainer.add(this.statValuesContainer);
const startingX = -this.statsBox.width + xOffset;
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
// since the player won't have HP to show, it doesn't need to change from the current version
for (const [i, s] of this.statOrder.entries()) {
const isHp = s === Stat.HP;
// we do a check for i > statOverflow to see when the stat labels go onto the next column
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
const statX =
i > statOverflow
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
let statY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
if (isHp || s === Stat.SPD) {
statY += 5;
} else if (this.player === !!(i % 2)) {
// we compare i % 2 against this.player to tell us where to place the label
// because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players
// this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
statY += 10;
}
const statLabel = globalScene.add
.sprite(statX, statY, "pbinfo_stat", Stat[s])
.setName("icon_stat_label_" + i.toString())
.setOrigin(0);
statLabels.push(statLabel);
this.statValuesContainer.add(statLabel);
const statNumber = globalScene.add
.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", !isHp ? "3" : "empty")
.setName("icon_stat_number_" + i.toString())
.setOrigin(0);
this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber);
if (isHp) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
}
}
/**
* Submethod of the constructor that creates and adds the pokemon type icons to the battle info
*/
protected abstract constructTypeIcons(): void;
/**
* @param x - The x position of the battle info container
* @param y - The y position of the battle info container
* @param player - Whether this battle info belongs to a player or an enemy
* @param posParams - The parameters influencing the position of elements within the battle info container
*/
constructor(x: number, y: number, player: boolean, posParams: BattleInfoParamList) {
super(globalScene, x, y);
this.baseY = y;
this.player = player;
this.mini = !player;
this.boss = false;
this.offset = false;
this.lastName = null;
this.lastTeraType = PokemonType.UNKNOWN;
this.lastStatus = StatusEffect.NONE;
this.lastHp = -1;
this.lastMaxHp = -1;
this.lastHpFrame = null;
this.lastExp = -1;
this.lastLevelExp = -1;
this.lastLevel = -1;
this.baseLvContainerX = posParams.levelContainerX;
// Initially invisible and shown via Pokemon.showInfo
this.setVisible(false);
this.box = globalScene.add.sprite(0, 0, this.getTextureName()).setName("box").setOrigin(1, 0.5);
this.add(this.box);
this.nameText = addTextObject(posParams.nameTextX, posParams.nameTextY, "", TextStyle.BATTLE_INFO)
.setName("text_name")
.setOrigin(0);
this.add(this.nameText);
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO)
.setName("text_gender")
.setOrigin(0)
.setPositionRelative(this.nameText, 0, 2);
this.add(this.genderText);
this.constructIcons();
this.statusIndicator = globalScene.add
.sprite(0, 0, getLocalizedSpriteKey("statuses"))
.setName("icon_status")
.setVisible(false)
.setOrigin(0)
.setPositionRelative(this.nameText, 0, 11.5);
this.add(this.statusIndicator);
this.levelContainer = globalScene.add
.container(posParams.levelContainerX, posParams.levelContainerY)
.setName("container_level");
this.add(this.levelContainer);
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
this.levelContainer.add(levelOverlay);
this.hpBar = globalScene.add.image(posParams.hpBarX, posParams.hpBarY, "overlay_hp").setName("hp_bar").setOrigin(0);
this.add(this.hpBar);
this.levelNumbersContainer = globalScene.add
.container(9.5, globalScene.uiTheme ? 0 : -0.5)
.setName("container_level");
this.levelContainer.add(this.levelNumbersContainer);
this.constructStatContainer(posParams.statBox);
this.constructTypeIcons();
}
getStatsValueContainer(): Phaser.GameObjects.Container {
return this.statValuesContainer;
}
//#region Initialization methods
initSplicedIcon(pokemon: Pokemon, baseWidth: number) {
this.splicedIcon.setPositionRelative(
this.nameText,
baseWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
2.5,
);
this.splicedIcon.setVisible(pokemon.isFusion(true));
if (!this.splicedIcon.visible) {
return;
}
this.splicedIcon
.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
}
/**
* Called by {@linkcode initInfo} to initialize the shiny icon
* @param pokemon - The pokemon object attached to this battle info
* @param baseXOffset - The x offset to use for the shiny icon
* @param doubleShiny - Whether the pokemon is shiny and its fusion species is also shiny
*/
protected initShinyIcon(pokemon: Pokemon, xOffset: number, doubleShiny: boolean) {
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setPositionRelative(
this.nameText,
xOffset +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
this.shinyIcon
.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`)
.setVisible(pokemon.isShiny())
.setTint(getVariantTint(baseVariant));
if (!this.shinyIcon.visible) {
return;
}
let shinyDescriptor = "";
if (doubleShiny || baseVariant) {
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
if (doubleShiny) {
shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
}
shinyDescriptor += ")";
}
this.shinyIcon
.on("pointerover", () => globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor))
.on("pointerout", () => globalScene.ui.hideTooltip());
}
initInfo(pokemon: Pokemon) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
this.name = pokemon.getNameToRender();
this.box.name = pokemon.getNameToRender();
this.genderText
.setText(getGenderSymbol(pokemon.gender))
.setColor(getGenderColor(pokemon.gender))
.setPositionRelative(this.nameText, nameTextWidth, 0);
this.lastTeraType = pokemon.getTeraType();
this.teraIcon
.setVisible(pokemon.isTerastallized)
.on("pointerover", () => {
if (pokemon.isTerastallized) {
globalScene.ui.showTooltip(
"",
i18next.t("fightUiHandler:teraHover", {
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
}),
);
}
})
.on("pointerout", () => globalScene.ui.hideTooltip())
.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
const isFusion = pokemon.isFusion(true);
this.initSplicedIcon(pokemon, nameTextWidth);
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
this.initShinyIcon(pokemon, nameTextWidth, doubleShiny);
this.fusionShinyIcon.setVisible(doubleShiny).copyPosition(this.shinyIcon);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
this.hpBar.setFrame(this.lastHpFrame);
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
this.shinyIcon.setVisible(pokemon.isShiny());
this.setTypes(pokemon.getTypes(true, false, undefined, true));
const stats = this.statOrder.map(() => 0);
this.lastStats = stats.join("");
this.updateStats(stats);
}
//#endregion
/**
* Return the texture name of the battle info box
*/
abstract getTextureName(): string;
setMini(_mini: boolean): void {}
toggleStats(visible: boolean): void {
globalScene.tweens.add({
targets: this.statsContainer,
duration: fixedInt(125),
ease: "Sine.easeInOut",
alpha: visible ? 1 : 0,
});
}
setOffset(offset: boolean): void {
if (this.offset === offset) {
return;
}
this.offset = offset;
this.x += 10 * (this.offset === this.player ? 1 : -1);
this.y += 27 * (this.offset ? 1 : -1);
this.baseY = this.y;
}
//#region Update methods and helpers
/**
* Update the status icon to match the pokemon's current status
* @param pokemon - The pokemon object attached to this battle info
* @param xOffset - The offset from the name text
*/
updateStatusIcon(pokemon: Pokemon, xOffset = 0) {
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
if (this.lastStatus !== StatusEffect.NONE) {
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
}
this.statusIndicator.setVisible(!!this.lastStatus).setPositionRelative(this.nameText, xOffset, 11.5);
}
}
/** Update the pokemon name inside the container */
protected updateName(pokemon: Pokemon): boolean {
const name = pokemon.getNameToRender();
if (this.lastName === name) {
return false;
}
this.updateNameText(pokemon);
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
return true;
}
protected updateTeraType(ty: PokemonType): boolean {
if (this.lastTeraType === ty) {
return false;
}
this.teraIcon
.setVisible(ty !== PokemonType.UNKNOWN)
.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(ty)))
.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
this.lastTeraType = ty;
return true;
}
/**
* Update the type icons to match the pokemon's types
*/
setTypes(types: PokemonType[]): void {
this.type1Icon
.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`)
.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
}
/**
* Called by {@linkcode updateInfo} to update the position of the tera, spliced, and shiny icons
* @param isFusion - Whether the pokemon is a fusion or not
*/
protected updateIconDisplay(isFusion: boolean): void {
this.teraIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
this.splicedIcon
.setVisible(isFusion)
.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
1.5,
);
this.shinyIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
}
//#region Hp Bar Display handling
/**
* Called every time the hp frame is updated by the tween
* @param pokemon - The pokemon object attached to this battle info
*/
protected updateHpFrame(): void {
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
}
/**
* Called by every frame in the hp animation tween created in {@linkcode updatePokemonHp}
* @param _pokemon - The pokemon the battle-info bar belongs to
*/
protected onHpTweenUpdate(_pokemon: Pokemon): void {
this.updateHpFrame();
}
/** Update the pokemonHp bar */
protected updatePokemonHp(pokemon: Pokemon, resolve: (r: void | PromiseLike<void>) => void, instant?: boolean): void {
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
const speed = globalScene.hpBarSpeed;
if (speed) {
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
}
globalScene.tweens.add({
targets: this.hpBar,
ease: "Sine.easeOut",
scaleX: pokemon.getHpRatio(true),
duration: duration,
onUpdate: () => {
this.onHpTweenUpdate(pokemon);
},
onComplete: () => {
this.updateHpFrame();
resolve();
},
});
this.lastMaxHp = pokemon.getMaxHp();
}
//#endregion
async updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
let resolve: (r: void | PromiseLike<void>) => void = () => {};
const promise = new Promise<void>(r => (resolve = r));
if (!globalScene) {
return resolve();
}
const gender: Gender = pokemon.summonData?.illusion?.gender ?? pokemon.gender;
this.genderText.setText(getGenderSymbol(gender)).setColor(getGenderColor(gender));
const nameUpdated = this.updateName(pokemon);
const teraTypeUpdated = this.updateTeraType(pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN);
const isFusion = pokemon.isFusion(true);
if (nameUpdated || teraTypeUpdated) {
this.updateIconDisplay(isFusion);
}
this.updateStatusIcon(pokemon);
this.setTypes(pokemon.getTypes(true, false, undefined, true));
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
return this.updatePokemonHp(pokemon, resolve, instant);
}
if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
}
const stats = pokemon.getStatStages();
const statsStr = stats.join("");
if (this.lastStats !== statsStr) {
this.updateStats(stats);
this.lastStats = statsStr;
}
this.shinyIcon.setVisible(pokemon.isShiny(true));
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setTint(getVariantTint(baseVariant));
this.fusionShinyIcon.setVisible(doubleShiny).setPosition(this.shinyIcon.x, this.shinyIcon.y);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
resolve();
await promise;
}
//#endregion
updateNameText(pokemon: Pokemon): void {
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
let nameTextWidth: number;
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
nameTextWidth = nameSizeTest.displayWidth;
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
nameTextWidth >
(this.player || !this.boss ? 60 : 98) -
((gender !== Gender.GENDERLESS ? 6 : 0) +
(pokemon.fusionSpecies ? 8 : 0) +
(pokemon.isShiny() ? 8 : 0) +
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
) {
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
nameSizeTest.setText(displayName);
nameTextWidth = nameSizeTest.displayWidth;
}
nameSizeTest.destroy();
this.nameText.setText(displayName);
this.lastName = pokemon.getNameToRender();
if (this.nameText.visible) {
this.nameText.setInteractive(
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
Phaser.Geom.Rectangle.Contains,
);
}
}
/**
* Set the level numbers container to display the provided level
*
* @remarks
* The numbers in the pokemon's level uses images for each number rather than a text object with a special font.
* This method sets the images for each digit of the level number and then positions the level container based
* on the number of digits.
*
* @param level - The level to display
* @param textureKey - The texture key for the level numbers
*/
setLevel(level: number, textureKey: "numbers" | "numbers_red" = "numbers"): void {
this.levelNumbersContainer.removeAll(true);
const levelStr = level.toString();
for (let i = 0; i < levelStr.length; i++) {
this.levelNumbersContainer.add(globalScene.add.image(i * 8, 0, textureKey, levelStr[i]));
}
this.levelContainer.setX(this.baseLvContainerX - 8 * Math.max(levelStr.length - 3, 0));
}
updateStats(stats: number[]): void {
for (const [i, s] of this.statOrder.entries()) {
if (s !== Stat.HP) {
this.statNumbers[i].setFrame(stats[s - 1].toString());
}
}
}
getBaseY(): number {
return this.baseY;
}
resetY(): void {
this.y = this.baseY;
}
}

View File

@ -0,0 +1,235 @@
import { globalScene } from "#app/global-scene";
import BattleFlyout from "../battle-flyout";
import { addTextObject, TextStyle } from "#app/ui/text";
import { addWindow, WindowVariant } from "#app/ui/ui-theme";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import type { EnemyPokemon } from "#app/field/pokemon";
import type { GameObjects } from "phaser";
import BattleInfo from "./battle-info";
import type { BattleInfoParamList } from "./battle-info";
export class EnemyBattleInfo extends BattleInfo {
protected player: false = false;
protected championRibbon: Phaser.GameObjects.Sprite;
protected ownedIcon: Phaser.GameObjects.Sprite;
protected flyoutMenu: BattleFlyout;
protected hpBarSegmentDividers: GameObjects.Rectangle[] = [];
// #region Type effectiveness hint objects
protected effectivenessContainer: Phaser.GameObjects.Container;
protected effectivenessWindow: Phaser.GameObjects.NineSlice;
protected effectivenessText: Phaser.GameObjects.Text;
protected currentEffectiveness?: string;
// #endregion
override get statOrder(): Stat[] {
return [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
}
override getTextureName(): string {
return this.boss ? "pbinfo_enemy_boss" : "pbinfo_enemy_mini";
}
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-15, -15.5, "pbinfo_enemy_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-15, -2.5, "pbinfo_enemy_type2").setName("icon_type_2").setOrigin(0);
this.type3Icon = globalScene.add.sprite(0, 15.5, "pbinfo_enemy_type3").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
constructor() {
const posParams: BattleInfoParamList = {
nameTextX: -124,
nameTextY: -11.2,
levelContainerX: -50,
levelContainerY: -5,
hpBarX: -71,
hpBarY: 4.5,
statBox: {
xOffset: 5,
paddingX: 2,
statOverflow: 0,
},
};
super(140, -141, false, posParams);
this.ownedIcon = globalScene.add
.sprite(0, 0, "icon_owned")
.setName("icon_owned")
.setVisible(false)
.setOrigin(0, 0)
.setPositionRelative(this.nameText, 0, 11.75);
this.championRibbon = globalScene.add
.sprite(0, 0, "champion_ribbon")
.setName("icon_champion_ribbon")
.setVisible(false)
.setOrigin(0, 0)
.setPositionRelative(this.nameText, 8, 11.75);
// Ensure these two icons are positioned below the stats container
this.addAt([this.ownedIcon, this.championRibbon], this.getIndex(this.statsContainer));
this.flyoutMenu = new BattleFlyout(this.player);
this.add(this.flyoutMenu);
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
this.effectivenessContainer = globalScene.add
.container(0, 0)
.setVisible(false)
.setPositionRelative(this.type1Icon, 22, 4);
this.add(this.effectivenessContainer);
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
this.effectivenessContainer.add([this.effectivenessWindow, this.effectivenessText]);
}
override initInfo(pokemon: EnemyPokemon): void {
this.flyoutMenu.initInfo(pokemon);
super.initInfo(pokemon);
if (this.nameText.visible) {
this.nameText
.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
i18next.t("battleInfo:generation", {
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
}),
),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
}
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
const opponentPokemonDexAttr = pokemon.getDexAttr();
if (
globalScene.gameMode.isClassic &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
this.championRibbon.setVisible(true);
}
// Check if Player owns all genders and forms of the Pokemon
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
// Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
if (missingDexAttrs || !playerOwnsThisAbility) {
this.ownedIcon.setTint(0x808080);
}
if (this.boss) {
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
}
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.currentEffectiveness = effectiveness;
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu.flyoutVisible) {
this.effectivenessContainer.setVisible(false);
return;
}
this.effectivenessText.setText(effectiveness);
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
this.effectivenessContainer.setVisible(true);
}
/**
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
*/
toggleFlyout(visible: boolean): void {
this.flyoutMenu.toggleFlyout(visible);
if (visible) {
this.effectivenessContainer.setVisible(false);
} else {
this.updateEffectiveness(this.currentEffectiveness);
}
}
updateBossSegments(pokemon: EnemyPokemon): void {
const boss = !!pokemon.bossSegments;
if (boss !== this.boss) {
this.boss = boss;
[
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.ownedIcon,
this.championRibbon,
this.statusIndicator,
this.levelContainer,
this.statValuesContainer,
].map(e => (e.x += 48 * (boss ? -1 : 1)));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
}
this.bossSegments = boss ? pokemon.bossSegments : 0;
this.updateBossSegmentDividers(pokemon);
}
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
while (this.hpBarSegmentDividers.length) {
this.hpBarSegmentDividers.pop()?.destroy();
}
if (this.boss && this.bossSegments > 1) {
const uiTheme = globalScene.uiTheme;
const maxHp = pokemon.getMaxHp();
for (let s = 1; s < this.bossSegments; s++) {
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
const divider = globalScene.add.rectangle(
0,
0,
1,
this.hpBar.height - (uiTheme ? 0 : 1),
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
);
divider.setOrigin(0.5, 0).setName("hpBar_divider_" + s.toString());
this.add(divider);
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
this.hpBarSegmentDividers.push(divider);
}
}
}
override updateStatusIcon(pokemon: EnemyPokemon): void {
super.updateStatusIcon(pokemon, (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0));
}
protected override updatePokemonHp(
pokemon: EnemyPokemon,
resolve: (r: void | PromiseLike<void>) => void,
instant?: boolean,
): void {
super.updatePokemonHp(pokemon, resolve, instant);
this.lastHp = pokemon.hp;
}
}

View File

@ -0,0 +1,242 @@
import { getLevelRelExp, getLevelTotalExp } from "#app/data/exp";
import type { PlayerPokemon } from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { Stat } from "#enums/stat";
import BattleInfo from "./battle-info";
import type { BattleInfoParamList } from "./battle-info";
export class PlayerBattleInfo extends BattleInfo {
protected player: true = true;
protected hpNumbersContainer: Phaser.GameObjects.Container;
override get statOrder(): Stat[] {
return [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
}
override getTextureName(): string {
return this.mini ? "pbinfo_player_mini" : "pbinfo_player";
}
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-139, -17, "pbinfo_player_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-139, -1, "pbinfo_player_type2").setName("icon_type_2").setOrigin(0);
this.type3Icon = globalScene.add.sprite(-154, -17, "pbinfo_player_type3").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
constructor() {
const posParams: BattleInfoParamList = {
nameTextX: -115,
nameTextY: -15.2,
levelContainerX: -41,
levelContainerY: -10,
hpBarX: -61,
hpBarY: -1,
statBox: {
xOffset: 8,
paddingX: 4,
statOverflow: 1,
},
};
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true, posParams);
this.hpNumbersContainer = globalScene.add.container(-15, 10).setName("container_hp");
// hp number container must be beneath the stat container for overlay to display properly
this.addAt(this.hpNumbersContainer, this.getIndex(this.statsContainer));
const expBar = globalScene.add.image(-98, 18, "overlay_exp").setName("overlay_exp").setOrigin(0);
this.add(expBar);
const expMaskRect = globalScene.make
.graphics({})
.setScale(6)
.fillStyle(0xffffff)
.beginPath()
.fillRect(127, 126, 85, 2);
const expMask = expMaskRect.createGeometryMask();
expBar.setMask(expMask);
this.expBar = expBar;
this.expMaskRect = expMaskRect;
}
override initInfo(pokemon: PlayerPokemon): void {
super.initInfo(pokemon);
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
this.statValuesContainer.setPosition(8, 7);
}
override setMini(mini: boolean): void {
if (this.mini === mini) {
return;
}
this.mini = mini;
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
if (this.player) {
this.y -= 12 * (mini ? 1 : -1);
this.baseY = this.y;
}
const offsetElements = [
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.statusIndicator,
this.levelContainer,
];
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
el.x += 4 * (mini ? 1 : -1);
el.y += -8 * (mini ? 1 : -1);
});
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
const toggledElements = [this.hpNumbersContainer, this.expBar];
toggledElements.forEach(el => el.setVisible(!mini));
}
/**
* Updates the Hp Number text (that is the "HP/Max HP" text that appears below the player's health bar)
* while the health bar is tweening.
* @param pokemon - The Pokemon the health bar belongs to.
*/
protected override onHpTweenUpdate(pokemon: PlayerPokemon): void {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
this.lastHp = tweenHp;
this.updateHpFrame();
}
updatePokemonExp(pokemon: PlayerPokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
const levelUp = this.lastLevel < pokemon.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
ratio = levelUp ? 1 : 0;
instant = true;
}
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
1 - Math.max(this.lastLevel - 100, 0) / 150,
);
let duration =
this.visible && !instant
? ((levelExp - this.lastLevelExp) / relLevelExp) *
BattleInfo.EXP_GAINS_DURATION_BASE *
durationMultiplier *
levelDurationMultiplier
: 0;
const speed = globalScene.expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
} else {
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
}
if (duration) {
globalScene.playSound("se/exp");
}
return new Promise(resolve => {
globalScene.tweens.add({
targets: this.expMaskRect,
ease: "Sine.easeIn",
x: ratio * 510,
duration: duration,
onComplete: () => {
if (!globalScene) {
return resolve();
}
if (duration) {
globalScene.sound.stopByKey("se/exp");
}
if (ratio === 1) {
globalScene.playSound("se/level_up");
this.setLevel(this.lastLevel);
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
this.expMaskRect.x = 0;
this.updateInfo(pokemon, instant).then(() => resolve());
});
return;
}
resolve();
},
});
});
}
/**
* Updates the info on the info bar.
*
* In addition to performing all the steps of {@linkcode BattleInfo.updateInfo},
* it also updates the EXP Bar
*/
override async updateInfo(pokemon: PlayerPokemon, instant?: boolean): Promise<void> {
await super.updateInfo(pokemon, instant);
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
const oldLevelCapped = this.lastLevelCapped;
this.lastLevelCapped = isLevelCapped;
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
const durationMultipler = Math.max(
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10),
0.1,
);
await this.updatePokemonExp(pokemon, false, durationMultipler);
} else if (isLevelCapped !== oldLevelCapped) {
this.setLevel(pokemon.level);
}
}
/**
* Set the HP numbers text, that is the "HP/Max HP" text that appears below the player's health bar.
* @param hp - The current HP of the player.
* @param maxHp - The maximum HP of the player.
*/
setHpNumbers(hp: number, maxHp: number): void {
if (!globalScene) {
return;
}
this.hpNumbersContainer.removeAll(true);
const hpStr = hp.toString();
const maxHpStr = maxHp.toString();
let offset = 0;
for (let i = maxHpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
}
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
for (let i = hpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
}
}
/**
* Set the level numbers container to display the provided level
*
* Overrides the default implementation to handle displaying level capped numbers in red.
* @param level - The level to display
*/
override setLevel(level: number): void {
super.setLevel(level, level >= globalScene.getMaxExpLevel() ? "numbers_red" : "numbers");
}
}

View File

@ -43,14 +43,13 @@ export default class EggCounterContainer extends Phaser.GameObjects.Container {
this.add(this.eggCountWindow);
const eggSprite = globalScene.add.sprite(19, 18, "egg", "egg_0");
eggSprite.setScale(0.32);
const eggSprite = globalScene.add.sprite(19, 18, "egg", "egg_0").setScale(0.32);
this.eggCountText = addTextObject(28, 13, `${this.eggCount}`, TextStyle.MESSAGE, { fontSize: "66px" });
this.eggCountText.setName("text-egg-count");
this.eggCountText = addTextObject(28, 13, `${this.eggCount}`, TextStyle.MESSAGE, { fontSize: "66px" }).setName(
"text-egg-count",
);
this.add(eggSprite);
this.add(this.eggCountText);
this.add([eggSprite, this.eggCountText]);
}
/**

View File

@ -22,18 +22,12 @@ export default class EvolutionSceneHandler extends MessageUiHandler {
const ui = this.getUi();
this.evolutionContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6);
ui.add(this.evolutionContainer);
const messageBg = globalScene.add.sprite(0, 0, "bg", globalScene.windowType);
messageBg.setOrigin(0, 1);
messageBg.setVisible(false);
ui.add(messageBg);
const messageBg = globalScene.add.sprite(0, 0, "bg", globalScene.windowType).setOrigin(0, 1).setVisible(false);
this.messageBg = messageBg;
this.messageContainer = globalScene.add.container(12, -39);
this.messageContainer.setVisible(false);
ui.add(this.messageContainer);
this.messageContainer = globalScene.add.container(12, -39).setVisible(false);
const message = addTextObject(0, 0, "", TextStyle.MESSAGE, {
maxLines: 2,
@ -43,6 +37,8 @@ export default class EvolutionSceneHandler extends MessageUiHandler {
});
this.messageContainer.add(message);
ui.add([this.evolutionContainer, this.messageBg, this.messageContainer]);
this.message = message;
this.initPromptSprite(this.messageContainer);
@ -52,10 +48,8 @@ export default class EvolutionSceneHandler extends MessageUiHandler {
super.show(_args);
globalScene.ui.bringToTop(this.evolutionContainer);
globalScene.ui.bringToTop(this.messageBg);
globalScene.ui.bringToTop(this.messageContainer);
this.messageBg.setVisible(true);
this.messageContainer.setVisible(true);
globalScene.ui.bringToTop(this.messageBg.setVisible(true));
globalScene.ui.bringToTop(this.messageContainer.setVisible(true));
return true;
}

View File

@ -10,7 +10,7 @@ import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils/common";
import { MoveCategory } from "#enums/MoveCategory";
import i18next from "i18next";
import { Button } from "#enums/buttons";
import type { PokemonMove } from "#app/field/pokemon";
import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import type { CommandPhase } from "#app/phases/command-phase";
import MoveInfoOverlay from "./move-info-overlay";
@ -279,7 +279,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
this.moveInfoOverlay.show(pokemonMove.getMove());
pokemon.getOpponents().forEach(opponent => {
opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
(opponent as EnemyPokemon).updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
});
}
@ -292,7 +292,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
this.accuracyText.setVisible(hasMove);
this.moveCategoryIcon.setVisible(hasMove);
this.cursorObj.setPosition(13 + (cursor % 2 === 1 ? 100 : 0), -31 + (cursor >= 2 ? 15 : 0));
this.cursorObj.setPosition(13 + (cursor % 2 === 1 ? 114 : 0), -31 + (cursor >= 2 ? 15 : 0));
return changed;
}
@ -322,7 +322,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
const moveset = pokemon.getMoveset();
for (let moveIndex = 0; moveIndex < 4; moveIndex++) {
const moveText = addTextObject(moveIndex % 2 === 0 ? 0 : 100, moveIndex < 2 ? 0 : 16, "-", TextStyle.WINDOW);
const moveText = addTextObject(moveIndex % 2 === 0 ? 0 : 114, moveIndex < 2 ? 0 : 16, "-", TextStyle.WINDOW);
moveText.setName("text-empty-move");
if (moveIndex < moveset.length) {
@ -391,7 +391,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents();
opponents.forEach(opponent => {
opponent.updateEffectiveness(undefined);
(opponent as EnemyPokemon).updateEffectiveness();
});
}

View File

@ -5,16 +5,7 @@ import { addTextObject, getTextColor, TextStyle } from "./text";
import type { UiTheme } from "#enums/ui-theme";
import { addWindow, WindowVariant } from "./ui-theme";
import { globalScene } from "#app/global-scene";
export enum DropDownColumn {
GEN,
TYPES,
BIOME,
CAUGHT,
UNLOCKS,
MISC,
SORT,
}
import type { DropDownColumn } from "#enums/drop-down-column";
export class FilterBar extends Phaser.GameObjects.Container {
private window: Phaser.GameObjects.NineSlice;
@ -49,13 +40,9 @@ export class FilterBar extends Phaser.GameObjects.Container {
this.cursorOffset = cursorOffset;
this.window = addWindow(0, 0, width, height, false, false, undefined, undefined, WindowVariant.THIN);
this.add(this.window);
this.cursorObj = globalScene.add.image(1, 1, "cursor");
this.cursorObj.setScale(0.5);
this.cursorObj.setVisible(false);
this.cursorObj.setOrigin(0, 0);
this.add(this.cursorObj);
this.cursorObj = globalScene.add.image(1, 1, "cursor").setScale(0.5).setVisible(false).setOrigin(0);
this.add([this.window, this.cursorObj]);
}
/**

View File

@ -33,7 +33,7 @@ enum MenuOptions {
}
let wikiUrl = "https://wiki.pokerogue.net/start";
const discordUrl = "https://discord.gg/uWpTfdKG49";
const discordUrl = "https://discord.gg/pokerogue";
const githubUrl = "https://github.com/pagefaultgames/pokerogue";
const redditUrl = "https://www.reddit.com/r/pokerogue";
const donateUrl = "https://github.com/sponsors/pagefaultgames";

View File

@ -23,7 +23,8 @@ import type { Species } from "#enums/species";
import { Button } from "#enums/buttons";
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
import { PokedexMonContainer } from "#app/ui/pokedex-mon-container";
import { DropDownColumn, FilterBar } from "#app/ui/filter-bar";
import { FilterBar } from "#app/ui/filter-bar";
import { DropDownColumn } from "#enums/drop-down-column";
import { ScrollBar } from "#app/ui/scroll-bar";
import { Abilities } from "#enums/abilities";
import {

View File

@ -8,7 +8,7 @@ import type Pokemon from "../field/pokemon";
import i18next from "i18next";
import type { DexEntry, StarterDataEntry } from "../system/game-data";
import { DexAttr } from "../system/game-data";
import { fixedInt } from "#app/utils/common";
import { fixedInt, getShinyDescriptor } from "#app/utils/common";
import ConfirmUiHandler from "./confirm-ui-handler";
import { StatsContainer } from "./stats-container";
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
@ -343,18 +343,19 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.pokemonShinyIcon.setVisible(pokemon.isShiny());
this.pokemonShinyIcon.setTint(getVariantTint(baseVariant));
if (this.pokemonShinyIcon.visible) {
const shinyDescriptor =
doubleShiny || baseVariant
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
: "";
this.pokemonShinyIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
true,
),
);
this.pokemonShinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
let shinyDescriptor = "";
if (doubleShiny || baseVariant) {
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
if (doubleShiny) {
shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
}
shinyDescriptor += ")";
}
this.pokemonShinyIcon
.on("pointerover", () =>
globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0));
const newVariant = BigInt(1 << (pokemon.variant + 4));

View File

@ -39,12 +39,6 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler
label: "Español (LATAM)",
};
break;
case "it":
this.settings[languageIndex].options[0] = {
value: "Italiano",
label: "Italiano",
};
break;
case "fr":
this.settings[languageIndex].options[0] = {
value: "Français",
@ -57,24 +51,18 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler
label: "Deutsch",
};
break;
case "it":
this.settings[languageIndex].options[0] = {
value: "Italiano",
label: "Italiano",
};
break;
case "pt-BR":
this.settings[languageIndex].options[0] = {
value: "Português (BR)",
label: "Português (BR)",
};
break;
case "zh-CN":
this.settings[languageIndex].options[0] = {
value: "简体中文",
label: "简体中文",
};
break;
case "zh-TW":
this.settings[languageIndex].options[0] = {
value: "繁體中文",
label: "繁體中文",
};
break;
case "ko":
case "ko-KR":
this.settings[languageIndex].options[0] = {
@ -88,10 +76,46 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler
label: "日本語",
};
break;
case "ca-ES":
case "zh-CN":
this.settings[languageIndex].options[0] = {
value: "简体中文",
label: "简体中文",
};
break;
case "zh-TW":
this.settings[languageIndex].options[0] = {
value: "繁體中文",
label: "繁體中文",
};
break;
case "ca":
this.settings[languageIndex].options[0] = {
value: "Català",
label: "Català",
label: "Català (Needs Help)",
};
break;
case "tr":
this.settings[languageIndex].options[0] = {
value: "Türkçe",
label: "Türkçe (Needs Help)",
};
break;
case "ru":
this.settings[languageIndex].options[0] = {
value: "Русский",
label: "Русский (Needs Help)",
};
break;
case "da":
this.settings[languageIndex].options[0] = {
value: "Dansk",
label: "Dansk (Needs Help)",
};
break;
case "ro":
this.settings[languageIndex].options[0] = {
value: "Română",
label: "Română (Needs Help)",
};
break;
default:

View File

@ -53,7 +53,8 @@ import { Button } from "#enums/buttons";
import { EggSourceType } from "#enums/egg-source-types";
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
import { StarterContainer } from "#app/ui/starter-container";
import { DropDownColumn, FilterBar } from "#app/ui/filter-bar";
import { FilterBar } from "#app/ui/filter-bar";
import { DropDownColumn } from "#enums/drop-down-column";
import { ScrollBar } from "#app/ui/scroll-bar";
import { SelectChallengePhase } from "#app/phases/select-challenge-phase";
import { EncounterPhase } from "#app/phases/encounter-phase";
@ -108,17 +109,21 @@ const languageSettings: { [key: string]: LanguageSetting } = {
instructionTextSize: "38px",
},
de: {
starterInfoTextSize: "48px",
starterInfoTextSize: "54px",
instructionTextSize: "35px",
starterInfoXPos: 33,
starterInfoXPos: 35,
},
"es-ES": {
starterInfoTextSize: "52px",
instructionTextSize: "35px",
starterInfoTextSize: "50px",
instructionTextSize: "38px",
starterInfoYOffset: 0.5,
starterInfoXPos: 38,
},
"es-MX": {
starterInfoTextSize: "52px",
instructionTextSize: "35px",
starterInfoTextSize: "50px",
instructionTextSize: "38px",
starterInfoYOffset: 0.5,
starterInfoXPos: 38,
},
fr: {
starterInfoTextSize: "54px",
@ -128,21 +133,16 @@ const languageSettings: { [key: string]: LanguageSetting } = {
starterInfoTextSize: "56px",
instructionTextSize: "38px",
},
pt_BR: {
starterInfoTextSize: "47px",
instructionTextSize: "38px",
"pt-BR": {
starterInfoTextSize: "48px",
instructionTextSize: "42px",
starterInfoYOffset: 0.5,
starterInfoXPos: 33,
},
zh: {
starterInfoTextSize: "47px",
instructionTextSize: "38px",
starterInfoYOffset: 1,
starterInfoXPos: 24,
},
pt: {
starterInfoTextSize: "48px",
instructionTextSize: "42px",
starterInfoXPos: 33,
starterInfoTextSize: "56px",
instructionTextSize: "36px",
starterInfoXPos: 26,
},
ko: {
starterInfoTextSize: "60px",
@ -156,9 +156,29 @@ const languageSettings: { [key: string]: LanguageSetting } = {
starterInfoYOffset: 0.5,
starterInfoXPos: 33,
},
"ca-ES": {
starterInfoTextSize: "52px",
ca: {
starterInfoTextSize: "48px",
instructionTextSize: "38px",
starterInfoYOffset: 0.5,
starterInfoXPos: 29,
},
da:{
starterInfoTextSize: "56px",
instructionTextSize: "38px",
},
tr:{
starterInfoTextSize: "56px",
instructionTextSize: "38px",
},
ro:{
starterInfoTextSize: "56px",
instructionTextSize: "38px",
},
ru: {
starterInfoTextSize: "46px",
instructionTextSize: "38px",
starterInfoYOffset: 0.5,
starterInfoXPos: 26,
},
};

View File

@ -11,6 +11,7 @@ import {
isNullOrUndefined,
toReadableString,
formatStat,
getShinyDescriptor,
} from "#app/utils/common";
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
@ -444,18 +445,19 @@ export default class SummaryUiHandler extends UiHandler {
this.shinyIcon.setVisible(this.pokemon.isShiny(false));
this.shinyIcon.setTint(getVariantTint(baseVariant));
if (this.shinyIcon.visible) {
const shinyDescriptor =
doubleShiny || baseVariant
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${this.pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : this.pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
: "";
this.shinyIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
true,
),
);
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
let shinyDescriptor = "";
if (doubleShiny || baseVariant) {
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
if (doubleShiny) {
shinyDescriptor += "/" + getShinyDescriptor(this.pokemon.fusionVariant);
}
shinyDescriptor += ")";
}
this.shinyIcon
.on("pointerover", () =>
globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
}
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
@ -1237,7 +1239,7 @@ export default class SummaryUiHandler extends UiHandler {
this.moveSelect = true;
this.extraMoveRowContainer.setVisible(true);
this.selectedMoveIndex = -1;
this.setCursor(0);
this.setCursor(this.summaryUiMode === SummaryUiMode.LEARN_MOVE ? 4 : 0);
this.showMoveEffect();
}

View File

@ -2,6 +2,7 @@ import { MoneyFormat } from "#enums/money-format";
import { Moves } from "#enums/moves";
import i18next from "i18next";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import type { Variant } from "#app/sprites/variant";
export type nil = null | undefined;
@ -434,14 +435,18 @@ export function hasAllLocalizedSprites(lang?: string): boolean {
case "es-ES":
case "es-MX":
case "fr":
case "da":
case "de":
case "it":
case "zh-CN":
case "zh-TW":
case "pt-BR":
case "ro":
case "tr":
case "ko":
case "ja":
case "ca-ES":
case "ca":
case "ru":
return true;
default:
return false;
@ -576,3 +581,18 @@ export function animationFileName(move: Moves): string {
export function camelCaseToKebabCase(str: string): string {
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase());
}
/** Get the localized shiny descriptor for the provided variant
* @param variant - The variant to get the shiny descriptor for
* @returns The localized shiny descriptor
*/
export function getShinyDescriptor(variant: Variant): string {
switch (variant) {
case 2:
return i18next.t("common:epicShiny");
case 1:
return i18next.t("common:rareShiny");
case 0:
return i18next.t("common:commonShiny");
}
}

View File

@ -40,7 +40,7 @@ describe("Moves - Aroma Veil", () => {
game.move.select(Moves.GROWL);
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.HEAL_BLOCK);
await game.move.selectEnemyMove(Moves.HEAL_BLOCK);
await game.toNextTurn();
party.forEach(p => {
expect(p.getTag(BattlerTagType.HEAL_BLOCK)).toBeUndefined();
@ -54,8 +54,8 @@ describe("Moves - Aroma Veil", () => {
game.move.select(Moves.GROWL);
game.move.select(Moves.GROWL, 1);
await game.forceEnemyMove(Moves.IMPRISON, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.IMPRISON, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(game.scene.arena.getTag(ArenaTagType.IMPRISON)).toBeDefined();
party.forEach(p => {

View File

@ -24,11 +24,12 @@ describe("Abilities - Aura Break", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleStyle("single");
game.override.moveset([Moves.MOONBLAST, Moves.DARK_PULSE, Moves.MOONBLAST, Moves.DARK_PULSE]);
game.override.enemyMoveset(Moves.SPLASH);
game.override.enemyAbility(Abilities.AURA_BREAK);
game.override.enemySpecies(Species.SHUCKLE);
game.override
.battleStyle("single")
.moveset([Moves.MOONBLAST, Moves.DARK_PULSE])
.enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.AURA_BREAK)
.enemySpecies(Species.SHUCKLE);
});
it("reverses the effect of Fairy Aura", async () => {

View File

@ -39,7 +39,7 @@ describe("Abilities - Battery", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.PIKACHU, Species.CHARJABUG]);
await game.classicMode.startBattle([Species.PIKACHU, Species.CHARJABUG]);
game.move.select(Moves.DAZZLING_GLEAM);
game.move.select(Moves.SPLASH, 1);
@ -54,7 +54,7 @@ describe("Abilities - Battery", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.PIKACHU, Species.CHARJABUG]);
await game.classicMode.startBattle([Species.PIKACHU, Species.CHARJABUG]);
game.move.select(Moves.BREAKING_SWIPE);
game.move.select(Moves.SPLASH, 1);
@ -69,7 +69,7 @@ describe("Abilities - Battery", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.CHARJABUG, Species.PIKACHU]);
await game.classicMode.startBattle([Species.CHARJABUG, Species.PIKACHU]);
game.move.select(Moves.DAZZLING_GLEAM);
game.move.select(Moves.SPLASH, 1);

View File

@ -59,8 +59,8 @@ describe("Abilities - Commander", () => {
expect(game.scene.currentBattle.turnCommands[0]?.skip).toBeTruthy();
// Force both enemies to target the Tatsugiri
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.phaseInterceptor.to("BerryPhase", false);
game.scene.getEnemyField().forEach(enemy => expect(enemy.getLastXMoves(1)[0].result).toBe(MoveResult.MISS));
@ -100,8 +100,8 @@ describe("Abilities - Commander", () => {
expect(game.scene.currentBattle.turnCommands[0]?.skip).toBeTruthy();
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER]);
@ -183,8 +183,8 @@ describe("Abilities - Commander", () => {
expect(game.scene.currentBattle.turnCommands[0]?.skip).toBeTruthy();
await game.forceEnemyMove(Moves.WHIRLWIND, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.WHIRLWIND, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
// Test may time out here if Whirlwind forced out a Pokemon
await game.phaseInterceptor.to("TurnEndPhase");

View File

@ -54,10 +54,7 @@ describe("Abilities - Contrary", () => {
});
it("should block negative effects", async () => {
game.override
.enemyPassiveAbility(Abilities.CLEAR_BODY)
.enemyMoveset([Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL])
.moveset([Moves.SPLASH]);
game.override.enemyPassiveAbility(Abilities.CLEAR_BODY).enemyMoveset(Moves.HOWL).moveset([Moves.SPLASH]);
await game.classicMode.startBattle([Species.SLOWBRO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;

View File

@ -33,7 +33,7 @@ describe("Abilities - COSTAR", () => {
test("ability copies positive stat stages", async () => {
game.override.enemyAbility(Abilities.BALL_FETCH);
await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
let [leftPokemon, rightPokemon] = game.scene.getPlayerField();
@ -58,7 +58,7 @@ describe("Abilities - COSTAR", () => {
test("ability copies negative stat stages", async () => {
game.override.enemyAbility(Abilities.INTIMIDATE);
await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
let [leftPokemon, rightPokemon] = game.scene.getPlayerField();

View File

@ -76,7 +76,7 @@ describe("Abilities - Cud Chew", () => {
farigiraf.hp = farigiraf.getMaxHp() / 2 - 1;
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
// doesn't trigger since cud chew hasn't eaten berry yet
@ -86,7 +86,7 @@ describe("Abilities - Cud Chew", () => {
// get heal pulsed back to full before the cud chew proc
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.HEAL_PULSE);
await game.move.selectEnemyMove(Moves.HEAL_PULSE);
await game.phaseInterceptor.to("TurnEndPhase");
// globalScene.queueAbilityDisplay should be called twice:

View File

@ -77,8 +77,8 @@ describe("Abilities - Dancer", () => {
game.move.select(Moves.REVELATION_DANCE, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2);
game.move.select(Moves.FIERY_DANCE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2);
await game.forceEnemyMove(Moves.INSTRUCT, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.MIRROR_MOVE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.INSTRUCT, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.MIRROR_MOVE, BattlerIndex.PLAYER);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MovePhase"); // Oricorio rev dance
await game.phaseInterceptor.to("MovePhase"); // Feebas fiery dance

View File

@ -50,8 +50,8 @@ describe("Abilities - Desolate Land", () => {
game.move.select(Moves.SPLASH, 0, 2);
game.move.select(Moves.SPLASH, 1, 2);
await game.forceEnemyMove(Moves.ROAR, 0);
await game.forceEnemyMove(Moves.SPLASH, 1);
await game.move.selectEnemyMove(Moves.ROAR, 0);
await game.move.selectEnemyMove(Moves.SPLASH, 1);
await game.phaseInterceptor.to("TurnEndPhase");
@ -66,8 +66,8 @@ describe("Abilities - Desolate Land", () => {
game.move.select(Moves.SPLASH, 0, 2);
game.move.select(Moves.SPLASH, 1, 2);
await game.forceEnemyMove(Moves.ROAR, 1);
await game.forceEnemyMove(Moves.SPLASH, 0);
await game.move.selectEnemyMove(Moves.ROAR, 1);
await game.move.selectEnemyMove(Moves.SPLASH, 0);
await game.phaseInterceptor.to("TurnEndPhase");
@ -103,8 +103,8 @@ describe("Abilities - Desolate Land", () => {
game.move.select(Moves.SPLASH, 0, 2);
game.move.select(Moves.SPLASH, 1, 2);
await game.forceEnemyMove(Moves.MEMENTO, 0);
await game.forceEnemyMove(Moves.MEMENTO, 1);
await game.move.selectEnemyMove(Moves.MEMENTO, 0);
await game.move.selectEnemyMove(Moves.MEMENTO, 1);
await game.phaseInterceptor.to("TurnEndPhase");

View File

@ -67,8 +67,8 @@ describe("Abilities - Flower Gift", () => {
// turn 1
game.move.select(Moves.SUNNY_DAY, 0);
game.move.select(ally_move, 1, ally_target);
await game.forceEnemyMove(enemy_move, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(enemy_move, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
// Ensure sunny day is used last.
await game.setTurnOrder([attacker_index, target_index, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to(TurnEndPhase);
@ -79,8 +79,8 @@ describe("Abilities - Flower Gift", () => {
// turn 2. Make target use recover to reset hp calculation.
game.move.select(Moves.SPLASH, 0, target_index);
game.move.select(ally_move, 1, ally_target);
await game.forceEnemyMove(enemy_move, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(enemy_move, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, target_index, attacker_index]);
await game.phaseInterceptor.to(TurnEndPhase);
const damageWithGift = initialHp - target.hp;

View File

@ -49,7 +49,7 @@ describe("Abilities - Flower Veil", () => {
await game.classicMode.startBattle([Species.BULBASAUR]);
const user = game.scene.getPlayerPokemon()!;
game.move.select(Moves.REST);
await game.forceEnemyMove(Moves.TACKLE);
await game.move.selectEnemyMove(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
expect(user.status?.effect).toBe(StatusEffect.SLEEP);
@ -57,7 +57,7 @@ describe("Abilities - Flower Veil", () => {
// remove sleep status so we can get burn from the orb
user.resetStatus();
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(user.status?.effect).toBe(StatusEffect.BURN);
});
@ -71,8 +71,8 @@ describe("Abilities - Flower Veil", () => {
vi.spyOn(ally, "getAbility").mockReturnValue(allAbilities[Abilities.BALL_FETCH]);
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.YAWN, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.YAWN, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.YAWN, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.YAWN, BattlerIndex.PLAYER_2);
await game.phaseInterceptor.to("BerryPhase");
const user = game.scene.getPlayerPokemon()!;
@ -86,7 +86,7 @@ describe("Abilities - Flower Veil", () => {
await game.classicMode.startBattle([Species.BULBASAUR]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.THUNDER_WAVE);
await game.move.selectEnemyMove(Moves.THUNDER_WAVE);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
vi.spyOn(allMoves[Moves.THUNDER_WAVE], "accuracy", "get").mockClear();
@ -101,8 +101,8 @@ describe("Abilities - Flower Veil", () => {
vi.spyOn(ally, "getAbility").mockReturnValue(allAbilities[Abilities.BALL_FETCH]);
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.THUNDER_WAVE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.THUNDER_WAVE, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.THUNDER_WAVE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.THUNDER_WAVE, BattlerIndex.PLAYER_2);
await game.phaseInterceptor.to("BerryPhase");
expect(user.status?.effect).toBe(StatusEffect.PARALYSIS);
expect(ally.status?.effect).toBe(StatusEffect.PARALYSIS);

View File

@ -30,7 +30,7 @@ describe("Abilities - Forecast", () => {
*/
const testWeatherFormChange = async (game: GameManager, weather: WeatherType, form: number, initialForm?: number) => {
game.override.weather(weather).starterForms({ [Species.CASTFORM]: initialForm });
await game.startBattle([Species.CASTFORM]);
await game.classicMode.startBattle([Species.CASTFORM]);
game.move.select(Moves.SPLASH);
@ -44,7 +44,7 @@ describe("Abilities - Forecast", () => {
*/
const testRevertFormAgainstAbility = async (game: GameManager, ability: Abilities) => {
game.override.starterForms({ [Species.CASTFORM]: SUNNY_FORM }).enemyAbility(ability);
await game.startBattle([Species.CASTFORM]);
await game.classicMode.startBattle([Species.CASTFORM]);
game.move.select(Moves.SPLASH);
@ -81,7 +81,7 @@ describe("Abilities - Forecast", () => {
[Species.GROUDON]: 1,
[Species.RAYQUAZA]: 1,
});
await game.startBattle([
await game.classicMode.startBattle([
Species.CASTFORM,
Species.FEEBAS,
Species.KYOGRE,
@ -201,7 +201,7 @@ describe("Abilities - Forecast", () => {
it("has no effect on Pokémon other than Castform", async () => {
game.override.enemyAbility(Abilities.FORECAST).enemySpecies(Species.SHUCKLE);
await game.startBattle([Species.CASTFORM]);
await game.classicMode.startBattle([Species.CASTFORM]);
game.move.select(Moves.RAIN_DANCE);
await game.phaseInterceptor.to(TurnEndPhase);
@ -212,7 +212,7 @@ describe("Abilities - Forecast", () => {
it("reverts to Normal Form when Forecast is suppressed, changes form to match the weather when it regains it", async () => {
game.override.enemyMoveset([Moves.GASTRO_ACID]).weather(WeatherType.RAIN);
await game.startBattle([Species.CASTFORM, Species.PIKACHU]);
await game.classicMode.startBattle([Species.CASTFORM, Species.PIKACHU]);
const castform = game.scene.getPlayerPokemon()!;
expect(castform.formIndex).toBe(RAINY_FORM);
@ -243,7 +243,7 @@ describe("Abilities - Forecast", () => {
it("does not change Castform's form until after Stealth Rock deals damage", async () => {
game.override.weather(WeatherType.RAIN).enemyMoveset([Moves.STEALTH_ROCK]);
await game.startBattle([Species.PIKACHU, Species.CASTFORM]);
await game.classicMode.startBattle([Species.PIKACHU, Species.CASTFORM]);
// First turn - set up stealth rock
game.move.select(Moves.SPLASH);
@ -267,7 +267,7 @@ describe("Abilities - Forecast", () => {
it("should be in Normal Form after the user is switched out", async () => {
game.override.weather(WeatherType.RAIN);
await game.startBattle([Species.CASTFORM, Species.MAGIKARP]);
await game.classicMode.startBattle([Species.CASTFORM, Species.MAGIKARP]);
const castform = game.scene.getPlayerPokemon()!;
expect(castform.formIndex).toBe(RAINY_FORM);

View File

@ -43,8 +43,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
@ -60,8 +60,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
@ -83,8 +83,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
@ -93,8 +93,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
@ -109,8 +109,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
@ -120,8 +120,8 @@ describe("Moves - Friend Guard", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;

View File

@ -1,6 +1,6 @@
import { BattlerIndex } from "#app/battle";
import { allAbilities } from "#app/data/data-lists";
import { ArenaTagSide } from "#app/data/arena-tag";
import { allAbilities } from "#app/data/data-lists";
import { ArenaTagType } from "#app/enums/arena-tag-type";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import { Stat } from "#app/enums/stat";
@ -74,8 +74,8 @@ describe("Abilities - Good As Gold", () => {
game.move.select(Moves.SWORDS_DANCE, 0);
game.move.select(Moves.SAFEGUARD, 1);
await game.forceEnemyMove(Moves.STEALTH_ROCK);
await game.forceEnemyMove(Moves.HAZE);
await game.move.selectEnemyMove(Moves.STEALTH_ROCK);
await game.move.selectEnemyMove(Moves.HAZE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to("BerryPhase");
expect(good_as_gold.getAbility().id).toBe(Abilities.GOOD_AS_GOLD);
@ -107,35 +107,33 @@ describe("Abilities - Good As Gold", () => {
expect(game.scene.getPlayerField()[1].getTag(BattlerTagType.HELPING_HAND)).toBeUndefined();
});
it("should block the ally's heal bell, but only if the good as gold user is on the field", async () => {
game.override.battleStyle("double");
game.override.moveset([Moves.HEAL_BELL, Moves.SPLASH]);
game.override.statusEffect(StatusEffect.BURN);
await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.ABRA]);
const [good_as_gold, ball_fetch] = game.scene.getPlayerField();
// Force second pokemon to have ball fetch to isolate to a single mon.
vi.spyOn(ball_fetch, "getAbility").mockReturnValue(allAbilities[Abilities.BALL_FETCH]);
// TODO: re-enable when heal bell is fixed
it.todo("should block the ally's heal bell, but only if the good as gold user is on the field", async () => {
game.override.battleStyle("double").statusEffect(StatusEffect.BURN);
await game.classicMode.startBattle([Species.MILOTIC, Species.FEEBAS, Species.ABRA]);
const [milotic, feebas, abra] = game.scene.getPlayerParty();
game.field.mockAbility(milotic, Abilities.GOOD_AS_GOLD);
game.field.mockAbility(feebas, Abilities.BALL_FETCH);
game.field.mockAbility(abra, Abilities.BALL_FETCH);
// turn 1
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.HEAL_BELL, 1);
game.move.use(Moves.SPLASH, 0);
game.move.use(Moves.HEAL_BELL, 1);
await game.toNextTurn();
expect(good_as_gold.status?.effect).toBe(StatusEffect.BURN);
expect(milotic.status?.effect).toBe(StatusEffect.BURN);
game.doSwitchPokemon(2);
game.move.select(Moves.HEAL_BELL, 0);
game.move.use(Moves.HEAL_BELL, 1);
await game.toNextTurn();
expect(good_as_gold.status?.effect).toBeUndefined();
expect(milotic.status?.effect).toBeUndefined();
});
it("should not block field targeted effects like rain dance", async () => {
game.override.battleStyle("single");
game.override.enemyMoveset([Moves.RAIN_DANCE]);
game.override.weather(WeatherType.NONE);
await game.classicMode.startBattle([Species.MAGIKARP]);
game.move.select(Moves.SPLASH, 0);
game.move.use(Moves.SPLASH, 0);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.RAIN);

View File

@ -39,7 +39,7 @@ describe("Abilities - Gorilla Tactics", () => {
const initialAtkStat = darmanitan.getStat(Stat.ATK);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
@ -57,13 +57,13 @@ describe("Abilities - Gorilla Tactics", () => {
// First turn, lock move to Growl
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
// Second turn, Growl is interrupted by Disable
await game.toNextTurn();
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.DISABLE);
await game.move.selectEnemyMove(Moves.DISABLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnEndPhase");

View File

@ -241,13 +241,13 @@ describe("Abilities - Gulp Missile", () => {
await game.classicMode.startBattle([Species.CRAMORANT]);
game.move.select(Moves.SURF);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM);
game.move.select(Moves.SUBSTITUTE);
await game.forceEnemyMove(Moves.POWER_TRIP);
await game.move.selectEnemyMove(Moves.POWER_TRIP);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextTurn();

View File

@ -61,7 +61,7 @@ describe("Abilities - Harvest", () => {
await game.classicMode.startBattle([Species.FEEBAS]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.NUZZLE);
await game.move.selectEnemyMove(Moves.NUZZLE);
await game.phaseInterceptor.to("BerryPhase");
expect(getPlayerBerries()).toHaveLength(0);
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toHaveLength(1);
@ -87,7 +87,7 @@ describe("Abilities - Harvest", () => {
// Chug a few berries without harvest (should get tracked)
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.NUZZLE);
await game.move.selectEnemyMove(Moves.NUZZLE);
await game.toNextTurn();
expect(milotic.battleData.berriesEaten).toEqual(expect.arrayContaining([BerryType.ENIGMA, BerryType.LUM]));
@ -98,7 +98,7 @@ describe("Abilities - Harvest", () => {
vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApplyPostTurn").mockReturnValueOnce(false);
game.override.ability(Abilities.HARVEST);
game.move.select(Moves.GASTRO_ACID);
await game.forceEnemyMove(Moves.NUZZLE);
await game.move.selectEnemyMove(Moves.NUZZLE);
await game.toNextTurn();
@ -109,7 +109,7 @@ describe("Abilities - Harvest", () => {
// proc a high roll and we _should_ get a berry back!
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(milotic.battleData.berriesEaten).toHaveLength(3);
@ -126,7 +126,7 @@ describe("Abilities - Harvest", () => {
regieleki.hp = 1;
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.doKillOpponents();
await game.phaseInterceptor.to("TurnEndPhase");
@ -154,7 +154,7 @@ describe("Abilities - Harvest", () => {
regieleki.hp = regieleki.getMaxHp() / 4 + 1;
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SUPER_FANG);
await game.move.selectEnemyMove(Moves.SUPER_FANG);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
@ -165,7 +165,7 @@ describe("Abilities - Harvest", () => {
// heal up so harvest doesn't proc and kill enemy
game.move.select(Moves.EARTHQUAKE);
await game.forceEnemyMove(Moves.HEAL_PULSE);
await game.move.selectEnemyMove(Moves.HEAL_PULSE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextWave();
@ -191,7 +191,7 @@ describe("Abilities - Harvest", () => {
feebas.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF];
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Force RNG roll to hit the first berry we find that matches.
@ -219,7 +219,7 @@ describe("Abilities - Harvest", () => {
player.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF];
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expectBerriesContaining(...initBerries);
@ -231,7 +231,7 @@ describe("Abilities - Harvest", () => {
await game.classicMode.startBattle([Species.FEEBAS]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.INCINERATE);
await game.move.selectEnemyMove(Moves.INCINERATE);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
@ -242,7 +242,7 @@ describe("Abilities - Harvest", () => {
await game.classicMode.startBattle([Species.FEEBAS]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.KNOCK_OFF);
await game.move.selectEnemyMove(Moves.KNOCK_OFF);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
@ -308,7 +308,7 @@ describe("Abilities - Harvest", () => {
// steal a sitrus and immediately consume it
game.move.select(Moves.FALSE_SWIPE);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(player.battleData.berriesEaten).toEqual([BerryType.SITRUS]);

View File

@ -38,7 +38,7 @@ describe("Abilities - Heatproof", () => {
});
it("reduces Fire type damage by half", async () => {
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const initialHP = 1000;
@ -61,7 +61,7 @@ describe("Abilities - Heatproof", () => {
it("reduces Burn damage by half", async () => {
game.override.enemyStatusEffect(StatusEffect.BURN).enemySpecies(Species.ABRA);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;

View File

@ -34,7 +34,7 @@ describe("Abilities - Hyper Cutter", () => {
// Reference Link: https://bulbapedia.bulbagarden.net/wiki/Hyper_Cutter_(Ability)
it("only prevents ATK drops", async () => {
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;

View File

@ -106,8 +106,7 @@ describe("Abilities - Ice Face", () => {
});
it("transforms to Ice Face when Hail or Snow starts", async () => {
game.override.moveset([Moves.QUICK_ATTACK]);
game.override.enemyMoveset([Moves.HAIL, Moves.HAIL, Moves.HAIL, Moves.HAIL]);
game.override.moveset([Moves.QUICK_ATTACK]).enemyMoveset(Moves.HAIL);
await game.classicMode.startBattle([Species.MAGIKARP]);
@ -128,8 +127,7 @@ describe("Abilities - Ice Face", () => {
});
it("transforms to Ice Face when summoned on arena with active Snow or Hail", async () => {
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
game.override.moveset([Moves.SNOWSCAPE]);
game.override.enemyMoveset(Moves.TACKLE).moveset([Moves.SNOWSCAPE]);
await game.classicMode.startBattle([Species.EISCUE, Species.NINJASK]);
@ -155,8 +153,7 @@ describe("Abilities - Ice Face", () => {
});
it("will not revert to its Ice Face if there is already Hail when it changes into Noice", async () => {
game.override.enemySpecies(Species.SHUCKLE);
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
game.override.enemySpecies(Species.SHUCKLE).enemyMoveset(Moves.TACKLE);
await game.classicMode.startBattle([Species.EISCUE]);
@ -175,7 +172,7 @@ describe("Abilities - Ice Face", () => {
});
it("persists form change when switched out", async () => {
game.override.enemyMoveset([Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK]);
game.override.enemyMoveset(Moves.QUICK_ATTACK);
await game.classicMode.startBattle([Species.EISCUE, Species.MAGIKARP]);

View File

@ -29,17 +29,18 @@ describe("Abilities - Libero", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleStyle("single");
game.override.ability(Abilities.LIBERO);
game.override.startingLevel(100);
game.override.enemySpecies(Species.RATTATA);
game.override.enemyMoveset([Moves.ENDURE, Moves.ENDURE, Moves.ENDURE, Moves.ENDURE]);
game.override
.battleStyle("single")
.ability(Abilities.LIBERO)
.startingLevel(100)
.enemySpecies(Species.RATTATA)
.enemyMoveset(Moves.ENDURE);
});
test("ability applies and changes a pokemon's type", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -54,7 +55,7 @@ describe("Abilities - Libero", () => {
test.skip("ability applies only once per switch in", async () => {
game.override.moveset([Moves.SPLASH, Moves.AGILITY]);
await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.BULBASAUR]);
let leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -90,7 +91,7 @@ describe("Abilities - Libero", () => {
test("ability applies correctly even if the pokemon's move has a variable type", async () => {
game.override.moveset([Moves.WEATHER_BALL]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -110,7 +111,7 @@ describe("Abilities - Libero", () => {
game.override.moveset([Moves.TACKLE]);
game.override.passiveAbility(Abilities.REFRIGERATE);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -128,7 +129,7 @@ describe("Abilities - Libero", () => {
test("ability applies correctly even if the pokemon's move calls another move", async () => {
game.override.moveset([Moves.NATURE_POWER]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -143,7 +144,7 @@ describe("Abilities - Libero", () => {
test("ability applies correctly even if the pokemon's move is delayed / charging", async () => {
game.override.moveset([Moves.DIG]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -158,7 +159,7 @@ describe("Abilities - Libero", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset(Moves.SPLASH);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -173,10 +174,9 @@ describe("Abilities - Libero", () => {
});
test("ability applies correctly even if the pokemon's move is protected against", async () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]);
game.override.moveset([Moves.TACKLE]).enemyMoveset(Moves.PROTECT);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -191,7 +191,7 @@ describe("Abilities - Libero", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemySpecies(Species.GASTLY);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -205,7 +205,7 @@ describe("Abilities - Libero", () => {
test("ability is not applied if pokemon's type is the same as the move's type", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -220,7 +220,7 @@ describe("Abilities - Libero", () => {
test("ability is not applied if pokemon is terastallized", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -236,7 +236,7 @@ describe("Abilities - Libero", () => {
test("ability is not applied if pokemon uses struggle", async () => {
game.override.moveset([Moves.STRUGGLE]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -250,7 +250,7 @@ describe("Abilities - Libero", () => {
test("ability is not applied if the pokemon's move fails", async () => {
game.override.moveset([Moves.BURN_UP]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -265,7 +265,7 @@ describe("Abilities - Libero", () => {
game.override.moveset([Moves.TRICK_OR_TREAT]);
game.override.enemySpecies(Species.GASTLY);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -279,7 +279,7 @@ describe("Abilities - Libero", () => {
test("ability applies correctly and the pokemon curses itself", async () => {
game.override.moveset([Moves.CURSE]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);

View File

@ -52,7 +52,7 @@ describe("Abilities - Magic Bounce", () => {
game.override.enemyMoveset([Moves.FLY]);
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.FLY);
await game.move.selectEnemyMove(Moves.FLY);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase");
@ -183,7 +183,7 @@ describe("Abilities - Magic Bounce", () => {
// turn 1
game.move.select(Moves.ENCORE);
await game.forceEnemyMove(Moves.TACKLE);
await game.move.selectEnemyMove(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
expect(enemyPokemon.getTag(BattlerTagType.ENCORE)!["moveId"]).toBe(Moves.TACKLE);
@ -209,7 +209,7 @@ describe("Abilities - Magic Bounce", () => {
// turn 1
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.TACKLE);
await game.move.selectEnemyMove(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
@ -218,7 +218,7 @@ describe("Abilities - Magic Bounce", () => {
// turn 2
game.move.select(Moves.ENCORE);
await game.forceEnemyMove(Moves.TACKLE);
await game.move.selectEnemyMove(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("BerryPhase");
expect(enemyPokemon.getTag(BattlerTagType.ENCORE)!["moveId"]).toBe(Moves.TACKLE);
@ -254,7 +254,7 @@ describe("Abilities - Magic Bounce", () => {
vi.spyOn(stomping_tantrum, "calculateBattlePower");
game.move.select(Moves.SPORE);
await game.forceEnemyMove(Moves.CHARM);
await game.move.selectEnemyMove(Moves.CHARM);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.getLastXMoves(1)[0].result).toBe("success");
@ -346,7 +346,7 @@ describe("Abilities - Magic Bounce", () => {
game.override.moveset([Moves.TOXIC, Moves.CHARM]);
await game.classicMode.startBattle([Species.BULBASAUR]);
game.move.select(Moves.TOXIC);
await game.forceEnemyMove(Moves.FLY);
await game.move.selectEnemyMove(Moves.FLY);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC);

View File

@ -46,7 +46,7 @@ describe("Abilities - Magic Guard", () => {
it("ability should prevent damage caused by weather", async () => {
game.override.weather(WeatherType.SANDSTORM);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -70,7 +70,7 @@ describe("Abilities - Magic Guard", () => {
//Toxic keeps track of the turn counters -> important that Magic Guard keeps track of post-Toxic turns
game.override.statusEffect(StatusEffect.POISON);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -91,7 +91,7 @@ describe("Abilities - Magic Guard", () => {
game.override.enemyMoveset([Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED]);
game.override.statusEffect(StatusEffect.POISON);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -110,7 +110,7 @@ describe("Abilities - Magic Guard", () => {
game.override.enemyStatusEffect(StatusEffect.BURN);
game.override.enemyAbility(Abilities.MAGIC_GUARD);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
game.move.select(Moves.SPLASH);
@ -132,7 +132,7 @@ describe("Abilities - Magic Guard", () => {
game.override.enemyStatusEffect(StatusEffect.TOXIC);
game.override.enemyAbility(Abilities.MAGIC_GUARD);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
game.move.select(Moves.SPLASH);
@ -159,7 +159,7 @@ describe("Abilities - Magic Guard", () => {
const newTag = getArenaTag(ArenaTagType.SPIKES, 5, Moves.SPIKES, 0, 0, ArenaTagSide.BOTH)!;
game.scene.arena.tags.push(newTag);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
@ -184,7 +184,7 @@ describe("Abilities - Magic Guard", () => {
game.scene.arena.tags.push(playerTag);
game.scene.arena.tags.push(enemyTag);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
@ -206,7 +206,7 @@ describe("Abilities - Magic Guard", () => {
});
it("Magic Guard prevents against damage from volatile status effects", async () => {
await game.startBattle([Species.DUSKULL]);
await game.classicMode.startBattle([Species.DUSKULL]);
game.override.moveset([Moves.CURSE]);
game.override.enemyAbility(Abilities.MAGIC_GUARD);
@ -231,7 +231,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents crash damage", async () => {
game.override.moveset([Moves.HIGH_JUMP_KICK]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -249,7 +249,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents damage from recoil", async () => {
game.override.moveset([Moves.TAKE_DOWN]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -266,7 +266,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard does not prevent damage from Struggle's recoil", async () => {
game.override.moveset([Moves.STRUGGLE]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -284,7 +284,7 @@ describe("Abilities - Magic Guard", () => {
//This tests different move attributes than the recoil tests above
it("Magic Guard prevents self-damage from attacking moves", async () => {
game.override.moveset([Moves.STEEL_BEAM]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -301,7 +301,7 @@ describe("Abilities - Magic Guard", () => {
/*
it("Magic Guard does not prevent self-damage from confusion", async () => {
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
game.move.select(Moves.CHARM);
@ -311,7 +311,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard does not prevent self-damage from non-attacking moves", async () => {
game.override.moveset([Moves.BELLY_DRUM]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -333,7 +333,7 @@ describe("Abilities - Magic Guard", () => {
game.override.enemyMoveset([Moves.SPORE, Moves.SPORE, Moves.SPORE, Moves.SPORE]);
game.override.enemyAbility(Abilities.BAD_DREAMS);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -355,7 +355,7 @@ describe("Abilities - Magic Guard", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyAbility(Abilities.AFTERMATH);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -379,7 +379,7 @@ describe("Abilities - Magic Guard", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyAbility(Abilities.IRON_BARBS);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -402,7 +402,7 @@ describe("Abilities - Magic Guard", () => {
game.override.moveset([Moves.ABSORB]);
game.override.enemyAbility(Abilities.LIQUID_OOZE);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
@ -425,7 +425,7 @@ describe("Abilities - Magic Guard", () => {
game.override.passiveAbility(Abilities.SOLAR_POWER);
game.override.weather(WeatherType.SUNNY);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -55,7 +55,7 @@ describe("Abilities - Mimicry", () => {
const playerPokemon = game.scene.getPlayerPokemon();
game.move.select(Moves.TRANSFORM);
await game.forceEnemyMove(Moves.PSYCHIC_TERRAIN);
await game.move.selectEnemyMove(Moves.PSYCHIC_TERRAIN);
await game.toNextTurn();
expect(playerPokemon?.getTypes().includes(PokemonType.PSYCHIC)).toBe(true);
@ -64,7 +64,7 @@ describe("Abilities - Mimicry", () => {
}
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon?.getTypes().includes(PokemonType.ELECTRIC)).toBe(true);
});
@ -75,13 +75,13 @@ describe("Abilities - Mimicry", () => {
const playerPokemon = game.scene.getPlayerPokemon();
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.FORESTS_CURSE);
await game.move.selectEnemyMove(Moves.FORESTS_CURSE);
await game.toNextTurn();
expect(playerPokemon?.summonData.addedType).toBe(PokemonType.GRASS);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.GRASSY_TERRAIN);
await game.move.selectEnemyMove(Moves.GRASSY_TERRAIN);
await game.phaseInterceptor.to("TurnEndPhase");
expect(playerPokemon?.summonData.addedType).toBeNull();

View File

@ -46,7 +46,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate, enemy should lose -1 atk
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
@ -63,7 +63,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate, enemy should lose -1 atk
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(userPokemon.getStatStage(Stat.ATK)).toBe(-1);
@ -82,8 +82,8 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate, enemy should lose -2 atk each
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.toNextTurn();
expect(enemy1.getStatStage(Stat.ATK)).toBe(-2);
@ -104,8 +104,8 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate, enemy should lose -1 atk
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.toNextTurn();
expect(enemy1.getStatStage(Stat.ATK)).toBe(0);
@ -124,7 +124,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
@ -144,8 +144,8 @@ describe("Ability - Mirror Armor", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER_2);
await game.toNextTurn();
expect(player1.getStatStage(Stat.ATK)).toBe(0);
@ -168,7 +168,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense
game.move.select(Moves.TICKLE);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(userPokemon.getStatStage(Stat.DEF)).toBe(-1);
@ -187,7 +187,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.TICKLE, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
@ -206,7 +206,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats
game.move.select(Moves.TICKLE);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
@ -224,7 +224,7 @@ describe("Ability - Mirror Armor", () => {
// Enemy uses octolock, player loses stats at end of turn
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.OCTOLOCK, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.OCTOLOCK, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
@ -242,7 +242,7 @@ describe("Ability - Mirror Armor", () => {
// Player uses octolock, enemy loses stats at end of turn
game.move.select(Moves.OCTOLOCK);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(userPokemon.getStatStage(Stat.DEF)).toBe(0);
@ -261,7 +261,7 @@ describe("Ability - Mirror Armor", () => {
const userPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(userPokemon.getStatStage(Stat.ATK)).toBe(-1);
@ -276,11 +276,11 @@ describe("Ability - Mirror Armor", () => {
const userPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.STICKY_WEB, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.STICKY_WEB, BattlerIndex.PLAYER);
await game.toNextTurn();
game.doSwitchPokemon(1);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.toNextTurn();
expect(userPokemon.getStatStage(Stat.SPD)).toBe(0);
@ -297,14 +297,14 @@ describe("Ability - Mirror Armor", () => {
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.STICKY_WEB, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.STICKY_WEB, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.toNextTurn();
game.doSwitchPokemon(2);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH, BattlerIndex.PLAYER_2);
await game.toNextTurn();
expect(enemy1.getStatStage(Stat.SPD)).toBe(-1);

View File

@ -38,7 +38,7 @@ describe("Abilities - Moxie", () => {
it("should raise ATK stat stage by 1 when winning a battle", async () => {
const moveToUse = Moves.AERIAL_ACE;
await game.startBattle([Species.MIGHTYENA, Species.MIGHTYENA]);
await game.classicMode.startBattle([Species.MIGHTYENA, Species.MIGHTYENA]);
const playerPokemon = game.scene.getPlayerPokemon()!;
@ -56,7 +56,7 @@ describe("Abilities - Moxie", () => {
async () => {
game.override.battleStyle("double");
const moveToUse = Moves.AERIAL_ACE;
await game.startBattle([Species.MIGHTYENA, Species.MIGHTYENA]);
await game.classicMode.startBattle([Species.MIGHTYENA, Species.MIGHTYENA]);
const [firstPokemon, secondPokemon] = game.scene.getPlayerField();

View File

@ -24,13 +24,15 @@ describe("Abilities - Mycelium Might", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleStyle("single");
game.override.disableCrits();
game.override.enemySpecies(Species.SHUCKLE);
game.override.enemyAbility(Abilities.CLEAR_BODY);
game.override.enemyMoveset([Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK]);
game.override.ability(Abilities.MYCELIUM_MIGHT);
game.override.moveset([Moves.QUICK_ATTACK, Moves.BABY_DOLL_EYES]);
game.override
.battleStyle("single")
.disableCrits()
.enemySpecies(Species.SHUCKLE)
.enemyAbility(Abilities.CLEAR_BODY)
.enemyMoveset(Moves.QUICK_ATTACK)
.ability(Abilities.MYCELIUM_MIGHT)
.moveset([Moves.QUICK_ATTACK, Moves.BABY_DOLL_EYES]);
});
/**
@ -41,7 +43,7 @@ describe("Abilities - Mycelium Might", () => {
**/
it("will move last in its priority bracket and ignore protective abilities", async () => {
await game.startBattle([Species.REGIELEKI]);
await game.classicMode.startBattle([Species.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
@ -64,8 +66,8 @@ describe("Abilities - Mycelium Might", () => {
}, 20000);
it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => {
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
await game.startBattle([Species.REGIELEKI]);
game.override.enemyMoveset(Moves.TACKLE);
await game.classicMode.startBattle([Species.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
@ -87,7 +89,7 @@ describe("Abilities - Mycelium Might", () => {
}, 20000);
it("will not affect non-status moves", async () => {
await game.startBattle([Species.REGIELEKI]);
await game.classicMode.startBattle([Species.REGIELEKI]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();

View File

@ -110,16 +110,16 @@ describe("Abilities - Neutralizing Gas", () => {
await game.classicMode.startBattle([Species.ACCELGOR, Species.ACCELGOR]);
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); // Now one neut gas user is left
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER_2);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); // No neut gas users are left

View File

@ -34,7 +34,7 @@ describe("Abilities - Pastel Veil", () => {
});
it("prevents the user and its allies from being afflicted by poison", async () => {
await game.startBattle([Species.MAGIKARP, Species.GALAR_PONYTA]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.GALAR_PONYTA]);
const ponyta = game.scene.getPlayerField()[1];
const magikarp = game.scene.getPlayerField()[0];
ponyta.abilityIndex = 1;
@ -50,7 +50,7 @@ describe("Abilities - Pastel Veil", () => {
});
it("it heals the poisoned status condition of allies if user is sent out into battle", async () => {
await game.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA]);
const ponyta = game.scene.getPlayerParty()[2];
const magikarp = game.scene.getPlayerField()[0];
ponyta.abilityIndex = 1;

View File

@ -64,14 +64,14 @@ describe("Abilities - Perish Song", () => {
const magikarp = game.scene.getEnemyPokemon();
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.PERISH_SONG);
await game.move.selectEnemyMove(Moves.PERISH_SONG);
await game.toNextTurn();
expect(feebas?.summonData.tags[0].turnCount).toBe(3);
expect(magikarp?.summonData.tags[0].turnCount).toBe(3);
game.doSwitchPokemon(1);
await game.forceEnemyMove(Moves.SPLASH);
await game.move.selectEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const cursola = game.scene.getPlayerPokemon();
@ -79,7 +79,7 @@ describe("Abilities - Perish Song", () => {
expect(magikarp?.summonData.tags[0].turnCount).toBe(2);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.AQUA_JET);
await game.move.selectEnemyMove(Moves.AQUA_JET);
await game.toNextTurn();
expect(cursola?.summonData.tags.length).toBe(0);
@ -94,7 +94,7 @@ describe("Abilities - Perish Song", () => {
const cursola = game.scene.getPlayerPokemon();
game.move.select(Moves.WHIRLWIND);
await game.forceEnemyMove(Moves.PERISH_SONG);
await game.move.selectEnemyMove(Moves.PERISH_SONG);
await game.toNextTurn();
const magikarp = game.scene.getEnemyPokemon();
@ -102,7 +102,7 @@ describe("Abilities - Perish Song", () => {
expect(magikarp?.summonData.tags.length).toBe(0);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.AQUA_JET);
await game.move.selectEnemyMove(Moves.AQUA_JET);
await game.toNextTurn();
expect(cursola?.summonData.tags[0].turnCount).toBe(2);

View File

@ -25,10 +25,11 @@ describe("Abilities - POWER CONSTRUCT", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
const moveToUse = Moves.SPLASH;
game.override.battleStyle("single");
game.override.ability(Abilities.POWER_CONSTRUCT);
game.override.moveset([moveToUse]);
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
game.override
.battleStyle("single")
.ability(Abilities.POWER_CONSTRUCT)
.moveset([moveToUse])
.enemyMoveset(Moves.TACKLE);
});
test("check if fainted 50% Power Construct Pokemon switches to base form on arena reset", async () => {

View File

@ -39,7 +39,7 @@ describe("Abilities - Power Spot", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]);
await game.classicMode.startBattle([Species.REGIELEKI, Species.STONJOURNER]);
game.move.select(Moves.DAZZLING_GLEAM);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(MoveEffectPhase);
@ -53,7 +53,7 @@ describe("Abilities - Power Spot", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]);
await game.classicMode.startBattle([Species.REGIELEKI, Species.STONJOURNER]);
game.move.select(Moves.BREAKING_SWIPE);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(MoveEffectPhase);
@ -67,7 +67,7 @@ describe("Abilities - Power Spot", () => {
vi.spyOn(moveToCheck, "calculateBattlePower");
await game.startBattle([Species.STONJOURNER, Species.REGIELEKI]);
await game.classicMode.startBattle([Species.STONJOURNER, Species.REGIELEKI]);
game.move.select(Moves.BREAKING_SWIPE);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -29,17 +29,18 @@ describe("Abilities - Protean", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleStyle("single");
game.override.ability(Abilities.PROTEAN);
game.override.startingLevel(100);
game.override.enemySpecies(Species.RATTATA);
game.override.enemyMoveset([Moves.ENDURE, Moves.ENDURE, Moves.ENDURE, Moves.ENDURE]);
game.override
.battleStyle("single")
.ability(Abilities.PROTEAN)
.startingLevel(100)
.enemySpecies(Species.RATTATA)
.enemyMoveset(Moves.ENDURE);
});
test("ability applies and changes a pokemon's type", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -54,7 +55,7 @@ describe("Abilities - Protean", () => {
test.skip("ability applies only once per switch in", async () => {
game.override.moveset([Moves.SPLASH, Moves.AGILITY]);
await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]);
await game.classicMode.startBattle([Species.MAGIKARP, Species.BULBASAUR]);
let leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -90,7 +91,7 @@ describe("Abilities - Protean", () => {
test("ability applies correctly even if the pokemon's move has a variable type", async () => {
game.override.moveset([Moves.WEATHER_BALL]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -110,7 +111,7 @@ describe("Abilities - Protean", () => {
game.override.moveset([Moves.TACKLE]);
game.override.passiveAbility(Abilities.REFRIGERATE);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -128,7 +129,7 @@ describe("Abilities - Protean", () => {
test("ability applies correctly even if the pokemon's move calls another move", async () => {
game.override.moveset([Moves.NATURE_POWER]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -143,7 +144,7 @@ describe("Abilities - Protean", () => {
test("ability applies correctly even if the pokemon's move is delayed / charging", async () => {
game.override.moveset([Moves.DIG]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -158,7 +159,7 @@ describe("Abilities - Protean", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset(Moves.SPLASH);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -173,10 +174,9 @@ describe("Abilities - Protean", () => {
});
test("ability applies correctly even if the pokemon's move is protected against", async () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]);
game.override.moveset([Moves.TACKLE]).enemyMoveset(Moves.PROTECT);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -191,7 +191,7 @@ describe("Abilities - Protean", () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemySpecies(Species.GASTLY);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -205,7 +205,7 @@ describe("Abilities - Protean", () => {
test("ability is not applied if pokemon's type is the same as the move's type", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -220,7 +220,7 @@ describe("Abilities - Protean", () => {
test("ability is not applied if pokemon is terastallized", async () => {
game.override.moveset([Moves.SPLASH]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -236,7 +236,7 @@ describe("Abilities - Protean", () => {
test("ability is not applied if pokemon uses struggle", async () => {
game.override.moveset([Moves.STRUGGLE]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -250,7 +250,7 @@ describe("Abilities - Protean", () => {
test("ability is not applied if the pokemon's move fails", async () => {
game.override.moveset([Moves.BURN_UP]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -265,7 +265,7 @@ describe("Abilities - Protean", () => {
game.override.moveset([Moves.TRICK_OR_TREAT]);
game.override.enemySpecies(Species.GASTLY);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
@ -279,7 +279,7 @@ describe("Abilities - Protean", () => {
test("ability applies correctly and the pokemon curses itself", async () => {
game.override.moveset([Moves.CURSE]);
await game.startBattle([Species.MAGIKARP]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);

View File

@ -41,7 +41,7 @@ describe("Abilities - Quick Draw", () => {
});
test("makes pokemon going first in its priority bracket", async () => {
await game.startBattle();
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
@ -63,7 +63,7 @@ describe("Abilities - Quick Draw", () => {
retry: 5,
},
async () => {
await game.startBattle();
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
@ -83,7 +83,7 @@ describe("Abilities - Quick Draw", () => {
test("does not increase priority", async () => {
game.override.enemyMoveset([Moves.EXTREME_SPEED]);
await game.startBattle();
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;

Some files were not shown because too many files have changed in this diff Show More