Merge branch 'beta' (pre ME bugfixes) in 'ME balance changes'

This commit is contained in:
MokaStitcher 2024-10-21 13:05:56 +02:00
commit 69e8b49e79
535 changed files with 25577 additions and 31196 deletions

View File

@ -1,8 +1,12 @@
name: Deploy name: Deploy Main
on: on:
push: {} push:
pull_request: {} branches:
- main
pull_request:
branches:
- main
jobs: jobs:
deploy: deploy:
@ -22,7 +26,7 @@ jobs:
env: env:
NODE_ENV: production NODE_ENV: production
- name: Set up SSH - name: Set up SSH
if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch if: github.event_name == 'push' && github.ref_name == 'main'
run: | run: |
mkdir ~/.ssh mkdir ~/.ssh
echo "${{ secrets.SSH_PUBLIC_KEY }}" > ~/.ssh/id_ed25519.pub echo "${{ secrets.SSH_PUBLIC_KEY }}" > ~/.ssh/id_ed25519.pub
@ -30,12 +34,12 @@ jobs:
chmod 600 ~/.ssh/* chmod 600 ~/.ssh/*
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy build on server - name: Deploy build on server
if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch if: github.event_name == 'push' && github.ref_name == 'main'
run: | run: |
rsync --del --no-times --checksum -vrm dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_DIR }} rsync --del --no-times --checksum -vrm dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_DIR }}
ssh -t ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "~/prmanifest --inpath ${{ secrets.DESTINATION_DIR }} --outpath ${{ secrets.DESTINATION_DIR }}/manifest.json" ssh -t ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "~/prmanifest --inpath ${{ secrets.DESTINATION_DIR }} --outpath ${{ secrets.DESTINATION_DIR }}/manifest.json"
- name: Purge Cloudflare Cache - name: Purge Cloudflare Cache
if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch if: github.event_name == 'push' && github.ref_name == 'main'
id: purge-cache id: purge-cache
uses: NathanVaughn/actions-cloudflare-purge@v3.1.0 uses: NathanVaughn/actions-cloudflare-purge@v3.1.0
with: with:

View File

@ -17,7 +17,12 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
2. Run `npm run start:dev` to locally run the project in `localhost:8000` 2. Run `npm run start:dev` to locally run the project in `localhost:8000`
#### Linting #### Linting
We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. To view the complete rules, check out the [eslint.config.js](./eslint.config.js) file.
### 📚 Documentation
You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html).
For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file.
For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file.
### ❔ FAQ ### ❔ FAQ

View File

@ -49,7 +49,7 @@ async function promptFileName(selectedType) {
{ {
type: "input", type: "input",
name: "userInput", name: "userInput",
message: `Please provide a file name for the ${selectedType} test:`, message: `Please provide the name of the ${selectedType}:`,
}, },
]); ]);
@ -110,7 +110,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("${description}", () => { describe("${description}", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -130,14 +130,21 @@ describe("${description}", () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset([ Moves.SPLASH ]) .moveset([ Moves.SPLASH ])
.ability(Abilities.BALL_FETCH)
.battleType("single") .battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH); .enemyMoveset(Moves.SPLASH);
}); });
it("test case", async () => { it("should do X", async () => {
// await game.classicMode.startBattle([Species.MAGIKARP]); await game.classicMode.startBattle([ Species.FEEBAS ]);
// game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(true).toBe(true);
}); });
}); });
`; `;

View File

@ -1,5 +1,5 @@
import tseslint from '@typescript-eslint/eslint-plugin'; import tseslint from '@typescript-eslint/eslint-plugin';
import stylisticTs from '@stylistic/eslint-plugin-ts' import stylisticTs from '@stylistic/eslint-plugin-ts';
import parser from '@typescript-eslint/parser'; import parser from '@typescript-eslint/parser';
import importX from 'eslint-plugin-import-x'; import importX from 'eslint-plugin-import-x';
@ -16,15 +16,15 @@ export default [
'@typescript-eslint': tseslint '@typescript-eslint': tseslint
}, },
rules: { rules: {
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and != "eqeqeq": ["error", "always"], // Enforces the use of `===` and `!==` instead of `==` and `!=`
"indent": ["error", 2], // Enforces a 2-space indentation "indent": ["error", 2, { "SwitchCase": 1 }], // Enforces a 2-space indentation, enforces indentation of `case ...:` statements
"quotes": ["error", "double"], // Enforces the use of double quotes for strings "quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead "no-var": "error", // Disallows the use of `var`, enforcing `let` or `const` instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned "prefer-const": "error", // Enforces the use of `const` for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this) "no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", { "@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used. "args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
"ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the rest. "ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the others.
}], }],
"eol-last": ["error", "always"], // Enforces at least one newline at the end of files "eol-last": ["error", "always"], // Enforces at least one newline at the end of files
"@stylistic/ts/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax "@stylistic/ts/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax
@ -32,15 +32,20 @@ export default [
"no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax "no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors "brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements "curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@stylistic/ts/brace-style": ["error", "1tbs"], "@stylistic/ts/brace-style": ["error", "1tbs"], // Enforces the following brace style: https://eslint.style/rules/js/brace-style#_1tbs
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines "no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines "skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments "ignoreComments": false // Enforces the rule on lines containing comments
}], }],
"space-before-blocks": ["error", "always"], // Enforces a space before blocks "space-before-blocks": ["error", "always"], // Enforces a space before blocks
"keyword-spacing": ["error", { "before": true, "after": true }], // Enforces spacing before and after keywords "keyword-spacing": ["error", { "before": true, "after": true }], // Enforces spacing before and after keywords
"comma-spacing": ["error", { "before": false, "after": true }], // Enforces spacing after comma "comma-spacing": ["error", { "before": false, "after": true }], // Enforces spacing after commas
"import-x/extensions": ["error", "never", { "json": "always" }], // Enforces no extension for imports unless json "import-x/extensions": ["error", "never", { "json": "always" }], // Enforces no extension for imports unless json
"array-bracket-spacing": ["error", "always", { "objectsInArrays": false, "arraysInArrays": false }], // Enforces consistent spacing inside array brackets
"object-curly-spacing": ["error", "always", { "arraysInObjects": false, "objectsInObjects": false }], // Enforces consistent spacing inside braces of object literals, destructuring assignments, and import/export specifiers
"computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets
"space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines
} }
} }
] ]

14
global.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import type { SetupServerApi } from "msw/node";
export {};
declare global {
/**
* Only used in testing.
* Can technically be undefined/null but for ease of use we are going to assume it is always defined.
* Used to load i18n files exclusively.
*
* To set up your own server in a test see `game_data.test.ts`
*/
var i18nServer: SetupServerApi;
}

5
package-lock.json generated
View File

@ -1,12 +1,13 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.0.4", "version": "1.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.0.4", "version": "1.1.0",
"hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",

