diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 167a108e58c..ccc8604ff7e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,18 +5,58 @@ on: # but only for the main branch push: branches: - - main # Trigger on push events to the main branch + - main # Trigger on push events to the main branch - beta # Trigger on push events to the beta branch + # go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft until they fix this or ditch yml for workflows + paths: + # src and test files + - "src/**" + - "test/**" + - "public/**" + # Workflows that can impact tests + - ".github/workflows/test*.yml" + # top-level files + - "package*.json" + - ".nvrmc" # Updates to node version can break tests + - "vite.*.ts" # 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 + pull_request: branches: - - main # Trigger on pull request events targeting the main branch + - main # Trigger on pull request events targeting the main branch - beta # Trigger on pull request events targeting the beta branch + paths: # go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft because until then we have to duplicate this + # src and test files + - "src/**" + - "test/**" + - "public/**" + # Workflows that can impact tests + - ".github/workflows/test*.yml" + # top-level files + - "package*.json" + - ".nvrmc" # Updates to node version can break tests + - "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 merge_group: types: [checks_requested] jobs: run-tests: - name: Run Tests + name: Run Tests strategy: matrix: shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -24,4 +64,4 @@ jobs: with: project: main shard: ${{ matrix.shard }} - totalShards: 10 \ No newline at end of file + totalShards: 10 diff --git a/package-lock.json b/package-lock.json index d72bd6dcc58..33e9dd08104 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pokemon-rogue-battle", - "version": "1.8.1", + "version": "1.8.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pokemon-rogue-battle", - "version": "1.8.1", + "version": "1.8.4", "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", diff --git a/package.json b/package.json index f4542403c06..6b1c73db158 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.8.1", + "version": "1.8.4", "type": "module", "scripts": { "start": "vite", diff --git a/public/images/pokemon/890-eternamax.json b/public/images/pokemon/890-eternamax.json index 70a327ef22c..98cb6f20446 100644 --- a/public/images/pokemon/890-eternamax.json +++ b/public/images/pokemon/890-eternamax.json @@ -1,20 +1,755 @@ -{ "frames": [ - { - "filename": "0001.png", - "frame": { "x": 0, "y": 0, "w": 96, "h": 98 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 96, "h": 98 }, - "sourceSize": { "w": 96, "h": 98 }, - "duration": 100 - } - ], - "meta": { - "app": "https://www.aseprite.org/", - "version": "1.3.13-x64", - "image": "890-eternamax.png", - "format": "RGBA8888", - "size": { "w": 96, "h": 98 }, - "scale": "1" - } +{ + "textures": [ + { + "image": "890-eternamax.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$" + } } diff --git a/public/images/pokemon/890-eternamax.png b/public/images/pokemon/890-eternamax.png index a1cf684c026..33c8f5f9631 100644 Binary files a/public/images/pokemon/890-eternamax.png and b/public/images/pokemon/890-eternamax.png differ diff --git a/public/images/pokemon/shiny/890-eternamax.json b/public/images/pokemon/shiny/890-eternamax.json index 39c1d175c8c..26813186ba8 100644 --- a/public/images/pokemon/shiny/890-eternamax.json +++ b/public/images/pokemon/shiny/890-eternamax.json @@ -1,20 +1,755 @@ -{ "frames": [ - { - "filename": "0001.png", - "frame": { "x": 1, "y": 1, "w": 92, "h": 94 }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { "x": 3, "y": 1, "w": 92, "h": 94 }, - "sourceSize": { "w": 96, "h": 98 }, - "duration": 100 - } - ], - "meta": { - "app": "https://www.aseprite.org/", - "version": "1.3.13-x64", - "image": "890-eternamax.png", - "format": "RGBA8888", - "size": { "w": 94, "h": 96 }, - "scale": "1" - } +{ + "textures": [ + { + "image": "890-eternamax.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:1eb3f67ba4e434995b4589c97560f1be:539129d777c30d08fa799dcebaeb523e:cf277fd83435e8c90cd46073c543568b$" + } } diff --git a/public/images/pokemon/shiny/890-eternamax.png b/public/images/pokemon/shiny/890-eternamax.png index 8e493b12f3e..3e7b5c1721f 100644 Binary files a/public/images/pokemon/shiny/890-eternamax.png and b/public/images/pokemon/shiny/890-eternamax.png differ diff --git a/public/images/pokemon/variant/890-eternamax_2.json b/public/images/pokemon/variant/890-eternamax_2.json index de0107b9854..895a2f27841 100644 --- a/public/images/pokemon/variant/890-eternamax_2.json +++ b/public/images/pokemon/variant/890-eternamax_2.json @@ -1,20 +1,755 @@ -{ "frames": [ - { - "filename": "0001.png", - "frame": { "x": 1, "y": 1, "w": 92, "h": 94 }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { "x": 3, "y": 1, "w": 92, "h": 94 }, - "sourceSize": { "w": 96, "h": 98 }, - "duration": 100 - } - ], - "meta": { - "app": "https://www.aseprite.org/", - "version": "1.3.13-x64", - "image": "890-eternamax_2.png", - "format": "RGBA8888", - "size": { "w": 94, "h": 96 }, - "scale": "1" - } -} +{ + "textures": [ + { + "image": "890-eternamax_2.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/890-eternamax_2.png b/public/images/pokemon/variant/890-eternamax_2.png index adafb2f1d35..2327900b971 100644 Binary files a/public/images/pokemon/variant/890-eternamax_2.png and b/public/images/pokemon/variant/890-eternamax_2.png differ diff --git a/public/images/pokemon/variant/890-eternamax_3.json b/public/images/pokemon/variant/890-eternamax_3.json index 9d3f6070a27..ecc084c689f 100644 --- a/public/images/pokemon/variant/890-eternamax_3.json +++ b/public/images/pokemon/variant/890-eternamax_3.json @@ -1,20 +1,755 @@ -{ "frames": [ - { - "filename": "0001.png", - "frame": { "x": 1, "y": 1, "w": 96, "h": 98 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 96, "h": 98 }, - "sourceSize": { "w": 96, "h": 98 }, - "duration": 100 - } - ], - "meta": { - "app": "https://www.aseprite.org/", - "version": "1.3.13-x64", - "image": "890-eternamax_3.png", - "format": "RGBA8888", - "size": { "w": 98, "h": 100 }, - "scale": "1" - } -} +{ + "textures": [ + { + "image": "890-eternamax_3.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/890-eternamax_3.png b/public/images/pokemon/variant/890-eternamax_3.png index 21a3e5be381..140837cfbd0 100644 Binary files a/public/images/pokemon/variant/890-eternamax_3.png and b/public/images/pokemon/variant/890-eternamax_3.png differ diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index eb3efaa15b2..175b56139a6 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -719,7 +719,7 @@ "888-crowned": [0, 1, 1], "889": [0, 1, 1], "889-crowned": [0, 1, 1], - "890-eternamax": [0, 2, 2], + "890-eternamax": [0, 1, 1], "890": [0, 1, 1], "891": [1, 1, 1], "892-gigantamax-rapid": [1, 2, 2], diff --git a/public/locales b/public/locales index 8538aa3e0f6..e98f0eb9c20 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 8538aa3e0f6f38c9c9c74fd0cf6df1e2f8a0bd6d +Subproject commit e98f0eb9c2022bc78b53f0444424c636498e725a diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 7ab96566ef5..a759cbb84c2 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -167,7 +167,7 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { BattlerTagType } from "#enums/battler-tag-type"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { StatusEffect } from "#enums/status-effect"; -import { globalScene, initGlobalScene } from "#app/global-scene"; +import { initGlobalScene } from "#app/global-scene"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; import { timedEventManager } from "./global-event-manager"; @@ -2665,7 +2665,7 @@ export default class BattleScene extends SceneBase { case "mystery_encounter_delibirdy": // Firel Delibirdy return 82.28; case "title_afd": // Andr06 - PokéRogue Title Remix (AFD) - return 47.660; + return 47.66; case "battle_rival_3_afd": // Andr06 - Final N Battle Remix (AFD) return 49.147; } @@ -2937,14 +2937,19 @@ export default class BattleScene extends SceneBase { * @param show Whether to show or hide the bar */ public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { - this.unshiftPhase( - show - ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) - : new HideAbilityPhase(pokemon.getBattlerIndex(), passive), - ); + this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase()); this.clearPhaseQueueSplice(); } + /** + * Hides the ability bar if it is currently visible + */ + public hideAbilityBar(): void { + if (this.abilityBar.isVisible()) { + this.unshiftPhase(new HideAbilityPhase()); + } + } + /** * Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order) */ diff --git a/src/data/ability.ts b/src/data/ability.ts index eaf77f376f4..a7107ce2e9d 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -854,7 +854,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { } if (this.allOthers) { - const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); + const ally = pokemon.getAlly(); + const otherPokemon = !Utils.isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ ally ]) : pokemon.getOpponents(); for (const other of otherPokemon) { globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); } @@ -997,7 +998,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon) && attacker.canSetStatus(effect, true, false, pokemon); } @@ -1037,7 +1038,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon) && attacker.canAddTag(this.tagType); } @@ -1084,7 +1085,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + return !simulated && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon); } @@ -1117,8 +1118,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) - && !attacker.getTag(BattlerTagType.PERISH_SONG); + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.getTag(BattlerTagType.PERISH_SONG); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -1162,7 +1162,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && attacker.getAbility().isSwappable && !move.hitsSubstitute(attacker, pokemon); } @@ -1188,7 +1188,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && attacker.getAbility().isSuppressable + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && attacker.getAbility().isSuppressable && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon); } @@ -1219,7 +1219,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { return attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon) - && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); + && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -1924,7 +1924,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && !(pokemon !== attacker && move.hitsSubstitute(attacker, pokemon)) && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker - && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) + && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) ) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; return simulated || attacker.canSetStatus(effect, true, false, pokemon); @@ -1963,7 +1963,7 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ return super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && - (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && + (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status; } @@ -2460,12 +2460,12 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { } override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return pokemon.getAlly()?.isActive(true); + return pokemon.getAlly()?.isActive(true) ?? false; } override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); - if (!simulated) { + if (!simulated && !Utils.isNullOrUndefined(target)) { globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); } @@ -2486,12 +2486,12 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { } override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return pokemon.getAlly()?.isActive(true); + return pokemon.getAlly()?.isActive(true) ?? false; } override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); - if (!simulated) { + if (!simulated && !Utils.isNullOrUndefined(target)) { for (const s of BATTLE_STATS) { target.setStatStage(s, 0); } @@ -2712,7 +2712,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } const ally = pokemon.getAlly(); - if (!ally || ally.getStatStages().every(s => s === 0)) { + if (Utils.isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)) { return false; } @@ -2721,7 +2721,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const ally = pokemon.getAlly(); - if (!simulated) { + if (!simulated && !Utils.isNullOrUndefined(ally)) { for (const s of BATTLE_STATS) { pokemon.setStatStage(s, ally.getStatStage(s)); } @@ -2866,8 +2866,9 @@ export class CommanderAbAttr extends AbAttr { // another Pokemon, this effect cannot apply. // TODO: Should this work with X + Dondozo fusions? - return globalScene.currentBattle?.double && pokemon.getAlly()?.species.speciesId === Species.DONDOZO - && !(pokemon.getAlly().isFainted() || pokemon.getAlly().getTag(BattlerTagType.COMMANDED)); + const ally = pokemon.getAlly(); + return globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.species.speciesId === Species.DONDOZO + && !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED)); } override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { @@ -2877,7 +2878,7 @@ export class CommanderAbAttr extends AbAttr { // Play an animation of the source jumping into the ally Dondozo's mouth globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.COMMANDER_APPLY); // Apply boosts from this effect to the ally Dondozo - pokemon.getAlly().addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id); + pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id); // Cancel the source Pokemon's next move (if a move is queued) globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon); } @@ -3497,8 +3498,18 @@ export class BonusCritAbAttr extends AbAttr { constructor() { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.BooleanHolder).value = true; + + /** + * Apply the bonus crit ability by increasing the value in the provided number holder by 1 + * + * @param pokemon The pokemon with the BonusCrit ability (unused) + * @param passive Unused + * @param simulated Unused + * @param cancelled Unused + * @param args Args[0] is a number holder containing the crit stage. + */ + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: [Utils.NumberHolder, ...any]): void { + (args[0] as Utils.NumberHolder).value += 1; } } @@ -4077,7 +4088,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { */ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { private allyTarget: boolean; - private target: Pokemon; + private target: Pokemon | undefined; constructor(allyTarget = false) { super(true); @@ -4090,11 +4101,11 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { } else { this.target = pokemon; } - return !Utils.isNullOrUndefined(this.target.status); + return !Utils.isNullOrUndefined(this.target?.status); } override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - if (!simulated && this.target.status) { + if (!simulated && this.target?.status) { globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); this.target.resetStatus(false); this.target.updateInfo(); @@ -4749,7 +4760,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { } override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - const diedToDirectDamage = move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon); + const diedToDirectDamage = move !== undefined && attacker !== undefined && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}); const cancelled = new Utils.BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { @@ -5440,6 +5451,8 @@ class ForceSwitchOutHelper { * It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss */ } else { + const allyPokemon = switchOutTarget.getAlly(); + if (!globalScene.currentBattle.waveIndex || globalScene.currentBattle.waveIndex % 10 === 0) { return false; } @@ -5447,14 +5460,12 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); - - if (globalScene.currentBattle.double) { - const allyPokemon = switchOutTarget.getAlly(); + if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } } - if (!switchOutTarget.getAlly()?.isActive(true)) { + if (!allyPokemon?.isActive(true)) { globalScene.clearEnemyHeldItemModifiers(); if (switchOutTarget.hp) { @@ -6440,9 +6451,9 @@ export function initAbilities() { new Ability(Abilities.CUTE_CHARM, 3) .attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED), new Ability(Abilities.PLUS, 3) - .conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5), + .conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => (p.getAlly()?.hasAbility(a) ?? false)), StatMultiplierAbAttr, Stat.SPATK, 1.5), new Ability(Abilities.MINUS, 3) - .conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5), + .conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => (p.getAlly()?.hasAbility(a) ?? false)), StatMultiplierAbAttr, Stat.SPATK, 1.5), new Ability(Abilities.FORECAST, 3) .uncopiable() .unreplaceable() @@ -6669,7 +6680,7 @@ export function initAbilities() { .attr(PostDefendMoveDisableAbAttr, 30) .bypassFaint(), new Ability(Abilities.HEALER, 5) - .conditionalAttr(pokemon => pokemon.getAlly() && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true), + .conditionalAttr(pokemon => !Utils.isNullOrUndefined(pokemon.getAlly()) && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true), new Ability(Abilities.FRIEND_GUARD, 5) .attr(AlliedFieldDamageReductionAbAttr, 0.75) .ignorable(), diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 8f1d6b09a73..871f622f70a 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -28,7 +28,6 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase"; @@ -1160,9 +1159,11 @@ class TailwindTag extends ArenaTag { ); } // Raise attack by one stage if party member has WIND_RIDER ability + // TODO: Ability displays should be handled by the ability if (pokemon.hasAbility(Abilities.WIND_RIDER)) { - globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex())); + globalScene.queueAbilityDisplay(pokemon, false, true); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true)); + globalScene.queueAbilityDisplay(pokemon, false, false); } } } diff --git a/src/data/balance/passives.ts b/src/data/balance/passives.ts index e39c86ee4b3..624e242944b 100644 --- a/src/data/balance/passives.ts +++ b/src/data/balance/passives.ts @@ -15,7 +15,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [Species.VENUSAUR]: { 0: Abilities.GRASSY_SURGE, 1: Abilities.SEED_SOWER, 2: Abilities.FLOWER_VEIL }, [Species.CHARMANDER]: { 0: Abilities.SHEER_FORCE }, [Species.CHARMELEON]: { 0: Abilities.BEAST_BOOST }, - [Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.INTIMIDATE, 3: Abilities.UNNERVE }, + [Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.TURBOBLAZE, 3: Abilities.UNNERVE }, [Species.SQUIRTLE]: { 0: Abilities.DAUNTLESS_SHIELD }, [Species.WARTORTLE]: { 0: Abilities.DAUNTLESS_SHIELD }, [Species.BLASTOISE]: { 0: Abilities.DAUNTLESS_SHIELD, 1: Abilities.BULLETPROOF, 2: Abilities.BULLETPROOF }, @@ -154,14 +154,14 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [Species.LEAFEON]: { 0: Abilities.GRASSY_SURGE }, [Species.GLACEON]: { 0: Abilities.SNOW_WARNING }, [Species.SYLVEON]: { 0: Abilities.COMPETITIVE }, - [Species.PORYGON]: { 0: Abilities.LEVITATE }, - [Species.PORYGON2]: { 0: Abilities.LEVITATE }, + [Species.PORYGON]: { 0: Abilities.TRANSISTOR }, + [Species.PORYGON2]: { 0: Abilities.TRANSISTOR }, [Species.PORYGON_Z]: { 0: Abilities.PROTEAN }, [Species.OMANYTE]: { 0: Abilities.STURDY }, [Species.OMASTAR]: { 0: Abilities.STURDY }, [Species.KABUTO]: { 0: Abilities.TOUGH_CLAWS }, [Species.KABUTOPS]: { 0: Abilities.TOUGH_CLAWS }, - [Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.DELTA_STREAM }, + [Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.INTIMIDATE }, [Species.ARTICUNO]: { 0: Abilities.SNOW_WARNING }, [Species.ZAPDOS]: { 0: Abilities.DRIZZLE }, [Species.MOLTRES]: { 0: Abilities.DROUGHT }, @@ -309,7 +309,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [Species.SHIFTRY]: { 0: Abilities.SHARPNESS }, [Species.TAILLOW]: { 0: Abilities.AERILATE }, [Species.SWELLOW]: { 0: Abilities.AERILATE }, - [Species.WINGULL]: { 0: Abilities.DRIZZLE }, + [Species.WINGULL]: { 0: Abilities.WATER_ABSORB }, [Species.PELIPPER]: { 0: Abilities.SWIFT_SWIM }, [Species.RALTS]: { 0: Abilities.NEUROFORCE }, [Species.KIRLIA]: { 0: Abilities.NEUROFORCE }, @@ -612,8 +612,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [Species.REUNICLUS]: { 0: Abilities.PSYCHIC_SURGE }, [Species.DUCKLETT]: { 0: Abilities.DRIZZLE }, [Species.SWANNA]: { 0: Abilities.DRIZZLE }, - [Species.VANILLITE]: { 0: Abilities.SNOW_WARNING }, - [Species.VANILLISH]: { 0: Abilities.SNOW_WARNING }, + [Species.VANILLITE]: { 0: Abilities.REFRIGERATE }, + [Species.VANILLISH]: { 0: Abilities.REFRIGERATE }, [Species.VANILLUXE]: { 0: Abilities.SLUSH_RUSH }, [Species.DEERLING]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT }, [Species.SAWSBUCK]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT }, @@ -838,7 +838,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [Species.CELESTEELA]: { 0: Abilities.HEATPROOF }, [Species.KARTANA]: { 0: Abilities.TECHNICIAN }, [Species.GUZZLORD]: { 0: Abilities.POISON_HEAL }, - [Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.PRISM_ARMOR }, + [Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.UNNERVE }, [Species.MAGEARNA]: { 0: Abilities.STEELY_SPIRIT, 1: Abilities.STEELY_SPIRIT }, [Species.MARSHADOW]: { 0: Abilities.IRON_FIST }, [Species.POIPOLE]: { 0: Abilities.LEVITATE }, diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index c391c4010b8..546dbb4a3db 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -30,7 +30,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MovePhase } from "#app/phases/move-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import i18next from "#app/plugins/i18n"; @@ -1901,12 +1900,14 @@ export class TruantTag extends AbilityBattlerTag { if (lastMove && lastMove.move !== Moves.NONE) { (globalScene.getCurrentPhase() as MovePhase).cancel(); - globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); + // TODO: Ability displays should be handled by the ability + globalScene.queueAbilityDisplay(pokemon, passive, true); globalScene.queueMessage( i18next.t("battlerTags:truantLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); + globalScene.queueAbilityDisplay(pokemon, passive, false); } return true; diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 455421ffefd..868fc7d2e60 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1272,23 +1272,22 @@ function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrPr if (soft && isValidForChallenge.value) { return true; } - pokemonFormChanges[species.speciesId].forEach(f1 => { - // Exclude form changes that require the mon to be on the field to begin with, - // such as Castform - if (!("item" in f1)) { - return; + + const result = pokemonFormChanges[species.speciesId].some(f1 => { + // Exclude form changes that require the mon to be on the field to begin with + if (!("item" in f1.trigger)) { + return false; } - species.forms.forEach((f2, formIndex) => { + + return species.forms.some((f2, formIndex) => { if (f1.formKey === f2.formKey) { - const formProps = { ...props }; - formProps.formIndex = formIndex; + const formProps = { ...props, formIndex }; const isFormValidForChallenge = new Utils.BooleanHolder(true); applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps); - if (isFormValidForChallenge.value) { - return true; - } + return isFormValidForChallenge.value; } + return false; }); }); - return false; + return result; } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 8204f13fcca..421314b1945 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -106,7 +106,6 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms"; import type { GameMode } from "#app/game-mode"; import { applyChallenges, ChallengeType } from "../challenge"; @@ -347,7 +346,7 @@ export default class Move implements Localizable { * @param target The {@linkcode Pokemon} targeted by this move * @returns `true` if the move can bypass the target's Substitute; `false` otherwise. */ - hitsSubstitute(user: Pokemon, target: Pokemon | null): boolean { + hitsSubstitute(user: Pokemon, target?: Pokemon): boolean { if ([ MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES ].includes(this.moveTarget) || !target?.getTag(BattlerTagType.SUBSTITUTE)) { return false; @@ -619,12 +618,30 @@ export default class Move implements Localizable { /** * Checks if the move flag applies to the pokemon(s) using/receiving the move + * + * This method will take the `user`'s ability into account when reporting flags, e.g. + * calling this method for {@linkcode MoveFlags.MAKES_CONTACT | MAKES_CONTACT} + * will return `false` if the user has a {@linkcode Abilities.LONG_REACH} that is not being suppressed. + * + * **Note:** This method only checks if the move should have effectively have the flag applied to its use. + * It does *not* check whether the flag will trigger related effects. + * For example using this method to check {@linkcode MoveFlags.WIND_MOVE} + * will not consider {@linkcode Abilities.WIND_RIDER | Wind Rider }. + * + * To simply check whether the move has a flag, use {@linkcode hasFlag}. * @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target * @param user {@linkcode Pokemon} the Pokemon using the move * @param target {@linkcode Pokemon} the Pokemon receiving the move + * @param isFollowUp (defaults to `false`) `true` if the move was used as a follow up * @returns boolean + * @see {@linkcode hasFlag} */ - checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon | null): boolean { + doesFlagEffectApply({ flag, user, target, isFollowUp = false }: { + flag: MoveFlags; + user: Pokemon; + target?: Pokemon; + isFollowUp?: boolean; + }): boolean { // special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact switch (flag) { case MoveFlags.MAKES_CONTACT: @@ -634,16 +651,18 @@ export default class Move implements Localizable { break; case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { - const abilityEffectsIgnored = new Utils.BooleanHolder(false); + const abilityEffectsIgnored = new Utils.BooleanHolder(false); applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; } + // Sunsteel strike, Moongeist beam, and photon geyser will not ignore abilities if invoked + // by another move, such as via metronome. } - break; + return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp; case MoveFlags.IGNORE_PROTECT: if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr) - && this.checkFlag(MoveFlags.MAKES_CONTACT, user, null)) { + && this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user })) { return true; } break; @@ -789,9 +808,9 @@ export default class Move implements Localizable { } applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power); - - if (source.getAlly()) { - applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power); + const ally = source.getAlly(); + if (!Utils.isNullOrUndefined(ally)) { + applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, ally, target, this, simulated, power); } const fieldAuras = new Set( @@ -912,7 +931,8 @@ export default class Move implements Localizable { ]; // ...and cannot enhance Pollen Puff when targeting an ally. - const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && targets.includes(user.getAlly()?.getBattlerIndex()) + const ally = user.getAlly(); + const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && !Utils.isNullOrUndefined(ally) && targets.includes(ally.getBattlerIndex()) return (!restrictSpread || !isMultiTarget) && !this.isChargingMove() @@ -1214,7 +1234,7 @@ export class MoveEffectAttr extends MoveAttr { canApply(user: Pokemon, target: Pokemon, move: Move, args?: any[]) { return !! (this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp) && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || - move.checkFlag(MoveFlags.IGNORE_PROTECT, user, target)); + move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })); } /** Applies move effects so long as they are able based on {@linkcode canApply} */ @@ -1924,7 +1944,9 @@ export class PartyStatusCureAttr extends MoveEffectAttr { pokemon.resetStatus(); pokemon.updateInfo(); } else { - globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition)); + // TODO: Ability displays should be handled by the ability + globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, true); + globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, false); } } } @@ -1945,7 +1967,7 @@ export class FlameBurstAttr extends MoveEffectAttr { const targetAlly = target.getAlly(); const cancelled = new Utils.BooleanHolder(false); - if (targetAlly) { + if (!Utils.isNullOrUndefined(targetAlly)) { applyAbAttrs(BlockNonDirectDamageAbAttr, targetAlly, cancelled); } @@ -1958,7 +1980,7 @@ export class FlameBurstAttr extends MoveEffectAttr { } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return target.getAlly() ? -5 : 0; + return !Utils.isNullOrUndefined(target.getAlly()) ? -5 : 0; } } @@ -4356,10 +4378,10 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { const userAlly = user.getAlly(); const enemyAlly = enemy?.getAlly(); - if (userAlly && userAlly.turnData.acted) { + if (!Utils.isNullOrUndefined(userAlly) && userAlly.turnData.acted) { pokemonActed.push(userAlly); } - if (enemyAlly && enemyAlly.turnData.acted) { + if (!Utils.isNullOrUndefined(enemyAlly) && enemyAlly.turnData.acted) { pokemonActed.push(enemyAlly); } } @@ -6161,9 +6183,8 @@ export class RevivalBlessingAttr extends MoveEffectAttr { pokemon.resetStatus(); pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true); - - if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1) { - const allyPokemon = user.getAlly(); + const allyPokemon = user.getAlly(); + if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1 && !Utils.isNullOrUndefined(allyPokemon)) { // Handle cases where revived pokemon needs to get switched in on same turn if (allyPokemon.isFainted() || allyPokemon === pokemon) { // Enemy switch phase should be removed and replaced with the revived pkmn switching in @@ -6342,18 +6363,19 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } + const allyPokemon = switchOutTarget.getAlly(); + if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); // in double battles redirect potential moves off fled pokemon - if (globalScene.currentBattle.double) { - const allyPokemon = switchOutTarget.getAlly(); + if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } } - if (!switchOutTarget.getAlly()?.isActive(true)) { + if (!allyPokemon?.isActive(true)) { globalScene.clearEnemyHeldItemModifiers(); if (switchOutTarget.hp) { @@ -7029,7 +7051,7 @@ export class RepeatMoveAttr extends MoveEffectAttr { const firstTarget = globalScene.getField()[moveTargets[0]]; if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) { const ally = firstTarget.getAlly(); - if (ally.isActive()) { // ally exists, is not dead and can sponge the blast + if (!Utils.isNullOrUndefined(ally) && ally.isActive()) { // ally exists, is not dead and can sponge the blast moveTargets = [ ally.getBattlerIndex() ]; } } @@ -7403,10 +7425,11 @@ export class AbilityCopyAttr extends MoveEffectAttr { globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); user.setTempAbility(target.getAbility()); + const ally = user.getAlly(); - if (this.copyToPartner && globalScene.currentBattle?.double && user.getAlly().hp) { - globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user.getAlly()), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); - user.getAlly().setTempAbility(target.getAbility()); + if (this.copyToPartner && globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.hp) { // TODO is this the best way to check that the ally is active? + globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(ally), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); + ally.setTempAbility(target.getAbility()); } return true; @@ -7414,9 +7437,10 @@ export class AbilityCopyAttr extends MoveEffectAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { + const ally = user.getAlly(); let ret = target.getAbility().isCopiable && user.getAbility().isReplaceable; if (this.copyToPartner && globalScene.currentBattle?.double) { - ret = ret && (!user.getAlly().hp || user.getAlly().getAbility().isReplaceable); + ret = ret && (!ally?.hp || ally?.getAbility().isReplaceable); } else { ret = ret && user.getAbility().id !== target.getAbility().id; } @@ -8187,6 +8211,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT let set: Pokemon[] = []; let multiple = false; + const ally: Pokemon | undefined = user.getAlly(); switch (moveTarget) { case MoveTarget.USER: @@ -8197,7 +8222,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT case MoveTarget.OTHER: case MoveTarget.ALL_NEAR_OTHERS: case MoveTarget.ALL_OTHERS: - set = (opponents.concat([ user.getAlly() ])); + set = !Utils.isNullOrUndefined(ally) ? (opponents.concat([ ally ])) : opponents; multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS; break; case MoveTarget.NEAR_ENEMY: @@ -8214,21 +8239,22 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT return { targets: [ -1 as BattlerIndex ], multiple: false }; case MoveTarget.NEAR_ALLY: case MoveTarget.ALLY: - set = [ user.getAlly() ]; + set = !Utils.isNullOrUndefined(ally) ? [ ally ] : []; break; case MoveTarget.USER_OR_NEAR_ALLY: case MoveTarget.USER_AND_ALLIES: case MoveTarget.USER_SIDE: - set = [ user, user.getAlly() ]; + set = !Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ]; multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY; break; case MoveTarget.ALL: case MoveTarget.BOTH_SIDES: - set = [ user, user.getAlly() ].concat(opponents); + set = (!Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ]).concat(opponents); multiple = true; break; case MoveTarget.CURSE: - set = user.getTypes(true).includes(PokemonType.GHOST) ? (opponents.concat([ user.getAlly() ])) : [ user ]; + const extraTargets = !Utils.isNullOrUndefined(ally) ? [ ally ] : []; + set = user.getTypes(true).includes(PokemonType.GHOST) ? (opponents.concat(extraTargets)) : [ user ]; break; } @@ -10122,7 +10148,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) }) .ignoresSubstitute() .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p.hasAbility(a, false)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p?.hasAbility(a, false)))), new StatusMove(Moves.HAPPY_HOUR, PokemonType.NORMAL, -1, 30, -1, 0, 6) // No animation .attr(AddArenaTagAttr, ArenaTagType.HAPPY_HOUR, null, true) .target(MoveTarget.USER_SIDE), @@ -10312,7 +10338,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) }) .ignoresSubstitute() .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p.hasAbility(a, false)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p?.hasAbility(a, false)))), new AttackMove(Moves.THROAT_CHOP, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED), new AttackMove(Moves.POLLEN_PUFF, PokemonType.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7) diff --git a/src/field/arena.ts b/src/field/arena.ts index 4f243789567..cf48647e45e 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -40,7 +40,6 @@ import { TrainerType } from "#enums/trainer-type"; import { Abilities } from "#enums/abilities"; import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms"; import { CommonAnimPhase } from "#app/phases/common-anim-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { WeatherType } from "#enums/weather-type"; import { FieldEffectModifier } from "#app/modifier/modifier"; @@ -378,7 +377,6 @@ export class Arena { const isCherrimWithFlowerGift = p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM; if (isCastformWithForecast || isCherrimWithFlowerGift) { - new ShowAbilityPhase(p.getBattlerIndex()); globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger); } }); @@ -395,7 +393,6 @@ export class Arena { p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM; if (isCastformWithForecast || isCherrimWithFlowerGift) { - new ShowAbilityPhase(p.getBattlerIndex()); return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger); } }); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 20a8855fa55..f3e758e4efd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1340,8 +1340,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Retrieves the critical-hit stage considering the move used and the Pokemon - * who used it. + * Calculate the critical-hit stage of a move used against this pokemon by + * the given source + * * @param source the {@linkcode Pokemon} who using the move * @param move the {@linkcode Move} being used * @returns the final critical-hit stage value @@ -1360,14 +1361,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { source.isPlayer(), critStage, ); - const bonusCrit = new Utils.BooleanHolder(false); - //@ts-ignore - if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { - // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. - if (bonusCrit.value) { - critStage.value += 1; - } - } + applyAbAttrs(BonusCritAbAttr, source, null, false, critStage) const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { if (critBoostTag instanceof DragonCheerTag) { @@ -1447,7 +1441,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const ally = this.getAlly(); - if (ally) { + if (!Utils.isNullOrUndefined(ally)) { applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, stat, statValue, simulated, this, move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility); } @@ -3714,7 +3708,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : i18next.t("arenaTag:yourTeam"); } - getAlly(): Pokemon { + getAlly(): Pokemon | undefined { return ( this.isPlayer() ? globalScene.getPlayerField() @@ -3900,7 +3894,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); const ally = this.getAlly(); - if (ally) { + if (!isNullOrUndefined(ally)) { const ignore = this.hasAbilityWithAttr(MoveAbilityBypassAbAttr) || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES); applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.ACC, accuracyMultiplier, false, this, ignore); applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.EVA, evasionMultiplier, false, this, ignore); @@ -4336,11 +4330,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { damage, ); + const ally = this.getAlly(); /** Additionally apply friend guard damage reduction if ally has it. */ - if (globalScene.currentBattle.double && this.getAlly()?.isActive(true)) { + if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) { applyPreDefendAbAttrs( AlliedFieldDamageReductionAbAttr, - this.getAlly(), + ally, source, move, cancelled, diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index a7158264ab7..e6a0c66548e 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -17,6 +17,23 @@ export class BattleEndPhase extends BattlePhase { start() { super.start(); + // cull any extra `BattleEnd` phases from the queue. + globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => { + if (phase instanceof BattleEndPhase) { + this.isVictory ||= phase.isVictory; + return false; + } + return true; + }); + // `phaseQueuePrepend` is private, so we have to use this inefficient loop. + while (globalScene.tryRemoveUnshiftedPhase(phase => { + if (phase instanceof BattleEndPhase) { + this.isVictory ||= phase.isVictory; + return true; + } + return false; + })) {} + globalScene.gameData.gameStats.battles++; if ( globalScene.gameMode.isEndless && diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 2e4861aacfc..166b8c1ae2d 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -39,7 +39,7 @@ export class EnemyCommandPhase extends FieldPhase { if ( battle.double && enemyPokemon.hasAbility(Abilities.COMMANDER) && - enemyPokemon.getAlly().getTag(BattlerTagType.COMMANDED) + enemyPokemon.getAlly()?.getTag(BattlerTagType.COMMANDED) ) { this.skipTurn = true; } diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index dfc0e0653a5..7e1ae4ec07b 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -209,8 +209,8 @@ export class FaintPhase extends PokemonPhase { } // in double battles redirect potential moves off fainted pokemon - if (globalScene.currentBattle.double) { - const allyPokemon = pokemon.getAlly(); + const allyPokemon = pokemon.getAlly(); + if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(pokemon, allyPokemon); } diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 2090592367d..f105b625cc8 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -45,6 +45,8 @@ export class GameOverPhase extends BattlePhase { start() { super.start(); + globalScene.hideAbilityBar(); + // Failsafe if players somehow skip floor 200 in classic mode if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) { this.isVictory = true; diff --git a/src/phases/hide-ability-phase.ts b/src/phases/hide-ability-phase.ts index 0745b3f832a..142bb4b251d 100644 --- a/src/phases/hide-ability-phase.ts +++ b/src/phases/hide-ability-phase.ts @@ -1,27 +1,12 @@ import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#app/battle"; -import { PokemonPhase } from "./pokemon-phase"; - -export class HideAbilityPhase extends PokemonPhase { - private passive: boolean; - - constructor(battlerIndex: BattlerIndex, passive = false) { - super(battlerIndex); - - this.passive = passive; - } +import { Phase } from "#app/phase"; +export class HideAbilityPhase extends Phase { start() { super.start(); - const pokemon = this.getPokemon(); - - if (pokemon) { - globalScene.abilityBar.hide().then(() => { - this.end(); - }); - } else { + globalScene.abilityBar.hide().then(() => { this.end(); - } + }); } } diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 995684f8c03..7cc389651dd 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -69,6 +69,7 @@ import type { Phase } from "#app/phase"; import { ShowAbilityPhase } from "./show-ability-phase"; import { MovePhase } from "./move-phase"; import { MoveEndPhase } from "./move-end-phase"; +import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; export class MoveEffectPhase extends PokemonPhase { public move: PokemonMove; @@ -288,7 +289,8 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && - (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) && + (bypassIgnoreProtect.value || + !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || @@ -306,7 +308,7 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target's magic bounce ability not ignored and able to reflect this move? */ const canMagicBounce = !isReflecting && - !move.checkFlag(MoveFlags.IGNORE_ABILITIES, user, target) && + !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); @@ -326,12 +328,14 @@ export class MoveEffectPhase extends PokemonPhase { ? getMoveTargets(target, move.id).targets : [user.getBattlerIndex()]; if (!isReflecting) { + // TODO: Ability displays should be handled by the ability queuedPhases.push( new ShowAbilityPhase( target.getBattlerIndex(), target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), ), ); + queuedPhases.push(new HideAbilityPhase()); } queuedPhases.push( diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 46e266a32b7..f3a40ce69bd 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -1,13 +1,20 @@ import { globalScene } from "#app/global-scene"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { PokemonPhase } from "./pokemon-phase"; +import type { BattlerIndex } from "#app/battle"; export class MoveEndPhase extends PokemonPhase { + private wasFollowUp: boolean; + constructor(battlerIndex: BattlerIndex, wasFollowUp: boolean = false) { + super(battlerIndex); + this.wasFollowUp = wasFollowUp; + } + start() { super.start(); const pokemon = this.getPokemon(); - if (pokemon.isActive(true)) { + if (!this.wasFollowUp && pokemon?.isActive(true)) { pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index f8edaa56981..33e772eb2ea 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -42,7 +42,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { MoveChargePhase } from "#app/phases/move-charge-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { NumberHolder } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; @@ -169,7 +168,7 @@ export class MovePhase extends BattlePhase { // Check move to see if arena.ignoreAbilities should be true. if (!this.followUp || this.reflected) { - if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { + if (this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })) { globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex()); } } @@ -466,13 +465,10 @@ export class MovePhase extends BattlePhase { } /** - * Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`, - * then ends the phase. + * Queues a {@linkcode MoveEndPhase} and then ends the phase */ public end(): void { - if (!this.followUp && this.canMove()) { - globalScene.unshiftPhase(new MoveEndPhase(this.pokemon.getBattlerIndex())); - } + globalScene.unshiftPhase(new MoveEndPhase(this.pokemon.getBattlerIndex(), this.followUp)); super.end(); } @@ -535,11 +531,16 @@ export class MovePhase extends BattlePhase { if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { redirectTarget.value = currentTarget; - globalScene.unshiftPhase( - new ShowAbilityPhase( - this.pokemon.getBattlerIndex(), - this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), - ), + // TODO: Ability displays should be handled by the ability + globalScene.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), + true, + ); + globalScene.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), + false, ); } diff --git a/src/phases/new-battle-phase.ts b/src/phases/new-battle-phase.ts index 8cdbdc5891a..09b8ab1d335 100644 --- a/src/phases/new-battle-phase.ts +++ b/src/phases/new-battle-phase.ts @@ -5,6 +5,11 @@ export class NewBattlePhase extends BattlePhase { start() { super.start(); + // cull any extra `NewBattle` phases from the queue. + globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !(phase instanceof NewBattlePhase)); + // `phaseQueuePrepend` is private, so we have to use this inefficient loop. + while (globalScene.tryRemoveUnshiftedPhase(phase => phase instanceof NewBattlePhase)) {} + globalScene.newBattle(); this.end(); diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index 1ed63f76b64..e650d714abc 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -42,8 +42,12 @@ export class RevivalBlessingPhase extends BattlePhase { true, ); - if (globalScene.currentBattle.double && globalScene.getPlayerParty().length > 1) { - const allyPokemon = this.user.getAlly(); + const allyPokemon = this.user.getAlly(); + if ( + globalScene.currentBattle.double && + globalScene.getPlayerParty().length > 1 && + !Utils.isNullOrUndefined(allyPokemon) + ) { if (slotIndex <= 1) { // Revived ally pokemon globalScene.unshiftPhase( diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index 1b3c6dde568..8097af33fe0 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -35,7 +35,7 @@ export class ShowAbilityPhase extends PokemonPhase { // If the bar is already out, hide it before showing the new one if (globalScene.abilityBar.isVisible()) { - globalScene.unshiftPhase(new HideAbilityPhase(this.battlerIndex, this.passive)); + globalScene.unshiftPhase(new HideAbilityPhase()); globalScene.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); return this.end(); } diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index f58744ef5ce..4c82661a3bb 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -17,7 +17,7 @@ import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { ResetNegativeStatStageModifier } from "#app/modifier/modifier"; import { handleTutorial, Tutorial } from "#app/tutorial"; -import { NumberHolder, BooleanHolder } from "#app/utils"; +import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; @@ -161,7 +161,7 @@ export class StatStageChangePhase extends PokemonPhase { pokemon, ); const ally = pokemon.getAlly(); - if (ally) { + if (!isNullOrUndefined(ally)) { applyPreStatStageChangeAbAttrs( ConditionalUserFieldProtectStatAbAttr, ally, diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 836647fbfb4..ddfc0955508 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -28,6 +28,8 @@ export class TurnEndPhase extends FieldPhase { globalScene.currentBattle.incrementTurn(); globalScene.eventTarget.dispatchEvent(new TurnEndEvent(globalScene.currentBattle.turn)); + globalScene.hideAbilityBar(); + const handlePokemon = (pokemon: Pokemon) => { if (!pokemon.switchOutStatus) { pokemon.lapseTags(BattlerTagLapseType.TURN_END); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 2388918dca2..391ceec503d 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1793,7 +1793,9 @@ export class GameData { const dexEntry = this.dexData[species.speciesId]; const caughtAttr = dexEntry.caughtAttr; const formIndex = pokemon.formIndex; - const dexAttr = pokemon.getDexAttr(); + + // This makes sure that we do not try to unlock data which cannot be unlocked + const dexAttr = pokemon.getDexAttr() & species.getFullUnlocksData(); // Mark as caught dexEntry.caughtAttr |= dexAttr; @@ -1803,6 +1805,10 @@ export class GameData { // always true except for the case of Urshifu. const formKey = pokemon.getFormKey(); if (formIndex > 0) { + // In case a Pikachu with formIndex > 0 was unlocked, base form Pichu is also unlocked + if (pokemon.species.speciesId === Species.PIKACHU && species.speciesId === Species.PICHU) { + dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0); + } if (pokemon.species.speciesId === Species.URSHIFU) { if (formIndex === 2) { dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0); diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index 3c5abc2cc18..074f60c2c5d 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -10,6 +10,9 @@ import * as v1_1_0 from "./versions/v1_1_0"; // --- v1.7.0 PATCHES --- // import * as v1_7_0 from "./versions/v1_7_0"; +// --- v1.8.3 PATCHES --- // +import * as v1_8_3 from "./versions/v1_8_3"; + const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value)); /** @@ -174,6 +177,12 @@ class SystemVersionConverter extends VersionConverter { console.log("Applying v1.7.0 system data migration!"); this.callMigrators(data, v1_7_0.systemMigrators); } + if (curMinor === 8) { + if (curPatch <= 2) { + console.log("Applying v1.8.3 system data migration!"); + this.callMigrators(data, v1_8_3.systemMigrators); + } + } } console.log(`System data successfully migrated to v${version}!`); diff --git a/src/system/version_migration/versions/v1_8_3.ts b/src/system/version_migration/versions/v1_8_3.ts new file mode 100644 index 00000000000..d35530c28e9 --- /dev/null +++ b/src/system/version_migration/versions/v1_8_3.ts @@ -0,0 +1,30 @@ +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { DexAttr, type SystemSaveData } from "#app/system/game-data"; +import { Species } from "#enums/species"; + +export const systemMigrators = [ + /** + * If a starter is caught, but the only forms registered as caught are not starterSelectable, + * unlock the default form. + * @param data {@linkcode SystemSaveData} + */ + function migratePichuForms(data: SystemSaveData) { + if (data.starterData && data.dexData) { + // This is Pichu's Pokédex number + const sd = 172; + const caughtAttr = data.dexData[sd]?.caughtAttr; + const species = getPokemonSpecies(sd); + // An extra check because you never know + if (species.speciesId === Species.PICHU && caughtAttr) { + // Ensuring that only existing forms are unlocked + data.dexData[sd].caughtAttr &= species.getFullUnlocksData(); + // If no forms are unlocked now, since Pichu is caught, we unlock form 0 + data.dexData[sd].caughtAttr |= DexAttr.DEFAULT_FORM; + } + } + }, +] as const; + +export const settingsMigrators = [] as const; + +export const sessionMigrators = [] as const; diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index 56c357b2212..c296329473d 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -39,20 +39,24 @@ describe("Abilities - Dancer", () => { game.move.select(Moves.SPLASH); game.move.select(Moves.SWORDS_DANCE, 1); await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]); - await game.phaseInterceptor.to("MovePhase"); - // immediately copies ally move - await game.phaseInterceptor.to("MovePhase", false); + await game.phaseInterceptor.to("MovePhase"); // feebas uses swords dance + await game.phaseInterceptor.to("MovePhase", false); // oricorio copies swords dance + let currentPhase = game.scene.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(Moves.SWORDS_DANCE); - await game.phaseInterceptor.to("MoveEndPhase"); - await game.phaseInterceptor.to("MovePhase"); - // immediately copies enemy move - await game.phaseInterceptor.to("MovePhase", false); + + await game.phaseInterceptor.to("MoveEndPhase"); // end oricorio's move + await game.phaseInterceptor.to("MovePhase"); // magikarp 1 copies swords dance + await game.phaseInterceptor.to("MovePhase"); // magikarp 2 copies swords dance + await game.phaseInterceptor.to("MovePhase"); // magikarp (left) uses victory dance + await game.phaseInterceptor.to("MovePhase", false); // oricorio copies magikarp's victory dance + currentPhase = game.scene.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(Moves.VICTORY_DANCE); - await game.phaseInterceptor.to("BerryPhase"); + + await game.phaseInterceptor.to("BerryPhase"); // finish the turn // doesn't use PP if copied move is also in moveset expect(oricorio.moveset[0]?.ppUsed).toBe(0); diff --git a/test/abilities/mold_breaker.test.ts b/test/abilities/mold_breaker.test.ts new file mode 100644 index 00000000000..8f050a68d76 --- /dev/null +++ b/test/abilities/mold_breaker.test.ts @@ -0,0 +1,51 @@ +import { BattlerIndex } from "#app/battle"; +import { globalScene } from "#app/global-scene"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Mold Breaker", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.MOLD_BREAKER) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should turn off the ignore abilities arena variable after the user's move", async () => { + game.override.enemyMoveset(Moves.SPLASH) + .ability(Abilities.MOLD_BREAKER) + .moveset([ Moves.ERUPTION ]) + .startingLevel(100) + .enemyLevel(2); + await game.classicMode.startBattle([ Species.MAGIKARP ]); + const enemy = game.scene.getEnemyPokemon()!; + + expect(enemy.isFainted()).toBe(false); + game.move.select(Moves.SPLASH); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.phaseInterceptor.to("MoveEndPhase", true); + expect(globalScene.arena.ignoreAbilities).toBe(false); + }); +}); diff --git a/test/abilities/super_luck.test.ts b/test/abilities/super_luck.test.ts new file mode 100644 index 00000000000..bc9524de801 --- /dev/null +++ b/test/abilities/super_luck.test.ts @@ -0,0 +1,43 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Abilities - Super Luck", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.TACKLE]) + .ability(Abilities.SUPER_LUCK) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should increase the crit stage of a user by 1", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + const enemy = game.scene.getEnemyPokemon()!; + const fn = vi.spyOn(enemy, "getCritStage"); + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to("BerryPhase"); + expect(fn).toHaveReturnedWith(1); + fn.mockRestore(); + }); +}); diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index ef201cbf8dd..294025a10e7 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -498,6 +498,7 @@ describe("Abilities - Wimp Out", () => { const hasFled = enemyPokemon.switchOutStatus; expect(isVisible && !hasFled).toBe(true); }); + it("wimp out will not skip battles when triggered in a double battle", async () => { const wave = 2; game.override @@ -525,4 +526,29 @@ describe("Abilities - Wimp Out", () => { await game.toNextWave(); expect(game.scene.currentBattle.waveIndex).toBe(wave + 1); }); + + it("wimp out should not skip battles when triggering the same turn as another enemy faints", async () => { + const wave = 2; + game.override + .enemySpecies(Species.WIMPOD) + .enemyAbility(Abilities.WIMP_OUT) + .startingLevel(50) + .enemyLevel(1) + .enemyMoveset([ Moves.SPLASH, Moves.ENDURE ]) + .battleType("double") + .moveset([ Moves.DRAGON_ENERGY, Moves.SPLASH ]) + .startingWave(wave); + + await game.classicMode.startBattle([ Species.REGIDRAGO, Species.MAGIKARP ]); + + // turn 1 + game.move.select(Moves.DRAGON_ENERGY, 0); + game.move.select(Moves.SPLASH, 1); + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.ENDURE); + + await game.phaseInterceptor.to("SelectModifierPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(wave + 1); + + }); });