View File

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -399,13 +399,13 @@
"x": 0, "x": 0,
"y": 6, "y": 6,
"w": 36, "w": 36,
"h": 55 "h": 54
}, },
"frame": { "frame": {
"x": 72, "x": 72,
"y": 55, "y": 55,
"w": 36, "w": 36,
"h": 55 "h": 54
} }
}, },
{ {
@ -420,13 +420,13 @@
"x": 0, "x": 0,
"y": 6, "y": 6,
"w": 36, "w": 36,
"h": 55 "h": 54
}, },
"frame": { "frame": {
"x": 72, "x": 72,
"y": 55, "y": 55,
"w": 36, "w": 36,
"h": 55 "h": 54
} }
}, },
{ {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 411 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 B

After

Width:  |  Height:  |  Size: 942 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -399,13 +399,13 @@
"x": 0, "x": 0,
"y": 6, "y": 6,
"w": 36, "w": 36,
"h": 55 "h": 54
}, },
"frame": { "frame": {
"x": 72, "x": 72,
"y": 55, "y": 55,
"w": 36, "w": 36,
"h": 55 "h": 54
} }
}, },
{ {
@ -420,13 +420,13 @@
"x": 0, "x": 0,
"y": 6, "y": 6,
"w": 36, "w": 36,
"h": 55 "h": 54
}, },
"frame": { "frame": {
"x": 72, "x": 72,
"y": 55, "y": 55,
"w": 36, "w": 36,
"h": 55 "h": 54
} }
}, },
{ {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 411 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,40 @@
{
"1": {
"566678": "0e6296",
"e0e4f4": "513981",
"625287": "4e4094",
"536273": "1f1233",
"988b98": "b24c86",
"9170b9": "8b69c3",
"c4cce1": "3b235c",
"e6d3e9": "f1a4c5",
"bfacc1": "da75a5",
"515f70": "197497",
"c5cee3": "63cee1",
"8e96aa": "301848",
"8b93a6": "3aa8c4",
"80737f": "8a2166",
"4b454f": "6f1357",
"36404c": "0c5474",
"b791f2": "c7a1e5"
},
"2": {
"566678": "8e480b",
"e0e4f4": "176463",
"625287": "274159",
"536273": "02262c",
"988b98": "2a6563",
"9170b9": "2f667c",
"c4cce1": "0d484a",
"e6d3e9": "9cead8",
"bfacc1": "5db6a9",
"515f70": "a34205",
"c5cee3": "f7af58",
"8e96aa": "073338",
"8b93a6": "d27e26",
"80737f": "2b736f",
"4b454f": "194f51",
"36404c": "842401",
"b791f2": "4a9699"
}
}

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "6706_2.png",
"format": "RGBA8888",
"size": {
"w": 82,
"h": 82
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 82,
"h": 72
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 82,
"h": 72
},
"frame": {
"x": 0,
"y": 0,
"w": 82,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:02eb46aa66ac70df612e129b7801a85c:a77cca14b23f4f3aece64d1a82449a0f:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "6706_3.png",
"format": "RGBA8888",
"size": {
"w": 82,
"h": 82
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 82,
"h": 72
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 82,
"h": 72
},
"frame": {
"x": 0,
"y": 0,
"w": 82,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:02eb46aa66ac70df612e129b7801a85c:a77cca14b23f4f3aece64d1a82449a0f:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -3721,8 +3721,8 @@
], ],
"6706": [ "6706": [
0, 0,
2, 1,
2 1
], ],
"6713": [ "6713": [
0, 0,
@ -7754,8 +7754,8 @@
], ],
"6706": [ "6706": [
0, 0,
2, 1,
2 1
], ],
"6713": [ "6713": [
0, 0,
@ -8493,8 +8493,8 @@
], ],
"705": [ "705": [
0, 0,
2, 1,
2 1
], ],
"706": [ "706": [
0, 0,
@ -9568,8 +9568,8 @@
], ],
"6706": [ "6706": [
0, 0,
2, 1,
2 1
], ],
"female": {}, "female": {},
"back": { "back": {
@ -11095,8 +11095,8 @@
], ],
"6706": [ "6706": [
0, 0,
2, 1,
2 1
], ],
"6713": [ "6713": [
0, 0,

View File

@ -0,0 +1,38 @@
{
"1": {
"566678": "197497",
"8e96aa": "3b235c",
"929aad": "3aa8c4",
"625287": "4e4094",
"536273": "301848",
"988b98": "b24c86",
"36404c": "0c5474",
"c4cce1": "513981",
"e6d3e9": "f1a4c5",
"bfacc1": "d074a0",
"546475": "0e6296",
"c5cee3": "63cee1",
"80737f": "8a2166",
"4b454f": "6f1357",
"9170b9": "8b69c3",
"b791f2": "c7a1e5"
},
"2": {
"566678": "a34205",
"8e96aa": "073338",
"929aad": "d27e26",
"625287": "0e3f47",
"536273": "042329",
"988b98": "2b736f",
"36404c": "842401",
"c4cce1": "0d484a",
"e6d3e9": "9cead8",
"bfacc1": "5db6a9",
"546475": "8e480b",
"c5cee3": "f7af58",
"80737f": "194f51",
"4b454f": "274159",
"9170b9": "2f667c",
"b791f2": "4a9699"
}
}

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "6706_2.png",
"format": "RGBA8888",
"size": {
"w": 79,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 79,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 79,
"h": 73
},
"frame": {
"x": 0,
"y": 0,
"w": 79,
"h": 73
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:64f7e6dfa489012922487e45ba53d557:4d24652b372939abe499497c4b6647b0:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "6706_3.png",
"format": "RGBA8888",
"size": {
"w": 79,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 79,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 79,
"h": 73
},
"frame": {
"x": 0,
"y": 0,
"w": 79,
"h": 73
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:64f7e6dfa489012922487e45ba53d557:4d24652b372939abe499497c4b6647b0:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,40 @@
{
"1": {
"566678": "0e6296",
"e0e4f4": "513981",
"625287": "4e4094",
"536273": "1f1233",
"988b98": "b24c86",
"36404c": "0c5474",
"c4cce1": "3b235c",
"e6d3e9": "f1a4c5",
"bfacc1": "da75a5",
"515f70": "197497",
"c5cee3": "63cee1",
"b791f2": "c7a1e5",
"8b93a6": "3aa8c4",
"80737f": "8a2166",
"4b454f": "6f1357",
"9170b9": "8b69c3",
"8e96aa": "301848"
},
"2": {
"566678": "8e480b",
"e0e4f4": "176463",
"625287": "274159",
"536273": "02262c",
"988b98": "2a6563",
"36404c": "842401",
"c4cce1": "0d484a",
"e6d3e9": "9cead8",
"bfacc1": "5db6a9",
"515f70": "a34205",
"c5cee3": "f7af58",
"b791f2": "4a9699",
"8b93a6": "d27e26",
"80737f": "2b736f",
"4b454f": "194f51",
"9170b9": "2f667c",
"8e96aa": "073338"
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -0,0 +1,33 @@
{
"1": {
"101010":"101010",
"4d454d":"8a2166",
"807380":"b93f84",
"bfacbf":"e56ca6",
"f2daf2":"fbb3d2",
"665980":"4e4094",
"8f7db3":"8b69c3",
"b8a1e5":"c7a1e5",
"4d993d":"aa6a00",
"66cc52":"ffd047",
"4e9c3e":"0c5474",
"67cf53":"3aa8c4",
"b6f2aa":"63cee1"
},
"2": {
"101010":"101010",
"4d454d":"194f51",
"807380":"2b736f",
"bfacbf":"5db6a9",
"f2daf2":"9cead8",
"665980":"274159",
"8f7db3":"2f667c",
"b8a1e5":"4a9699",
"4d993d":"007d61",
"66cc52":"49ffbf",
"4e9c3e":"842401",
"67cf53":"a34205",
"b6f2aa":"d27e26"
}
}

View File

@ -1,272 +0,0 @@
{
"textures": [
{
"image": "705_2.png",
"format": "RGBA8888",
"size": {
"w": 154,
"h": 154
},
"scale": 1,
"frames": [
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 46,
"h": 58
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 58
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 46,
"h": 58
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 58
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 2,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 46,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 2,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 46,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 91,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 1,
"y": 0,
"w": 42,
"h": 58
},
"frame": {
"x": 0,
"y": 58,
"w": 42,
"h": 58
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 1,
"y": 0,
"w": 42,
"h": 58
},
"frame": {
"x": 0,
"y": 58,
"w": 42,
"h": 58
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 58
},
"frame": {
"x": 42,
"y": 58,
"w": 41,
"h": 58
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 58
},
"frame": {
"x": 42,
"y": 58,
"w": 41,
"h": 58
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 36,
"h": 58
},
"frame": {
"x": 83,
"y": 58,
"w": 36,
"h": 58
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 36,
"h": 58
},
"frame": {
"x": 83,
"y": 58,
"w": 36,
"h": 58
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 35,
"h": 58
},
"frame": {
"x": 119,
"y": 58,
"w": 35,
"h": 58
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,272 +0,0 @@
{
"textures": [
{
"image": "705_3.png",
"format": "RGBA8888",
"size": {
"w": 154,
"h": 154
},
"scale": 1,
"frames": [
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 46,
"h": 58
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 58
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 46,
"h": 58
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 58
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 2,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 46,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 2,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 46,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 45,
"h": 58
},
"frame": {
"x": 91,
"y": 0,
"w": 45,
"h": 58
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 1,
"y": 0,
"w": 42,
"h": 58
},
"frame": {
"x": 0,
"y": 58,
"w": 42,
"h": 58
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 1,
"y": 0,
"w": 42,
"h": 58
},
"frame": {
"x": 0,
"y": 58,
"w": 42,
"h": 58
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 58
},
"frame": {
"x": 42,
"y": 58,
"w": 41,
"h": 58
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 58
},
"frame": {
"x": 42,
"y": 58,
"w": 41,
"h": 58
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 36,
"h": 58
},
"frame": {
"x": 83,
"y": 58,
"w": 36,
"h": 58
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 36,
"h": 58
},
"frame": {
"x": 83,
"y": 58,
"w": 36,
"h": 58
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 49,
"h": 58
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 35,
"h": 58
},
"frame": {
"x": 119,
"y": 58,
"w": 35,
"h": 58
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,38 @@
{
"1": {
"566678": "197497",
"8e96aa": "3b235c",
"929aad": "3aa8c4",
"625287": "4e4094",
"536273": "301848",
"988b98": "b24c86",
"36404c": "0c5474",
"c4cce1": "513981",
"e6d3e9": "f1a4c5",
"bfacc1": "d074a0",
"546475": "0e6296",
"c5cee3": "63cee1",
"80737f": "8a2166",
"4b454f": "6f1357",
"9170b9": "8b69c3",
"b791f2": "c7a1e5"
},
"2": {
"566678": "a34205",
"8e96aa": "073338",
"929aad": "d27e26",
"625287": "0e3f47",
"536273": "042329",
"988b98": "2b736f",
"36404c": "842401",
"c4cce1": "0d484a",
"e6d3e9": "9cead8",
"bfacc1": "5db6a9",
"546475": "8e480b",
"c5cee3": "f7af58",
"80737f": "194f51",
"4b454f": "274159",
"9170b9": "2f667c",
"b791f2": "4a9699"
}
}

View File

@ -1,776 +0,0 @@
{
"textures": [
{
"image": "6706_2.png",
"format": "RGBA8888",
"size": {
"w": 358,
"h": 358
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 4,
"w": 84,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 84,
"h": 69
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 4,
"w": 84,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 84,
"h": 69
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 84,
"y": 0,
"w": 83,
"h": 72
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 84,
"y": 0,
"w": 83,
"h": 72
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 0,
"y": 69,
"w": 83,
"h": 72
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 167,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 167,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 250,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0036.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 250,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 167,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 167,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 83,
"y": 72,
"w": 82,
"h": 73
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 83,
"y": 72,
"w": 82,
"h": 73
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 249,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 249,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 0,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 0,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 0,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 0,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 81,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 81,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 81,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 81,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 165,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 165,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 247,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 247,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 162,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 162,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 81,
"h": 70
},
"frame": {
"x": 162,
"y": 288,
"w": 81,
"h": 70
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 81,
"h": 70
},
"frame": {
"x": 162,
"y": 288,
"w": 81,
"h": 70
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 242,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 242,
"y": 215,
"w": 80,
"h": 73
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,776 +0,0 @@
{
"textures": [
{
"image": "6706_3.png",
"format": "RGBA8888",
"size": {
"w": 358,
"h": 358
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 4,
"w": 84,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 84,
"h": 69
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 4,
"w": 84,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 84,
"h": 69
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 84,
"y": 0,
"w": 83,
"h": 72
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 84,
"y": 0,
"w": 83,
"h": 72
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 1,
"w": 83,
"h": 72
},
"frame": {
"x": 0,
"y": 69,
"w": 83,
"h": 72
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 167,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 167,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 250,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0036.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 3,
"w": 83,
"h": 70
},
"frame": {
"x": 250,
"y": 0,
"w": 83,
"h": 70
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 167,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 167,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 83,
"y": 72,
"w": 82,
"h": 73
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 83,
"y": 72,
"w": 82,
"h": 73
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 3,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 0,
"y": 141,
"w": 82,
"h": 73
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 249,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 4,
"y": 0,
"w": 82,
"h": 73
},
"frame": {
"x": 249,
"y": 70,
"w": 82,
"h": 73
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 0,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 0,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 0,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 0,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 81,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 5,
"y": 0,
"w": 81,
"h": 73
},
"frame": {
"x": 81,
"y": 214,
"w": 81,
"h": 73
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 81,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 81,
"h": 71
},
"frame": {
"x": 81,
"y": 287,
"w": 81,
"h": 71
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 165,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 165,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 247,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"w": 82,
"h": 72
},
"frame": {
"x": 247,
"y": 143,
"w": 82,
"h": 72
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 162,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 162,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 81,
"h": 70
},
"frame": {
"x": 162,
"y": 288,
"w": 81,
"h": 70
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 81,
"h": 70
},
"frame": {
"x": 162,
"y": 288,
"w": 81,
"h": 70
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 242,
"y": 215,
"w": 80,
"h": 73
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 86,
"h": 73
},
"spriteSourceSize": {
"x": 6,
"y": 0,
"w": 80,
"h": 73
},
"frame": {
"x": 242,
"y": 215,
"w": 80,
"h": 73
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -853,14 +853,14 @@
"spriteSourceSize": { "x": 7, "y": 2, "w": 27, "h": 26 }, "spriteSourceSize": { "x": 7, "y": 2, "w": 27, "h": 26 },
"sourceSize": { "w": 40, "h": 30 } "sourceSize": { "w": 40, "h": 30 }
}, },
"981_2.png": { "981_2": {
"frame": { "x": 108, "y": 87, "w": 23, "h": 30 }, "frame": { "x": 108, "y": 87, "w": 23, "h": 30 },
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": true,
"spriteSourceSize": { "x": 9, "y": 0, "w": 23, "h": 30 }, "spriteSourceSize": { "x": 9, "y": 0, "w": 23, "h": 30 },
"sourceSize": { "w": 40, "h": 30 } "sourceSize": { "w": 40, "h": 30 }
}, },
"981_3.png": { "981_3": {
"frame": { "x": 246, "y": 86, "w": 23, "h": 30 }, "frame": { "x": 246, "y": 86, "w": 23, "h": 30 },
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -1 +1 @@
Subproject commit 005989574fe5638897fa2933b6f57ae4b9c83635 Subproject commit 3ccef8472dd7cc7c362538489954cb8fdad27e5f

View File

@ -1,57 +1,57 @@
import Phaser from "phaser"; import Phaser from "phaser";
import UI from "./ui/ui"; import UI from "#app/ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species"; import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils"; import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "./utils"; import * as Utils from "#app/utils";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball"; import { PokeballType } from "#app/data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import { Phase } from "./phase"; import { Phase } from "#app/phase";
import { initGameSpeed } from "./system/game-speed"; import { initGameSpeed } from "#app/system/game-speed";
import { Arena, ArenaBase } from "./field/arena"; import { Arena, ArenaBase } from "#app/field/arena";
import { GameData } from "./system/game-data"; import { GameData } from "#app/system/game-data";
import { addTextObject, getTextColor, TextStyle } from "./ui/text"; import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
import { allMoves } from "./data/move"; import { allMoves } from "#app/data/move";
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "./modifier/modifier-type"; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import AbilityBar from "./ui/ability-bar"; import AbilityBar from "#app/ui/ability-bar";
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, ChangeMovePriorityAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "./data/ability"; import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "#app/data/ability";
import Battle, { BattleType, FixedBattleConfig } from "./battle"; import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
import { GameMode, GameModes, getGameMode } from "./game-mode"; import { GameMode, GameModes, getGameMode } from "#app/game-mode";
import FieldSpritePipeline from "./pipelines/field-sprite"; import FieldSpritePipeline from "#app/pipelines/field-sprite";
import SpritePipeline from "./pipelines/sprite"; import SpritePipeline from "#app/pipelines/sprite";
import PartyExpBar from "./ui/party-exp-bar"; import PartyExpBar from "#app/ui/party-exp-bar";
import { trainerConfigs, TrainerSlot } from "./data/trainer-config"; import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
import Trainer, { TrainerVariant } from "./field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import TrainerData from "./system/trainer-data"; import TrainerData from "#app/system/trainer-data";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { pokemonPrevolutions } from "./data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokeballTray from "./ui/pokeball-tray"; import PokeballTray from "#app/ui/pokeball-tray";
import InvertPostFX from "./pipelines/invert"; import InvertPostFX from "#app/pipelines/invert";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv"; import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
import { Voucher, vouchers } from "./system/voucher"; import { Voucher, vouchers } from "#app/system/voucher";
import { Gender } from "./data/gender"; import { Gender } from "#app/data/gender";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import { addUiThemeOverrides } from "./ui/ui-theme"; import { addUiThemeOverrides } from "#app/ui/ui-theme";
import PokemonData from "./system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { Nature } from "./data/nature"; import { Nature } from "#app/data/nature";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
import { FormChangePhase } from "./phases/form-change-phase"; import { FormChangePhase } from "#app/phases/form-change-phase";
import { getTypeRgb } from "./data/type"; import { getTypeRgb } from "#app/data/type";
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
import CharSprite from "./ui/char-sprite"; import CharSprite from "#app/ui/char-sprite";
import DamageNumberHandler from "./field/damage-number-handler"; import DamageNumberHandler from "#app/field/damage-number-handler";
import PokemonInfoContainer from "./ui/pokemon-info-container"; import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "./data/balance/biomes"; import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "./scene-base"; import { SceneBase } from "#app/scene-base";
import CandyBar from "./ui/candy-bar"; import CandyBar from "#app/ui/candy-bar";
import { Variant, variantData } from "./data/variant"; import { Variant, variantData } from "#app/data/variant";
import { Localizable } from "#app/interfaces/locales"; import { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { InputsController } from "./inputs-controller"; import { InputsController } from "#app/inputs-controller";
import { UiInputs } from "./ui-inputs"; import { UiInputs } from "#app/ui-inputs";
import { NewArenaEvent } from "./events/battle-scene"; import { NewArenaEvent } from "#app/events/battle-scene";
import { ArenaFlyout } from "./ui/arena-flyout"; import { ArenaFlyout } from "#app/ui/arena-flyout";
import { EaseType } from "#enums/ease-type"; import { EaseType } from "#enums/ease-type";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { BattleStyle } from "#enums/battle-style"; import { BattleStyle } from "#enums/battle-style";
@ -66,27 +66,27 @@ import { TimedEventManager } from "#app/timed-event-manager";
import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PokemonAnimType } from "#enums/pokemon-anim-type";
import i18next from "i18next"; import i18next from "i18next";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { battleSpecDialogue } from "./data/dialogue"; import { battleSpecDialogue } from "#app/data/dialogue";
import { LoadingScene } from "./loading-scene"; import { LoadingScene } from "#app/loading-scene";
import { LevelCapPhase } from "./phases/level-cap-phase"; import { LevelCapPhase } from "#app/phases/level-cap-phase";
import { LoginPhase } from "./phases/login-phase"; import { LoginPhase } from "#app/phases/login-phase";
import { MessagePhase } from "./phases/message-phase"; import { MessagePhase } from "#app/phases/message-phase";
import { MovePhase } from "./phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
import { NewBiomeEncounterPhase } from "./phases/new-biome-encounter-phase"; import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
import { NextEncounterPhase } from "./phases/next-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import { PokemonAnimPhase } from "./phases/pokemon-anim-phase"; import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
import { QuietFormChangePhase } from "./phases/quiet-form-change-phase"; import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
import { ReturnPhase } from "./phases/return-phase"; import { ReturnPhase } from "#app/phases/return-phase";
import { SelectBiomePhase } from "./phases/select-biome-phase"; import { SelectBiomePhase } from "#app/phases/select-biome-phase";
import { ShowTrainerPhase } from "./phases/show-trainer-phase"; import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
import { SummonPhase } from "./phases/summon-phase"; import { SummonPhase } from "#app/phases/summon-phase";
import { SwitchPhase } from "./phases/switch-phase"; import { SwitchPhase } from "#app/phases/switch-phase";
import { TitlePhase } from "./phases/title-phase"; import { TitlePhase } from "#app/phases/title-phase";
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase"; import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
import { TurnInitPhase } from "./phases/turn-init-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase";
import { ShopCursorTarget } from "./enums/shop-cursor-target"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter"; import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -94,7 +94,8 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase"; import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { ExpGainsSpeed } from "./enums/exp-gains-speed"; import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -789,7 +790,7 @@ export default class BattleScene extends SceneBase {
} }
getEnemyParty(): EnemyPokemon[] { getEnemyParty(): EnemyPokemon[] {
return this.currentBattle?.enemyParty || []; return this.currentBattle?.enemyParty ?? [];
} }
getEnemyPokemon(): EnemyPokemon | undefined { getEnemyPokemon(): EnemyPokemon | undefined {
@ -889,6 +890,9 @@ export default class BattleScene extends SceneBase {
} }
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource); const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource);
if (Overrides.OPP_FUSION_OVERRIDE) {
pokemon.generateFusionSpecies();
}
overrideModifiers(this, false); overrideModifiers(this, false);
overrideHeldItems(this, pokemon, false); overrideHeldItems(this, pokemon, false);
@ -1386,7 +1390,7 @@ export default class BattleScene extends SceneBase {
case Species.GRENINJA: case Species.GRENINJA:
return Utils.randSeedInt(2); return Utils.randSeedInt(2);
case Species.ZYGARDE: case Species.ZYGARDE:
return Utils.randSeedInt(3); return Utils.randSeedInt(4);
case Species.MINIOR: case Species.MINIOR:
return Utils.randSeedInt(6); return Utils.randSeedInt(6);
case Species.ALCREMIE: case Species.ALCREMIE:
@ -2313,7 +2317,10 @@ export default class BattleScene extends SceneBase {
} }
} }
this.currentPhase?.start(); if (this.currentPhase) {
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
this.currentPhase.start();
}
} }
overridePhase(phase: Phase): boolean { overridePhase(phase: Phase): boolean {
@ -2323,6 +2330,7 @@ export default class BattleScene extends SceneBase {
this.standbyPhase = this.currentPhase; this.standbyPhase = this.currentPhase;
this.currentPhase = phase; this.currentPhase = phase;
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
phase.start(); phase.start();
return true; return true;
@ -2356,17 +2364,6 @@ export default class BattleScene extends SceneBase {
return false; return false;
} }
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority);
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority);
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value);
if (lowerPriorityPhase) {
this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase);
} else {
this.pushPhase(movePhase);
}
}
/** /**
* Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase() * Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase()
* @param phase {@linkcode Phase} the phase to be added * @param phase {@linkcode Phase} the phase to be added
@ -2429,7 +2426,7 @@ export default class BattleScene extends SceneBase {
return Math.floor(moneyValue / 10) * 10; return Math.floor(moneyValue / 10) * 10;
} }
addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise<boolean> { addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean, cost?: number): Promise<boolean> {
if (!modifier) { if (!modifier) {
return Promise.resolve(false); return Promise.resolve(false);
} }
@ -2486,6 +2483,8 @@ export default class BattleScene extends SceneBase {
} }
} else if (modifier instanceof FusePokemonModifier) { } else if (modifier instanceof FusePokemonModifier) {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon); args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} else if (modifier instanceof RememberMoveModifier && !Utils.isNullOrUndefined(cost)) {
args.push(cost);
} }
if (modifier.shouldApply(pokemon, ...args)) { if (modifier.shouldApply(pokemon, ...args)) {
@ -2684,7 +2683,7 @@ export default class BattleScene extends SceneBase {
} }
/** /**
* Removes all modifiers from enemy of PersistentModifier type * Removes all modifiers from enemy pokemon of {@linkcode PersistentModifier} type
*/ */
clearEnemyModifiers(): void { clearEnemyModifiers(): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PersistentModifier); const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PersistentModifier);
@ -2695,10 +2694,11 @@ export default class BattleScene extends SceneBase {
} }
/** /**
* Removes all modifiers from enemy of PokemonHeldItemModifier type * Removes all modifiers from enemy pokemon of {@linkcode PokemonHeldItemModifier} type
* @param pokemon - If specified, only removes held items from that {@linkcode Pokemon}
*/ */
clearEnemyHeldItemModifiers(): void { clearEnemyHeldItemModifiers(pokemon?: Pokemon): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier); const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon));
for (const m of modifiersToRemove) { for (const m of modifiersToRemove) {
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1); this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
} }
@ -3055,7 +3055,7 @@ export default class BattleScene extends SceneBase {
const pId = partyMember.id; const pId = partyMember.id;
const participated = participantIds.has(pId); const participated = participantIds.has(pId);
if (participated && pokemonDefeated) { if (participated && pokemonDefeated) {
partyMember.addFriendship(2); partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE);
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier); const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) { if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) {
machoBraceModifier.stackCount++; machoBraceModifier.stackCount++;
@ -3169,13 +3169,20 @@ export default class BattleScene extends SceneBase {
/** /**
* Loads or generates a mystery encounter * Loads or generates a mystery encounter
* @param encounterType used to load session encounter when restarting game, etc. * @param encounterType used to load session encounter when restarting game, etc.
* @param canBypass optional boolean to indicate that the request is coming from a function that needs to access a Mystery Encounter outside of gameplay requirements
* @returns * @returns
*/ */
getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter { getMysteryEncounter(encounterType?: MysteryEncounterType, canBypass?: boolean): MysteryEncounter {
// Loading override or session encounter // Loading override or session encounter
let encounter: MysteryEncounter | null; let encounter: MysteryEncounter | null;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) { if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
if (canBypass) {
return encounter;
}
} else if (canBypass) {
encounter = allMysteryEncounters[encounterType ?? -1];
return encounter;
} else { } else {
encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null; encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null;
} }

View File

@ -118,6 +118,14 @@ export class Ability implements Localizable {
this.nameAppend += " (N)"; this.nameAppend += " (N)";
return this; return this;
} }
/**
* Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case.
* @returns the ability
*/
edgeCase(): this {
return this;
}
} }
type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>; type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
@ -634,15 +642,15 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr {
* Examples include: Absorb, Draining Kiss, Bitter Blade, etc. * Examples include: Absorb, Draining Kiss, Bitter Blade, etc.
* Also displays a message to show this ability was activated. * Also displays a message to show this ability was activated.
* @param pokemon {@linkcode Pokemon} with this ability * @param pokemon {@linkcode Pokemon} with this ability
* @param passive N/A * @param _passive N/A
* @param attacker {@linkcode Pokemon} that is attacking this Pokemon * @param attacker {@linkcode Pokemon} that is attacking this Pokemon
* @param move {@linkcode PokemonMove} that is being used * @param move {@linkcode PokemonMove} that is being used
* @param hitResult N/A * @param _hitResult N/A
* @args N/A * @param _args N/A
* @returns true if healing should be reversed on a healing move, false otherwise. * @returns true if healing should be reversed on a healing move, false otherwise.
*/ */
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.hasAttr(HitHealAttr)) { if (move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) { if (!simulated) {
pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) }));
} }
@ -669,8 +677,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr {
this.allOthers = allOthers; this.allOthers = allOthers;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) { if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) { if (simulated) {
return true; return true;
} }
@ -707,12 +715,12 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr {
this.selfTarget = selfTarget; this.selfTarget = selfTarget;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate); const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate);
const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1];
const damageReceived = lastAttackReceived?.damage || 0; const damageReceived = lastAttackReceived?.damage || 0;
if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) { if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) { if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages));
} }
@ -734,8 +742,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType; this.tagType = tagType;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) { if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag; const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag;
if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) {
if (!simulated) { if (!simulated) {
@ -758,8 +766,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType; this.tagType = tagType;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) { if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (!pokemon.getTag(this.tagType) && !simulated) { if (!pokemon.getTag(this.tagType) && !simulated) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }));
@ -771,8 +779,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
} }
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) { if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) { if (simulated) {
return true; return true;
} }
@ -787,7 +795,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendTypeChange", { return i18next.t("abilityTriggers:postDefendTypeChange", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName, abilityName,
@ -805,8 +813,8 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
this.terrainType = terrainType; this.terrainType = terrainType;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) { if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) { if (simulated) {
return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined);
} else { } else {
@ -829,8 +837,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
this.effects = effects; this.effects = effects;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status
&& (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon)) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
if (simulated) { if (simulated) {
return attacker.canSetStatus(effect, true, false, pokemon); return attacker.canSetStatus(effect, true, false, pokemon);
@ -869,8 +878,8 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
this.turnCount = turnCount; this.turnCount = turnCount;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) { if (simulated) {
return attacker.canAddTag(this.tagType); return attacker.canAddTag(this.tagType);
} else { } else {
@ -893,7 +902,11 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr {
this.stages = stages; this.stages = stages;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.hitsSubstitute(attacker, pokemon)) {
return false;
}
if (!simulated) { if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages));
} }
@ -901,7 +914,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr {
return true; return true;
} }
getCondition(): AbAttrCondition { override getCondition(): AbAttrCondition {
return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical;
} }
} }
@ -915,8 +928,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
this.damageRatio = damageRatio; this.damageRatio = damageRatio;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)
&& !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon)) {
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio));
return true; return true;
@ -925,7 +939,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendContactDamage", { return i18next.t("abilityTriggers:postDefendContactDamage", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName abilityName
@ -948,8 +962,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
this.turns = turns; this.turns = turns;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) {
if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) {
return false; return false;
} else { } else {
@ -963,24 +977,24 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName });
} }
} }
export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
private weatherType: WeatherType; private weatherType: WeatherType;
protected condition: PokemonDefendCondition | null; protected condition?: PokemonDefendCondition;
constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) { constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) {
super(); super();
this.weatherType = weatherType; this.weatherType = weatherType;
this.condition = condition ?? null; this.condition = condition;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition !== null && !this.condition(pokemon, attacker, move)) { if (this.condition && !this.condition(pokemon, attacker, move) || move.hitsSubstitute(attacker, pokemon)) {
return false; return false;
} }
if (!pokemon.scene.arena.weather?.isImmutable()) { if (!pokemon.scene.arena.weather?.isImmutable()) {
@ -999,8 +1013,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
super(); super();
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)
&& !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) { if (!simulated) {
const tempAbilityId = attacker.getAbility().id; const tempAbilityId = attacker.getAbility().id;
attacker.summonData.ability = pokemon.getAbility().id; attacker.summonData.ability = pokemon.getAbility().id;
@ -1012,7 +1027,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) });
} }
} }
@ -1025,8 +1040,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
this.ability = ability; this.ability = ability;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr)
&& !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) { if (!simulated) {
attacker.summonData.ability = this.ability; attacker.summonData.ability = this.ability;
} }
@ -1037,7 +1053,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendAbilityGive", { return i18next.t("abilityTriggers:postDefendAbilityGive", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName abilityName
@ -1056,8 +1072,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
this.chance = chance; this.chance = chance;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (attacker.getTag(BattlerTagType.DISABLED) === null) { if (attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon)) {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
if (simulated) { if (simulated) {
return true; return true;
@ -1724,17 +1740,17 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
} }
export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition | null; private condition?: PokemonDefendCondition;
constructor(condition?: PokemonDefendCondition) { constructor(condition?: PokemonDefendCondition) {
super(); super();
this.condition = condition ?? null; this.condition = condition;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move)) && !move.hitsSubstitute(attacker, pokemon)) {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable);
if (heldItems.length) { if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
@ -4476,7 +4492,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC
export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
private multiplier: number; private multiplier: number;
private tagType: BattlerTagType; private tagType: BattlerTagType;
private recoilDamageFunc: ((pokemon: Pokemon) => number) | undefined; private recoilDamageFunc?: ((pokemon: Pokemon) => number);
private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string; private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string;
constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) { constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) {
@ -4492,16 +4508,16 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
* Applies the pre-defense ability to the Pokémon. * Applies the pre-defense ability to the Pokémon.
* Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form. * Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form.
* *
* @param {Pokemon} pokemon The Pokémon with the ability. * @param pokemon The Pokémon with the ability.
* @param {boolean} passive n/a * @param _passive n/a
* @param {Pokemon} attacker The attacking Pokémon. * @param attacker The attacking Pokémon.
* @param {PokemonMove} move The move being used. * @param move The move being used.
* @param {Utils.BooleanHolder} cancelled n/a * @param _cancelled n/a
* @param {any[]} args Additional arguments. * @param args Additional arguments.
* @returns {boolean} Whether the immunity was applied. * @returns `true` if the immunity was applied.
*/ */
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) { if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) { if (!simulated) {
(args[0] as Utils.NumberHolder).value = this.multiplier; (args[0] as Utils.NumberHolder).value = this.multiplier;
pokemon.removeTag(this.tagType); pokemon.removeTag(this.tagType);
@ -4517,12 +4533,12 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
/** /**
* Gets the message triggered when the Pokémon avoids damage using the form-changing ability. * Gets the message triggered when the Pokémon avoids damage using the form-changing ability.
* @param {Pokemon} pokemon The Pokémon with the ability. * @param pokemon The Pokémon with the ability.
* @param {string} abilityName The name of the ability. * @param abilityName The name of the ability.
* @param {...any} args n/a * @param _args n/a
* @returns {string} The trigger message. * @returns The trigger message.
*/ */
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return this.triggerMessageFunc(pokemon, abilityName); return this.triggerMessageFunc(pokemon, abilityName);
} }
} }
@ -4623,7 +4639,7 @@ async function applyAbAttrsInternal<TAttr extends AbAttr>(
messages: string[] = [], messages: string[] = [],
) { ) {
for (const passive of [ false, true ]) { for (const passive of [ false, true ]) {
if (!pokemon?.canApplyAbility(passive)) { if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
continue; continue;
} }
@ -4897,8 +4913,7 @@ export function initAbilities() {
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1) .attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1)
.ignorable(), .ignorable(),
new Ability(Abilities.SHIELD_DUST, 3) new Ability(Abilities.SHIELD_DUST, 3)
.attr(IgnoreMoveEffectsAbAttr) .attr(IgnoreMoveEffectsAbAttr),
.partial(),
new Ability(Abilities.OWN_TEMPO, 3) new Ability(Abilities.OWN_TEMPO, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED) .attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
.attr(IntimidateImmunityAbAttr) .attr(IntimidateImmunityAbAttr)
@ -4942,8 +4957,7 @@ export function initAbilities() {
.attr(TypeImmunityStatStageChangeAbAttr, Type.ELECTRIC, Stat.SPATK, 1) .attr(TypeImmunityStatStageChangeAbAttr, Type.ELECTRIC, Stat.SPATK, 1)
.ignorable(), .ignorable(),
new Ability(Abilities.SERENE_GRACE, 3) new Ability(Abilities.SERENE_GRACE, 3)
.attr(MoveEffectChanceMultiplierAbAttr, 2) .attr(MoveEffectChanceMultiplierAbAttr, 2),
.partial(),
new Ability(Abilities.SWIFT_SWIM, 3) new Ability(Abilities.SWIFT_SWIM, 3)
.attr(StatMultiplierAbAttr, Stat.SPD, 2) .attr(StatMultiplierAbAttr, Stat.SPD, 2)
.condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)),
@ -5227,7 +5241,8 @@ export function initAbilities() {
new Ability(Abilities.SHEER_FORCE, 5) new Ability(Abilities.SHEER_FORCE, 5)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096) .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096)
.attr(MoveEffectChanceMultiplierAbAttr, 0) .attr(MoveEffectChanceMultiplierAbAttr, 0)
.partial(), .edgeCase() // Should disable shell bell and Meloetta's relic song transformation
.edgeCase(), // Should disable life orb, eject button, red card, kee/maranga berry if they get implemented
new Ability(Abilities.CONTRARY, 5) new Ability(Abilities.CONTRARY, 5)
.attr(StatStageChangeMultiplierAbAttr, -1) .attr(StatStageChangeMultiplierAbAttr, -1)
.ignorable(), .ignorable(),
@ -5270,7 +5285,7 @@ export function initAbilities() {
/** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */ /** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */
(pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1) (pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1)
) )
.partial(), .edgeCase(), // Cannot recover berries used up by fling or natural gift (unimplemented)
new Ability(Abilities.TELEPATHY, 5) new Ability(Abilities.TELEPATHY, 5)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
.ignorable(), .ignorable(),
@ -5349,7 +5364,7 @@ export function initAbilities() {
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.VICTORY_STAR, 5) new Ability(Abilities.VICTORY_STAR, 5)
.attr(StatMultiplierAbAttr, Stat.ACC, 1.1) .attr(StatMultiplierAbAttr, Stat.ACC, 1.1)
.partial(), .partial(), // Does not boost ally's accuracy
new Ability(Abilities.TURBOBLAZE, 5) new Ability(Abilities.TURBOBLAZE, 5)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(MoveAbilityBypassAbAttr), .attr(MoveAbilityBypassAbAttr),
@ -5460,7 +5475,7 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.bypassFaint() .bypassFaint()
.partial(), .partial(), // Meteor form should protect against status effects and yawn
new Ability(Abilities.STAKEOUT, 7) new Ability(Abilities.STAKEOUT, 7)
.attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2), .attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2),
new Ability(Abilities.WATER_BUBBLE, 7) new Ability(Abilities.WATER_BUBBLE, 7)
@ -5503,7 +5518,8 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
// Add BattlerTagType.DISGUISE if the pokemon is in its disguised form // Add BattlerTagType.DISGUISE if the pokemon is in its disguised form
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
.attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, .attr(FormBlockDamageAbAttr,
(target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE,
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
(pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
@ -5517,19 +5533,21 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.POWER_CONSTRUCT, 7) // TODO: 10% Power Construct Zygarde isn't accounted for yet. If changed, update Zygarde's getSpeciesFormIndex entry accordingly new Ability(Abilities.POWER_CONSTRUCT, 7)
.attr(PostBattleInitFormChangeAbAttr, () => 2) .conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostBattleInitFormChangeAbAttr, () => 2)
.attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2) .conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostBattleInitFormChangeAbAttr, () => 3)
.attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2) .conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2)
.conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2)
.conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "10-complete" ? 5 : 3)
.conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "10-complete" ? 5 : 3)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.bypassFaint() .bypassFaint(),
.partial(), new Ability(Abilities.CORROSION, 7)
new Ability(Abilities.CORROSION, 7) // TODO: Test Corrosion against Magic Bounce once it is implemented
.attr(IgnoreTypeStatusEffectImmunityAbAttr, [ StatusEffect.POISON, StatusEffect.TOXIC ], [ Type.STEEL, Type.POISON ]) .attr(IgnoreTypeStatusEffectImmunityAbAttr, [ StatusEffect.POISON, StatusEffect.TOXIC ], [ Type.STEEL, Type.POISON ])
.partial(), .edgeCase(), // Should interact correctly with magic coat/bounce (not yet implemented), fling with toxic orb (not implemented yet), and synchronize (not fully implemented yet)
new Ability(Abilities.COMATOSE, 7) new Ability(Abilities.COMATOSE, 7)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
@ -5665,7 +5683,8 @@ export function initAbilities() {
.conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0) .conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0)
// When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE // When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW) .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
.attr(FormBlockDamageAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, .attr(FormBlockDamageAbAttr,
(target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE,
(pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName })) (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint() .bypassFaint()
@ -5683,7 +5702,7 @@ export function initAbilities() {
new Ability(Abilities.WANDERING_SPIRIT, 8) new Ability(Abilities.WANDERING_SPIRIT, 8)
.attr(PostDefendAbilitySwapAbAttr) .attr(PostDefendAbilitySwapAbAttr)
.bypassFaint() .bypassFaint()
.partial(), .edgeCase(), // interacts incorrectly with rock head. It's meant to switch abilities before recoil would apply so that a pokemon with rock head would lose rock head first and still take the recoil
new Ability(Abilities.GORILLA_TACTICS, 8) new Ability(Abilities.GORILLA_TACTICS, 8)
.attr(GorillaTacticsAbAttr), .attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8) new Ability(Abilities.NEUTRALIZING_GAS, 8)
@ -5692,7 +5711,7 @@ export function initAbilities() {
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.partial(), .partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed
new Ability(Abilities.PASTEL_VEIL, 8) new Ability(Abilities.PASTEL_VEIL, 8)
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
@ -5797,7 +5816,7 @@ export function initAbilities() {
new Ability(Abilities.GOOD_AS_GOLD, 9) new Ability(Abilities.GOOD_AS_GOLD, 9)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS)
.ignorable() .ignorable()
.partial(), .partial(), // Lots of weird interactions with moves and abilities such as negating status moves that target the field
new Ability(Abilities.VESSEL_OF_RUIN, 9) new Ability(Abilities.VESSEL_OF_RUIN, 9)
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75) .attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) })) .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) }))
@ -5830,7 +5849,7 @@ export function initAbilities() {
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
new Ability(Abilities.SUPREME_OVERLORD, 9) new Ability(Abilities.SUPREME_OVERLORD, 9)
.attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5)) .attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5))
.partial(), .partial(), // Counter resets every wave
new Ability(Abilities.COSTAR, 9) new Ability(Abilities.COSTAR, 9)
.attr(PostSummonCopyAllyStatsAbAttr), .attr(PostSummonCopyAllyStatsAbAttr),
new Ability(Abilities.TOXIC_DEBRIS, 9) new Ability(Abilities.TOXIC_DEBRIS, 9)
@ -5863,25 +5882,25 @@ export function initAbilities() {
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9) new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9) new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9) new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.TERA_SHIFT, 9) new Ability(Abilities.TERA_SHIFT, 9)
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1) .attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)

View File

@ -1,10 +1,10 @@
import { Arena } from "#app/field/arena"; import { Arena } from "#app/field/arena";
import BattleScene from "#app/battle-scene"; import BattleScene from "#app/battle-scene";
import { Type } from "#app/data/type"; import { Type } from "#app/data/type";
import * as Utils from "#app/utils"; import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils";
import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "#app/data/move"; import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "#app/data/move";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import Pokemon, { HitResult, PlayerPokemon, PokemonMove, EnemyPokemon } from "#app/field/pokemon"; import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon";
import { StatusEffect } from "#app/data/status-effect"; import { StatusEffect } from "#app/data/status-effect";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability"; import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
@ -19,6 +19,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
export enum ArenaTagSide { export enum ArenaTagSide {
BOTH, BOTH,
@ -27,22 +28,15 @@ export enum ArenaTagSide {
} }
export abstract class ArenaTag { export abstract class ArenaTag {
public tagType: ArenaTagType; constructor(
public turnCount: integer; public tagType: ArenaTagType,
public sourceMove?: Moves; public turnCount: number,
public sourceId?: integer; public sourceMove?: Moves,
public side: ArenaTagSide; public sourceId?: number,
public side: ArenaTagSide = ArenaTagSide.BOTH
) {}
apply(arena: Arena, simulated: boolean, ...args: unknown[]): boolean {
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) {
this.tagType = tagType;
this.turnCount = turnCount;
this.sourceMove = sourceMove;
this.sourceId = sourceId;
this.side = side;
}
apply(arena: Arena, args: any[]): boolean {
return true; return true;
} }
@ -65,6 +59,44 @@ export abstract class ArenaTag {
? allMoves[this.sourceMove].name ? allMoves[this.sourceMove].name
: null; : null;
} }
/**
* When given a arena tag or json representing one, load the data for it.
* This is meant to be inherited from by any arena tag with custom attributes
* @param {ArenaTag | any} source An arena tag
*/
loadTag(source : ArenaTag | any) : void {
this.turnCount = source.turnCount;
this.sourceMove = source.sourceMove;
this.sourceId = source.sourceId;
this.side = source.side;
}
/**
* Helper function that retrieves the source Pokemon
* @param scene medium to retrieve the source Pokemon
* @returns The source {@linkcode Pokemon} or `null` if none is found
*/
public getSourcePokemon(scene: BattleScene): Pokemon | null {
return this.sourceId ? scene.getPokemonById(this.sourceId) : null;
}
/**
* Helper function that retrieves the Pokemon affected
* @param scene - medium to retrieve the involved Pokemon
* @returns list of PlayerPokemon or EnemyPokemon on the field
*/
public getAffectedPokemon(scene: BattleScene): Pokemon[] {
switch (this.side) {
case ArenaTagSide.PLAYER:
return scene.getPlayerField() ?? [];
case ArenaTagSide.ENEMY:
return scene.getEnemyField() ?? [];
case ArenaTagSide.BOTH:
default:
return scene.getField(true) ?? [];
}
}
} }
/** /**
@ -72,7 +104,7 @@ export abstract class ArenaTag {
* Prevents Pokémon on the opposing side from lowering the stats of the Pokémon in the Mist. * Prevents Pokémon on the opposing side from lowering the stats of the Pokémon in the Mist.
*/ */
export class MistTag extends ArenaTag { export class MistTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side); super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side);
} }
@ -90,10 +122,20 @@ export class MistTag extends ArenaTag {
} }
} }
apply(arena: Arena, args: any[]): boolean { /**
(args[0] as Utils.BooleanHolder).value = true; * Cancels the lowering of stats
* @param arena the {@linkcode Arena} containing this effect
* @param simulated `true` if the effect should be applied quietly
* @param cancelled a {@linkcode BooleanHolder} whose value is set to `true`
* to flag the stat reduction as cancelled
* @returns `true` if a stat reduction was cancelled; `false` otherwise
*/
override apply(arena: Arena, simulated: boolean, cancelled: BooleanHolder): boolean {
cancelled.value = true;
if (!simulated) {
arena.scene.queueMessage(i18next.t("arenaTag:mistApply")); arena.scene.queueMessage(i18next.t("arenaTag:mistApply"));
}
return true; return true;
} }
@ -116,7 +158,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
* @param side - The side (player or enemy) the tag affects. * @param side - The side (player or enemy) the tag affects.
* @param weakenedCategories - The categories of moves that are weakened by this tag. * @param weakenedCategories - The categories of moves that are weakened by this tag.
*/ */
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, weakenedCategories: MoveCategory[]) { constructor(tagType: ArenaTagType, turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide, weakenedCategories: MoveCategory[]) {
super(tagType, turnCount, sourceMove, sourceId, side); super(tagType, turnCount, sourceMove, sourceId, side);
this.weakenedCategories = weakenedCategories; this.weakenedCategories = weakenedCategories;
@ -125,17 +167,15 @@ export class WeakenMoveScreenTag extends ArenaTag {
/** /**
* Applies the weakening effect to the move. * Applies the weakening effect to the move.
* *
* @param arena - The arena where the move is applied. * @param arena the {@linkcode Arena} where the move is applied.
* @param args - The arguments for the move application. * @param simulated n/a
* @param args[0] - The category of the move. * @param moveCategory the attacking move's {@linkcode MoveCategory}.
* @param args[1] - A boolean indicating whether it is a double battle. * @param damageMultiplier A {@linkcode NumberHolder} containing the damage multiplier
* @param args[2] - An object of type `Utils.NumberHolder` that holds the damage multiplier * @returns `true` if the attacking move was weakened; `false` otherwise.
*
* @returns True if the move was weakened, otherwise false.
*/ */
apply(arena: Arena, args: any[]): boolean { override apply(arena: Arena, simulated: boolean, moveCategory: MoveCategory, damageMultiplier: NumberHolder): boolean {
if (this.weakenedCategories.includes((args[0] as MoveCategory))) { if (this.weakenedCategories.includes(moveCategory)) {
(args[2] as Utils.NumberHolder).value = (args[1] as boolean) ? 2732/4096 : 0.5; damageMultiplier.value = arena.scene.currentBattle.double ? 2732 / 4096 : 0.5;
return true; return true;
} }
return false; return false;
@ -147,7 +187,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
* Used by {@linkcode Moves.REFLECT} * Used by {@linkcode Moves.REFLECT}
*/ */
class ReflectTag extends WeakenMoveScreenTag { class ReflectTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [ MoveCategory.PHYSICAL ]); super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [ MoveCategory.PHYSICAL ]);
} }
@ -163,7 +203,7 @@ class ReflectTag extends WeakenMoveScreenTag {
* Used by {@linkcode Moves.LIGHT_SCREEN} * Used by {@linkcode Moves.LIGHT_SCREEN}
*/ */
class LightScreenTag extends WeakenMoveScreenTag { class LightScreenTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [ MoveCategory.SPECIAL ]); super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [ MoveCategory.SPECIAL ]);
} }
@ -179,7 +219,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
* Used by {@linkcode Moves.AURORA_VEIL} * Used by {@linkcode Moves.AURORA_VEIL}
*/ */
class AuroraVeilTag extends WeakenMoveScreenTag { class AuroraVeilTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ]); super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ]);
} }
@ -202,7 +242,7 @@ export class ConditionalProtectTag extends ArenaTag {
/** Does this apply to all moves, including those that ignore other forms of protection? */ /** Does this apply to all moves, including those that ignore other forms of protection? */
protected ignoresBypass: boolean; protected ignoresBypass: boolean;
constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) { constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) {
super(tagType, 1, sourceMove, sourceId, side); super(tagType, 1, sourceMove, sourceId, side);
this.protectConditionFunc = condition; this.protectConditionFunc = condition;
@ -217,39 +257,35 @@ export class ConditionalProtectTag extends ArenaTag {
onRemove(arena: Arena): void { } onRemove(arena: Arena): void { }
/** /**
* apply(): Checks incoming moves against the condition function * Checks incoming moves against the condition function
* and protects the target if conditions are met * and protects the target if conditions are met
* @param arena The arena containing this tag * @param arena the {@linkcode Arena} containing this tag
* @param args\[0\] (Utils.BooleanHolder) Signals if the move is cancelled * @param simulated `true` if the tag is applied quietly; `false` otherwise.
* @param args\[1\] (Pokemon) The Pokemon using the move * @param isProtected a {@linkcode BooleanHolder} used to flag if the move is protected against
* @param args\[2\] (Pokemon) The intended target of the move * @param attacker the attacking {@linkcode Pokemon}
* @param args\[3\] (Moves) The parameters to the condition function * @param defender the defending {@linkcode Pokemon}
* @param args\[4\] (Utils.BooleanHolder) Signals if the applied protection supercedes protection-ignoring effects * @param moveId the {@linkcode Moves | identifier} for the move being used
* @returns * @param ignoresProtectBypass a {@linkcode BooleanHolder} used to flag if a protection effect supercedes effects that ignore protection
* @returns `true` if this tag protected against the attack; `false` otherwise
*/ */
apply(arena: Arena, args: any[]): boolean { override apply(arena: Arena, simulated: boolean, isProtected: BooleanHolder, attacker: Pokemon, defender: Pokemon,
const [ cancelled, user, target, moveId, ignoresBypass ] = args; moveId: Moves, ignoresProtectBypass: BooleanHolder): boolean {
if (cancelled instanceof Utils.BooleanHolder if ((this.side === ArenaTagSide.PLAYER) === defender.isPlayer()
&& user instanceof Pokemon
&& target instanceof Pokemon
&& typeof moveId === "number"
&& ignoresBypass instanceof Utils.BooleanHolder) {
if ((this.side === ArenaTagSide.PLAYER) === target.isPlayer()
&& this.protectConditionFunc(arena, moveId)) { && this.protectConditionFunc(arena, moveId)) {
if (!cancelled.value) { if (!isProtected.value) {
cancelled.value = true; isProtected.value = true;
user.stopMultiHit(target); if (!simulated) {
attacker.stopMultiHit(defender);
new CommonBattleAnim(CommonAnim.PROTECT, target).play(arena.scene); new CommonBattleAnim(CommonAnim.PROTECT, defender).play(arena.scene);
arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(target) })); arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) }));
}
} }
ignoresBypass.value = ignoresBypass.value || this.ignoresBypass; ignoresProtectBypass.value = ignoresProtectBypass.value || this.ignoresBypass;
return true; return true;
} }
}
return false; return false;
} }
} }
@ -264,7 +300,7 @@ export class ConditionalProtectTag extends ArenaTag {
*/ */
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
const priority = new Utils.IntegerHolder(move.priority); const priority = new NumberHolder(move.priority);
const effectPhase = arena.scene.getCurrentPhase(); const effectPhase = arena.scene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase) { if (effectPhase instanceof MoveEffectPhase) {
@ -280,7 +316,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
* Condition: The incoming move has increased priority. * Condition: The incoming move has increased priority.
*/ */
class QuickGuardTag extends ConditionalProtectTag { class QuickGuardTag extends ConditionalProtectTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.QUICK_GUARD, Moves.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc); super(ArenaTagType.QUICK_GUARD, Moves.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc);
} }
} }
@ -311,7 +347,7 @@ const WideGuardConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean =
* can be an ally or enemy. * can be an ally or enemy.
*/ */
class WideGuardTag extends ConditionalProtectTag { class WideGuardTag extends ConditionalProtectTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.WIDE_GUARD, Moves.WIDE_GUARD, sourceId, side, WideGuardConditionFunc); super(ArenaTagType.WIDE_GUARD, Moves.WIDE_GUARD, sourceId, side, WideGuardConditionFunc);
} }
} }
@ -333,7 +369,7 @@ const MatBlockConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean =>
* Condition: The incoming move is a Physical or Special attack move. * Condition: The incoming move is a Physical or Special attack move.
*/ */
class MatBlockTag extends ConditionalProtectTag { class MatBlockTag extends ConditionalProtectTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.MAT_BLOCK, Moves.MAT_BLOCK, sourceId, side, MatBlockConditionFunc); super(ArenaTagType.MAT_BLOCK, Moves.MAT_BLOCK, sourceId, side, MatBlockConditionFunc);
} }
@ -371,7 +407,7 @@ const CraftyShieldConditionFunc: ProtectConditionFunc = (arena, moveId) => {
* not target all Pokemon or sides of the field. * not target all Pokemon or sides of the field.
*/ */
class CraftyShieldTag extends ConditionalProtectTag { class CraftyShieldTag extends ConditionalProtectTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.CRAFTY_SHIELD, Moves.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true); super(ArenaTagType.CRAFTY_SHIELD, Moves.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true);
} }
} }
@ -383,12 +419,12 @@ class CraftyShieldTag extends ConditionalProtectTag {
export class NoCritTag extends ArenaTag { export class NoCritTag extends ArenaTag {
/** /**
* Constructor method for the NoCritTag class * Constructor method for the NoCritTag class
* @param turnCount `integer` the number of turns this effect lasts * @param turnCount `number` the number of turns this effect lasts
* @param sourceMove {@linkcode Moves} the move that created this effect * @param sourceMove {@linkcode Moves} the move that created this effect
* @param sourceId `integer` the ID of the {@linkcode Pokemon} that created this effect * @param sourceId `number` the ID of the {@linkcode Pokemon} that created this effect
* @param side {@linkcode ArenaTagSide} the side to which this effect belongs * @param side {@linkcode ArenaTagSide} the side to which this effect belongs
*/ */
constructor(turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side); super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side);
} }
@ -418,7 +454,7 @@ class WishTag extends ArenaTag {
private triggerMessage: string; private triggerMessage: string;
private healHp: number; private healHp: number;
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.WISH, turnCount, Moves.WISH, sourceId, side); super(ArenaTagType.WISH, turnCount, Moves.WISH, sourceId, side);
} }
@ -428,7 +464,7 @@ class WishTag extends ArenaTag {
if (user) { if (user) {
this.battlerIndex = user.getBattlerIndex(); this.battlerIndex = user.getBattlerIndex();
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
this.healHp = Utils.toDmgValue(user.getMaxHp() / 2); this.healHp = toDmgValue(user.getMaxHp() / 2);
} else { } else {
console.warn("Failed to get source for WishTag onAdd"); console.warn("Failed to get source for WishTag onAdd");
} }
@ -459,18 +495,25 @@ export class WeakenMoveTypeTag extends ArenaTag {
* @param sourceMove - The move that created the tag. * @param sourceMove - The move that created the tag.
* @param sourceId - The ID of the source of the tag. * @param sourceId - The ID of the source of the tag.
*/ */
constructor(tagType: ArenaTagType, turnCount: integer, type: Type, sourceMove: Moves, sourceId: integer) { constructor(tagType: ArenaTagType, turnCount: number, type: Type, sourceMove: Moves, sourceId: number) {
super(tagType, turnCount, sourceMove, sourceId); super(tagType, turnCount, sourceMove, sourceId);
this.weakenedType = type; this.weakenedType = type;
} }
apply(arena: Arena, args: any[]): boolean { /**
if ((args[0] as Type) === this.weakenedType) { * Reduces an attack's power by 0.33x if it matches this tag's weakened type.
(args[1] as Utils.NumberHolder).value *= 0.33; * @param arena n/a
* @param simulated n/a
* @param type the attack's {@linkcode Type}
* @param power a {@linkcode NumberHolder} containing the attack's power
* @returns `true` if the attack's power was reduced; `false` otherwise.
*/
override apply(arena: Arena, simulated: boolean, type: Type, power: NumberHolder): boolean {
if (type === this.weakenedType) {
power.value *= 0.33;
return true; return true;
} }
return false; return false;
} }
} }
@ -480,7 +523,7 @@ export class WeakenMoveTypeTag extends ArenaTag {
* Weakens Electric type moves for a set amount of turns, usually 5. * Weakens Electric type moves for a set amount of turns, usually 5.
*/ */
class MudSportTag extends WeakenMoveTypeTag { class MudSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer, sourceId: integer) { constructor(turnCount: number, sourceId: number) {
super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId); super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId);
} }
@ -498,7 +541,7 @@ class MudSportTag extends WeakenMoveTypeTag {
* Weakens Fire type moves for a set amount of turns, usually 5. * Weakens Fire type moves for a set amount of turns, usually 5.
*/ */
class WaterSportTag extends WeakenMoveTypeTag { class WaterSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer, sourceId: integer) { constructor(turnCount: number, sourceId: number) {
super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId); super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId);
} }
@ -512,15 +555,16 @@ class WaterSportTag extends WeakenMoveTypeTag {
} }
/** /**
* Arena Tag class for the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}. * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Ion_Deluge_(move) | Ion Deluge}
* and the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}.
* Converts Normal-type moves to Electric type for the rest of the turn. * Converts Normal-type moves to Electric type for the rest of the turn.
*/ */
export class PlasmaFistsTag extends ArenaTag { export class IonDelugeTag extends ArenaTag {
constructor() { constructor(sourceMove?: Moves) {
super(ArenaTagType.PLASMA_FISTS, 1, Moves.PLASMA_FISTS); super(ArenaTagType.ION_DELUGE, 1, sourceMove);
} }
/** Queues Plasma Fists' on-add message */ /** Queues an on-add message */
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
} }
@ -530,13 +574,12 @@ export class PlasmaFistsTag extends ArenaTag {
/** /**
* Converts Normal-type moves to Electric type * Converts Normal-type moves to Electric type
* @param arena n/a * @param arena n/a
* @param args * @param simulated n/a
* - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type} * @param moveType a {@linkcode NumberHolder} containing a move's {@linkcode Type}
* @returns `true` if the given move type changed; `false` otherwise. * @returns `true` if the given move type changed; `false` otherwise.
*/ */
apply(arena: Arena, args: any[]): boolean { override apply(arena: Arena, simulated: boolean, moveType: NumberHolder): boolean {
const moveType = args[0]; if (moveType.value === Type.NORMAL) {
if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) {
moveType.value = Type.ELECTRIC; moveType.value = Type.ELECTRIC;
return true; return true;
} }
@ -548,8 +591,8 @@ export class PlasmaFistsTag extends ArenaTag {
* Abstract class to implement arena traps. * Abstract class to implement arena traps.
*/ */
export class ArenaTrapTag extends ArenaTag { export class ArenaTrapTag extends ArenaTag {
public layers: integer; public layers: number;
public maxLayers: integer; public maxLayers: number;
/** /**
* Creates a new instance of the ArenaTrapTag class. * Creates a new instance of the ArenaTrapTag class.
@ -560,7 +603,7 @@ export class ArenaTrapTag extends ArenaTag {
* @param side - The side (player or enemy) the tag affects. * @param side - The side (player or enemy) the tag affects.
* @param maxLayers - The maximum amount of layers this tag can have. * @param maxLayers - The maximum amount of layers this tag can have.
*/ */
constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, maxLayers: integer) { constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, maxLayers: number) {
super(tagType, 0, sourceMove, sourceId, side); super(tagType, 0, sourceMove, sourceId, side);
this.layers = 1; this.layers = 1;
@ -575,22 +618,34 @@ export class ArenaTrapTag extends ArenaTag {
} }
} }
apply(arena: Arena, args: any[]): boolean { /**
const pokemon = args[0] as Pokemon; * Activates the hazard effect onto a Pokemon when it enters the field
* @param arena the {@linkcode Arena} containing this tag
* @param simulated if `true`, only checks if the hazard would activate.
* @param pokemon the {@linkcode Pokemon} triggering this hazard
* @returns `true` if this hazard affects the given Pokemon; `false` otherwise.
*/
override apply(arena: Arena, simulated: boolean, pokemon: Pokemon): boolean {
if (this.sourceId === pokemon.id || (this.side === ArenaTagSide.PLAYER) !== pokemon.isPlayer()) { if (this.sourceId === pokemon.id || (this.side === ArenaTagSide.PLAYER) !== pokemon.isPlayer()) {
return false; return false;
} }
return this.activateTrap(pokemon); return this.activateTrap(pokemon, simulated);
} }
activateTrap(pokemon: Pokemon): boolean { activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
return false; return false;
} }
getMatchupScoreMultiplier(pokemon: Pokemon): number { getMatchupScoreMultiplier(pokemon: Pokemon): number {
return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2); return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2);
} }
loadTag(source: any): void {
super.loadTag(source);
this.layers = source.layers;
this.maxLayers = source.maxLayers;
}
} }
/** /**
@ -599,7 +654,7 @@ export class ArenaTrapTag extends ArenaTag {
* in damage for 1, 2, or 3 layers of Spikes respectively if they are summoned into this trap. * in damage for 1, 2, or 3 layers of Spikes respectively if they are summoned into this trap.
*/ */
class SpikesTag extends ArenaTrapTag { class SpikesTag extends ArenaTrapTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3); super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3);
} }
@ -612,14 +667,18 @@ class SpikesTag extends ArenaTrapTag {
} }
} }
activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
if (pokemon.isGrounded()) { if (pokemon.isGrounded()) {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (simulated) {
return !cancelled.value;
}
if (!cancelled.value) { if (!cancelled.value) {
const damageHpRatio = 1 / (10 - 2 * this.layers); const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
@ -643,7 +702,7 @@ class SpikesTag extends ArenaTrapTag {
class ToxicSpikesTag extends ArenaTrapTag { class ToxicSpikesTag extends ArenaTrapTag {
private neutralized: boolean; private neutralized: boolean;
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, side, 2); super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, side, 2);
this.neutralized = false; this.neutralized = false;
} }
@ -663,8 +722,11 @@ class ToxicSpikesTag extends ArenaTrapTag {
} }
} }
activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
if (pokemon.isGrounded()) { if (pokemon.isGrounded()) {
if (simulated) {
return true;
}
if (pokemon.isOfType(Type.POISON)) { if (pokemon.isOfType(Type.POISON)) {
this.neutralized = true; this.neutralized = true;
if (pokemon.scene.arena.removeTag(this.tagType)) { if (pokemon.scene.arena.removeTag(this.tagType)) {
@ -701,7 +763,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
class DelayedAttackTag extends ArenaTag { class DelayedAttackTag extends ArenaTag {
public targetIndex: BattlerIndex; public targetIndex: BattlerIndex;
constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: integer, targetIndex: BattlerIndex) { constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: number, targetIndex: BattlerIndex) {
super(tagType, 3, sourceMove, sourceId); super(tagType, 3, sourceMove, sourceId);
this.targetIndex = targetIndex; this.targetIndex = targetIndex;
@ -726,7 +788,7 @@ class DelayedAttackTag extends ArenaTag {
* who is summoned into the trap, based on the Rock type's type effectiveness. * who is summoned into the trap, based on the Rock type's type effectiveness.
*/ */
class StealthRockTag extends ArenaTrapTag { class StealthRockTag extends ArenaTrapTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1); super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1);
} }
@ -768,8 +830,8 @@ class StealthRockTag extends ArenaTrapTag {
return damageHpRatio; return damageHpRatio;
} }
activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (cancelled.value) { if (cancelled.value) {
@ -779,12 +841,16 @@ class StealthRockTag extends ArenaTrapTag {
const damageHpRatio = this.getDamageHpRatio(pokemon); const damageHpRatio = this.getDamageHpRatio(pokemon);
if (damageHpRatio) { if (damageHpRatio) {
const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); if (simulated) {
return true;
}
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) { if (pokemon.turnData) {
pokemon.turnData.damageTaken += damage; pokemon.turnData.damageTaken += damage;
} }
return true;
} }
return false; return false;
@ -802,7 +868,7 @@ class StealthRockTag extends ArenaTrapTag {
* to any Pokémon who is summoned into this trap. * to any Pokémon who is summoned into this trap.
*/ */
class StickyWebTag extends ArenaTrapTag { class StickyWebTag extends ArenaTrapTag {
constructor(sourceId: integer, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1); super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1);
} }
@ -814,14 +880,20 @@ class StickyWebTag extends ArenaTrapTag {
} }
} }
activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
if (pokemon.isGrounded()) { if (pokemon.isGrounded()) {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new BooleanHolder(false);
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled); applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
if (simulated) {
return !cancelled.value;
}
if (!cancelled.value) { if (!cancelled.value) {
pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() })); pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
const stages = new Utils.NumberHolder(-1); const stages = new NumberHolder(-1);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value));
return true;
} }
} }
@ -836,12 +908,19 @@ class StickyWebTag extends ArenaTrapTag {
* also reversing the turn order for all Pokémon on the field as well. * also reversing the turn order for all Pokémon on the field as well.
*/ */
export class TrickRoomTag extends ArenaTag { export class TrickRoomTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer) { constructor(turnCount: number, sourceId: number) {
super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId); super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId);
} }
apply(arena: Arena, args: any[]): boolean { /**
const speedReversed = args[0] as Utils.BooleanHolder; * Reverses Speed-based turn order for all Pokemon on the field
* @param arena n/a
* @param simulated n/a
* @param speedReversed a {@linkcode BooleanHolder} used to flag if Speed-based
* turn order should be reversed.
* @returns `true` if turn order is successfully reversed; `false` otherwise
*/
override apply(arena: Arena, simulated: boolean, speedReversed: BooleanHolder): boolean {
speedReversed.value = !speedReversed.value; speedReversed.value = !speedReversed.value;
return true; return true;
} }
@ -864,7 +943,7 @@ export class TrickRoomTag extends ArenaTag {
* {@linkcode Abilities.LEVITATE} for the duration of the arena tag, usually 5 turns. * {@linkcode Abilities.LEVITATE} for the duration of the arena tag, usually 5 turns.
*/ */
export class GravityTag extends ArenaTag { export class GravityTag extends ArenaTag {
constructor(turnCount: integer) { constructor(turnCount: number) {
super(ArenaTagType.GRAVITY, turnCount, Moves.GRAVITY); super(ArenaTagType.GRAVITY, turnCount, Moves.GRAVITY);
} }
@ -872,7 +951,8 @@ export class GravityTag extends ArenaTag {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
arena.scene.getField(true).forEach((pokemon) => { arena.scene.getField(true).forEach((pokemon) => {
if (pokemon !== null) { if (pokemon !== null) {
pokemon.removeTag(BattlerTagType.MAGNET_RISEN); pokemon.removeTag(BattlerTagType.FLOATING);
pokemon.removeTag(BattlerTagType.TELEKINESIS);
} }
}); });
} }
@ -888,7 +968,7 @@ export class GravityTag extends ArenaTag {
* Applies this arena tag for 4 turns (including the turn the move was used). * Applies this arena tag for 4 turns (including the turn the move was used).
*/ */
class TailwindTag extends ArenaTag { class TailwindTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side); super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side);
} }
@ -926,7 +1006,7 @@ class TailwindTag extends ArenaTag {
* Doubles the prize money from trainers and money moves like {@linkcode Moves.PAY_DAY} and {@linkcode Moves.MAKE_IT_RAIN}. * Doubles the prize money from trainers and money moves like {@linkcode Moves.PAY_DAY} and {@linkcode Moves.MAKE_IT_RAIN}.
*/ */
class HappyHourTag extends ArenaTag { class HappyHourTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.HAPPY_HOUR, turnCount, Moves.HAPPY_HOUR, sourceId, side); super(ArenaTagType.HAPPY_HOUR, turnCount, Moves.HAPPY_HOUR, sourceId, side);
} }
@ -940,7 +1020,7 @@ class HappyHourTag extends ArenaTag {
} }
class SafeguardTag extends ArenaTag { class SafeguardTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side); super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side);
} }
@ -953,42 +1033,35 @@ class SafeguardTag extends ArenaTag {
} }
} }
class NoneTag extends ArenaTag {
constructor() {
super(ArenaTagType.NONE, 0);
}
}
/** /**
* This arena tag facilitates the application of the move Imprison * This arena tag facilitates the application of the move Imprison
* Imprison remains in effect as long as the source Pokemon is active and present on the field. * Imprison remains in effect as long as the source Pokemon is active and present on the field.
* Imprison will apply to any opposing Pokemon that switch onto the field as well. * Imprison will apply to any opposing Pokemon that switch onto the field as well.
*/ */
class ImprisonTag extends ArenaTrapTag { class ImprisonTag extends ArenaTrapTag {
private source: Pokemon;
constructor(sourceId: number, side: ArenaTagSide) { constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.IMPRISON, Moves.IMPRISON, sourceId, side, 1); super(ArenaTagType.IMPRISON, Moves.IMPRISON, sourceId, side, 1);
} }
/**
* Helper function that retrieves the Pokemon affected
* @param {BattleScene} scene medium to retrieve the involved Pokemon
* @returns list of PlayerPokemon or EnemyPokemon on the field
*/
private retrieveField(scene: BattleScene): PlayerPokemon[] | EnemyPokemon[] {
if (!this.source.isPlayer()) {
return scene.getPlayerField() ?? [];
}
return scene.getEnemyField() ?? [];
}
/** /**
* This function applies the effects of Imprison to the opposing Pokemon already present on the field. * This function applies the effects of Imprison to the opposing Pokemon already present on the field.
* @param arena * @param arena
*/ */
override onAdd({ scene }: Arena) { override onAdd({ scene }: Arena) {
this.source = scene.getPokemonById(this.sourceId!)!; const source = this.getSourcePokemon(scene);
if (this.source) { if (source) {
const party = this.retrieveField(scene); const party = this.getAffectedPokemon(scene);
party?.forEach((p: PlayerPokemon | EnemyPokemon ) => { party?.forEach((p: Pokemon ) => {
if (p.isAllowedInBattle()) {
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
}
}); });
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", {pokemonNameWithAffix: getPokemonNameWithAffix(this.source)})); scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} }
} }
@ -997,8 +1070,9 @@ class ImprisonTag extends ArenaTrapTag {
* @param _arena * @param _arena
* @returns `true` if the source of the tag is still active on the field | `false` if not * @returns `true` if the source of the tag is still active on the field | `false` if not
*/ */
override lapse(_arena: Arena): boolean { override lapse({ scene }: Arena): boolean {
return this.source.isActive(true); const source = this.getSourcePokemon(scene);
return source ? source.isActive(true) : false;
} }
/** /**
@ -1007,7 +1081,8 @@ class ImprisonTag extends ArenaTrapTag {
* @returns `true` * @returns `true`
*/ */
override activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon): boolean {
if (this.source.isActive(true)) { const source = this.getSourcePokemon(pokemon.scene);
if (source && source.isActive(true) && pokemon.isAllowedInBattle()) {
pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
} }
return true; return true;
@ -1018,14 +1093,97 @@ class ImprisonTag extends ArenaTrapTag {
* @param arena * @param arena
*/ */
override onRemove({ scene }: Arena): void { override onRemove({ scene }: Arena): void {
const party = this.retrieveField(scene); const party = this.getAffectedPokemon(scene);
party?.forEach((p: PlayerPokemon | EnemyPokemon) => { party?.forEach((p: Pokemon) => {
p.removeTag(BattlerTagType.IMPRISON); p.removeTag(BattlerTagType.IMPRISON);
}); });
} }
} }
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { /**
* Arena Tag implementing the "sea of fire" effect from the combination
* of {@link https://bulbapedia.bulbagarden.net/wiki/Fire_Pledge_(move) | Fire Pledge}
* and {@link https://bulbapedia.bulbagarden.net/wiki/Grass_Pledge_(move) | Grass Pledge}.
* Damages all non-Fire-type Pokemon on the given side of the field at the end
* of each turn for 4 turns.
*/
class FireGrassPledgeTag extends ArenaTag {
constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.FIRE_GRASS_PLEDGE, 4, Moves.FIRE_PLEDGE, sourceId, side);
}
override onAdd(arena: Arena): void {
// "A sea of fire enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
}
override lapse(arena: Arena): boolean {
const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER)
? arena.scene.getPlayerField()
: arena.scene.getEnemyField();
field.filter(pokemon => !pokemon.isOfType(Type.FIRE)).forEach(pokemon => {
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
// TODO: Replace this with a proper animation
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM));
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
});
return super.lapse(arena);
}
}
/**
* Arena Tag implementing the "rainbow" effect from the combination
* of {@link https://bulbapedia.bulbagarden.net/wiki/Water_Pledge_(move) | Water Pledge}
* and {@link https://bulbapedia.bulbagarden.net/wiki/Fire_Pledge_(move) | Fire Pledge}.
* Doubles the secondary effect chance of moves from Pokemon on the
* given side of the field for 4 turns.
*/
class WaterFirePledgeTag extends ArenaTag {
constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.WATER_FIRE_PLEDGE, 4, Moves.WATER_PLEDGE, sourceId, side);
}
override onAdd(arena: Arena): void {
// "A rainbow appeared in the sky on your/the opposing team's side!"
arena.scene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
}
/**
* Doubles the chance for the given move's secondary effect(s) to trigger
* @param arena the {@linkcode Arena} containing this tag
* @param simulated n/a
* @param moveChance a {@linkcode NumberHolder} containing
* the move's current effect chance
* @returns `true` if the move's effect chance was doubled (currently always `true`)
*/
override apply(arena: Arena, simulated: boolean, moveChance: NumberHolder): boolean {
moveChance.value *= 2;
return true;
}
}
/**
* Arena Tag implementing the "swamp" effect from the combination
* of {@link https://bulbapedia.bulbagarden.net/wiki/Grass_Pledge_(move) | Grass Pledge}
* and {@link https://bulbapedia.bulbagarden.net/wiki/Water_Pledge_(move) | Water Pledge}.
* Quarters the Speed of Pokemon on the given side of the field for 4 turns.
*/
class GrassWaterPledgeTag extends ArenaTag {
constructor(sourceId: number, side: ArenaTagSide) {
super(ArenaTagType.GRASS_WATER_PLEDGE, 4, Moves.GRASS_PLEDGE, sourceId, side);
}
override onAdd(arena: Arena): void {
// "A swamp enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
}
}
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
switch (tagType) { switch (tagType) {
case ArenaTagType.MIST: case ArenaTagType.MIST:
return new MistTag(turnCount, sourceId, side); return new MistTag(turnCount, sourceId, side);
@ -1043,8 +1201,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new MudSportTag(turnCount, sourceId); return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT: case ArenaTagType.WATER_SPORT:
return new WaterSportTag(turnCount, sourceId); return new WaterSportTag(turnCount, sourceId);
case ArenaTagType.PLASMA_FISTS: case ArenaTagType.ION_DELUGE:
return new PlasmaFistsTag(); return new IonDelugeTag(sourceMove);
case ArenaTagType.SPIKES: case ArenaTagType.SPIKES:
return new SpikesTag(sourceId, side); return new SpikesTag(sourceId, side);
case ArenaTagType.TOXIC_SPIKES: case ArenaTagType.TOXIC_SPIKES:
@ -1076,7 +1234,26 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new SafeguardTag(turnCount, sourceId, side); return new SafeguardTag(turnCount, sourceId, side);
case ArenaTagType.IMPRISON: case ArenaTagType.IMPRISON:
return new ImprisonTag(sourceId, side); return new ImprisonTag(sourceId, side);
case ArenaTagType.FIRE_GRASS_PLEDGE:
return new FireGrassPledgeTag(sourceId, side);
case ArenaTagType.WATER_FIRE_PLEDGE:
return new WaterFirePledgeTag(sourceId, side);
case ArenaTagType.GRASS_WATER_PLEDGE:
return new GrassWaterPledgeTag(sourceId, side);
default: default:
return null; return null;
} }
} }
/**
* When given a battler tag or json representing one, creates an actual ArenaTag object with the same data.
* @param {ArenaTag | any} source An arena tag
* @return {ArenaTag} The valid arena tag
*/
export function loadArenaTag(source: ArenaTag | any): ArenaTag {
const tag = getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.targetIndex, source.side)
?? new NoneTag();
tag.loadTag(source);
return tag;
}

View File

@ -0,0 +1,603 @@
import { Species } from "#enums/species";
import { EggTier } from "#enums/egg-type";
/**
* Map of all starters and their respective {@linkcode EggTier}, which determines the type of egg the starter hatches from.
*/
export const speciesEggTiers = {
[Species.BULBASAUR]: EggTier.COMMON,
[Species.CHARMANDER]: EggTier.COMMON,
[Species.SQUIRTLE]: EggTier.COMMON,
[Species.CATERPIE]: EggTier.COMMON,
[Species.WEEDLE]: EggTier.COMMON,
[Species.PIDGEY]: EggTier.COMMON,
[Species.RATTATA]: EggTier.COMMON,
[Species.SPEAROW]: EggTier.COMMON,
[Species.EKANS]: EggTier.COMMON,
[Species.PIKACHU]: EggTier.COMMON,
[Species.SANDSHREW]: EggTier.COMMON,
[Species.NIDORAN_F]: EggTier.COMMON,
[Species.NIDORAN_M]: EggTier.COMMON,
[Species.CLEFAIRY]: EggTier.COMMON,
[Species.VULPIX]: EggTier.COMMON,
[Species.JIGGLYPUFF]: EggTier.COMMON,
[Species.ZUBAT]: EggTier.COMMON,
[Species.ODDISH]: EggTier.COMMON,
[Species.PARAS]: EggTier.COMMON,
[Species.VENONAT]: EggTier.COMMON,
[Species.DIGLETT]: EggTier.COMMON,
[Species.MEOWTH]: EggTier.COMMON,
[Species.PSYDUCK]: EggTier.COMMON,
[Species.MANKEY]: EggTier.RARE,
[Species.GROWLITHE]: EggTier.RARE,
[Species.POLIWAG]: EggTier.COMMON,
[Species.ABRA]: EggTier.RARE,
[Species.MACHOP]: EggTier.COMMON,
[Species.BELLSPROUT]: EggTier.COMMON,
[Species.TENTACOOL]: EggTier.COMMON,
[Species.GEODUDE]: EggTier.COMMON,
[Species.PONYTA]: EggTier.COMMON,
[Species.SLOWPOKE]: EggTier.COMMON,
[Species.MAGNEMITE]: EggTier.RARE,
[Species.FARFETCHD]: EggTier.COMMON,
[Species.DODUO]: EggTier.COMMON,
[Species.SEEL]: EggTier.COMMON,
[Species.GRIMER]: EggTier.COMMON,
[Species.SHELLDER]: EggTier.RARE,
[Species.GASTLY]: EggTier.RARE,
[Species.ONIX]: EggTier.COMMON,
[Species.DROWZEE]: EggTier.COMMON,
[Species.KRABBY]: EggTier.COMMON,
[Species.VOLTORB]: EggTier.COMMON,
[Species.EXEGGCUTE]: EggTier.COMMON,
[Species.CUBONE]: EggTier.COMMON,
[Species.HITMONLEE]: EggTier.RARE,
[Species.HITMONCHAN]: EggTier.RARE,
[Species.LICKITUNG]: EggTier.COMMON,
[Species.KOFFING]: EggTier.COMMON,
[Species.RHYHORN]: EggTier.COMMON,
[Species.CHANSEY]: EggTier.COMMON,
[Species.TANGELA]: EggTier.COMMON,
[Species.KANGASKHAN]: EggTier.RARE,
[Species.HORSEA]: EggTier.COMMON,
[Species.GOLDEEN]: EggTier.COMMON,
[Species.STARYU]: EggTier.COMMON,
[Species.MR_MIME]: EggTier.COMMON,
[Species.SCYTHER]: EggTier.RARE,
[Species.JYNX]: EggTier.RARE,
[Species.ELECTABUZZ]: EggTier.RARE,
[Species.MAGMAR]: EggTier.RARE,
[Species.PINSIR]: EggTier.RARE,
[Species.TAUROS]: EggTier.RARE,
[Species.MAGIKARP]: EggTier.RARE,
[Species.LAPRAS]: EggTier.RARE,
[Species.DITTO]: EggTier.COMMON,
[Species.EEVEE]: EggTier.COMMON,
[Species.PORYGON]: EggTier.RARE,
[Species.OMANYTE]: EggTier.COMMON,
[Species.KABUTO]: EggTier.COMMON,
[Species.AERODACTYL]: EggTier.RARE,
[Species.SNORLAX]: EggTier.RARE,
[Species.ARTICUNO]: EggTier.EPIC,
[Species.ZAPDOS]: EggTier.EPIC,
[Species.MOLTRES]: EggTier.EPIC,
[Species.DRATINI]: EggTier.RARE,
[Species.MEWTWO]: EggTier.LEGENDARY,
[Species.MEW]: EggTier.EPIC,
[Species.CHIKORITA]: EggTier.COMMON,
[Species.CYNDAQUIL]: EggTier.COMMON,
[Species.TOTODILE]: EggTier.COMMON,
[Species.SENTRET]: EggTier.COMMON,
[Species.HOOTHOOT]: EggTier.COMMON,
[Species.LEDYBA]: EggTier.COMMON,
[Species.SPINARAK]: EggTier.COMMON,
[Species.CHINCHOU]: EggTier.COMMON,
[Species.PICHU]: EggTier.COMMON,
[Species.CLEFFA]: EggTier.COMMON,
[Species.IGGLYBUFF]: EggTier.COMMON,
[Species.TOGEPI]: EggTier.COMMON,
[Species.NATU]: EggTier.COMMON,
[Species.MAREEP]: EggTier.COMMON,
[Species.MARILL]: EggTier.RARE,
[Species.SUDOWOODO]: EggTier.COMMON,
[Species.HOPPIP]: EggTier.COMMON,
[Species.AIPOM]: EggTier.COMMON,
[Species.SUNKERN]: EggTier.COMMON,
[Species.YANMA]: EggTier.COMMON,
[Species.WOOPER]: EggTier.COMMON,
[Species.MURKROW]: EggTier.COMMON,
[Species.MISDREAVUS]: EggTier.COMMON,
[Species.UNOWN]: EggTier.COMMON,
[Species.WOBBUFFET]: EggTier.COMMON,
[Species.GIRAFARIG]: EggTier.COMMON,
[Species.PINECO]: EggTier.COMMON,
[Species.DUNSPARCE]: EggTier.COMMON,
[Species.GLIGAR]: EggTier.COMMON,
[Species.SNUBBULL]: EggTier.COMMON,
[Species.QWILFISH]: EggTier.COMMON,
[Species.SHUCKLE]: EggTier.COMMON,
[Species.HERACROSS]: EggTier.RARE,
[Species.SNEASEL]: EggTier.RARE,
[Species.TEDDIURSA]: EggTier.RARE,
[Species.SLUGMA]: EggTier.COMMON,
[Species.SWINUB]: EggTier.COMMON,
[Species.CORSOLA]: EggTier.COMMON,
[Species.REMORAID]: EggTier.COMMON,
[Species.DELIBIRD]: EggTier.COMMON,
[Species.MANTINE]: EggTier.COMMON,
[Species.SKARMORY]: EggTier.RARE,
[Species.HOUNDOUR]: EggTier.COMMON,
[Species.PHANPY]: EggTier.COMMON,
[Species.STANTLER]: EggTier.COMMON,
[Species.SMEARGLE]: EggTier.COMMON,
[Species.TYROGUE]: EggTier.COMMON,
[Species.SMOOCHUM]: EggTier.COMMON,
[Species.ELEKID]: EggTier.COMMON,
[Species.MAGBY]: EggTier.COMMON,
[Species.MILTANK]: EggTier.RARE,
[Species.RAIKOU]: EggTier.EPIC,
[Species.ENTEI]: EggTier.EPIC,
[Species.SUICUNE]: EggTier.EPIC,
[Species.LARVITAR]: EggTier.RARE,
[Species.LUGIA]: EggTier.LEGENDARY,
[Species.HO_OH]: EggTier.LEGENDARY,
[Species.CELEBI]: EggTier.EPIC,
[Species.TREECKO]: EggTier.COMMON,
[Species.TORCHIC]: EggTier.RARE,
[Species.MUDKIP]: EggTier.COMMON,
[Species.POOCHYENA]: EggTier.COMMON,
[Species.ZIGZAGOON]: EggTier.COMMON,
[Species.WURMPLE]: EggTier.COMMON,
[Species.LOTAD]: EggTier.COMMON,
[Species.SEEDOT]: EggTier.COMMON,
[Species.TAILLOW]: EggTier.COMMON,
[Species.WINGULL]: EggTier.COMMON,
[Species.RALTS]: EggTier.COMMON,
[Species.SURSKIT]: EggTier.COMMON,
[Species.SHROOMISH]: EggTier.COMMON,
[Species.SLAKOTH]: EggTier.RARE,
[Species.NINCADA]: EggTier.RARE,
[Species.WHISMUR]: EggTier.COMMON,
[Species.MAKUHITA]: EggTier.COMMON,
[Species.AZURILL]: EggTier.RARE,
[Species.NOSEPASS]: EggTier.COMMON,
[Species.SKITTY]: EggTier.COMMON,
[Species.SABLEYE]: EggTier.COMMON,
[Species.MAWILE]: EggTier.COMMON,
[Species.ARON]: EggTier.COMMON,
[Species.MEDITITE]: EggTier.COMMON,
[Species.ELECTRIKE]: EggTier.COMMON,
[Species.PLUSLE]: EggTier.COMMON,
[Species.MINUN]: EggTier.COMMON,
[Species.VOLBEAT]: EggTier.COMMON,
[Species.ILLUMISE]: EggTier.COMMON,
[Species.ROSELIA]: EggTier.COMMON,
[Species.GULPIN]: EggTier.COMMON,
[Species.CARVANHA]: EggTier.COMMON,
[Species.WAILMER]: EggTier.COMMON,
[Species.NUMEL]: EggTier.COMMON,
[Species.TORKOAL]: EggTier.COMMON,
[Species.SPOINK]: EggTier.COMMON,
[Species.SPINDA]: EggTier.COMMON,
[Species.TRAPINCH]: EggTier.COMMON,
[Species.CACNEA]: EggTier.COMMON,
[Species.SWABLU]: EggTier.COMMON,
[Species.ZANGOOSE]: EggTier.RARE,
[Species.SEVIPER]: EggTier.COMMON,
[Species.LUNATONE]: EggTier.COMMON,
[Species.SOLROCK]: EggTier.COMMON,
[Species.BARBOACH]: EggTier.COMMON,
[Species.CORPHISH]: EggTier.COMMON,
[Species.BALTOY]: EggTier.COMMON,
[Species.LILEEP]: EggTier.COMMON,
[Species.ANORITH]: EggTier.COMMON,
[Species.FEEBAS]: EggTier.RARE,
[Species.CASTFORM]: EggTier.COMMON,
[Species.KECLEON]: EggTier.COMMON,
[Species.SHUPPET]: EggTier.COMMON,
[Species.DUSKULL]: EggTier.COMMON,
[Species.TROPIUS]: EggTier.COMMON,
[Species.CHIMECHO]: EggTier.COMMON,
[Species.ABSOL]: EggTier.RARE,
[Species.WYNAUT]: EggTier.COMMON,
[Species.SNORUNT]: EggTier.COMMON,
[Species.SPHEAL]: EggTier.COMMON,
[Species.CLAMPERL]: EggTier.COMMON,
[Species.RELICANTH]: EggTier.COMMON,
[Species.LUVDISC]: EggTier.COMMON,
[Species.BAGON]: EggTier.RARE,
[Species.BELDUM]: EggTier.RARE,
[Species.REGIROCK]: EggTier.EPIC,
[Species.REGICE]: EggTier.EPIC,
[Species.REGISTEEL]: EggTier.EPIC,
[Species.LATIAS]: EggTier.EPIC,
[Species.LATIOS]: EggTier.EPIC,
[Species.KYOGRE]: EggTier.LEGENDARY,
[Species.GROUDON]: EggTier.LEGENDARY,
[Species.RAYQUAZA]: EggTier.LEGENDARY,
[Species.JIRACHI]: EggTier.EPIC,
[Species.DEOXYS]: EggTier.EPIC,
[Species.TURTWIG]: EggTier.COMMON,
[Species.CHIMCHAR]: EggTier.COMMON,
[Species.PIPLUP]: EggTier.COMMON,
[Species.STARLY]: EggTier.COMMON,
[Species.BIDOOF]: EggTier.COMMON,
[Species.KRICKETOT]: EggTier.COMMON,
[Species.SHINX]: EggTier.COMMON,
[Species.BUDEW]: EggTier.COMMON,
[Species.CRANIDOS]: EggTier.COMMON,
[Species.SHIELDON]: EggTier.COMMON,
[Species.BURMY]: EggTier.COMMON,
[Species.COMBEE]: EggTier.COMMON,
[Species.PACHIRISU]: EggTier.COMMON,
[Species.BUIZEL]: EggTier.COMMON,
[Species.CHERUBI]: EggTier.COMMON,
[Species.SHELLOS]: EggTier.COMMON,
[Species.DRIFLOON]: EggTier.COMMON,
[Species.BUNEARY]: EggTier.COMMON,
[Species.GLAMEOW]: EggTier.COMMON,
[Species.CHINGLING]: EggTier.COMMON,
[Species.STUNKY]: EggTier.COMMON,
[Species.BRONZOR]: EggTier.COMMON,
[Species.BONSLY]: EggTier.COMMON,
[Species.MIME_JR]: EggTier.COMMON,
[Species.HAPPINY]: EggTier.COMMON,
[Species.CHATOT]: EggTier.COMMON,
[Species.SPIRITOMB]: EggTier.RARE,
[Species.GIBLE]: EggTier.RARE,
[Species.MUNCHLAX]: EggTier.RARE,
[Species.RIOLU]: EggTier.COMMON,
[Species.HIPPOPOTAS]: EggTier.COMMON,
[Species.SKORUPI]: EggTier.COMMON,
[Species.CROAGUNK]: EggTier.COMMON,
[Species.CARNIVINE]: EggTier.COMMON,
[Species.FINNEON]: EggTier.COMMON,
[Species.MANTYKE]: EggTier.COMMON,
[Species.SNOVER]: EggTier.COMMON,
[Species.ROTOM]: EggTier.RARE,
[Species.UXIE]: EggTier.EPIC,
[Species.MESPRIT]: EggTier.EPIC,
[Species.AZELF]: EggTier.EPIC,
[Species.DIALGA]: EggTier.LEGENDARY,
[Species.PALKIA]: EggTier.LEGENDARY,
[Species.HEATRAN]: EggTier.EPIC,
[Species.REGIGIGAS]: EggTier.EPIC,
[Species.GIRATINA]: EggTier.LEGENDARY,
[Species.CRESSELIA]: EggTier.EPIC,
[Species.PHIONE]: EggTier.RARE,
[Species.MANAPHY]: EggTier.EPIC,
[Species.DARKRAI]: EggTier.EPIC,
[Species.SHAYMIN]: EggTier.EPIC,
[Species.ARCEUS]: EggTier.LEGENDARY,
[Species.VICTINI]: EggTier.EPIC,
[Species.SNIVY]: EggTier.COMMON,
[Species.TEPIG]: EggTier.COMMON,
[Species.OSHAWOTT]: EggTier.COMMON,
[Species.PATRAT]: EggTier.COMMON,
[Species.LILLIPUP]: EggTier.COMMON,
[Species.PURRLOIN]: EggTier.COMMON,
[Species.PANSAGE]: EggTier.COMMON,
[Species.PANSEAR]: EggTier.COMMON,
[Species.PANPOUR]: EggTier.COMMON,
[Species.MUNNA]: EggTier.COMMON,
[Species.PIDOVE]: EggTier.COMMON,
[Species.BLITZLE]: EggTier.COMMON,
[Species.ROGGENROLA]: EggTier.COMMON,
[Species.WOOBAT]: EggTier.COMMON,
[Species.DRILBUR]: EggTier.RARE,
[Species.AUDINO]: EggTier.COMMON,
[Species.TIMBURR]: EggTier.RARE,
[Species.TYMPOLE]: EggTier.COMMON,
[Species.THROH]: EggTier.RARE,
[Species.SAWK]: EggTier.RARE,
[Species.SEWADDLE]: EggTier.COMMON,
[Species.VENIPEDE]: EggTier.COMMON,
[Species.COTTONEE]: EggTier.COMMON,
[Species.PETILIL]: EggTier.COMMON,
[Species.BASCULIN]: EggTier.RARE,
[Species.SANDILE]: EggTier.RARE,
[Species.DARUMAKA]: EggTier.RARE,
[Species.MARACTUS]: EggTier.COMMON,
[Species.DWEBBLE]: EggTier.COMMON,
[Species.SCRAGGY]: EggTier.COMMON,
[Species.SIGILYPH]: EggTier.RARE,
[Species.YAMASK]: EggTier.COMMON,
[Species.TIRTOUGA]: EggTier.COMMON,
[Species.ARCHEN]: EggTier.COMMON,
[Species.TRUBBISH]: EggTier.COMMON,
[Species.ZORUA]: EggTier.COMMON,
[Species.MINCCINO]: EggTier.COMMON,
[Species.GOTHITA]: EggTier.COMMON,
[Species.SOLOSIS]: EggTier.COMMON,
[Species.DUCKLETT]: EggTier.COMMON,
[Species.VANILLITE]: EggTier.COMMON,
[Species.DEERLING]: EggTier.COMMON,
[Species.EMOLGA]: EggTier.COMMON,
[Species.KARRABLAST]: EggTier.COMMON,
[Species.FOONGUS]: EggTier.COMMON,
[Species.FRILLISH]: EggTier.COMMON,
[Species.ALOMOMOLA]: EggTier.RARE,
[Species.JOLTIK]: EggTier.COMMON,
[Species.FERROSEED]: EggTier.COMMON,
[Species.KLINK]: EggTier.COMMON,
[Species.TYNAMO]: EggTier.COMMON,
[Species.ELGYEM]: EggTier.COMMON,
[Species.LITWICK]: EggTier.COMMON,
[Species.AXEW]: EggTier.RARE,
[Species.CUBCHOO]: EggTier.COMMON,
[Species.CRYOGONAL]: EggTier.RARE,
[Species.SHELMET]: EggTier.COMMON,
[Species.STUNFISK]: EggTier.COMMON,
[Species.MIENFOO]: EggTier.COMMON,
[Species.DRUDDIGON]: EggTier.RARE,
[Species.GOLETT]: EggTier.COMMON,
[Species.PAWNIARD]: EggTier.RARE,
[Species.BOUFFALANT]: EggTier.RARE,
[Species.RUFFLET]: EggTier.COMMON,
[Species.VULLABY]: EggTier.COMMON,
[Species.HEATMOR]: EggTier.COMMON,
[Species.DURANT]: EggTier.RARE,
[Species.DEINO]: EggTier.RARE,
[Species.LARVESTA]: EggTier.RARE,
[Species.COBALION]: EggTier.EPIC,
[Species.TERRAKION]: EggTier.EPIC,
[Species.VIRIZION]: EggTier.EPIC,
[Species.TORNADUS]: EggTier.EPIC,
[Species.THUNDURUS]: EggTier.EPIC,
[Species.RESHIRAM]: EggTier.LEGENDARY,
[Species.ZEKROM]: EggTier.LEGENDARY,
[Species.LANDORUS]: EggTier.EPIC,
[Species.KYUREM]: EggTier.LEGENDARY,
[Species.KELDEO]: EggTier.EPIC,
[Species.MELOETTA]: EggTier.EPIC,
[Species.GENESECT]: EggTier.EPIC,
[Species.CHESPIN]: EggTier.COMMON,
[Species.FENNEKIN]: EggTier.COMMON,
[Species.FROAKIE]: EggTier.RARE,
[Species.BUNNELBY]: EggTier.COMMON,
[Species.FLETCHLING]: EggTier.COMMON,
[Species.SCATTERBUG]: EggTier.COMMON,
[Species.LITLEO]: EggTier.COMMON,
[Species.FLABEBE]: EggTier.COMMON,
[Species.SKIDDO]: EggTier.COMMON,
[Species.PANCHAM]: EggTier.COMMON,
[Species.FURFROU]: EggTier.COMMON,
[Species.ESPURR]: EggTier.COMMON,
[Species.HONEDGE]: EggTier.RARE,
[Species.SPRITZEE]: EggTier.COMMON,
[Species.SWIRLIX]: EggTier.COMMON,
[Species.INKAY]: EggTier.COMMON,
[Species.BINACLE]: EggTier.COMMON,
[Species.SKRELP]: EggTier.COMMON,
[Species.CLAUNCHER]: EggTier.COMMON,
[Species.HELIOPTILE]: EggTier.COMMON,
[Species.TYRUNT]: EggTier.COMMON,
[Species.AMAURA]: EggTier.COMMON,
[Species.HAWLUCHA]: EggTier.RARE,
[Species.DEDENNE]: EggTier.COMMON,
[Species.CARBINK]: EggTier.COMMON,
[Species.GOOMY]: EggTier.RARE,
[Species.KLEFKI]: EggTier.COMMON,
[Species.PHANTUMP]: EggTier.COMMON,
[Species.PUMPKABOO]: EggTier.COMMON,
[Species.BERGMITE]: EggTier.COMMON,
[Species.NOIBAT]: EggTier.COMMON,
[Species.XERNEAS]: EggTier.LEGENDARY,
[Species.YVELTAL]: EggTier.LEGENDARY,
[Species.ZYGARDE]: EggTier.LEGENDARY,
[Species.DIANCIE]: EggTier.EPIC,
[Species.HOOPA]: EggTier.EPIC,
[Species.VOLCANION]: EggTier.EPIC,
[Species.ETERNAL_FLOETTE]: EggTier.RARE,
[Species.ROWLET]: EggTier.COMMON,
[Species.LITTEN]: EggTier.COMMON,
[Species.POPPLIO]: EggTier.RARE,
[Species.PIKIPEK]: EggTier.COMMON,
[Species.YUNGOOS]: EggTier.COMMON,
[Species.GRUBBIN]: EggTier.COMMON,
[Species.CRABRAWLER]: EggTier.COMMON,
[Species.ORICORIO]: EggTier.COMMON,
[Species.CUTIEFLY]: EggTier.COMMON,
[Species.ROCKRUFF]: EggTier.COMMON,
[Species.WISHIWASHI]: EggTier.COMMON,
[Species.MAREANIE]: EggTier.COMMON,
[Species.MUDBRAY]: EggTier.COMMON,
[Species.DEWPIDER]: EggTier.COMMON,
[Species.FOMANTIS]: EggTier.COMMON,
[Species.MORELULL]: EggTier.COMMON,
[Species.SALANDIT]: EggTier.COMMON,
[Species.STUFFUL]: EggTier.COMMON,
[Species.BOUNSWEET]: EggTier.COMMON,
[Species.COMFEY]: EggTier.RARE,
[Species.ORANGURU]: EggTier.RARE,
[Species.PASSIMIAN]: EggTier.RARE,
[Species.WIMPOD]: EggTier.COMMON,
[Species.SANDYGAST]: EggTier.COMMON,
[Species.PYUKUMUKU]: EggTier.COMMON,
[Species.TYPE_NULL]: EggTier.RARE,
[Species.MINIOR]: EggTier.RARE,
[Species.KOMALA]: EggTier.COMMON,
[Species.TURTONATOR]: EggTier.RARE,
[Species.TOGEDEMARU]: EggTier.COMMON,
[Species.MIMIKYU]: EggTier.RARE,
[Species.BRUXISH]: EggTier.RARE,
[Species.DRAMPA]: EggTier.RARE,
[Species.DHELMISE]: EggTier.RARE,
[Species.JANGMO_O]: EggTier.RARE,
[Species.TAPU_KOKO]: EggTier.EPIC,
[Species.TAPU_LELE]: EggTier.EPIC,
[Species.TAPU_BULU]: EggTier.EPIC,
[Species.TAPU_FINI]: EggTier.EPIC,
[Species.COSMOG]: EggTier.EPIC,
[Species.NIHILEGO]: EggTier.EPIC,
[Species.BUZZWOLE]: EggTier.EPIC,
[Species.PHEROMOSA]: EggTier.EPIC,
[Species.XURKITREE]: EggTier.EPIC,
[Species.CELESTEELA]: EggTier.EPIC,
[Species.KARTANA]: EggTier.EPIC,
[Species.GUZZLORD]: EggTier.EPIC,
[Species.NECROZMA]: EggTier.LEGENDARY,
[Species.MAGEARNA]: EggTier.EPIC,
[Species.MARSHADOW]: EggTier.EPIC,
[Species.POIPOLE]: EggTier.EPIC,
[Species.STAKATAKA]: EggTier.EPIC,
[Species.BLACEPHALON]: EggTier.EPIC,
[Species.ZERAORA]: EggTier.EPIC,
[Species.MELTAN]: EggTier.EPIC,
[Species.ALOLA_RATTATA]: EggTier.COMMON,
[Species.ALOLA_SANDSHREW]: EggTier.COMMON,
[Species.ALOLA_VULPIX]: EggTier.COMMON,
[Species.ALOLA_DIGLETT]: EggTier.COMMON,
[Species.ALOLA_MEOWTH]: EggTier.COMMON,
[Species.ALOLA_GEODUDE]: EggTier.COMMON,
[Species.ALOLA_GRIMER]: EggTier.COMMON,
[Species.GROOKEY]: EggTier.COMMON,
[Species.SCORBUNNY]: EggTier.RARE,
[Species.SOBBLE]: EggTier.COMMON,
[Species.SKWOVET]: EggTier.COMMON,
[Species.ROOKIDEE]: EggTier.COMMON,
[Species.BLIPBUG]: EggTier.COMMON,
[Species.NICKIT]: EggTier.COMMON,
[Species.GOSSIFLEUR]: EggTier.COMMON,
[Species.WOOLOO]: EggTier.COMMON,
[Species.CHEWTLE]: EggTier.COMMON,
[Species.YAMPER]: EggTier.COMMON,
[Species.ROLYCOLY]: EggTier.COMMON,
[Species.APPLIN]: EggTier.COMMON,
[Species.SILICOBRA]: EggTier.COMMON,
[Species.CRAMORANT]: EggTier.COMMON,
[Species.ARROKUDA]: EggTier.COMMON,
[Species.TOXEL]: EggTier.COMMON,
[Species.SIZZLIPEDE]: EggTier.COMMON,
[Species.CLOBBOPUS]: EggTier.COMMON,
[Species.SINISTEA]: EggTier.COMMON,
[Species.HATENNA]: EggTier.COMMON,
[Species.IMPIDIMP]: EggTier.COMMON,
[Species.MILCERY]: EggTier.COMMON,
[Species.FALINKS]: EggTier.RARE,
[Species.PINCURCHIN]: EggTier.COMMON,
[Species.SNOM]: EggTier.COMMON,
[Species.STONJOURNER]: EggTier.COMMON,
[Species.EISCUE]: EggTier.COMMON,
[Species.INDEEDEE]: EggTier.RARE,
[Species.MORPEKO]: EggTier.COMMON,
[Species.CUFANT]: EggTier.COMMON,
[Species.DRACOZOLT]: EggTier.RARE,
[Species.ARCTOZOLT]: EggTier.RARE,
[Species.DRACOVISH]: EggTier.RARE,
[Species.ARCTOVISH]: EggTier.RARE,
[Species.DURALUDON]: EggTier.RARE,
[Species.DREEPY]: EggTier.RARE,
[Species.ZACIAN]: EggTier.LEGENDARY,
[Species.ZAMAZENTA]: EggTier.LEGENDARY,
[Species.ETERNATUS]: EggTier.COMMON,
[Species.KUBFU]: EggTier.EPIC,
[Species.ZARUDE]: EggTier.EPIC,
[Species.REGIELEKI]: EggTier.EPIC,
[Species.REGIDRAGO]: EggTier.EPIC,
[Species.GLASTRIER]: EggTier.EPIC,
[Species.SPECTRIER]: EggTier.EPIC,
[Species.CALYREX]: EggTier.LEGENDARY,
[Species.GALAR_MEOWTH]: EggTier.COMMON,
[Species.GALAR_PONYTA]: EggTier.COMMON,
[Species.GALAR_SLOWPOKE]: EggTier.COMMON,
[Species.GALAR_FARFETCHD]: EggTier.COMMON,
[Species.GALAR_CORSOLA]: EggTier.COMMON,
[Species.GALAR_ZIGZAGOON]: EggTier.COMMON,
[Species.GALAR_DARUMAKA]: EggTier.RARE,
[Species.GALAR_YAMASK]: EggTier.COMMON,
[Species.GALAR_STUNFISK]: EggTier.COMMON,
[Species.GALAR_MR_MIME]: EggTier.COMMON,
[Species.GALAR_ARTICUNO]: EggTier.EPIC,
[Species.GALAR_ZAPDOS]: EggTier.EPIC,
[Species.GALAR_MOLTRES]: EggTier.EPIC,
[Species.HISUI_GROWLITHE]: EggTier.RARE,
[Species.HISUI_VOLTORB]: EggTier.COMMON,
[Species.HISUI_QWILFISH]: EggTier.RARE,
[Species.HISUI_SNEASEL]: EggTier.RARE,
[Species.HISUI_ZORUA]: EggTier.COMMON,
[Species.ENAMORUS]: EggTier.EPIC,
[Species.SPRIGATITO]: EggTier.RARE,
[Species.FUECOCO]: EggTier.RARE,
[Species.QUAXLY]: EggTier.RARE,
[Species.LECHONK]: EggTier.COMMON,
[Species.TAROUNTULA]: EggTier.COMMON,
[Species.NYMBLE]: EggTier.COMMON,
[Species.PAWMI]: EggTier.COMMON,
[Species.TANDEMAUS]: EggTier.RARE,
[Species.FIDOUGH]: EggTier.COMMON,
[Species.SMOLIV]: EggTier.COMMON,
[Species.SQUAWKABILLY]: EggTier.COMMON,
[Species.NACLI]: EggTier.RARE,
[Species.CHARCADET]: EggTier.RARE,
[Species.TADBULB]: EggTier.COMMON,
[Species.WATTREL]: EggTier.COMMON,
[Species.MASCHIFF]: EggTier.COMMON,
[Species.SHROODLE]: EggTier.COMMON,
[Species.BRAMBLIN]: EggTier.COMMON,
[Species.TOEDSCOOL]: EggTier.COMMON,
[Species.KLAWF]: EggTier.COMMON,
[Species.CAPSAKID]: EggTier.COMMON,
[Species.RELLOR]: EggTier.COMMON,
[Species.FLITTLE]: EggTier.COMMON,
[Species.TINKATINK]: EggTier.RARE,
[Species.WIGLETT]: EggTier.COMMON,
[Species.BOMBIRDIER]: EggTier.COMMON,
[Species.FINIZEN]: EggTier.COMMON,
[Species.VAROOM]: EggTier.RARE,
[Species.CYCLIZAR]: EggTier.RARE,
[Species.ORTHWORM]: EggTier.RARE,
[Species.GLIMMET]: EggTier.RARE,
[Species.GREAVARD]: EggTier.COMMON,
[Species.FLAMIGO]: EggTier.RARE,
[Species.CETODDLE]: EggTier.COMMON,
[Species.VELUZA]: EggTier.RARE,
[Species.DONDOZO]: EggTier.RARE,
[Species.TATSUGIRI]: EggTier.RARE,
[Species.GREAT_TUSK]: EggTier.EPIC,
[Species.SCREAM_TAIL]: EggTier.EPIC,
[Species.BRUTE_BONNET]: EggTier.EPIC,
[Species.FLUTTER_MANE]: EggTier.EPIC,
[Species.SLITHER_WING]: EggTier.EPIC,
[Species.SANDY_SHOCKS]: EggTier.EPIC,
[Species.IRON_TREADS]: EggTier.EPIC,
[Species.IRON_BUNDLE]: EggTier.EPIC,
[Species.IRON_HANDS]: EggTier.EPIC,
[Species.IRON_JUGULIS]: EggTier.EPIC,
[Species.IRON_MOTH]: EggTier.EPIC,
[Species.IRON_THORNS]: EggTier.EPIC,
[Species.FRIGIBAX]: EggTier.RARE,
[Species.GIMMIGHOUL]: EggTier.RARE,
[Species.WO_CHIEN]: EggTier.EPIC,
[Species.CHIEN_PAO]: EggTier.EPIC,
[Species.TING_LU]: EggTier.EPIC,
[Species.CHI_YU]: EggTier.EPIC,
[Species.ROARING_MOON]: EggTier.EPIC,
[Species.IRON_VALIANT]: EggTier.EPIC,
[Species.KORAIDON]: EggTier.LEGENDARY,
[Species.MIRAIDON]: EggTier.LEGENDARY,
[Species.WALKING_WAKE]: EggTier.EPIC,
[Species.IRON_LEAVES]: EggTier.EPIC,
[Species.POLTCHAGEIST]: EggTier.RARE,
[Species.OKIDOGI]: EggTier.EPIC,
[Species.MUNKIDORI]: EggTier.EPIC,
[Species.FEZANDIPITI]: EggTier.EPIC,
[Species.OGERPON]: EggTier.EPIC,
[Species.GOUGING_FIRE]: EggTier.EPIC,
[Species.RAGING_BOLT]: EggTier.EPIC,
[Species.IRON_BOULDER]: EggTier.EPIC,
[Species.IRON_CROWN]: EggTier.EPIC,
[Species.TERAPAGOS]: EggTier.LEGENDARY,
[Species.PECHARUNT]: EggTier.EPIC,
[Species.PALDEA_TAUROS]: EggTier.RARE,
[Species.PALDEA_WOOPER]: EggTier.COMMON,
[Species.BLOODMOON_URSALUNA]: EggTier.EPIC,
};

View File

@ -2,6 +2,12 @@ import { Species } from "#enums/species";
export const POKERUS_STARTER_COUNT = 5; export const POKERUS_STARTER_COUNT = 5;
// #region Friendship constants
export const CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER = 2;
export const FRIENDSHIP_GAIN_FROM_BATTLE = 2;
export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 5;
export const FRIENDSHIP_LOSS_FROM_FAINT = 10;
/** /**
* Function to get the cumulative friendship threshold at which a candy is earned * Function to get the cumulative friendship threshold at which a candy is earned
* @param starterCost The cost of the starter, found in {@linkcode speciesStarterCosts} * @param starterCost The cost of the starter, found in {@linkcode speciesStarterCosts}

View File

@ -1107,12 +1107,7 @@ export const tmSpecies: TmSpecies = {
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLETCHLING, Species.FLETCHLING,
@ -2878,12 +2873,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLETCHLING, Species.FLETCHLING,
@ -3910,7 +3900,6 @@ export const tmSpecies: TmSpecies = {
Species.YAMPER, Species.YAMPER,
Species.BOLTUND, Species.BOLTUND,
Species.ZAMAZENTA, Species.ZAMAZENTA,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.GLASTRIER, Species.GLASTRIER,
Species.WYRDEER, Species.WYRDEER,
@ -3940,6 +3929,10 @@ export const tmSpecies: TmSpecies = {
Species.ALOLA_NINETALES, Species.ALOLA_NINETALES,
Species.ALOLA_PERSIAN, Species.ALOLA_PERSIAN,
Species.ALOLA_GOLEM, Species.ALOLA_GOLEM,
[
Species.URSHIFU,
"single-strike",
],
Species.HISUI_GROWLITHE, Species.HISUI_GROWLITHE,
Species.HISUI_ARCANINE, Species.HISUI_ARCANINE,
Species.HISUI_TYPHLOSION, Species.HISUI_TYPHLOSION,
@ -6987,14 +6980,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -8301,7 +8287,6 @@ export const tmSpecies: TmSpecies = {
[ [
Species.WORMADAM, Species.WORMADAM,
"sandy", "sandy",
"trash",
], ],
Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSHREW,
Species.ALOLA_SANDSLASH, Species.ALOLA_SANDSLASH,
@ -8612,12 +8597,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.LITLEO, Species.LITLEO,
@ -9406,14 +9386,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -9766,14 +9739,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.VIVILLON, Species.VIVILLON,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.ESPURR, Species.ESPURR,
Species.MEOWSTIC, Species.MEOWSTIC,
@ -11147,14 +11113,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -13657,12 +13616,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.FLETCHLING, Species.FLETCHLING,
Species.FLETCHINDER, Species.FLETCHINDER,
Species.TALONFLAME, Species.TALONFLAME,
@ -15326,14 +15280,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -16934,14 +16881,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -18483,14 +18423,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -20250,14 +20183,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -21583,12 +21509,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.LITLEO, Species.LITLEO,
@ -22516,7 +22437,6 @@ export const tmSpecies: TmSpecies = {
[ [
Species.WORMADAM, Species.WORMADAM,
"sandy", "sandy",
"trash",
], ],
Species.ALOLA_DIGLETT, Species.ALOLA_DIGLETT,
Species.ALOLA_DUGTRIO, Species.ALOLA_DUGTRIO,
@ -22691,14 +22611,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.VIVILLON, Species.VIVILLON,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -23402,12 +23315,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLETCHLING, Species.FLETCHLING,
@ -24134,12 +24042,7 @@ export const tmSpecies: TmSpecies = {
Species.KELDEO, Species.KELDEO,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.PANCHAM, Species.PANCHAM,
Species.PANGORO, Species.PANGORO,
Species.HONEDGE, Species.HONEDGE,
@ -24842,14 +24745,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -25712,14 +25608,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -26695,14 +26584,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -27845,14 +27727,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -28911,14 +28786,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -29514,14 +29382,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.VIVILLON, Species.VIVILLON,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.ESPURR, Species.ESPURR,
Species.MEOWSTIC, Species.MEOWSTIC,
@ -31408,14 +31269,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -32327,14 +32181,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -33037,14 +32884,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -33471,7 +33311,6 @@ export const tmSpecies: TmSpecies = {
Species.ARCTOVISH, Species.ARCTOVISH,
Species.ZACIAN, Species.ZACIAN,
Species.ZAMAZENTA, Species.ZAMAZENTA,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.REGIDRAGO, Species.REGIDRAGO,
Species.GLASTRIER, Species.GLASTRIER,
@ -33522,6 +33361,10 @@ export const tmSpecies: TmSpecies = {
Species.ALOLA_MUK, Species.ALOLA_MUK,
Species.GALAR_MEOWTH, Species.GALAR_MEOWTH,
Species.GALAR_STUNFISK, Species.GALAR_STUNFISK,
[
Species.URSHIFU,
"single-strike",
],
[ [
Species.CALYREX, Species.CALYREX,
"ice", "ice",
@ -36644,14 +36487,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -37389,14 +37225,7 @@ export const tmSpecies: TmSpecies = {
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -38266,23 +38095,11 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -39323,12 +39140,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESPIN, Species.CHESPIN,
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.SKIDDO, Species.SKIDDO,
@ -40356,7 +40168,10 @@ export const tmSpecies: TmSpecies = {
Species.FENNEKIN, Species.FENNEKIN,
Species.BRAIXEN, Species.BRAIXEN,
Species.DELPHOX, Species.DELPHOX,
[
Species.MEOWSTIC, Species.MEOWSTIC,
"male",
],
Species.KLEFKI, Species.KLEFKI,
Species.PHANTUMP, Species.PHANTUMP,
Species.TREVENANT, Species.TREVENANT,
@ -41805,7 +41620,6 @@ export const tmSpecies: TmSpecies = {
[ [
Species.WORMADAM, Species.WORMADAM,
"sandy", "sandy",
"trash",
], ],
Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSHREW,
Species.ALOLA_SANDSLASH, Species.ALOLA_SANDSLASH,
@ -43701,12 +43515,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.SKIDDO, Species.SKIDDO,
@ -44160,14 +43969,7 @@ export const tmSpecies: TmSpecies = {
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -44377,14 +44179,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.VIVILLON, Species.VIVILLON,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.ESPURR, Species.ESPURR,
Species.MEOWSTIC, Species.MEOWSTIC,
@ -45256,6 +45051,10 @@ export const tmSpecies: TmSpecies = {
Species.IRON_CROWN, Species.IRON_CROWN,
Species.TERAPAGOS, Species.TERAPAGOS,
Species.ALOLA_EXEGGUTOR, Species.ALOLA_EXEGGUTOR,
[
Species.INDEEDEE,
"male",
],
], ],
[Moves.GYRO_BALL]: [ [Moves.GYRO_BALL]: [
Species.SQUIRTLE, Species.SQUIRTLE,
@ -47501,12 +47300,7 @@ export const tmSpecies: TmSpecies = {
Species.ACCELGOR, Species.ACCELGOR,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.SKRELP, Species.SKRELP,
Species.DRAGALGE, Species.DRAGALGE,
Species.MAREANIE, Species.MAREANIE,
@ -48143,7 +47937,6 @@ export const tmSpecies: TmSpecies = {
Species.RUNERIGUS, Species.RUNERIGUS,
Species.MORPEKO, Species.MORPEKO,
Species.DURALUDON, Species.DURALUDON,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.SPECTRIER, Species.SPECTRIER,
Species.OVERQWIL, Species.OVERQWIL,
@ -48179,6 +47972,10 @@ export const tmSpecies: TmSpecies = {
Species.GALAR_WEEZING, Species.GALAR_WEEZING,
Species.GALAR_MOLTRES, Species.GALAR_MOLTRES,
Species.GALAR_YAMASK, Species.GALAR_YAMASK,
[
Species.URSHIFU,
"single-strike",
],
[ [
Species.CALYREX, Species.CALYREX,
"shadow", "shadow",
@ -48456,14 +48253,7 @@ export const tmSpecies: TmSpecies = {
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -49622,7 +49412,10 @@ export const tmSpecies: TmSpecies = {
Species.TORTERRA, Species.TORTERRA,
Species.BUDEW, Species.BUDEW,
Species.ROSERADE, Species.ROSERADE,
[
Species.WORMADAM, Species.WORMADAM,
"plant",
],
Species.MOTHIM, Species.MOTHIM,
Species.CHERUBI, Species.CHERUBI,
Species.CHERRIM, Species.CHERRIM,
@ -49698,14 +49491,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.VIVILLON, Species.VIVILLON,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -52635,7 +52421,10 @@ export const tmSpecies: TmSpecies = {
Species.TORTERRA, Species.TORTERRA,
Species.BUDEW, Species.BUDEW,
Species.ROSERADE, Species.ROSERADE,
[
Species.WORMADAM, Species.WORMADAM,
"plant",
],
Species.SNOVER, Species.SNOVER,
Species.ABOMASNOW, Species.ABOMASNOW,
Species.TANGROWTH, Species.TANGROWTH,
@ -53751,7 +53540,10 @@ export const tmSpecies: TmSpecies = {
Species.BIBAREL, Species.BIBAREL,
Species.BUDEW, Species.BUDEW,
Species.ROSERADE, Species.ROSERADE,
[
Species.WORMADAM, Species.WORMADAM,
"plant",
],
Species.PACHIRISU, Species.PACHIRISU,
Species.CHERUBI, Species.CHERUBI,
Species.CHERRIM, Species.CHERRIM,
@ -53863,14 +53655,7 @@ export const tmSpecies: TmSpecies = {
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -55590,12 +55375,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESPIN, Species.CHESPIN,
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.PANCHAM, Species.PANCHAM,
Species.PANGORO, Species.PANGORO,
Species.HELIOPTILE, Species.HELIOPTILE,
@ -55877,7 +55657,6 @@ export const tmSpecies: TmSpecies = {
Species.MR_RIME, Species.MR_RIME,
Species.MORPEKO, Species.MORPEKO,
Species.DURALUDON, Species.DURALUDON,
Species.URSHIFU,
Species.SPECTRIER, Species.SPECTRIER,
Species.MEOWSCARADA, Species.MEOWSCARADA,
Species.SQUAWKABILLY, Species.SQUAWKABILLY,
@ -55918,6 +55697,10 @@ export const tmSpecies: TmSpecies = {
Species.GALAR_MOLTRES, Species.GALAR_MOLTRES,
Species.GALAR_SLOWKING, Species.GALAR_SLOWKING,
Species.GALAR_STUNFISK, Species.GALAR_STUNFISK,
[
Species.URSHIFU,
"single-strike",
],
[ [
Species.CALYREX, Species.CALYREX,
"shadow", "shadow",
@ -56577,14 +56360,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -57019,14 +56795,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.PANCHAM, Species.PANCHAM,
Species.PANGORO, Species.PANGORO,
@ -57354,14 +57123,7 @@ export const tmSpecies: TmSpecies = {
Species.BRAIXEN, Species.BRAIXEN,
Species.DELPHOX, Species.DELPHOX,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.ESPURR, Species.ESPURR,
Species.MEOWSTIC, Species.MEOWSTIC,
@ -58997,7 +58759,6 @@ export const tmSpecies: TmSpecies = {
[ [
Species.WORMADAM, Species.WORMADAM,
"sandy", "sandy",
"trash",
], ],
Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSHREW,
Species.ALOLA_SANDSLASH, Species.ALOLA_SANDSLASH,
@ -60070,7 +59831,6 @@ export const tmSpecies: TmSpecies = {
Species.DURALUDON, Species.DURALUDON,
Species.ZACIAN, Species.ZACIAN,
Species.ZAMAZENTA, Species.ZAMAZENTA,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.GLASTRIER, Species.GLASTRIER,
Species.SPECTRIER, Species.SPECTRIER,
@ -60116,6 +59876,10 @@ export const tmSpecies: TmSpecies = {
Species.HISUI_ZOROARK, Species.HISUI_ZOROARK,
Species.HISUI_BRAVIARY, Species.HISUI_BRAVIARY,
Species.BLOODMOON_URSALUNA, Species.BLOODMOON_URSALUNA,
[
Species.URSHIFU,
"single-strike",
],
], ],
[Moves.PHANTOM_FORCE]: [ [Moves.PHANTOM_FORCE]: [
Species.HAUNTER, Species.HAUNTER,
@ -60446,14 +60210,7 @@ export const tmSpecies: TmSpecies = {
Species.QUILLADIN, Species.QUILLADIN,
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -60529,16 +60286,12 @@ export const tmSpecies: TmSpecies = {
Species.WHIMSICOTT, Species.WHIMSICOTT,
Species.ALOMOMOLA, Species.ALOMOMOLA,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
[
Species.MEOWSTIC, Species.MEOWSTIC,
"male",
],
Species.SPRITZEE, Species.SPRITZEE,
Species.AROMATISSE, Species.AROMATISSE,
Species.SYLVEON, Species.SYLVEON,
@ -61402,14 +61155,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -61899,14 +61645,7 @@ export const tmSpecies: TmSpecies = {
Species.MELOETTA, Species.MELOETTA,
Species.DELPHOX, Species.DELPHOX,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SPRITZEE, Species.SPRITZEE,
Species.AROMATISSE, Species.AROMATISSE,
@ -62617,7 +62356,6 @@ export const tmSpecies: TmSpecies = {
Species.SIRFETCHD, Species.SIRFETCHD,
Species.FALINKS, Species.FALINKS,
Species.PINCURCHIN, Species.PINCURCHIN,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.GLASTRIER, Species.GLASTRIER,
Species.TAROUNTULA, Species.TAROUNTULA,
@ -62647,6 +62385,10 @@ export const tmSpecies: TmSpecies = {
Species.GALAR_ZAPDOS, Species.GALAR_ZAPDOS,
Species.GALAR_CORSOLA, Species.GALAR_CORSOLA,
Species.GALAR_LINOONE, Species.GALAR_LINOONE,
[
Species.URSHIFU,
"single-strike",
],
[ [
Species.CALYREX, Species.CALYREX,
"ice", "ice",
@ -63541,12 +63283,7 @@ export const tmSpecies: TmSpecies = {
Species.KELDEO, Species.KELDEO,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.INKAY, Species.INKAY,
Species.MALAMAR, Species.MALAMAR,
Species.BINACLE, Species.BINACLE,
@ -64755,7 +64492,6 @@ export const tmSpecies: TmSpecies = {
Species.OBSTAGOON, Species.OBSTAGOON,
Species.PERRSERKER, Species.PERRSERKER,
Species.MORPEKO, Species.MORPEKO,
Species.URSHIFU,
Species.ZARUDE, Species.ZARUDE,
Species.GLASTRIER, Species.GLASTRIER,
Species.SPECTRIER, Species.SPECTRIER,
@ -64803,6 +64539,10 @@ export const tmSpecies: TmSpecies = {
Species.GALAR_LINOONE, Species.GALAR_LINOONE,
Species.GALAR_DARMANITAN, Species.GALAR_DARMANITAN,
Species.GALAR_STUNFISK, Species.GALAR_STUNFISK,
[
Species.URSHIFU,
"single-strike",
],
[ [
Species.CALYREX, Species.CALYREX,
"ice", "ice",
@ -65933,12 +65673,7 @@ export const tmSpecies: TmSpecies = {
Species.DELPHOX, Species.DELPHOX,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.BUNNELBY, Species.BUNNELBY,
Species.DIGGERSBY, Species.DIGGERSBY,
Species.FLETCHLING, Species.FLETCHLING,
@ -65950,14 +65685,7 @@ export const tmSpecies: TmSpecies = {
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
[
Species.FLOETTE, Species.FLOETTE,
"red",
"yellow",
"orange",
"blue",
"white",
],
Species.FLORGES, Species.FLORGES,
Species.SKIDDO, Species.SKIDDO,
Species.GOGOAT, Species.GOGOAT,
@ -66291,6 +66019,13 @@ export const tmSpecies: TmSpecies = {
Species.MUNKIDORI, Species.MUNKIDORI,
Species.FEZANDIPITI, Species.FEZANDIPITI,
Species.OGERPON, Species.OGERPON,
Species.ARCHALUDON,
Species.HYDRAPPLE,
Species.GOUGING_FIRE,
Species.RAGING_BOLT,
Species.IRON_BOULDER,
Species.IRON_CROWN,
Species.PECHARUNT,
Species.GALAR_MEOWTH, Species.GALAR_MEOWTH,
Species.GALAR_SLOWPOKE, Species.GALAR_SLOWPOKE,
Species.GALAR_SLOWBRO, Species.GALAR_SLOWBRO,
@ -66429,16 +66164,8 @@ export const tmSpecies: TmSpecies = {
Species.PIPLUP, Species.PIPLUP,
Species.PRINPLUP, Species.PRINPLUP,
Species.EMPOLEON, Species.EMPOLEON,
[
Species.SHELLOS, Species.SHELLOS,
"east",
"west",
],
[
Species.GASTRODON, Species.GASTRODON,
"east",
"west",
],
Species.MISMAGIUS, Species.MISMAGIUS,
Species.HAPPINY, Species.HAPPINY,
Species.SNOVER, Species.SNOVER,
@ -66456,25 +66183,11 @@ export const tmSpecies: TmSpecies = {
Species.CUBCHOO, Species.CUBCHOO,
Species.BEARTIC, Species.BEARTIC,
Species.CRYOGONAL, Species.CRYOGONAL,
[
Species.TORNADUS, Species.TORNADUS,
"incarnate",
"therian",
],
[
Species.KYUREM, Species.KYUREM,
"",
"black",
"white",
],
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.SKRELP, Species.SKRELP,
Species.DRAGALGE, Species.DRAGALGE,
Species.BERGMITE, Species.BERGMITE,
@ -66482,11 +66195,7 @@ export const tmSpecies: TmSpecies = {
Species.DIANCIE, Species.DIANCIE,
Species.PRIMARINA, Species.PRIMARINA,
Species.CRABOMINABLE, Species.CRABOMINABLE,
[
Species.MAGEARNA, Species.MAGEARNA,
"",
"original",
],
Species.INTELEON, Species.INTELEON,
Species.FROSMOTH, Species.FROSMOTH,
Species.EISCUE, Species.EISCUE,
@ -66779,12 +66488,7 @@ export const tmSpecies: TmSpecies = {
Species.CHESNAUGHT, Species.CHESNAUGHT,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.LITLEO, Species.LITLEO,
Species.PYROAR, Species.PYROAR,
Species.FLABEBE, Species.FLABEBE,
@ -67007,23 +66711,14 @@ export const tmSpecies: TmSpecies = {
Species.WEAVILE, Species.WEAVILE,
Species.GLACEON, Species.GLACEON,
Species.FROSLASS, Species.FROSLASS,
[
Species.PALKIA, Species.PALKIA,
"",
"origin",
],
Species.PHIONE, Species.PHIONE,
Species.MANAPHY, Species.MANAPHY,
Species.ARCEUS, Species.ARCEUS,
Species.OSHAWOTT, Species.OSHAWOTT,
Species.DEWOTT, Species.DEWOTT,
Species.SAMUROTT, Species.SAMUROTT,
[
Species.BASCULIN, Species.BASCULIN,
"red-striped",
"blue-striped",
"white-striped",
],
Species.MINCCINO, Species.MINCCINO,
Species.CINCCINO, Species.CINCCINO,
Species.DUCKLETT, Species.DUCKLETT,
@ -67036,12 +66731,7 @@ export const tmSpecies: TmSpecies = {
Species.KELDEO, Species.KELDEO,
Species.FROAKIE, Species.FROAKIE,
Species.FROGADIER, Species.FROGADIER,
[
Species.GRENINJA, Species.GRENINJA,
"",
"battle-bond",
"ash",
],
Species.FLABEBE, Species.FLABEBE,
Species.FLOETTE, Species.FLOETTE,
Species.FLORGES, Species.FLORGES,
@ -67304,6 +66994,10 @@ export const tmSpecies: TmSpecies = {
Species.FEZANDIPITI, Species.FEZANDIPITI,
Species.ALOLA_RAICHU, Species.ALOLA_RAICHU,
Species.ETERNAL_FLOETTE, Species.ETERNAL_FLOETTE,
[
Species.INDEEDEE,
"female",
],
], ],
[Moves.TEMPER_FLARE]: [ [Moves.TEMPER_FLARE]: [
Species.CHARMANDER, Species.CHARMANDER,

View File

@ -23,6 +23,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { StatStageChangePhase, StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase, StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; import { PokemonAnimType } from "#app/enums/pokemon-anim-type";
import BattleScene from "#app/battle-scene";
export enum BattlerTagLapseType { export enum BattlerTagLapseType {
FAINT, FAINT,
@ -90,6 +91,15 @@ export class BattlerTag {
this.sourceMove = source.sourceMove; this.sourceMove = source.sourceMove;
this.sourceId = source.sourceId; this.sourceId = source.sourceId;
} }
/**
* Helper function that retrieves the source Pokemon object
* @param scene medium to retrieve the source Pokemon
* @returns The source {@linkcode Pokemon} or `null` if none is found
*/
public getSourcePokemon(scene: BattleScene): Pokemon | null {
return this.sourceId ? scene.getPokemonById(this.sourceId) : null;
}
} }
export interface WeatherBattlerTag { export interface WeatherBattlerTag {
@ -120,7 +130,7 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
const phase = pokemon.scene.getCurrentPhase() as MovePhase; const phase = pokemon.scene.getCurrentPhase() as MovePhase;
const move = phase.move; const move = phase.move;
if (this.isMoveRestricted(move.moveId)) { if (this.isMoveRestricted(move.moveId, pokemon)) {
if (this.interruptedText(pokemon, move.moveId)) { if (this.interruptedText(pokemon, move.moveId)) {
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId)); pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
} }
@ -136,10 +146,11 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
/** /**
* Gets whether this tag is restricting a move. * Gets whether this tag is restricting a move.
* *
* @param {Moves} move {@linkcode Moves} ID to check restriction for. * @param move - {@linkcode Moves} ID to check restriction for.
* @returns {boolean} `true` if the move is restricted by this tag, otherwise `false`. * @param user - The {@linkcode Pokemon} involved
* @returns `true` if the move is restricted by this tag, otherwise `false`.
*/ */
abstract isMoveRestricted(move: Moves): boolean; public abstract isMoveRestricted(move: Moves, user?: Pokemon): boolean;
/** /**
* Checks if this tag is restricting a move based on a user's decisions during the target selection phase * Checks if this tag is restricting a move based on a user's decisions during the target selection phase
@ -327,6 +338,16 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false); pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
} }
/**
* Loads the Gorilla Tactics Battler Tag along with its unique class variable moveId
* @override
* @param source Gorilla Tactics' {@linkcode BattlerTag} information
*/
public override loadTag(source: BattlerTag | any): void {
super.loadTag(source);
this.moveId = source.moveId;
}
/** /**
* *
* @override * @override
@ -1376,7 +1397,7 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag {
const effectPhase = pokemon.scene.getCurrentPhase(); const effectPhase = pokemon.scene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) {
const attacker = effectPhase.getPokemon(); const attacker = effectPhase.getPokemon();
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ this.stat ], this.levels));
} }
} }
@ -1713,7 +1734,12 @@ export class TypeImmuneTag extends BattlerTag {
} }
} }
export class MagnetRisenTag extends TypeImmuneTag { /**
* Battler Tag that lifts the affected Pokemon into the air and provides immunity to Ground type moves.
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Magnet_Rise_(move) | Moves.MAGNET_RISE}
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS}
*/
export class FloatingTag extends TypeImmuneTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) { constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, sourceMove, Type.GROUND, 5); super(tagType, sourceMove, Type.GROUND, 5);
} }
@ -1721,15 +1747,19 @@ export class MagnetRisenTag extends TypeImmuneTag {
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
if (this.sourceMove === Moves.MAGNET_RISE) {
pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} }
}
onRemove(pokemon: Pokemon): void { onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon); super.onRemove(pokemon);
if (this.sourceMove === Moves.MAGNET_RISE) {
pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} }
} }
}
export class TypeBoostTag extends BattlerTag { export class TypeBoostTag extends BattlerTag {
public boostedType: Type; public boostedType: Type;
@ -2139,6 +2169,10 @@ export class GulpMissileTag extends BattlerTag {
return false; return false;
} }
if (moveEffectPhase.move.getMove().hitsSubstitute(attacker, pokemon)) {
return true;
}
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
@ -2310,6 +2344,21 @@ export class TarShotTag extends BattlerTag {
} }
} }
/**
* Battler Tag implementing the type-changing effect of {@link https://bulbapedia.bulbagarden.net/wiki/Electrify_(move) | Electrify}.
* While this tag is in effect, the afflicted Pokemon's moves are changed to Electric type.
*/
export class ElectrifiedTag extends BattlerTag {
constructor() {
super(BattlerTagType.ELECTRIFIED, BattlerTagLapseType.TURN_END, 1, Moves.ELECTRIFY);
}
override onAdd(pokemon: Pokemon): void {
// "{pokemonNameWithAffix}'s moves have been electrified!"
pokemon.scene.queueMessage(i18next.t("battlerTags:electrifiedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
}
/** /**
* Battler Tag that keeps track of how many times the user has Autotomized * Battler Tag that keeps track of how many times the user has Autotomized
* Each count of Autotomization reduces the weight by 100kg * Each count of Autotomization reduces the weight by 100kg
@ -2482,8 +2531,6 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
* Torment does not interrupt the move if the move is performed consecutively in the same turn and right after Torment is applied * Torment does not interrupt the move if the move is performed consecutively in the same turn and right after Torment is applied
*/ */
export class TormentTag extends MoveRestrictionBattlerTag { export class TormentTag extends MoveRestrictionBattlerTag {
private target: Pokemon;
constructor(sourceId: number) { constructor(sourceId: number) {
super(BattlerTagType.TORMENT, BattlerTagLapseType.AFTER_MOVE, 1, Moves.TORMENT, sourceId); super(BattlerTagType.TORMENT, BattlerTagLapseType.AFTER_MOVE, 1, Moves.TORMENT, sourceId);
} }
@ -2495,7 +2542,6 @@ export class TormentTag extends MoveRestrictionBattlerTag {
*/ */
override onAdd(pokemon: Pokemon) { override onAdd(pokemon: Pokemon) {
super.onAdd(pokemon); super.onAdd(pokemon);
this.target = pokemon;
pokemon.scene.queueMessage(i18next.t("battlerTags:tormentOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), 1500); pokemon.scene.queueMessage(i18next.t("battlerTags:tormentOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), 1500);
} }
@ -2514,15 +2560,18 @@ export class TormentTag extends MoveRestrictionBattlerTag {
* @param {Moves} move the move under investigation * @param {Moves} move the move under investigation
* @returns `true` if there is valid consecutive usage | `false` if the moves are different from each other * @returns `true` if there is valid consecutive usage | `false` if the moves are different from each other
*/ */
override isMoveRestricted(move: Moves): boolean { public override isMoveRestricted(move: Moves, user: Pokemon): boolean {
const lastMove = this.target.getLastXMoves(1)[0]; if (!user) {
return false;
}
const lastMove = user.getLastXMoves(1)[0];
if ( !lastMove ) { if ( !lastMove ) {
return false; return false;
} }
// This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY // This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY
// Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts // Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts
const moveObj = allMoves[lastMove.move]; const moveObj = allMoves[lastMove.move];
const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || this.target.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr); const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || user.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr);
const validLastMoveResult = (lastMove.result === MoveResult.SUCCESS) || (lastMove.result === MoveResult.MISS); const validLastMoveResult = (lastMove.result === MoveResult.SUCCESS) || (lastMove.result === MoveResult.MISS);
if (lastMove.move === move && validLastMoveResult && lastMove.move !== Moves.STRUGGLE && !isUnaffected) { if (lastMove.move === move && validLastMoveResult && lastMove.move !== Moves.STRUGGLE && !isUnaffected) {
return true; return true;
@ -2574,37 +2623,39 @@ export class TauntTag extends MoveRestrictionBattlerTag {
* The tag is only removed when the source-user is removed from the field. * The tag is only removed when the source-user is removed from the field.
*/ */
export class ImprisonTag extends MoveRestrictionBattlerTag { export class ImprisonTag extends MoveRestrictionBattlerTag {
private source: Pokemon | null;
constructor(sourceId: number) { constructor(sourceId: number) {
super(BattlerTagType.IMPRISON, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 1, Moves.IMPRISON, sourceId); super(BattlerTagType.IMPRISON, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 1, Moves.IMPRISON, sourceId);
} }
override onAdd(pokemon: Pokemon) {
if (this.sourceId) {
this.source = pokemon.scene.getPokemonById(this.sourceId);
}
}
/** /**
* Checks if the source of Imprison is still active * Checks if the source of Imprison is still active
* @param _pokemon * @override
* @param _lapseType * @param pokemon The pokemon this tag is attached to
* @returns `true` if the source is still active * @returns `true` if the source is still active
*/ */
override lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { public override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
return this.source?.isActive(true) ?? false; const source = this.getSourcePokemon(pokemon.scene);
if (source) {
if (lapseType === BattlerTagLapseType.PRE_MOVE) {
return super.lapse(pokemon, lapseType) && source.isActive(true);
} else {
return source.isActive(true);
}
}
return false;
} }
/** /**
* Checks if the source of the tag has the parameter move in its moveset and that the source is still active * Checks if the source of the tag has the parameter move in its moveset and that the source is still active
* @override
* @param {Moves} move the move under investigation * @param {Moves} move the move under investigation
* @returns `false` if either condition is not met * @returns `false` if either condition is not met
*/ */
override isMoveRestricted(move: Moves): boolean { public override isMoveRestricted(move: Moves, user: Pokemon): boolean {
if (this.source) { const source = this.getSourcePokemon(user.scene);
const sourceMoveset = this.source.getMoveset().map(m => m!.moveId); if (source) {
return sourceMoveset?.includes(move) && this.source.isActive(true); const sourceMoveset = source.getMoveset().map(m => m!.moveId);
return sourceMoveset?.includes(move) && source.isActive(true);
} }
return false; return false;
} }
@ -2621,16 +2672,16 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
/** /**
* Battler Tag that applies the effects of Syrup Bomb to the target Pokemon. * Battler Tag that applies the effects of Syrup Bomb to the target Pokemon.
* For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1. * For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1.
* The tag can also expire by taking the target Pokemon off the field. * The tag can also expire by taking the target Pokemon off the field, or the Pokemon that originally used the move.
*/ */
export class SyrupBombTag extends BattlerTag { export class SyrupBombTag extends BattlerTag {
constructor() { constructor(sourceId: number) {
super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB); super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB, sourceId);
} }
/** /**
* Adds the Syrup Bomb battler tag to the target Pokemon. * Adds the Syrup Bomb battler tag to the target Pokemon.
* @param {Pokemon} pokemon the target Pokemon * @param pokemon - The target {@linkcode Pokemon}
*/ */
override onAdd(pokemon: Pokemon) { override onAdd(pokemon: Pokemon) {
super.onAdd(pokemon); super.onAdd(pokemon);
@ -2639,15 +2690,16 @@ export class SyrupBombTag extends BattlerTag {
/** /**
* Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count * Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count
* @param {Pokemon} pokemon the target Pokemon * @param pokemon - The target {@linkcode Pokemon}
* @param {BattlerTagLapseType} _lapseType * @param _lapseType - N/A
* @returns `true` if the turnCount is still greater than 0 | `false` if the turnCount is 0 or the target Pokemon has been removed from the field * @returns `true` if the `turnCount` is still greater than `0`; `false` if the `turnCount` is `0` or the target or source Pokemon has been removed from the field
*/ */
override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean {
if (!pokemon.isActive(true)) { if (this.sourceId && !pokemon.scene.getPokemonById(this.sourceId)?.isActive(true)) {
return false; return false;
} }
pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // Custom message in lieu of an animation in mainline // Custom message in lieu of an animation in mainline
pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.scene.unshiftPhase(new StatStageChangePhase( pokemon.scene.unshiftPhase(new StatStageChangePhase(
pokemon.scene, pokemon.getBattlerIndex(), true, pokemon.scene, pokemon.getBattlerIndex(), true,
[ Stat.SPD ], -1, true, false, true [ Stat.SPD ], -1, true, false, true
@ -2656,14 +2708,64 @@ export class SyrupBombTag extends BattlerTag {
} }
} }
/**
* Telekinesis raises the target into the air for three turns and causes all moves used against the target (aside from OHKO moves) to hit the target unless the target is in a semi-invulnerable state from Fly/Dig.
* The first effect is provided by {@linkcode FloatingTag}, the accuracy-bypass effect is provided by TelekinesisTag
* The effects of Telekinesis can be baton passed to a teammate. Unlike the mainline games, Telekinesis can be baton-passed to Mega Gengar.
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS}
*/
export class TelekinesisTag extends BattlerTag {
constructor(sourceMove: Moves) {
super(BattlerTagType.TELEKINESIS, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 3, sourceMove, undefined, true);
}
override onAdd(pokemon: Pokemon) {
pokemon.scene.queueMessage(i18next.t("battlerTags:telekinesisOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
}
/**
* Tag that swaps the user's base ATK stat with its base DEF stat.
* @extends BattlerTag
*/
export class PowerTrickTag extends BattlerTag {
constructor(sourceMove: Moves, sourceId: number) {
super(BattlerTagType.POWER_TRICK, BattlerTagLapseType.CUSTOM, 0, sourceMove, sourceId, true);
}
onAdd(pokemon: Pokemon): void {
this.swapStat(pokemon);
pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
onRemove(pokemon: Pokemon): void {
this.swapStat(pokemon);
pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
/**
* Removes the Power Trick tag and reverts any stat changes if the tag is already applied.
* @param {Pokemon} pokemon The {@linkcode Pokemon} that already has the Power Trick tag.
*/
onOverlap(pokemon: Pokemon): void {
pokemon.removeTag(this.tagType);
}
/**
* Swaps the user's base ATK stat with its base DEF stat.
* @param {Pokemon} pokemon The {@linkcode Pokemon} whose stats will be swapped.
*/
swapStat(pokemon: Pokemon): void {
const temp = pokemon.getStat(Stat.ATK, false);
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.DEF, false), false);
pokemon.setStat(Stat.DEF, temp, false);
}
}
/** /**
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
* * @param sourceId - The ID of the pokemon adding the tag
* @param {BattlerTagType} tagType the type of the {@linkcode BattlerTagType}. * @returns The corresponding {@linkcode BattlerTag} object.
* @param turnCount the turn count.
* @param {Moves} sourceMove the source {@linkcode Moves}.
* @param sourceId the source ID.
* @returns {BattlerTag} the corresponding {@linkcode BattlerTag} object.
*/ */
export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag { export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag {
switch (tagType) { switch (tagType) {
@ -2786,8 +2888,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new CursedTag(sourceId); return new CursedTag(sourceId);
case BattlerTagType.CHARGED: case BattlerTagType.CHARGED:
return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true); return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true);
case BattlerTagType.MAGNET_RISEN: case BattlerTagType.FLOATING:
return new MagnetRisenTag(tagType, sourceMove); return new FloatingTag(tagType, sourceMove);
case BattlerTagType.MINIMIZED: case BattlerTagType.MINIMIZED:
return new MinimizeTag(); return new MinimizeTag();
case BattlerTagType.DESTINY_BOND: case BattlerTagType.DESTINY_BOND:
@ -2811,6 +2913,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new GulpMissileTag(tagType, sourceMove); return new GulpMissileTag(tagType, sourceMove);
case BattlerTagType.TAR_SHOT: case BattlerTagType.TAR_SHOT:
return new TarShotTag(); return new TarShotTag();
case BattlerTagType.ELECTRIFIED:
return new ElectrifiedTag();
case BattlerTagType.THROAT_CHOPPED: case BattlerTagType.THROAT_CHOPPED:
return new ThroatChoppedTag(); return new ThroatChoppedTag();
case BattlerTagType.GORILLA_TACTICS: case BattlerTagType.GORILLA_TACTICS:
@ -2830,7 +2934,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
case BattlerTagType.IMPRISON: case BattlerTagType.IMPRISON:
return new ImprisonTag(sourceId); return new ImprisonTag(sourceId);
case BattlerTagType.SYRUP_BOMB: case BattlerTagType.SYRUP_BOMB:
return new SyrupBombTag(); return new SyrupBombTag(sourceId);
case BattlerTagType.TELEKINESIS:
return new TelekinesisTag(sourceMove);
case BattlerTagType.POWER_TRICK:
return new PowerTrickTag(sourceMove, sourceId);
case BattlerTagType.NONE: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -11,6 +11,7 @@ import { EggTier } from "#enums/egg-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { EggSourceType } from "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { MANAPHY_EGG_MANAPHY_RATE, SAME_SPECIES_EGG_HA_RATE, GACHA_EGG_HA_RATE, GACHA_DEFAULT_RARE_EGGMOVE_RATE, SAME_SPECIES_EGG_RARE_EGGMOVE_RATE, GACHA_MOVE_UP_RARE_EGGMOVE_RATE, GACHA_DEFAULT_SHINY_RATE, GACHA_SHINY_UP_SHINY_RATE, SAME_SPECIES_EGG_SHINY_RATE, EGG_PITY_LEGENDARY_THRESHOLD, EGG_PITY_EPIC_THRESHOLD, EGG_PITY_RARE_THRESHOLD, SHINY_VARIANT_CHANCE, SHINY_EPIC_CHANCE, GACHA_DEFAULT_COMMON_EGG_THRESHOLD, GACHA_DEFAULT_RARE_EGG_THRESHOLD, GACHA_DEFAULT_EPIC_EGG_THRESHOLD, GACHA_LEGENDARY_UP_THRESHOLD_OFFSET, HATCH_WAVES_MANAPHY_EGG, HATCH_WAVES_COMMON_EGG, HATCH_WAVES_RARE_EGG, HATCH_WAVES_EPIC_EGG, HATCH_WAVES_LEGENDARY_EGG } from "#app/data/balance/rates"; import { MANAPHY_EGG_MANAPHY_RATE, SAME_SPECIES_EGG_HA_RATE, GACHA_EGG_HA_RATE, GACHA_DEFAULT_RARE_EGGMOVE_RATE, SAME_SPECIES_EGG_RARE_EGGMOVE_RATE, GACHA_MOVE_UP_RARE_EGGMOVE_RATE, GACHA_DEFAULT_SHINY_RATE, GACHA_SHINY_UP_SHINY_RATE, SAME_SPECIES_EGG_SHINY_RATE, EGG_PITY_LEGENDARY_THRESHOLD, EGG_PITY_EPIC_THRESHOLD, EGG_PITY_RARE_THRESHOLD, SHINY_VARIANT_CHANCE, SHINY_EPIC_CHANCE, GACHA_DEFAULT_COMMON_EGG_THRESHOLD, GACHA_DEFAULT_RARE_EGG_THRESHOLD, GACHA_DEFAULT_EPIC_EGG_THRESHOLD, GACHA_LEGENDARY_UP_THRESHOLD_OFFSET, HATCH_WAVES_MANAPHY_EGG, HATCH_WAVES_COMMON_EGG, HATCH_WAVES_RARE_EGG, HATCH_WAVES_EPIC_EGG, HATCH_WAVES_LEGENDARY_EGG } from "#app/data/balance/rates";
import { speciesEggTiers } from "#app/data/balance/species-egg-tiers";
export const EGG_SEED = 1073741824; export const EGG_SEED = 1073741824;
@ -160,7 +161,7 @@ export class Egg {
// Override egg tier and hatchwaves if species was given // Override egg tier and hatchwaves if species was given
if (eggOptions?.species) { if (eggOptions?.species) {
this._tier = this.getEggTierFromSpeciesStarterValue(); this._tier = this.getEggTier();
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves();
} }
// If species has no variant, set variantTier to common. This needs to // If species has no variant, set variantTier to common. This needs to
@ -261,11 +262,11 @@ export class Egg {
return "Manaphy"; return "Manaphy";
} }
switch (this.tier) { switch (this.tier) {
case EggTier.GREAT: case EggTier.RARE:
return i18next.t("egg:greatTier"); return i18next.t("egg:greatTier");
case EggTier.ULTRA: case EggTier.EPIC:
return i18next.t("egg:ultraTier"); return i18next.t("egg:ultraTier");
case EggTier.MASTER: case EggTier.LEGENDARY:
return i18next.t("egg:masterTier"); return i18next.t("egg:masterTier");
default: default:
return i18next.t("egg:defaultTier"); return i18next.t("egg:defaultTier");
@ -336,9 +337,9 @@ export class Egg {
switch (eggTier ?? this._tier) { switch (eggTier ?? this._tier) {
case EggTier.COMMON: case EggTier.COMMON:
return HATCH_WAVES_COMMON_EGG; return HATCH_WAVES_COMMON_EGG;
case EggTier.GREAT: case EggTier.RARE:
return HATCH_WAVES_RARE_EGG; return HATCH_WAVES_RARE_EGG;
case EggTier.ULTRA: case EggTier.EPIC:
return HATCH_WAVES_EPIC_EGG; return HATCH_WAVES_EPIC_EGG;
} }
return HATCH_WAVES_LEGENDARY_EGG; return HATCH_WAVES_LEGENDARY_EGG;
@ -347,7 +348,7 @@ export class Egg {
private rollEggTier(): EggTier { private rollEggTier(): EggTier {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
const tierValue = Utils.randInt(256); const tierValue = Utils.randInt(256);
return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.GREAT : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY;
} }
private rollSpecies(scene: BattleScene): Species | null { private rollSpecies(scene: BattleScene): Species | null {
@ -367,7 +368,7 @@ export class Egg {
*/ */
const rand = (Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1); const rand = (Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1);
return rand ? Species.PHIONE : Species.MANAPHY; return rand ? Species.PHIONE : Species.MANAPHY;
} else if (this.tier === EggTier.MASTER } else if (this.tier === EggTier.LEGENDARY
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) { && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!Utils.randSeedInt(2)) { if (!Utils.randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp); return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp);
@ -378,15 +379,15 @@ export class Egg {
let maxStarterValue: integer; let maxStarterValue: integer;
switch (this.tier) { switch (this.tier) {
case EggTier.GREAT: case EggTier.RARE:
minStarterValue = 4; minStarterValue = 4;
maxStarterValue = 5; maxStarterValue = 5;
break; break;
case EggTier.ULTRA: case EggTier.EPIC:
minStarterValue = 6; minStarterValue = 6;
maxStarterValue = 7; maxStarterValue = 7;
break; break;
case EggTier.MASTER: case EggTier.LEGENDARY:
minStarterValue = 8; minStarterValue = 8;
maxStarterValue = 9; maxStarterValue = 9;
break; break;
@ -398,8 +399,8 @@ export class Egg {
const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ]; const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ];
let speciesPool = Object.keys(speciesStarterCosts) let speciesPool = Object.keys(speciesEggTiers)
.filter(s => speciesStarterCosts[s] >= minStarterValue && speciesStarterCosts[s] <= maxStarterValue) .filter(s => speciesEggTiers[s] === this.tier)
.map(s => parseInt(s) as Species) .map(s => parseInt(s) as Species)
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
@ -430,7 +431,9 @@ export class Egg {
let totalWeight = 0; let totalWeight = 0;
const speciesWeights : number[] = []; const speciesWeights : number[] = [];
for (const speciesId of speciesPool) { for (const speciesId of speciesPool) {
let weight = Math.floor((((maxStarterValue - speciesStarterCosts[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); // Accounts for species that have starter costs outside of the normal range for their EggTier
const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue);
let weight = Math.floor((((maxStarterValue - speciesCostClamped) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
const species = getPokemonSpecies(speciesId); const species = getPokemonSpecies(speciesId);
if (species.isRegional()) { if (species.isRegional()) {
weight = Math.floor(weight / 2); weight = Math.floor(weight / 2);
@ -498,16 +501,16 @@ export class Egg {
private checkForPityTierOverrides(scene: BattleScene): void { private checkForPityTierOverrides(scene: BattleScene): void {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
scene.gameData.eggPity[EggTier.GREAT] += 1; scene.gameData.eggPity[EggTier.RARE] += 1;
scene.gameData.eggPity[EggTier.ULTRA] += 1; scene.gameData.eggPity[EggTier.EPIC] += 1;
scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset; scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
if (scene.gameData.eggPity[EggTier.MASTER] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.MASTER; this._tier = EggTier.LEGENDARY;
} else if (scene.gameData.eggPity[EggTier.ULTRA] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { } else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.ULTRA; this._tier = EggTier.EPIC;
} else if (scene.gameData.eggPity[EggTier.GREAT] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { } else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.GREAT; this._tier = EggTier.RARE;
} }
scene.gameData.eggPity[this._tier] = 0; scene.gameData.eggPity[this._tier] = 0;
} }
@ -516,38 +519,24 @@ export class Egg {
scene.gameData.gameStats.eggsPulled++; scene.gameData.gameStats.eggsPulled++;
if (this.isManaphyEgg()) { if (this.isManaphyEgg()) {
scene.gameData.gameStats.manaphyEggsPulled++; scene.gameData.gameStats.manaphyEggsPulled++;
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.ULTRA); this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC);
return; return;
} }
switch (this.tier) { switch (this.tier) {
case EggTier.GREAT: case EggTier.RARE:
scene.gameData.gameStats.rareEggsPulled++; scene.gameData.gameStats.rareEggsPulled++;
break; break;
case EggTier.ULTRA: case EggTier.EPIC:
scene.gameData.gameStats.epicEggsPulled++; scene.gameData.gameStats.epicEggsPulled++;
break; break;
case EggTier.MASTER: case EggTier.LEGENDARY:
scene.gameData.gameStats.legendaryEggsPulled++; scene.gameData.gameStats.legendaryEggsPulled++;
break; break;
} }
} }
private getEggTierFromSpeciesStarterValue(): EggTier { private getEggTier(): EggTier {
const speciesStartValue = speciesStarterCosts[this.species]; return speciesEggTiers[this.species];
if (speciesStartValue >= 1 && speciesStartValue <= 3) {
return EggTier.COMMON;
}
if (speciesStartValue >= 4 && speciesStartValue <= 5) {
return EggTier.GREAT;
}
if (speciesStartValue >= 6 && speciesStartValue <= 7) {
return EggTier.ULTRA;
}
if (speciesStartValue >= 8) {
return EggTier.MASTER;
}
return EggTier.COMMON;
} }
//// ////
@ -556,8 +545,8 @@ export class Egg {
} }
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species { export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species {
const legendarySpecies = Object.entries(speciesStarterCosts) const legendarySpecies = Object.entries(speciesEggTiers)
.filter(s => s[1] >= 8 && s[1] <= 9) .filter(s => s[1] === EggTier.LEGENDARY)
.map(s => parseInt(s[0])) .map(s => parseInt(s[0]))
.filter(s => getPokemonSpecies(s).isObtainable()); .filter(s => getPokemonSpecies(s).isObtainable());
@ -579,17 +568,9 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
/** /**
* Check for a given species EggTier Value * Check for a given species EggTier Value
* @param species - Species for wich we will check the egg tier it belongs to * @param pokemonSpecies - Species for wich we will check the egg tier it belongs to
* @returns The egg tier of a given pokemon species * @returns The egg tier of a given pokemon species
*/ */
export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier { export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier {
const speciesBaseValue = speciesStarterCosts[pokemonSpecies.getRootSpeciesId()]; return speciesEggTiers[pokemonSpecies.getRootSpeciesId()];
if (speciesBaseValue <= 3) {
return EggTier.COMMON;
} else if (speciesBaseValue <= 5) {
return EggTier.GREAT;
} else if (speciesBaseValue <= 7) {
return EggTier.ULTRA;
}
return EggTier.MASTER;
} }

File diff suppressed because it is too large Load Diff

View File

@ -128,6 +128,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -149,11 +150,11 @@ export const ATrainersTestEncounter: MysteryEncounter =
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.ULTRA tier: EggTier.EPIC
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
return initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -171,7 +172,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.GREAT tier: EggTier.RARE
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`));
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]); setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]);

View File

@ -166,6 +166,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -285,7 +286,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
@ -327,7 +328,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}); });
await scene.updateModifiers(true); await scene.updateModifiers(true);
transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
}) })
.build() .build()
@ -358,7 +359,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ]; greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
greedent.passive = true; greedent.passive = true;
transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false); await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
}) })

View File

@ -65,6 +65,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -110,6 +110,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -276,6 +276,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -148,6 +148,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -102,6 +102,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -227,7 +228,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Learn its Dance // Learn its Dance
hideOricorioPokemon(scene); await hideOricorioPokemon(scene);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
}) })
.build() .build()
@ -302,7 +303,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
} }
hideOricorioPokemon(scene); await hideOricorioPokemon(scene);
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false); await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
}) })

View File

@ -118,6 +118,7 @@ export const DarkDealEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party .withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party
.withCatchAllowed(true) .withCatchAllowed(true)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -189,7 +190,7 @@ export const DarkDealEncounter: MysteryEncounter =
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfig ], pokemonConfigs: [ pokemonConfig ],
}; };
return initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.build() .build()
) )

View File

@ -84,6 +84,7 @@ export const DelibirdyEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -51,6 +51,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -52,6 +52,7 @@ export const FieldTripEncounter: MysteryEncounter =
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -136,6 +136,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -229,12 +230,13 @@ export const FieryFalloutEncounter: MysteryEncounter =
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async (scene: BattleScene) => {
// Do NOT await this, to prevent player from repeatedly pressing options
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000); transitionMysteryEncounterIntroVisuals(scene, false, false, 2000);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Fire types help calm the Volcarona // Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals(scene);
setEncounterRewards(scene, setEncounterRewards(scene,
{ fillRemaining: true }, { fillRemaining: true },
undefined, undefined,

View File

@ -120,6 +120,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -76,6 +76,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -151,7 +152,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(scene, true, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
return true; return true;
} }

View File

@ -96,6 +96,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -398,7 +399,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
if (modifier.stackCount === 0) { if (modifier.stackCount === 0) {
scene.removeModifier(modifier); scene.removeModifier(modifier);
} }
scene.updateModifiers(true, true); await scene.updateModifiers(true, true);
// Generate a trainer name // Generate a trainer name
const traderName = generateRandomTraderName(); const traderName = generateRandomTraderName();

View File

@ -50,6 +50,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -128,7 +129,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
* *
* @param scene Battle scene * @param scene Battle scene
*/ */
async function handlePokemonGuidingYouPhase(scene: BattleScene) { function handlePokemonGuidingYouPhase(scene: BattleScene) {
const laprasSpecies = getPokemonSpecies(Species.LAPRAS); const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
const { mysteryEncounter } = scene.currentBattle; const { mysteryEncounter } = scene.currentBattle;

View File

@ -131,6 +131,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -152,11 +153,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 10); }, scene.currentBattle.waveIndex * 10);
return ret; await initBattlePromise!;
} }
) )
.withSimpleOption( .withSimpleOption(
@ -177,11 +178,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 100); }, scene.currentBattle.waveIndex * 100);
return ret; await initBattlePromise!;
} }
) )
.withSimpleOption( .withSimpleOption(
@ -205,11 +206,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 1000); }, scene.currentBattle.waveIndex * 1000);
return ret; await initBattlePromise!;
} }
) )
.withOutroDialogue([ .withOutroDialogue([

View File

@ -61,6 +61,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -183,7 +184,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
scene.unshiftPhase(new GameOverPhase(scene)); scene.unshiftPhase(new GameOverPhase(scene));
} else { } else {
// Show which Pokemon was KOed, then start battle against Gimmighoul // Show which Pokemon was KOed, then start battle against Gimmighoul
transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards(scene, { fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
} }

View File

@ -69,6 +69,7 @@ export const PartTimerEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -54,6 +54,7 @@ export const SafariZoneEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -302,13 +303,16 @@ async function summonSafariPokemon(scene: BattleScene) {
scene.unshiftPhase(new SummonPhase(scene, 0, false)); scene.unshiftPhase(new SummonPhase(scene, 0, false));
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared") ?? "", null, 1500, false)
.then(() => { // TODO: If we await showEncounterText here, then the text will display without
// the wild Pokemon on screen, but if we don't await it, then the text never
// shows up and the IV scanner breaks. For now, we place the IV scanner code
// separately so that at least the IV scanner works.
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier); const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
if (ivScannerModifier) { if (ivScannerModifier) {
scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6))); scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
} }
});
} }
function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> { function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {

View File

@ -62,6 +62,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -141,7 +142,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
encounter.setDialogueToken("newNature", getNatureName(newNature)); encounter.setDialogueToken("newNature", getNatureName(newNature));
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`); queueEncounterMessage(scene, `${namespace}:cheap_side_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo(); await chosenPokemon.updateInfo();
}) })
.build() .build()
) )
@ -203,7 +204,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
queueEncounterMessage(scene, `${namespace}:no_bad_effects`); queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo(); await chosenPokemon.updateInfo();
}) })
.build() .build()
) )

View File

@ -88,6 +88,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -58,6 +58,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -148,7 +149,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!; const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!; const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true }); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(scene, true, true);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
} }
) )

View File

@ -61,7 +61,7 @@ const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [ const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
[ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ], [ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ], [ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ],
[Species.JYNX], [ Species.SMOOCHUM, new BreederSpeciesEvolution(Species.JYNX, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE) ], [ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE) ], [ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE) ], [ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE) ],
@ -196,6 +196,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -244,7 +245,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene); await doPostEncounterCleanup(scene);
@ -296,7 +297,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene); await doPostEncounterCleanup(scene);
@ -348,7 +349,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene); await doPostEncounterCleanup(scene);
@ -497,7 +498,7 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: eggDescription, eggDescriptor: eggDescription,
tier: EggTier.GREAT tier: EggTier.RARE
}); });
} }
} }

View File

@ -53,6 +53,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

View File

@ -117,6 +117,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -200,7 +201,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
}); });
encounter.dialogue.outro = []; encounter.dialogue.outro = [];
transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
} }
) )

View File

@ -94,6 +94,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -110,8 +111,8 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Spawn 5 trainer battles back to back with Macho Brace in rewards // Spawn 5 trainer battles back to back with Macho Brace in rewards
scene.currentBattle.mysteryEncounter!.doContinueEncounter = (scene: BattleScene) => { scene.currentBattle.mysteryEncounter!.doContinueEncounter = async (scene: BattleScene) => {
return endTrainerBattleAndShowDialogue(scene); await endTrainerBattleAndShowDialogue(scene);
}; };
await transitionMysteryEncounterIntroVisuals(scene, true, false); await transitionMysteryEncounterIntroVisuals(scene, true, false);
await spawnNextTrainerOrEndEncounter(scene); await spawnNextTrainerOrEndEncounter(scene);

View File

@ -52,6 +52,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
} }
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -161,7 +162,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.build() .build()
) )
@ -237,7 +238,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.build() .build()
) )
@ -350,7 +351,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
}) })
.build() .build()
) )

View File

@ -54,6 +54,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
@ -104,7 +105,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Gain 2 Leftovers and 2 Shell Bell // Gain 2 Leftovers and 2 Shell Bell
transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals(scene);
await tryApplyDigRewardItems(scene); await tryApplyDigRewardItems(scene);
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]); const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
@ -135,7 +136,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
// Investigate garbage, battle Gmax Garbodor // Investigate garbage, battle Gmax Garbodor
scene.setFieldScale(0.75); scene.setFieldScale(0.75);
await showEncounterText(scene, `${namespace}:option.2.selected_2`); await showEncounterText(scene, `${namespace}:option.2.selected_2`);
transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals(scene);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -221,7 +222,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) {
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true); await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true);
} }
async function doGarbageDig(scene: BattleScene) { function doGarbageDig(scene: BattleScene) {
scene.playSound("battle_anims/PRSFX- Dig2"); scene.playSound("battle_anims/PRSFX- Dig2");
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => { scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
scene.playSound("battle_anims/PRSFX- Dig2"); scene.playSound("battle_anims/PRSFX- Dig2");

View File

@ -125,6 +125,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2")); scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2"));
return true; return true;
}) })
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)

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