Merge branch 'beta' into MoveEffectPhase

This commit is contained in:
innerthunder 2024-10-11 00:22:42 -07:00 committed by GitHub
commit e17939d47c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1463 changed files with 18091 additions and 144296 deletions

View File

@ -1,6 +1,6 @@
VITE_BYPASS_LOGIN=0 VITE_BYPASS_LOGIN=0
VITE_BYPASS_TUTORIAL=0 VITE_BYPASS_TUTORIAL=0
VITE_SERVER_URL=https://api.beta.pokerogue.net VITE_SERVER_URL=https://apibeta.pokerogue.net
VITE_DISCORD_CLIENT_ID=1248062921129459756 VITE_DISCORD_CLIENT_ID=1248062921129459756
VITE_GOOGLE_CLIENT_ID=955345393540-2k6lfftf0fdnb0krqmpthjnqavfvvf73.apps.googleusercontent.com VITE_GOOGLE_CLIENT_ID=955345393540-2k6lfftf0fdnb0krqmpthjnqavfvvf73.apps.googleusercontent.com
VITE_I18N_DEBUG=1 VITE_I18N_DEBUG=0

View File

@ -3,5 +3,5 @@ VITE_BYPASS_TUTORIAL=0
VITE_SERVER_URL=http://localhost:8001 VITE_SERVER_URL=http://localhost:8001
VITE_DISCORD_CLIENT_ID=1234567890 VITE_DISCORD_CLIENT_ID=1234567890
VITE_GOOGLE_CLIENT_ID=1234567890 VITE_GOOGLE_CLIENT_ID=1234567890
VITE_I18N_DEBUG=1 VITE_I18N_DEBUG=0
VITE_PORT=8000 VITE_PORT=8000

38
.github/CODEOWNERS vendored
View File

@ -5,41 +5,3 @@
# github actions/templates etc. - Dev Leads # github actions/templates etc. - Dev Leads
/.github @pagefaultgames/dev-leads /.github @pagefaultgames/dev-leads
# --- Translations ---
# all translations - Translation Leads
/src/locales @pagefaultgames/translation-leads
# Catalan (Spain/Spanish)
/src/locales/ca_ES @pagefaultgames/catalan-translation-team
# German
/src/locales/de @pagefaultgames/german-translation-team
# English
/src/locales/en @pagefaultgames/english-translation-team
# Spanish
/src/locales/es @pagefaultgames/spanish-translation-team
# French
/src/locales/fr @pagefaultgames/french-translation-team
# Italian
/src/locales/it @pagefaultgames/italian-translation-team
# Japenese
/src/locales/ja @pagefaultgames/japanese-translation-team
# Korean
/src/locales/ko @pagefaultgames/korean-translation-team
# Brasilian (Brasil/Portuguese)
/src/locales/pt_BR @pagefaultgames/portuguese_br-translation-team
# Chinese (simplified)
/src/locales/zh_CN @pagefaultgames/chinese_simplified-translation-team
# Chinese (traditional)
/src/locales/zh_TW @pagefaultgames/chinese_traditional-translation-team

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
github: patapancakes github: pagefaultgames

View File

@ -11,6 +11,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: 'recursive'
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "20" node-version: "20"

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:
@ -10,6 +14,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: 'recursive'
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "20" node-version: "20"
@ -20,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
@ -28,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

@ -22,6 +22,8 @@ jobs:
steps: steps:
- name: Check out Git repository # Step to check out the repository - name: Check out Git repository # Step to check out the repository
uses: actions/checkout@v4 # Use the checkout action version 4 uses: actions/checkout@v4 # Use the checkout action version 4
with:
submodules: 'recursive'
- name: Set up Node.js # Step to set up Node.js environment - name: Set up Node.js # Step to set up Node.js environment
uses: actions/setup-node@v4 # Use the setup-node action version 4 uses: actions/setup-node@v4 # Use the setup-node action version 4

View File

@ -26,6 +26,7 @@ jobs:
- name: Checkout repository for Typedoc - name: Checkout repository for Typedoc
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
submodules: 'recursive'
path: pokerogue_docs path: pokerogue_docs
- name: Install OS package - name: Install OS package

View File

@ -20,6 +20,8 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:

View File

@ -22,6 +22,7 @@ jobs:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: 'recursive'
path: tests-action path: tests-action
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "public/locales"]
path = public/locales
url = https://github.com/pagefaultgames/pokerogue-locales

View File

@ -17,13 +17,21 @@ 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
**How do I test a new _______?** **How do I test a new _______?**
- In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing - In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing
**How do I retrieve the translations?**
- The translations were moved to the [dedicated translation repository](https://github.com/pagefaultgames/pokerogue-locales) and are now applied as a submodule in this project.
- The command to retrieve the translations is `git submodule update --init --recursive`. If you still struggle to get it working, please reach out to #dev-corner channel in Discord.
## 🪧 To Do ## 🪧 To Do
Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to see how can you help us! Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to see how can you help us!

View File

@ -1,9 +1,7 @@
/** /**
* This script creates a test boilerplate file for a move or ability. * This script creates a test boilerplate file in the appropriate
* @param {string} type - The type of test to create. Either "move", "ability", * directory based on the type selected.
* or "item". * @example npm run create-test
* @param {string} fileName - The name of the file to create.
* @example npm run create-test move tackle
*/ */
import fs from "fs"; import fs from "fs";
@ -34,7 +32,7 @@ async function promptTestType() {
console.log("Exiting..."); console.log("Exiting...");
return process.exit(); return process.exit();
} else if (!typeChoices.includes(typeAnswer.selectedOption)) { } else if (!typeChoices.includes(typeAnswer.selectedOption)) {
console.error('Please provide a valid type ("move", "ability", or "item")!'); console.error(`Please provide a valid type (${typeChoices.join(", ")})!`);
return await promptTestType(); return await promptTestType();
} }
@ -51,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}:`,
}, },
]); ]);
@ -102,7 +100,7 @@ async function runInteractive() {
description = `Mystery Encounter - ${formattedName}`; description = `Mystery Encounter - ${formattedName}`;
break; break;
default: default:
console.error('Invalid type. Please use "move", "ability", or "item".'); console.error(`Invalid type. Please use one of the following: ${typeChoices.join(", ")}.`);
process.exit(1); process.exit(1);
} }
@ -112,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;
@ -132,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

@ -41,6 +41,11 @@ export default [
"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 comma
"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;
}

View File

@ -14,3 +14,8 @@ pre-push:
eslint: eslint:
glob: "*.{js,ts,jsx,tsx}" glob: "*.{js,ts,jsx,tsx}"
run: npx eslint --fix {push_files} run: npx eslint --fix {push_files}
post-merge:
commands:
update-submodules:
run: git submodule update --init --recursive

290
package-lock.json generated
View File

@ -7,11 +7,13 @@
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.0.4", "version": "1.0.4",
"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",
"i18next": "^23.11.1", "i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.6.1",
"i18next-korean-postposition-processor": "^1.0.0", "i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0", "json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0", "phaser": "^3.70.0",
@ -32,11 +34,12 @@
"inquirer": "^11.0.2", "inquirer": "^11.0.2",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"lefthook": "^1.6.12", "lefthook": "^1.6.12",
"msw": "^2.4.9",
"phaser3spectorjs": "^0.0.8", "phaser3spectorjs": "^0.0.8",
"typedoc": "^0.26.4", "typedoc": "^0.26.4",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.54", "typescript-eslint": "^8.0.0-alpha.54",
"vite": "^5.3.5", "vite": "^5.4.8",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.4", "vitest": "^2.0.4",
"vitest-canvas-mock": "^0.3.3" "vitest-canvas-mock": "^0.3.3"
@ -486,6 +489,37 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@bundled-es-modules/cookie": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz",
"integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==",
"dev": true,
"license": "ISC",
"dependencies": {
"cookie": "^0.5.0"
}
},
"node_modules/@bundled-es-modules/statuses": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz",
"integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==",
"dev": true,
"license": "ISC",
"dependencies": {
"statuses": "^2.0.1"
}
},
"node_modules/@bundled-es-modules/tough-cookie": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz",
"integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==",
"dev": true,
"license": "ISC",
"dependencies": {
"@types/tough-cookie": "^4.0.5",
"tough-cookie": "^4.1.4"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -1461,6 +1495,24 @@
"integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==", "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@mswjs/interceptors": {
"version": "0.35.8",
"resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.35.8.tgz",
"integrity": "sha512-PFfqpHplKa7KMdoQdj5td03uG05VK2Ng1dG0sP4pT9h0dGSX2v9txYt/AnrzPb/vAmfyBBC0NQV7VaBEX+efgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@open-draft/deferred-promise": "^2.2.0",
"@open-draft/logger": "^0.3.0",
"@open-draft/until": "^2.0.0",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.3",
"strict-event-emitter": "^0.5.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1499,6 +1551,31 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@open-draft/deferred-promise": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
"integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
"dev": true,
"license": "MIT"
},
"node_modules/@open-draft/logger": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
"integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-node-process": "^1.2.0",
"outvariant": "^1.4.0"
}
},
"node_modules/@open-draft/until": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
"integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
"dev": true,
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -1777,6 +1854,13 @@
"eslint": ">=8.40.0" "eslint": ">=8.40.0"
} }
}, },
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.56.11", "version": "8.56.11",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz",
@ -1842,6 +1926,13 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/statuses": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz",
"integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/tough-cookie": { "node_modules/@types/tough-cookie": {
"version": "4.0.5", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
@ -2622,6 +2713,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cross-fetch": { "node_modules/cross-fetch": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@ -3551,7 +3652,6 @@
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@ -3740,6 +3840,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/graphql": {
"version": "16.9.0",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz",
"integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
}
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -3797,6 +3907,13 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/headers-polyfill": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
"integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/html-encoding-sniffer": { "node_modules/html-encoding-sniffer": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
@ -3888,9 +4005,10 @@
} }
}, },
"node_modules/i18next-http-backend": { "node_modules/i18next-http-backend": {
"version": "2.5.2", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz", "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.6.1.tgz",
"integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==", "integrity": "sha512-rCilMAnlEQNeKOZY1+x8wLM5IpYOj10guGvEpeC59tNjj6MMreLIjIW8D1RclhD3ifLwn6d/Y9HEM1RUE6DSog==",
"license": "MIT",
"dependencies": { "dependencies": {
"cross-fetch": "4.0.0" "cross-fetch": "4.0.0"
} }
@ -4072,6 +4190,13 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/is-node-process": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
"integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": { "node_modules/is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -4805,6 +4930,90 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/msw": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/msw/-/msw-2.4.9.tgz",
"integrity": "sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@bundled-es-modules/cookie": "^2.0.0",
"@bundled-es-modules/statuses": "^1.0.1",
"@bundled-es-modules/tough-cookie": "^0.1.6",
"@inquirer/confirm": "^3.0.0",
"@mswjs/interceptors": "^0.35.8",
"@open-draft/until": "^2.1.0",
"@types/cookie": "^0.6.0",
"@types/statuses": "^2.0.4",
"chalk": "^4.1.2",
"graphql": "^16.8.1",
"headers-polyfill": "^4.0.2",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.2",
"path-to-regexp": "^6.3.0",
"strict-event-emitter": "^0.5.1",
"type-fest": "^4.9.0",
"yargs": "^17.7.2"
},
"bin": {
"msw": "cli/index.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/mswjs"
},
"peerDependencies": {
"typescript": ">= 4.8.x"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/msw/node_modules/@inquirer/confirm": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz",
"integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.1.0",
"@inquirer/type": "^1.5.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/msw/node_modules/@inquirer/type": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz",
"integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==",
"dev": true,
"license": "MIT",
"dependencies": {
"mute-stream": "^1.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/msw/node_modules/type-fest": {
"version": "4.26.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
"integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mustache": { "node_modules/mustache": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
@ -4835,7 +5044,6 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@ -4983,6 +5191,13 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/outvariant": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
"integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
"dev": true,
"license": "MIT"
},
"node_modules/p-limit": { "node_modules/p-limit": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -5105,6 +5320,13 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/path-to-regexp": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
"dev": true,
"license": "MIT"
},
"node_modules/path-type": { "node_modules/path-type": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@ -5220,9 +5442,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.39", "version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -5238,16 +5460,21 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.0.1", "picocolors": "^1.1.0",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/postcss/node_modules/picocolors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"dev": true
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -5644,11 +5871,10 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true, "dev": true,
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -5667,6 +5893,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/std-env": { "node_modules/std-env": {
"version": "3.7.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
@ -5674,6 +5910,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/strict-event-emitter": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
"integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
"dev": true,
"license": "MIT"
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@ -6231,15 +6474,14 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.3.5", "version": "5.4.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
"integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.39", "postcss": "^8.4.43",
"rollup": "^4.13.0" "rollup": "^4.20.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
@ -6258,6 +6500,7 @@
"less": "*", "less": "*",
"lightningcss": "^1.21.0", "lightningcss": "^1.21.0",
"sass": "*", "sass": "*",
"sass-embedded": "*",
"stylus": "*", "stylus": "*",
"sugarss": "*", "sugarss": "*",
"terser": "^5.4.0" "terser": "^5.4.0"
@ -6275,6 +6518,9 @@
"sass": { "sass": {
"optional": true "optional": true
}, },
"sass-embedded": {
"optional": true
},
"stylus": { "stylus": {
"optional": true "optional": true
}, },

View File

@ -19,7 +19,8 @@
"docs": "typedoc", "docs": "typedoc",
"depcruise": "depcruise src", "depcruise": "depcruise src",
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
"create-test": "node ./create-test-boilerplate.js" "create-test": "node ./create-test-boilerplate.js",
"postinstall": "npx lefthook install && npx lefthook run post-merge"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.3.0", "@eslint/js": "^9.3.0",
@ -36,11 +37,12 @@
"inquirer": "^11.0.2", "inquirer": "^11.0.2",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"lefthook": "^1.6.12", "lefthook": "^1.6.12",
"msw": "^2.4.9",
"phaser3spectorjs": "^0.0.8", "phaser3spectorjs": "^0.0.8",
"typedoc": "^0.26.4", "typedoc": "^0.26.4",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.54", "typescript-eslint": "^8.0.0-alpha.54",
"vite": "^5.3.5", "vite": "^5.4.8",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.4", "vitest": "^2.0.4",
"vitest-canvas-mock": "^0.3.3" "vitest-canvas-mock": "^0.3.3"
@ -50,6 +52,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"i18next": "^23.11.1", "i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.6.1",
"i18next-korean-postposition-processor": "^1.0.0", "i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0", "json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0", "phaser": "^3.70.0",

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: 637 B

After

Width:  |  Height:  |  Size: 399 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

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

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,

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

1
public/locales Submodule

@ -0,0 +1 @@
Subproject commit fc4a1effd5170def3c8314208a52cd0d8e6913ef

View File

@ -1,18 +1,8 @@
import { type enConfig } from "#app/locales/en/config";
import { TOptions } from "i18next"; import { TOptions } from "i18next";
//TODO: this needs to be type properly in the future
// Module declared to make referencing keys in the localization files type-safe. // Module declared to make referencing keys in the localization files type-safe.
declare module "i18next" { declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "menu", // needed here as well for typedoc
resources: typeof enConfig
}
interface TFunction { interface TFunction {
( (key: string | string[], options?: TOptions & Record<string, unknown>): string;
key: string | string[],
options?: TOptions & Record<string, unknown>
): string;
} }
} }

View File

@ -0,0 +1,9 @@
/**
* Pokerogue API response for path: `/savedata/session/clear`
*/
export interface PokerogueApiClearSessionData {
/** Contains the error message if any occured */
error?: string;
/** Is `true` if the request was successfully processed */
success?: boolean;
}

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, 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/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/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,7 @@ 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";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -889,6 +889,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);
@ -1040,10 +1043,6 @@ export default class BattleScene extends SceneBase {
this.gameMode = getGameMode(GameModes.CLASSIC); this.gameMode = getGameMode(GameModes.CLASSIC);
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
console.log("Seed:", this.seed);
this.resetSeed(); // Properly resets RNG after saving and quitting a session
this.disableMenu = false; this.disableMenu = false;
this.score = 0; this.score = 0;
@ -1078,6 +1077,12 @@ export default class BattleScene extends SceneBase {
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
this.currentBattle = null; // TODO: resolve ts-ignore this.currentBattle = null; // TODO: resolve ts-ignore
// Reset RNG after end of game or save & quit.
// This needs to happen after clearing this.currentBattle or the seed will be affected by the last wave played
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
console.log("Seed:", this.seed);
this.resetSeed();
this.biomeWaveText.setText(startingWave.toString()); this.biomeWaveText.setText(startingWave.toString());
this.biomeWaveText.setVisible(false); this.biomeWaveText.setVisible(false);
@ -1201,7 +1206,7 @@ export default class BattleScene extends SceneBase {
// Check for mystery encounter // Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180 // Can only occur in place of a standard (non-boss) wild battle, waves 10-180
if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER || !isNullOrUndefined(mysteryEncounterType)) { if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER) {
newBattleType = BattleType.MYSTERY_ENCOUNTER; newBattleType = BattleType.MYSTERY_ENCOUNTER;
// Reset base spawn weight // Reset base spawn weight
this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
@ -2354,17 +2359,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
@ -2443,7 +2437,10 @@ export default class BattleScene extends SceneBase {
} }
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) { if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
success = modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
success = modifier.apply(pokemon, true);
}
} }
if (playSound && !this.sound.get(soundName)) { if (playSound && !this.sound.get(soundName)) {
this.playSound(soundName); this.playSound(soundName);
@ -2470,7 +2467,7 @@ export default class BattleScene extends SceneBase {
for (const p in this.party) { for (const p in this.party) {
const pokemon = this.party[p]; const pokemon = this.party[p];
const args: any[] = [ pokemon ]; const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) { if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) { if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1); const hpRestoreMultiplier = new Utils.IntegerHolder(1);
@ -2483,8 +2480,8 @@ export default class BattleScene extends SceneBase {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon); args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} }
if (modifier.shouldApply(args)) { if (modifier.shouldApply(pokemon, ...args)) {
const result = modifier.apply(args); const result = modifier.apply(pokemon, ...args);
if (result instanceof Promise) { if (result instanceof Promise) {
modifierPromises.push(result.then(s => success ||= s)); modifierPromises.push(result.then(s => success ||= s));
} else { } else {
@ -2496,8 +2493,8 @@ export default class BattleScene extends SceneBase {
return Promise.allSettled([ this.party.map(p => p.updateInfo(instant)), ...modifierPromises ]).then(() => resolve(success)); return Promise.allSettled([ this.party.map(p => p.updateInfo(instant)), ...modifierPromises ]).then(() => resolve(success));
} else { } else {
const args = [ this ]; const args = [ this ];
if (modifier.shouldApply(args)) { if (modifier.shouldApply(...args)) {
const result = modifier.apply(args); const result = modifier.apply(...args);
if (result instanceof Promise) { if (result instanceof Promise) {
return result.then(success => resolve(success)); return result.then(success => resolve(success));
} else { } else {
@ -2519,7 +2516,10 @@ export default class BattleScene extends SceneBase {
} }
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) { if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, true);
}
} }
for (const rm of modifiersToRemove) { for (const rm of modifiersToRemove) {
this.removeModifier(rm, true); this.removeModifier(rm, true);
@ -2676,7 +2676,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);
@ -2687,10 +2687,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);
} }
@ -2753,7 +2754,10 @@ export default class BattleScene extends SceneBase {
if (modifierIndex > -1) { if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1); modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
modifier.apply([ this.getPokemonById(modifier.pokemonId), false ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, false);
}
} }
return true; return true;
} }
@ -2771,16 +2775,36 @@ export default class BattleScene extends SceneBase {
return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType); return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType);
} }
findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] { /**
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m)); * Get all of the modifiers that pass the `modifierFilter` function
* @param modifierFilter The function used to filter a target's modifiers
* @param isPlayer Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @returns the list of all modifiers that passed the `modifierFilter` function
*/
findModifiers(modifierFilter: ModifierPredicate, isPlayer: boolean = true): PersistentModifier[] {
return (isPlayer ? this.modifiers : this.enemyModifiers).filter(modifierFilter);
} }
/**
* Find the first modifier that pass the `modifierFilter` function
* @param modifierFilter The function used to filter a target's modifiers
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @returns the first modifier that passed the `modifierFilter` function; `undefined` if none passed
*/
findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined { findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined {
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m)); return (player ? this.modifiers : this.enemyModifiers).find(modifierFilter);
} }
applyShuffledModifiers(scene: BattleScene, modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] { /**
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply all modifiers that match `modifierType` in a random order
* @param scene {@linkcode BattleScene} used to randomize the order of modifiers
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyShuffledModifiers<T extends PersistentModifier>(scene: BattleScene, modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
const shuffleModifiers = mods => { const shuffleModifiers = mods => {
if (mods.length < 1) { if (mods.length < 1) {
@ -2794,15 +2818,23 @@ export default class BattleScene extends SceneBase {
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
applyModifiers(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] { /**
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply all modifiers that match `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
applyModifiersInternal(modifiers: PersistentModifier[], player: boolean, args: any[]): PersistentModifier[] { /** Helper function to apply all passed modifiers */
const appliedModifiers: PersistentModifier[] = []; applyModifiersInternal<T extends PersistentModifier>(modifiers: T[], player: boolean, args: Parameters<T["apply"]>): T[] {
const appliedModifiers: T[] = [];
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : ""); console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
appliedModifiers.push(modifier); appliedModifiers.push(modifier);
} }
@ -2811,10 +2843,17 @@ export default class BattleScene extends SceneBase {
return appliedModifiers; return appliedModifiers;
} }
applyModifier(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier | null { /**
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply the first modifier that matches `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the first modifier that matches `modifierType` and was applied; return `null` if none matched
*/
applyModifier<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T | null {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : ""); console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
return modifier; return modifier;
} }
@ -2880,7 +2919,7 @@ export default class BattleScene extends SceneBase {
} }
} }
validateAchv(achv: Achv, args?: any[]): boolean { validateAchv(achv: Achv, args?: unknown[]): boolean {
if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) { if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
@ -2893,7 +2932,7 @@ export default class BattleScene extends SceneBase {
return false; return false;
} }
validateVoucher(voucher: Voucher, args?: any[]): boolean { validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher); this.ui.achvBar.showAchv(voucher);
@ -3002,7 +3041,7 @@ export default class BattleScene extends SceneBase {
if (participantIds.size > 0) { if (participantIds.size > 0) {
if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
expValue = Math.floor(expValue * 1.5); expValue = Math.floor(expValue * 1.5);
} else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) { } else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) {
expValue = Math.floor(expValue * this.currentBattle.mysteryEncounter.expMultiplier); expValue = Math.floor(expValue * this.currentBattle.mysteryEncounter.expMultiplier);
} }
for (const partyMember of nonFaintedPartyMembers) { for (const partyMember of nonFaintedPartyMembers) {
@ -3091,20 +3130,16 @@ export default class BattleScene extends SceneBase {
private isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean { private isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean {
const [ lowestMysteryEncounterWave, highestMysteryEncounterWave ] = this.gameMode.getMysteryEncounterLegalWaves(); const [ lowestMysteryEncounterWave, highestMysteryEncounterWave ] = this.gameMode.getMysteryEncounterLegalWaves();
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(waveIndex) && waveIndex < highestMysteryEncounterWave && waveIndex > lowestMysteryEncounterWave) { if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(waveIndex) && waveIndex < highestMysteryEncounterWave && waveIndex > lowestMysteryEncounterWave) {
// If ME type is already defined in session data, no need to roll RNG check
if (!isNullOrUndefined(sessionDataEncounterType)) {
return true;
}
// Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor // Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance; const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents; const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents;
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well) // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well)
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET // Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
// Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (waveIndex - lowestMysteryEncounterWave); const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (waveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length; const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER; const favoredEncounterRate = sessionEncounterRate + Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!; const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
@ -3127,13 +3162,17 @@ 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];
} 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

@ -213,7 +213,7 @@ export default class Battle {
getBgmOverride(scene: BattleScene): string | null { getBgmOverride(scene: BattleScene): string | null {
const battlers = this.enemyParty.slice(0, this.getBattlerCount()); const battlers = this.enemyParty.slice(0, this.getBattlerCount());
if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
// Music is overridden for MEs during ME onInit() // Music is overridden for MEs during ME onInit()
// Should not use any BGM overrides before swapping from DEFAULT mode // Should not use any BGM overrides before swapping from DEFAULT mode
return null; return null;
@ -409,6 +409,13 @@ export default class Battle {
scene.rngSeedOverride = tempSeedOverride; scene.rngSeedOverride = tempSeedOverride;
return ret; return ret;
} }
/**
* Returns if the battle is of type {@linkcode BattleType.MYSTERY_ENCOUNTER}
*/
isBattleMysteryEncounter(): boolean {
return this.battleType === BattleType.MYSTERY_ENCOUNTER;
}
} }
export class FixedBattle extends Battle { export class FixedBattle extends Battle {
@ -490,7 +497,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
} }
/* 1/3 chance for evil team grunts to be double battles */ /* 1/3 chance for evil team grunts to be double battles */
const evilTeamGrunts = [TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT]; const evilTeamGrunts = [ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ];
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {

View File

@ -4,7 +4,7 @@ import { Constructor } from "#app/utils";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import { Weather, WeatherType } from "./weather"; import { Weather, WeatherType } from "./weather";
import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; import { BattlerTag, GroundedTag } from "./battler-tags";
import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender"; import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
@ -123,6 +123,7 @@ export class Ability implements Localizable {
type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>; type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
type AbAttrCondition = (pokemon: Pokemon) => boolean; type AbAttrCondition = (pokemon: Pokemon) => boolean;
// TODO: Can this be improved?
type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean; type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
@ -518,6 +519,7 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
if (pokemon.isFullHp() && typeMultiplier.value > 0.5) { if (pokemon.isFullHp() && typeMultiplier.value > 0.5) {
typeMultiplier.value = 0.5; typeMultiplier.value = 0.5;
pokemon.turnData.moveEffectiveness = 0.5;
return true; return true;
} }
return false; return false;
@ -536,53 +538,6 @@ export class PostDefendAbAttr extends AbAttr {
} }
} }
/**
* Applies the effects of Gulp Missile when the user is hit by an attack.
* @extends PostDefendAbAttr
*/
export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr {
constructor() {
super(true);
}
/**
* Damages the attacker and triggers the secondary effect based on the form or the BattlerTagType.
* @param {Pokemon} pokemon - The defending Pokemon.
* @param passive - n/a
* @param {Pokemon} attacker - The attacking Pokemon.
* @param {Move} move - The move being used.
* @param {HitResult} hitResult - n/a
* @param {any[]} args - n/a
* @returns Whether the effects of the ability are applied.
*/
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
const battlerTag = pokemon.getTag(GulpMissileTag);
if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) {
return false;
}
if (simulated) {
return true;
}
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
if (!cancelled.value) {
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER);
}
if (battlerTag.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1));
} else {
attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon);
}
pokemon.removeTag(battlerTag.tagType);
return true;
}
}
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const attackPriority = new Utils.IntegerHolder(move.priority); const attackPriority = new Utils.IntegerHolder(move.priority);
@ -679,15 +634,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) }));
} }
@ -714,8 +669,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;
} }
@ -752,12 +707,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));
} }
@ -779,8 +734,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) {
@ -803,8 +758,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 }));
@ -816,8 +771,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;
} }
@ -832,7 +787,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,
@ -850,8 +805,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 {
@ -874,8 +829,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);
@ -914,8 +870,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 {
@ -938,7 +894,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));
} }
@ -946,7 +906,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;
} }
} }
@ -960,8 +920,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;
@ -970,7 +931,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
@ -993,8 +954,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 {
@ -1008,24 +969,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()) {
@ -1044,8 +1005,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;
@ -1057,7 +1019,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) });
} }
} }
@ -1070,8 +1032,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;
} }
@ -1082,7 +1045,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
@ -1101,8 +1064,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;
@ -1769,17 +1732,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)];
@ -2479,7 +2442,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
pokemon.setStatStage(s, target.getStatStage(s)); pokemon.setStatStage(s, target.getStatStage(s));
} }
pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m!.moveId, m!.ppUsed, m!.ppUp)); // TODO: are those bangs correct? pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId ?? Moves.NONE, m?.ppUsed, m?.ppUp));
pokemon.summonData.types = target.getTypes(); pokemon.summonData.types = target.getTypes();
@ -3199,12 +3162,12 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
} else if (move?.getMove().power === -1) { } else if (move?.getMove().power === -1) {
movePower = 80; movePower = 80;
} else { } else {
movePower = move!.getMove().power; // TODO: is this bang correct? movePower = move?.getMove().power ?? 0;
} }
if (movePower > maxPowerSeen) { if (movePower > maxPowerSeen) {
maxPowerSeen = movePower; maxPowerSeen = movePower;
maxMove = move!.getName(); // TODO: is this bang correct? maxMove = move?.getName() ?? "";
} }
} }
} }
@ -4521,7 +4484,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) {
@ -4537,16 +4500,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);
@ -4562,12 +4525,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);
} }
} }
@ -4668,7 +4631,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;
} }
@ -5210,8 +5173,7 @@ export function initAbilities() {
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(MoveAbilityBypassAbAttr), .attr(MoveAbilityBypassAbAttr),
new Ability(Abilities.SUPER_LUCK, 4) new Ability(Abilities.SUPER_LUCK, 4)
.attr(BonusCritAbAttr) .attr(BonusCritAbAttr),
.partial(),
new Ability(Abilities.AFTERMATH, 4) new Ability(Abilities.AFTERMATH, 4)
.attr(PostFaintContactDamageAbAttr, 4) .attr(PostFaintContactDamageAbAttr, 4)
.bypassFaint(), .bypassFaint(),
@ -5223,8 +5185,7 @@ export function initAbilities() {
.attr(IgnoreOpponentStatStagesAbAttr) .attr(IgnoreOpponentStatStagesAbAttr)
.ignorable(), .ignorable(),
new Ability(Abilities.TINTED_LENS, 4) new Ability(Abilities.TINTED_LENS, 4)
//@ts-ignore .attr(DamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5),
.attr(DamageBoostAbAttr, 2, (user, target, move) => target?.getMoveEffectiveness(user, move) <= 0.5), // TODO: fix TS issues
new Ability(Abilities.FILTER, 4) new Ability(Abilities.FILTER, 4)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75)
.ignorable(), .ignorable(),
@ -5342,8 +5303,9 @@ export function initAbilities() {
.attr(WonderSkinAbAttr) .attr(WonderSkinAbAttr)
.ignorable(), .ignorable(),
new Ability(Abilities.ANALYTIC, 5) new Ability(Abilities.ANALYTIC, 5)
//@ts-ignore .attr(MovePowerBoostAbAttr, (user, target, move) =>
.attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), // TODO fix TS issues !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn)
|| user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command !== Command.FIGHT, 1.3),
new Ability(Abilities.ILLUSION, 5) new Ability(Abilities.ILLUSION, 5)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
@ -5508,8 +5470,7 @@ export function initAbilities() {
.bypassFaint() .bypassFaint()
.partial(), .partial(),
new Ability(Abilities.STAKEOUT, 7) new Ability(Abilities.STAKEOUT, 7)
//@ts-ignore .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()].command === Command.POKEMON, 2), // TODO: fix TS issues
new Ability(Abilities.WATER_BUBBLE, 7) new Ability(Abilities.WATER_BUBBLE, 7)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(MoveTypePowerBoostAbAttr, Type.WATER, 2) .attr(MoveTypePowerBoostAbAttr, Type.WATER, 2)
@ -5550,7 +5511,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)
@ -5647,8 +5609,7 @@ export function initAbilities() {
new Ability(Abilities.PRISM_ARMOR, 7) new Ability(Abilities.PRISM_ARMOR, 7)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75), .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75),
new Ability(Abilities.NEUROFORCE, 7) new Ability(Abilities.NEUROFORCE, 7)
//@ts-ignore .attr(MovePowerBoostAbAttr, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) >= 2, 1.25),
.attr(MovePowerBoostAbAttr, (user, target, move) => target?.getMoveEffectiveness(user, move) >= 2, 1.25), // TODO: fix TS issues
new Ability(Abilities.INTREPID_SWORD, 8) new Ability(Abilities.INTREPID_SWORD, 8)
.attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)),
@ -5669,13 +5630,19 @@ export function initAbilities() {
new Ability(Abilities.MIRROR_ARMOR, 8) new Ability(Abilities.MIRROR_ARMOR, 8)
.ignorable() .ignorable()
.unimplemented(), .unimplemented(),
/**
* Right now, the logic is attached to Surf and Dive moves. Ideally, the post-defend/hit should be an
* ability attribute but the current implementation of move effects for BattlerTag does not support this- in the case
* where Cramorant is fainted.
* @see {@linkcode GulpMissileTagAttr} and {@linkcode GulpMissileTag} for Gulp Missile implementation
*/
new Ability(Abilities.GULP_MISSILE, 8) new Ability(Abilities.GULP_MISSILE, 8)
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(PostDefendGulpMissileAbAttr), .bypassFaint(),
new Ability(Abilities.STALWART, 8) new Ability(Abilities.STALWART, 8)
.attr(BlockRedirectAbAttr), .attr(BlockRedirectAbAttr),
new Ability(Abilities.STEAM_ENGINE, 8) new Ability(Abilities.STEAM_ENGINE, 8)
@ -5707,7 +5674,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()
@ -5740,10 +5708,8 @@ export function initAbilities() {
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.ignorable(), .ignorable(),
new Ability(Abilities.HUNGER_SWITCH, 8) new Ability(Abilities.HUNGER_SWITCH, 8)
//@ts-ignore .attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) // TODO: fix ts-ignore .attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 1 : 0)
//@ts-ignore
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) // TODO: fix ts-ignore
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)

View File

@ -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,
@ -511,6 +512,40 @@ class WaterSportTag extends WeakenMoveTypeTag {
} }
} }
/**
* 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.
*/
export class IonDelugeTag extends ArenaTag {
constructor(sourceMove?: Moves) {
super(ArenaTagType.ION_DELUGE, 1, sourceMove);
}
/** Queues an on-add message */
onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
}
onRemove(arena: Arena): void { } // Removes default on-remove message
/**
* Converts Normal-type moves to Electric type
* @param arena n/a
* @param args
* - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type}
* @returns `true` if the given move type changed; `false` otherwise.
*/
apply(arena: Arena, args: any[]): boolean {
const moveType = args[0];
if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) {
moveType.value = Type.ELECTRIC;
return true;
}
return false;
}
}
/** /**
* Abstract class to implement arena traps. * Abstract class to implement arena traps.
*/ */
@ -992,6 +1027,81 @@ class ImprisonTag extends ArenaTrapTag {
} }
} }
/**
* 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(Utils.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" : ""}`));
}
override apply(arena: Arena, args: any[]): boolean {
const moveChance = args[0] as Utils.NumberHolder;
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" : ""}`));
}
}
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
switch (tagType) { switch (tagType) {
case ArenaTagType.MIST: case ArenaTagType.MIST:
@ -1010,6 +1120,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.ION_DELUGE:
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:
@ -1041,6 +1153,12 @@ 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;
} }

View File

@ -1,6 +1,6 @@
import { Type } from "./type"; import { Type } from "#app/data/type";
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import { pokemonEvolutions, SpeciesFormEvolution } from "./pokemon-evolutions"; import { pokemonEvolutions, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import i18next from "i18next"; import i18next from "i18next";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Species } from "#enums/species"; import { Species } from "#enums/species";

View File

@ -1,16 +1,16 @@
import { allMoves } from "./move"; import { allMoves } from "#app/data/move";
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
export const speciesEggMoves = { export const speciesEggMoves = {
[Species.BULBASAUR]: [ Moves.SAPPY_SEED, Moves.SLUDGE_WAVE, Moves.EARTH_POWER, Moves.MATCHA_GOTCHA ], [Species.BULBASAUR]: [ Moves.SAPPY_SEED, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.MATCHA_GOTCHA ],
[Species.CHARMANDER]: [ Moves.DRAGON_DANCE, Moves.BITTER_BLADE, Moves.EARTH_POWER, Moves.OBLIVION_WING ], [Species.CHARMANDER]: [ Moves.DRAGON_DANCE, Moves.BITTER_BLADE, Moves.EARTH_POWER, Moves.OBLIVION_WING ],
[Species.SQUIRTLE]: [ Moves.FREEZE_DRY, Moves.SHORE_UP, Moves.BOUNCY_BUBBLE, Moves.ORIGIN_PULSE ], [Species.SQUIRTLE]: [ Moves.FREEZE_DRY, Moves.SHORE_UP, Moves.BOUNCY_BUBBLE, Moves.ORIGIN_PULSE ],
[Species.CATERPIE]: [ Moves.SANDSEAR_STORM, Moves.SILK_TRAP, Moves.TWIN_BEAM, Moves.BLEAKWIND_STORM ], [Species.CATERPIE]: [ Moves.SANDSEAR_STORM, Moves.SILK_TRAP, Moves.TWIN_BEAM, Moves.BLEAKWIND_STORM ],
[Species.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.SWORDS_DANCE, Moves.ATTACK_ORDER, Moves.NOXIOUS_TORQUE ], [Species.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.NOXIOUS_TORQUE, Moves.ATTACK_ORDER, Moves.VICTORY_DANCE ],
[Species.PIDGEY]: [ Moves.WILDBOLT_STORM, Moves.SANDSEAR_STORM, Moves.CALM_MIND, Moves.BOOMBURST ], [Species.PIDGEY]: [ Moves.WILDBOLT_STORM, Moves.SANDSEAR_STORM, Moves.NASTY_PLOT, Moves.BOOMBURST ],
[Species.RATTATA]: [ Moves.HYPER_FANG, Moves.PSYCHIC_FANGS, Moves.FIRE_FANG, Moves.EXTREME_SPEED ], [Species.RATTATA]: [ Moves.HYPER_FANG, Moves.PSYCHIC_FANGS, Moves.FIRE_FANG, Moves.EXTREME_SPEED ],
[Species.SPEAROW]: [ Moves.FLOATY_FALL, Moves.EXTREME_SPEED, Moves.TIDY_UP, Moves.TRIPLE_ARROWS ], [Species.SPEAROW]: [ Moves.FLOATY_FALL, Moves.EXTREME_SPEED, Moves.TIDY_UP, Moves.TRIPLE_ARROWS ],
[Species.EKANS]: [ Moves.NOXIOUS_TORQUE, Moves.DRAGON_DANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ], [Species.EKANS]: [ Moves.NOXIOUS_TORQUE, Moves.DRAGON_DANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ],
@ -21,46 +21,46 @@ export const speciesEggMoves = {
[Species.ZUBAT]: [ Moves.FLOATY_FALL, Moves.DIRE_CLAW, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.ZUBAT]: [ Moves.FLOATY_FALL, Moves.DIRE_CLAW, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ],
[Species.ODDISH]: [ Moves.SLUDGE_BOMB, Moves.FIERY_DANCE, Moves.STRENGTH_SAP, Moves.SPORE ], [Species.ODDISH]: [ Moves.SLUDGE_BOMB, Moves.FIERY_DANCE, Moves.STRENGTH_SAP, Moves.SPORE ],
[Species.PARAS]: [ Moves.LEECH_LIFE, Moves.HORN_LEECH, Moves.CRABHAMMER, Moves.SAPPY_SEED ], [Species.PARAS]: [ Moves.LEECH_LIFE, Moves.HORN_LEECH, Moves.CRABHAMMER, Moves.SAPPY_SEED ],
[Species.VENONAT]: [ Moves.SLUDGE_BOMB, Moves.MOONLIGHT, Moves.EARTH_POWER, Moves.MYSTICAL_POWER ], [Species.VENONAT]: [ Moves.SLUDGE_BOMB, Moves.TOXIC_THREAD, Moves.EARTH_POWER, Moves.STORED_POWER ],
[Species.DIGLETT]: [ Moves.TRIPLE_DIVE, Moves.SWORDS_DANCE, Moves.TRIPLE_AXEL, Moves.HEADLONG_RUSH ], [Species.DIGLETT]: [ Moves.TRIPLE_DIVE, Moves.SWORDS_DANCE, Moves.TRIPLE_AXEL, Moves.HEADLONG_RUSH ],
[Species.MEOWTH]: [ Moves.COVET, Moves.SWORDS_DANCE, Moves.DOUBLE_KICK, Moves.TAIL_SLAP ], [Species.MEOWTH]: [ Moves.HEART_STAMP, Moves.SWORDS_DANCE, Moves.SIZZLY_SLIDE, Moves.TAIL_SLAP ],
[Species.PSYDUCK]: [ Moves.SPLISHY_SPLASH, Moves.AQUA_STEP, Moves.AURA_SPHERE, Moves.MYSTICAL_POWER ], [Species.PSYDUCK]: [ Moves.FROST_BREATH, Moves.AQUA_STEP, Moves.MYSTICAL_POWER, Moves.BOUNCY_BUBBLE ],
[Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.PLAY_ROUGH, Moves.METEOR_MASH, Moves.NO_RETREAT ], [Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.NO_RETREAT ],
[Species.GROWLITHE]: [ Moves.ZING_ZAP, Moves.PARTING_SHOT, Moves.MORNING_SUN, Moves.SACRED_FIRE ], [Species.GROWLITHE]: [ Moves.ZING_ZAP, Moves.PARTING_SHOT, Moves.MORNING_SUN, Moves.SACRED_FIRE ],
[Species.POLIWAG]: [ Moves.SLACK_OFF, Moves.WILDBOLT_STORM, Moves.DRAIN_PUNCH, Moves.SURGING_STRIKES ], [Species.POLIWAG]: [ Moves.SLACK_OFF, Moves.WILDBOLT_STORM, Moves.DRAIN_PUNCH, Moves.SURGING_STRIKES ],
[Species.ABRA]: [ Moves.AURA_SPHERE, Moves.BADDY_BAD, Moves.THUNDERBOLT, Moves.PSYSTRIKE ], [Species.ABRA]: [ Moves.AURA_SPHERE, Moves.BADDY_BAD, Moves.ICE_BEAM, Moves.PSYSTRIKE ],
[Species.MACHOP]: [ Moves.COMBAT_TORQUE, Moves.METEOR_MASH, Moves.MOUNTAIN_GALE, Moves.FISSURE ], [Species.MACHOP]: [ Moves.COMBAT_TORQUE, Moves.METEOR_MASH, Moves.MOUNTAIN_GALE, Moves.FISSURE ],
[Species.BELLSPROUT]: [ Moves.SOLAR_BLADE, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.VICTORY_DANCE ], [Species.BELLSPROUT]: [ Moves.SOLAR_BLADE, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.VICTORY_DANCE ],
[Species.TENTACOOL]: [ Moves.BANEFUL_BUNKER, Moves.STRENGTH_SAP, Moves.BOUNCY_BUBBLE, Moves.MALIGNANT_CHAIN ], [Species.TENTACOOL]: [ Moves.BANEFUL_BUNKER, Moves.STRENGTH_SAP, Moves.BOUNCY_BUBBLE, Moves.MALIGNANT_CHAIN ],
[Species.GEODUDE]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.SHORE_UP, Moves.HEAD_SMASH ], [Species.GEODUDE]: [ Moves.FLARE_BLITZ, Moves.HEAD_SMASH, Moves.SHORE_UP, Moves.SHELL_SMASH ],
[Species.PONYTA]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.SWORDS_DANCE, Moves.VOLT_TACKLE ], [Species.PONYTA]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.SWORDS_DANCE, Moves.VOLT_TACKLE ],
[Species.SLOWPOKE]: [ Moves.BOUNCY_BUBBLE, Moves.FLAMETHROWER, Moves.MYSTICAL_POWER, Moves.SHED_TAIL ], [Species.SLOWPOKE]: [ Moves.BOUNCY_BUBBLE, Moves.FLAMETHROWER, Moves.MYSTICAL_POWER, Moves.SHED_TAIL ],
[Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.BODY_PRESS, Moves.ICE_BEAM, Moves.THUNDERCLAP ], [Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.FLAMETHROWER, Moves.ICE_BEAM, Moves.THUNDERCLAP ],
[Species.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.ROOST, Moves.VICTORY_DANCE ], [Species.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.DRILL_RUN, Moves.VICTORY_DANCE ],
[Species.DODUO]: [ Moves.TRIPLE_AXEL, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.DODUO]: [ Moves.TRIPLE_AXEL, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ],
[Species.SEEL]: [ Moves.FREEZE_DRY, Moves.BOUNCY_BUBBLE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ], [Species.SEEL]: [ Moves.FREEZE_DRY, Moves.BOUNCY_BUBBLE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ],
[Species.GRIMER]: [ Moves.SUCKER_PUNCH, Moves.CURSE, Moves.STRENGTH_SAP, Moves.NOXIOUS_TORQUE ], [Species.GRIMER]: [ Moves.SUCKER_PUNCH, Moves.CURSE, Moves.STRENGTH_SAP, Moves.NOXIOUS_TORQUE ],
[Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ], [Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ],
[Species.GASTLY]: [ Moves.SLUDGE_BOMB, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE ], [Species.GASTLY]: [ Moves.SLUDGE_BOMB, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE ],
[Species.ONIX]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.HEAVY_SLAM, Moves.DIAMOND_STORM ], [Species.ONIX]: [ Moves.SHORE_UP, Moves.THOUSAND_WAVES, Moves.COIL, Moves.DIAMOND_STORM ],
[Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ], [Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ],
[Species.KRABBY]: [ Moves.FIRE_LASH, Moves.PLAY_ROUGH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ], [Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.JET_PUNCH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ],
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], [Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.FUSION_FLARE, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ], [Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ],
[Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ], [Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ],
[Species.LICKITUNG]: [ Moves.BODY_SLAM, Moves.FIRE_LASH, Moves.GRAV_APPLE, Moves.MILK_DRINK ], [Species.LICKITUNG]: [ Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.SLACK_OFF, Moves.MAGICAL_TORQUE ],
[Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ], [Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ],
[Species.RHYHORN]: [ Moves.SHORE_UP, Moves.ICE_HAMMER, Moves.ACCELEROCK, Moves.HEAD_SMASH ], [Species.RHYHORN]: [ Moves.SHORE_UP, Moves.ICE_HAMMER, Moves.ACCELEROCK, Moves.HEAD_SMASH ],
[Species.TANGELA]: [ Moves.STRENGTH_SAP, Moves.SNAP_TRAP, Moves.PARTING_SHOT, Moves.SAPPY_SEED ], [Species.TANGELA]: [ Moves.STRENGTH_SAP, Moves.SNAP_TRAP, Moves.PARTING_SHOT, Moves.SAPPY_SEED ],
[Species.KANGASKHAN]: [ Moves.POWER_UP_PUNCH, Moves.TRAILBLAZE, Moves.FACADE, Moves.SEISMIC_TOSS ], [Species.KANGASKHAN]: [ Moves.POWER_UP_PUNCH, Moves.TRAILBLAZE, Moves.FACADE, Moves.SEISMIC_TOSS ],
[Species.HORSEA]: [ Moves.SNIPE_SHOT, Moves.FROST_BREATH, Moves.HURRICANE, Moves.SPACIAL_REND ], [Species.HORSEA]: [ Moves.SNIPE_SHOT, Moves.FROST_BREATH, Moves.HURRICANE, Moves.SPACIAL_REND ],
[Species.GOLDEEN]: [ Moves.DRILL_RUN, Moves.FLIP_TURN, Moves.DRAGON_DANCE, Moves.FISHIOUS_REND ], [Species.GOLDEEN]: [ Moves.GLACIAL_LANCE, Moves.SUPERCELL_SLAM, Moves.DRAGON_DANCE, Moves.FISHIOUS_REND ],
[Species.STARYU]: [ Moves.CALM_MIND, Moves.BOUNCY_BUBBLE, Moves.MOONBLAST, Moves.MYSTICAL_POWER ], [Species.STARYU]: [ Moves.CALM_MIND, Moves.BOUNCY_BUBBLE, Moves.MOONBLAST, Moves.MYSTICAL_POWER ],
[Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.BUG_BITE, Moves.STORM_THROW, Moves.DOUBLE_IRON_BASH ], [Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.BUG_BITE, Moves.STORM_THROW, Moves.DOUBLE_IRON_BASH ],
[Species.PINSIR]: [ Moves.EARTHQUAKE, Moves.LEECH_LIFE, Moves.CLOSE_COMBAT, Moves.EXTREME_SPEED ], [Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ],
[Species.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.BLAZING_TORQUE, Moves.LIQUIDATION, Moves.COMBAT_TORQUE ], [Species.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.LIQUIDATION, Moves.COMBAT_TORQUE ],
[Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.DRAGON_ASCENT, Moves.SURGING_STRIKES ], [Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.DRAGON_ASCENT, Moves.SURGING_STRIKES ],
[Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SHELL_SMASH, Moves.STEAM_ERUPTION ], [Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SCALD, Moves.SHELL_SMASH ],
[Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ], [Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ],
[Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ], [Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ],
[Species.PORYGON]: [ Moves.THUNDERCLAP, Moves.AURA_SPHERE, Moves.FLAMETHROWER, Moves.TECHNO_BLAST ], [Species.PORYGON]: [ Moves.THUNDERCLAP, Moves.AURA_SPHERE, Moves.FLAMETHROWER, Moves.TECHNO_BLAST ],
@ -68,56 +68,56 @@ export const speciesEggMoves = {
[Species.KABUTO]: [ Moves.CEASELESS_EDGE, Moves.HIGH_HORSEPOWER, Moves.TRIPLE_DIVE, Moves.MIGHTY_CLEAVE ], [Species.KABUTO]: [ Moves.CEASELESS_EDGE, Moves.HIGH_HORSEPOWER, Moves.TRIPLE_DIVE, Moves.MIGHTY_CLEAVE ],
[Species.AERODACTYL]: [ Moves.FLOATY_FALL, Moves.FLARE_BLITZ, Moves.SWORDS_DANCE, Moves.MIGHTY_CLEAVE ], [Species.AERODACTYL]: [ Moves.FLOATY_FALL, Moves.FLARE_BLITZ, Moves.SWORDS_DANCE, Moves.MIGHTY_CLEAVE ],
[Species.ARTICUNO]: [ Moves.EARTH_POWER, Moves.CALM_MIND, Moves.AURORA_VEIL, Moves.AEROBLAST ], [Species.ARTICUNO]: [ Moves.EARTH_POWER, Moves.CALM_MIND, Moves.AURORA_VEIL, Moves.AEROBLAST ],
[Species.ZAPDOS]: [ Moves.WEATHER_BALL, Moves.CALM_MIND, Moves.SANDSEAR_STORM, Moves.ELECTRO_SHOT ], [Species.ZAPDOS]: [ Moves.BLEAKWIND_STORM, Moves.CALM_MIND, Moves.SANDSEAR_STORM, Moves.ELECTRO_SHOT ],
[Species.MOLTRES]: [ Moves.SCORCHING_SANDS, Moves.CALM_MIND, Moves.AEROBLAST, Moves.TORCH_SONG ], [Species.MOLTRES]: [ Moves.SCORCHING_SANDS, Moves.CALM_MIND, Moves.AEROBLAST, Moves.TORCH_SONG ],
[Species.DRATINI]: [ Moves.DRAGON_HAMMER, Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.GIGATON_HAMMER ], [Species.DRATINI]: [ Moves.DRAGON_HAMMER, Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.GIGATON_HAMMER ],
[Species.MEWTWO]: [ Moves.METEOR_MASH, Moves.MOONBLAST, Moves.THUNDEROUS_KICK, Moves.PHOTON_GEYSER ], [Species.MEWTWO]: [ Moves.METEOR_MASH, Moves.MOONBLAST, Moves.THUNDEROUS_KICK, Moves.PHOTON_GEYSER ],
[Species.MEW]: [ Moves.PHOTON_GEYSER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE, Moves.SHELL_SMASH ], [Species.MEW]: [ Moves.PHOTON_GEYSER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE, Moves.SHELL_SMASH ],
[Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ], [Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ],
[Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.SCORCHING_SANDS, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ], [Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ],
[Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.FISHIOUS_REND ], [Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.FISHIOUS_REND ],
[Species.SENTRET]: [ Moves.TIDY_UP, Moves.THIEF, Moves.NUZZLE, Moves.EXTREME_SPEED ], [Species.SENTRET]: [ Moves.TIDY_UP, Moves.FAKE_OUT, Moves.NUZZLE, Moves.EXTREME_SPEED ],
[Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ], [Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ],
[Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.THIEF, Moves.PARTING_SHOT, Moves.SPORE ], [Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.THIEF, Moves.PARTING_SHOT, Moves.SPORE ],
[Species.SPINARAK]: [ Moves.PARTING_SHOT, Moves.ATTACK_ORDER, Moves.GASTRO_ACID, Moves.STRENGTH_SAP ], [Species.SPINARAK]: [ Moves.PARTING_SHOT, Moves.ATTACK_ORDER, Moves.GASTRO_ACID, Moves.STRENGTH_SAP ],
[Species.CHINCHOU]: [ Moves.THUNDERCLAP, Moves.BOUNCY_BUBBLE, Moves.THUNDER_CAGE, Moves.TAIL_GLOW ], [Species.CHINCHOU]: [ Moves.THUNDERCLAP, Moves.BOUNCY_BUBBLE, Moves.THUNDER_CAGE, Moves.TAIL_GLOW ],
[Species.PICHU]: [ Moves.RISING_VOLTAGE, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.THUNDERCLAP ], [Species.PICHU]: [ Moves.MOONBLAST, Moves.TRIPLE_AXEL, Moves.FIERY_DANCE, Moves.AURA_WHEEL ],
[Species.CLEFFA]: [ Moves.CALM_MIND, Moves.EARTH_POWER, Moves.WISH, Moves.LIGHT_OF_RUIN ], [Species.CLEFFA]: [ Moves.CALM_MIND, Moves.EARTH_POWER, Moves.WISH, Moves.LIGHT_OF_RUIN ],
[Species.IGGLYBUFF]: [ Moves.DRAIN_PUNCH, Moves.GRAV_APPLE, Moves.SOFT_BOILED, Moves.EXTREME_SPEED ], [Species.IGGLYBUFF]: [ Moves.DRAIN_PUNCH, Moves.GRAV_APPLE, Moves.SOFT_BOILED, Moves.EXTREME_SPEED ],
[Species.TOGEPI]: [ Moves.SCORCHING_SANDS, Moves.ROOST, Moves.RELIC_SONG, Moves.FIERY_DANCE ], [Species.TOGEPI]: [ Moves.SCORCHING_SANDS, Moves.ROOST, Moves.RELIC_SONG, Moves.FIERY_DANCE ],
[Species.NATU]: [ Moves.AEROBLAST, Moves.ROOST, Moves.CALM_MIND, Moves.LUMINA_CRASH ], [Species.NATU]: [ Moves.AEROBLAST, Moves.ROOST, Moves.MOONBLAST, Moves.LUMINA_CRASH ],
[Species.MAREEP]: [ Moves.ICE_BEAM, Moves.PARABOLIC_CHARGE, Moves.CORE_ENFORCER, Moves.TAIL_GLOW ], [Species.MAREEP]: [ Moves.ICE_BEAM, Moves.PARABOLIC_CHARGE, Moves.CORE_ENFORCER, Moves.TAIL_GLOW ],
[Species.HOPPIP]: [ Moves.FLOATY_FALL, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SPORE ], [Species.HOPPIP]: [ Moves.FLOATY_FALL, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SPORE ],
[Species.AIPOM]: [ Moves.TIDY_UP, Moves.STORM_THROW, Moves.FAKE_OUT, Moves.POPULATION_BOMB ], [Species.AIPOM]: [ Moves.TIDY_UP, Moves.STORM_THROW, Moves.FAKE_OUT, Moves.POPULATION_BOMB ],
[Species.SUNKERN]: [ Moves.SPORE, Moves.SAPPY_SEED, Moves.FIERY_DANCE, Moves.HYDRO_STEAM ], [Species.SUNKERN]: [ Moves.SPORE, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.HYDRO_STEAM ],
[Species.YANMA]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.HEAT_WAVE, Moves.BLEAKWIND_STORM ], [Species.YANMA]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.HEAT_WAVE, Moves.BLEAKWIND_STORM ],
[Species.WOOPER]: [ Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.CURSE, Moves.SURGING_STRIKES ], [Species.WOOPER]: [ Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.CURSE, Moves.SURGING_STRIKES ],
[Species.MURKROW]: [ Moves.TRIPLE_ARROWS, Moves.FLOATY_FALL, Moves.TIDY_UP, Moves.WICKED_BLOW ], [Species.MURKROW]: [ Moves.TRIPLE_ARROWS, Moves.FLOATY_FALL, Moves.TIDY_UP, Moves.WICKED_BLOW ],
[Species.MISDREAVUS]: [ Moves.TAKE_HEART, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], [Species.MISDREAVUS]: [ Moves.TAKE_HEART, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ],
[Species.UNOWN]: [ Moves.NATURE_POWER, Moves.COSMIC_POWER, Moves.ANCIENT_POWER, Moves.MYSTICAL_POWER ], [Species.UNOWN]: [ Moves.NATURE_POWER, Moves.COSMIC_POWER, Moves.ANCIENT_POWER, Moves.MYSTICAL_POWER ],
[Species.GIRAFARIG]: [ Moves.MYSTICAL_POWER, Moves.NIGHT_DAZE, Moves.RECOVER, Moves.BOOMBURST ], [Species.GIRAFARIG]: [ Moves.MYSTICAL_POWER, Moves.NIGHT_DAZE, Moves.RECOVER, Moves.BOOMBURST ],
[Species.PINECO]: [ Moves.METAL_BURST, Moves.LUNGE, Moves.BODY_PRESS, Moves.SHORE_UP ], [Species.PINECO]: [ Moves.METAL_BURST, Moves.SHORE_UP, Moves.BODY_PRESS, Moves.DIAMOND_STORM ],
[Species.DUNSPARCE]: [ Moves.BODY_SLAM, Moves.MAGICAL_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ], [Species.DUNSPARCE]: [ Moves.WICKED_TORQUE, Moves.MAGICAL_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ],
[Species.GLIGAR]: [ Moves.FLOATY_FALL, Moves.THOUSAND_WAVES, Moves.ROOST, Moves.MIGHTY_CLEAVE ], [Species.GLIGAR]: [ Moves.FLOATY_FALL, Moves.THOUSAND_WAVES, Moves.ROOST, Moves.MIGHTY_CLEAVE ],
[Species.SNUBBULL]: [ Moves.FACADE, Moves.EARTHQUAKE, Moves.SWORDS_DANCE, Moves.EXTREME_SPEED ], [Species.SNUBBULL]: [ Moves.FACADE, Moves.EARTHQUAKE, Moves.SWORDS_DANCE, Moves.EXTREME_SPEED ],
[Species.QWILFISH]: [ Moves.BARB_BARRAGE, Moves.BANEFUL_BUNKER, Moves.KNOCK_OFF, Moves.FISHIOUS_REND ], [Species.QWILFISH]: [ Moves.BARB_BARRAGE, Moves.BANEFUL_BUNKER, Moves.KNOCK_OFF, Moves.FISHIOUS_REND ],
[Species.SHUCKLE]: [ Moves.STUFF_CHEEKS, Moves.HEAL_ORDER, Moves.BODY_PRESS, Moves.SALT_CURE ], [Species.SHUCKLE]: [ Moves.STUFF_CHEEKS, Moves.HEAL_ORDER, Moves.BODY_PRESS, Moves.SALT_CURE ],
[Species.HERACROSS]: [ Moves.ROCK_BLAST, Moves.FIRST_IMPRESSION, Moves.ICICLE_SPEAR, Moves.DRAGON_DANCE ], [Species.HERACROSS]: [ Moves.ROCK_BLAST, Moves.FIRST_IMPRESSION, Moves.ICICLE_SPEAR, Moves.DRAGON_DANCE ],
[Species.SNEASEL]: [ Moves.DIRE_CLAW, Moves.STORM_THROW, Moves.TRIPLE_AXEL, Moves.WICKED_BLOW ], [Species.SNEASEL]: [ Moves.DIRE_CLAW, Moves.STORM_THROW, Moves.TRIPLE_AXEL, Moves.WICKED_BLOW ],
[Species.TEDDIURSA]: [ Moves.MOUNTAIN_GALE, Moves.RAGING_BULL, Moves.SLACK_OFF, Moves.PRECIPICE_BLADES ], [Species.TEDDIURSA]: [ Moves.MOUNTAIN_GALE, Moves.FAKE_OUT, Moves.SLACK_OFF, Moves.PRECIPICE_BLADES ],
[Species.SLUGMA]: [ Moves.BURNING_BULWARK, Moves.POWER_GEM, Moves.SOLAR_BEAM, Moves.MAGMA_STORM ], [Species.SLUGMA]: [ Moves.BURNING_BULWARK, Moves.POWER_GEM, Moves.SOLAR_BEAM, Moves.MAGMA_STORM ],
[Species.SWINUB]: [ Moves.SLACK_OFF, Moves.LANDS_WRATH, Moves.MIGHTY_CLEAVE, Moves.GLACIAL_LANCE ], [Species.SWINUB]: [ Moves.SLACK_OFF, Moves.LANDS_WRATH, Moves.MIGHTY_CLEAVE, Moves.GLACIAL_LANCE ],
[Species.CORSOLA]: [ Moves.SCALD, Moves.FREEZE_DRY, Moves.STRENGTH_SAP, Moves.SALT_CURE ], [Species.CORSOLA]: [ Moves.SCALD, Moves.FREEZE_DRY, Moves.STRENGTH_SAP, Moves.SALT_CURE ],
[Species.REMORAID]: [ Moves.WATER_SHURIKEN, Moves.TAKE_HEART, Moves.SHELL_SIDE_ARM, Moves.BOUNCY_BUBBLE ], [Species.REMORAID]: [ Moves.WATER_SHURIKEN, Moves.TAKE_HEART, Moves.SHELL_SIDE_ARM, Moves.BOUNCY_BUBBLE ],
[Species.DELIBIRD]: [ Moves.DRILL_RUN, Moves.FLOATY_FALL, Moves.VICTORY_DANCE, Moves.GLACIAL_LANCE ], [Species.DELIBIRD]: [ Moves.BONEMERANG, Moves.FLOATY_FALL, Moves.VICTORY_DANCE, Moves.GLACIAL_LANCE ],
[Species.SKARMORY]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.BEAK_BLAST ], [Species.SKARMORY]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.BEAK_BLAST ],
[Species.HOUNDOUR]: [ Moves.HEAT_WAVE, Moves.FIERY_WRATH, Moves.SOLAR_BEAM, Moves.HYDRO_STEAM ], [Species.HOUNDOUR]: [ Moves.MOONLIGHT, Moves.FIERY_WRATH, Moves.SECRET_SWORD, Moves.HYDRO_STEAM ],
[Species.PHANPY]: [ Moves.SHORE_UP, Moves.SWORDS_DANCE, Moves.ICICLE_CRASH, Moves.COLLISION_COURSE ], [Species.PHANPY]: [ Moves.SHORE_UP, Moves.SWORDS_DANCE, Moves.ICICLE_CRASH, Moves.COLLISION_COURSE ],
[Species.STANTLER]: [ Moves.THUNDEROUS_KICK, Moves.HYPER_VOICE, Moves.BULK_UP, Moves.PHOTON_GEYSER ], [Species.STANTLER]: [ Moves.THUNDEROUS_KICK, Moves.PHOTON_GEYSER, Moves.SWORDS_DANCE, Moves.BOOMBURST ],
[Species.SMEARGLE]: [ Moves.CONVERSION, Moves.BURNING_BULWARK, Moves.SALT_CURE, Moves.DARK_VOID ], [Species.SMEARGLE]: [ Moves.CONVERSION, Moves.BURNING_BULWARK, Moves.SALT_CURE, Moves.DARK_VOID ],
[Species.TYROGUE]: [ Moves.VICTORY_DANCE, Moves.THUNDEROUS_KICK, Moves.METEOR_MASH, Moves.WICKED_BLOW ], [Species.TYROGUE]: [ Moves.VICTORY_DANCE, Moves.THUNDEROUS_KICK, Moves.METEOR_MASH, Moves.WICKED_BLOW ],
[Species.SMOOCHUM]: [ Moves.EXPANDING_FORCE, Moves.AURA_SPHERE, Moves.FREEZE_DRY, Moves.QUIVER_DANCE ], [Species.SMOOCHUM]: [ Moves.EXPANDING_FORCE, Moves.AURA_SPHERE, Moves.FREEZE_DRY, Moves.QUIVER_DANCE ],
[Species.ELEKID]: [ Moves.BLAZING_TORQUE, Moves.TIDY_UP, Moves.MOUNTAIN_GALE, Moves.ZIPPY_ZAP ], [Species.ELEKID]: [ Moves.FIRE_LASH, Moves.ZING_ZAP, Moves.MOUNTAIN_GALE, Moves.SHIFT_GEAR ],
[Species.MAGBY]: [ Moves.THUNDERCLAP, Moves.EARTH_POWER, Moves.ARMOR_CANNON, Moves.FLEUR_CANNON ], [Species.MAGBY]: [ Moves.THUNDERCLAP, Moves.EARTH_POWER, Moves.ARMOR_CANNON, Moves.FLEUR_CANNON ],
[Species.MILTANK]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.YAWN, Moves.SIZZLY_SLIDE ], [Species.MILTANK]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.YAWN, Moves.SIZZLY_SLIDE ],
[Species.RAIKOU]: [ Moves.PARABOLIC_CHARGE, Moves.NASTY_PLOT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], [Species.RAIKOU]: [ Moves.PARABOLIC_CHARGE, Moves.NASTY_PLOT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
@ -125,10 +125,10 @@ export const speciesEggMoves = {
[Species.SUICUNE]: [ Moves.RECOVER, Moves.NASTY_PLOT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], [Species.SUICUNE]: [ Moves.RECOVER, Moves.NASTY_PLOT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
[Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.SHORE_UP, Moves.DIAMOND_STORM ], [Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.SHORE_UP, Moves.DIAMOND_STORM ],
[Species.LUGIA]: [ Moves.NASTY_PLOT, Moves.LUMINA_CRASH, Moves.AURA_SPHERE, Moves.OBLIVION_WING ], [Species.LUGIA]: [ Moves.NASTY_PLOT, Moves.LUMINA_CRASH, Moves.AURA_SPHERE, Moves.OBLIVION_WING ],
[Species.HO_OH]: [ Moves.FLOATY_FALL, Moves.PRECIPICE_BLADES, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ], [Species.HO_OH]: [ Moves.BRAVE_BIRD, Moves.DRAGON_DANCE, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ],
[Species.CELEBI]: [ Moves.PHOTON_GEYSER, Moves.MATCHA_GOTCHA, Moves.REVIVAL_BLESSING, Moves.QUIVER_DANCE ], [Species.CELEBI]: [ Moves.PHOTON_GEYSER, Moves.MATCHA_GOTCHA, Moves.REVIVAL_BLESSING, Moves.QUIVER_DANCE ],
[Species.TREECKO]: [ Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.SECRET_SWORD, Moves.DRAGON_ENERGY ], [Species.TREECKO]: [ Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.SECRET_SWORD, Moves.DRAGON_ENERGY ],
[Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.V_CREATE ], [Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.BURNING_BULWARK, Moves.V_CREATE ],
[Species.MUDKIP]: [ Moves.SHORE_UP, Moves.MOUNTAIN_GALE, Moves.BULK_UP, Moves.SURGING_STRIKES ], [Species.MUDKIP]: [ Moves.SHORE_UP, Moves.MOUNTAIN_GALE, Moves.BULK_UP, Moves.SURGING_STRIKES ],
[Species.POOCHYENA]: [ Moves.JAW_LOCK, Moves.CLOSE_COMBAT, Moves.DIRE_CLAW, Moves.NO_RETREAT ], [Species.POOCHYENA]: [ Moves.JAW_LOCK, Moves.CLOSE_COMBAT, Moves.DIRE_CLAW, Moves.NO_RETREAT ],
[Species.ZIGZAGOON]: [ Moves.EXTREME_SPEED, Moves.NUZZLE, Moves.HIGH_HORSEPOWER, Moves.TIDY_UP ], [Species.ZIGZAGOON]: [ Moves.EXTREME_SPEED, Moves.NUZZLE, Moves.HIGH_HORSEPOWER, Moves.TIDY_UP ],
@ -154,7 +154,7 @@ export const speciesEggMoves = {
[Species.ELECTRIKE]: [ Moves.RISING_VOLTAGE, Moves.FLAMETHROWER, Moves.NASTY_PLOT, Moves.ICE_BEAM ], [Species.ELECTRIKE]: [ Moves.RISING_VOLTAGE, Moves.FLAMETHROWER, Moves.NASTY_PLOT, Moves.ICE_BEAM ],
[Species.PLUSLE]: [ Moves.FLAMETHROWER, Moves.GLITZY_GLOW, Moves.SPLISHY_SPLASH, Moves.TAIL_GLOW ], [Species.PLUSLE]: [ Moves.FLAMETHROWER, Moves.GLITZY_GLOW, Moves.SPLISHY_SPLASH, Moves.TAIL_GLOW ],
[Species.MINUN]: [ Moves.ICE_BEAM, Moves.BADDY_BAD, Moves.SPARKLY_SWIRL, Moves.TAIL_GLOW ], [Species.MINUN]: [ Moves.ICE_BEAM, Moves.BADDY_BAD, Moves.SPARKLY_SWIRL, Moves.TAIL_GLOW ],
[Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.LUNGE, Moves.DECORATE, Moves.VICTORY_DANCE ], [Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.STICKY_WEB, Moves.DECORATE, Moves.VICTORY_DANCE ],
[Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ], [Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ],
[Species.GULPIN]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.MALIGNANT_CHAIN ], [Species.GULPIN]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.MALIGNANT_CHAIN ],
[Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.SWORDS_DANCE, Moves.OBSTRUCT, Moves.SURGING_STRIKES ], [Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.SWORDS_DANCE, Moves.OBSTRUCT, Moves.SURGING_STRIKES ],
@ -168,7 +168,7 @@ export const speciesEggMoves = {
[Species.SWABLU]: [ Moves.ROOST, Moves.NASTY_PLOT, Moves.FLOATY_FALL, Moves.BOOMBURST ], [Species.SWABLU]: [ Moves.ROOST, Moves.NASTY_PLOT, Moves.FLOATY_FALL, Moves.BOOMBURST ],
[Species.ZANGOOSE]: [ Moves.FACADE, Moves.HIGH_HORSEPOWER, Moves.EXTREME_SPEED, Moves.TIDY_UP ], [Species.ZANGOOSE]: [ Moves.FACADE, Moves.HIGH_HORSEPOWER, Moves.EXTREME_SPEED, Moves.TIDY_UP ],
[Species.SEVIPER]: [ Moves.ICE_BEAM, Moves.BITTER_BLADE, Moves.SUCKER_PUNCH, Moves.NO_RETREAT ], [Species.SEVIPER]: [ Moves.ICE_BEAM, Moves.BITTER_BLADE, Moves.SUCKER_PUNCH, Moves.NO_RETREAT ],
[Species.LUNATONE]: [ Moves.POWER_GEM, Moves.NIGHT_DAZE, Moves.SHELL_SMASH, Moves.LUMINA_CRASH ], [Species.LUNATONE]: [ Moves.POWER_GEM, Moves.MOONGEIST_BEAM, Moves.SHELL_SMASH, Moves.LUMINA_CRASH ],
[Species.SOLROCK]: [ Moves.PSYSHIELD_BASH, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH, Moves.SACRED_FIRE ], [Species.SOLROCK]: [ Moves.PSYSHIELD_BASH, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH, Moves.SACRED_FIRE ],
[Species.BARBOACH]: [ Moves.DRAGON_DANCE, Moves.ZING_ZAP, Moves.ICE_SPINNER, Moves.SURGING_STRIKES ], [Species.BARBOACH]: [ Moves.DRAGON_DANCE, Moves.ZING_ZAP, Moves.ICE_SPINNER, Moves.SURGING_STRIKES ],
[Species.CORPHISH]: [ Moves.CEASELESS_EDGE, Moves.JET_PUNCH, Moves.SUCKER_PUNCH, Moves.SHELL_SMASH ], [Species.CORPHISH]: [ Moves.CEASELESS_EDGE, Moves.JET_PUNCH, Moves.SUCKER_PUNCH, Moves.SHELL_SMASH ],
@ -177,7 +177,7 @@ export const speciesEggMoves = {
[Species.ANORITH]: [ Moves.FIRST_IMPRESSION, Moves.LEECH_LIFE, Moves.DRAGON_DANCE, Moves.MIGHTY_CLEAVE ], [Species.ANORITH]: [ Moves.FIRST_IMPRESSION, Moves.LEECH_LIFE, Moves.DRAGON_DANCE, Moves.MIGHTY_CLEAVE ],
[Species.FEEBAS]: [ Moves.CALM_MIND, Moves.FREEZE_DRY, Moves.MOONBLAST, Moves.STEAM_ERUPTION ], [Species.FEEBAS]: [ Moves.CALM_MIND, Moves.FREEZE_DRY, Moves.MOONBLAST, Moves.STEAM_ERUPTION ],
[Species.CASTFORM]: [ Moves.BOOMBURST, Moves.HYDRO_STEAM, Moves.ERUPTION, Moves.QUIVER_DANCE ], [Species.CASTFORM]: [ Moves.BOOMBURST, Moves.HYDRO_STEAM, Moves.ERUPTION, Moves.QUIVER_DANCE ],
[Species.KECLEON]: [ Moves.DRAIN_PUNCH, Moves.DRAGON_DANCE, Moves.EXTREME_SPEED, Moves.MULTI_ATTACK ], [Species.KECLEON]: [ Moves.ZIPPY_ZAP, Moves.COIL, Moves.EXTREME_SPEED, Moves.MULTI_ATTACK ],
[Species.SHUPPET]: [ Moves.STORM_THROW, Moves.TIDY_UP, Moves.PARTING_SHOT, Moves.SPECTRAL_THIEF ], [Species.SHUPPET]: [ Moves.STORM_THROW, Moves.TIDY_UP, Moves.PARTING_SHOT, Moves.SPECTRAL_THIEF ],
[Species.DUSKULL]: [ Moves.BULK_UP, Moves.DRAIN_PUNCH, Moves.STRENGTH_SAP, Moves.RAGE_FIST ], [Species.DUSKULL]: [ Moves.BULK_UP, Moves.DRAIN_PUNCH, Moves.STRENGTH_SAP, Moves.RAGE_FIST ],
[Species.TROPIUS]: [ Moves.STUFF_CHEEKS, Moves.EARTH_POWER, Moves.APPLE_ACID, Moves.SAPPY_SEED ], [Species.TROPIUS]: [ Moves.STUFF_CHEEKS, Moves.EARTH_POWER, Moves.APPLE_ACID, Moves.SAPPY_SEED ],
@ -189,13 +189,13 @@ export const speciesEggMoves = {
[Species.RELICANTH]: [ Moves.DRAGON_DANCE, Moves.SHORE_UP, Moves.WAVE_CRASH, Moves.DIAMOND_STORM ], [Species.RELICANTH]: [ Moves.DRAGON_DANCE, Moves.SHORE_UP, Moves.WAVE_CRASH, Moves.DIAMOND_STORM ],
[Species.LUVDISC]: [ Moves.BATON_PASS, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ], [Species.LUVDISC]: [ Moves.BATON_PASS, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ],
[Species.BAGON]: [ Moves.FLOATY_FALL, Moves.FIRE_LASH, Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH ], [Species.BAGON]: [ Moves.FLOATY_FALL, Moves.FIRE_LASH, Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH ],
[Species.BELDUM]: [ Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.TRIPLE_AXEL, Moves.SHIFT_GEAR ], [Species.BELDUM]: [ Moves.HEADLONG_RUSH, Moves.DRAIN_PUNCH, Moves.TRIPLE_AXEL, Moves.SHIFT_GEAR ],
[Species.REGIROCK]: [ Moves.STONE_AXE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SALT_CURE ], [Species.REGIROCK]: [ Moves.STONE_AXE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SALT_CURE ],
[Species.REGICE]: [ Moves.EARTH_POWER, Moves.TAKE_HEART, Moves.RECOVER, Moves.FREEZE_DRY ], [Species.REGICE]: [ Moves.EARTH_POWER, Moves.TAKE_HEART, Moves.RECOVER, Moves.FREEZE_DRY ],
[Species.REGISTEEL]: [ Moves.BODY_PRESS, Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.GIGATON_HAMMER ], [Species.REGISTEEL]: [ Moves.BODY_PRESS, Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.GIGATON_HAMMER ],
[Species.LATIAS]: [ Moves.CORE_ENFORCER, Moves.FUSION_FLARE, Moves.SPARKLY_SWIRL, Moves.MYSTICAL_POWER ], [Species.LATIAS]: [ Moves.CORE_ENFORCER, Moves.FUSION_FLARE, Moves.SPARKLY_SWIRL, Moves.MYSTICAL_POWER ],
[Species.LATIOS]: [ Moves.CORE_ENFORCER, Moves.BLUE_FLARE, Moves.NASTY_PLOT, Moves.TACHYON_CUTTER ], [Species.LATIOS]: [ Moves.CORE_ENFORCER, Moves.BLUE_FLARE, Moves.NASTY_PLOT, Moves.TACHYON_CUTTER ],
[Species.KYOGRE]: [ Moves.BOUNCY_BUBBLE, Moves.HURRICANE, Moves.FREEZE_DRY, Moves.ELECTRO_SHOT ], [Species.KYOGRE]: [ Moves.RECOVER, Moves.HURRICANE, Moves.FREEZY_FROST, Moves.WILDBOLT_STORM ],
[Species.GROUDON]: [ Moves.STONE_AXE, Moves.SOLAR_BLADE, Moves.MORNING_SUN, Moves.SACRED_FIRE ], [Species.GROUDON]: [ Moves.STONE_AXE, Moves.SOLAR_BLADE, Moves.MORNING_SUN, Moves.SACRED_FIRE ],
[Species.RAYQUAZA]: [ Moves.V_CREATE, Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.OBLIVION_WING ], [Species.RAYQUAZA]: [ Moves.V_CREATE, Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.OBLIVION_WING ],
[Species.JIRACHI]: [ Moves.TACHYON_CUTTER, Moves.TRIPLE_ARROWS, Moves.ROCK_SLIDE, Moves.SHELL_SMASH ], [Species.JIRACHI]: [ Moves.TACHYON_CUTTER, Moves.TRIPLE_ARROWS, Moves.ROCK_SLIDE, Moves.SHELL_SMASH ],
@ -206,7 +206,7 @@ export const speciesEggMoves = {
[Species.STARLY]: [ Moves.SWORDS_DANCE, Moves.HEAD_CHARGE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ], [Species.STARLY]: [ Moves.SWORDS_DANCE, Moves.HEAD_CHARGE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ],
[Species.BIDOOF]: [ Moves.EXTREME_SPEED, Moves.COSMIC_POWER, Moves.POWER_TRIP, Moves.AQUA_STEP ], [Species.BIDOOF]: [ Moves.EXTREME_SPEED, Moves.COSMIC_POWER, Moves.POWER_TRIP, Moves.AQUA_STEP ],
[Species.KRICKETOT]: [ Moves.BONEMERANG, Moves.VICTORY_DANCE, Moves.STONE_AXE, Moves.POPULATION_BOMB ], [Species.KRICKETOT]: [ Moves.BONEMERANG, Moves.VICTORY_DANCE, Moves.STONE_AXE, Moves.POPULATION_BOMB ],
[Species.SHINX]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.FACADE, Moves.BOLT_STRIKE ], [Species.SHINX]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.ZIPPY_ZAP, Moves.BOLT_STRIKE ],
[Species.BUDEW]: [ Moves.FIERY_DANCE, Moves.ACID_SPRAY, Moves.BOUNCY_BUBBLE, Moves.QUIVER_DANCE ], [Species.BUDEW]: [ Moves.FIERY_DANCE, Moves.ACID_SPRAY, Moves.BOUNCY_BUBBLE, Moves.QUIVER_DANCE ],
[Species.CRANIDOS]: [ Moves.VOLT_TACKLE, Moves.ACCELEROCK, Moves.FLARE_BLITZ, Moves.SHIFT_GEAR ], [Species.CRANIDOS]: [ Moves.VOLT_TACKLE, Moves.ACCELEROCK, Moves.FLARE_BLITZ, Moves.SHIFT_GEAR ],
[Species.SHIELDON]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.DIAMOND_STORM ], [Species.SHIELDON]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.DIAMOND_STORM ],
@ -223,7 +223,7 @@ export const speciesEggMoves = {
[Species.STUNKY]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.DIRE_CLAW ], [Species.STUNKY]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.DIRE_CLAW ],
[Species.BRONZOR]: [ Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.GLARE, Moves.LUMINA_CRASH ], [Species.BRONZOR]: [ Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.GLARE, Moves.LUMINA_CRASH ],
[Species.BONSLY]: [ Moves.ACCELEROCK, Moves.SWORDS_DANCE, Moves.STRENGTH_SAP, Moves.SAPPY_SEED ], [Species.BONSLY]: [ Moves.ACCELEROCK, Moves.SWORDS_DANCE, Moves.STRENGTH_SAP, Moves.SAPPY_SEED ],
[Species.MIME_JR]: [ Moves.CALM_MIND, Moves.MOONBLAST, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ], [Species.MIME_JR]: [ Moves.CHILLY_RECEPTION, Moves.MOONBLAST, Moves.FROST_BREATH, Moves.LUMINA_CRASH ],
[Species.HAPPINY]: [ Moves.COTTON_GUARD, Moves.SEISMIC_TOSS, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ], [Species.HAPPINY]: [ Moves.COTTON_GUARD, Moves.SEISMIC_TOSS, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ],
[Species.CHATOT]: [ Moves.SPARKLING_ARIA, Moves.TORCH_SONG, Moves.BATON_PASS, Moves.BOOMBURST ], [Species.CHATOT]: [ Moves.SPARKLING_ARIA, Moves.TORCH_SONG, Moves.BATON_PASS, Moves.BOOMBURST ],
[Species.SPIRITOMB]: [ Moves.PARTING_SHOT, Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.SPECTRAL_THIEF ], [Species.SPIRITOMB]: [ Moves.PARTING_SHOT, Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.SPECTRAL_THIEF ],
@ -232,7 +232,7 @@ export const speciesEggMoves = {
[Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.DOUBLE_IRON_BASH ], [Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.DOUBLE_IRON_BASH ],
[Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ], [Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ],
[Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ], [Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ],
[Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_PUNCH, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ], [Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_SPINNER, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ],
[Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ], [Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ],
[Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ], [Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ],
[Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], [Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
@ -243,7 +243,7 @@ export const speciesEggMoves = {
[Species.AZELF]: [ Moves.PSYSTRIKE, Moves.ICE_BEAM, Moves.MOONBLAST, Moves.TAIL_GLOW ], [Species.AZELF]: [ Moves.PSYSTRIKE, Moves.ICE_BEAM, Moves.MOONBLAST, Moves.TAIL_GLOW ],
[Species.DIALGA]: [ Moves.CORE_ENFORCER, Moves.TAKE_HEART, Moves.RECOVER, Moves.MAKE_IT_RAIN ], [Species.DIALGA]: [ Moves.CORE_ENFORCER, Moves.TAKE_HEART, Moves.RECOVER, Moves.MAKE_IT_RAIN ],
[Species.PALKIA]: [ Moves.RECOVER, Moves.TAKE_HEART, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ], [Species.PALKIA]: [ Moves.RECOVER, Moves.TAKE_HEART, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ],
[Species.HEATRAN]: [ Moves.TORCH_SONG, Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.MATCHA_GOTCHA ], [Species.HEATRAN]: [ Moves.MATCHA_GOTCHA, Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.TORCH_SONG ],
[Species.REGIGIGAS]: [ Moves.SKILL_SWAP, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.GIGATON_HAMMER ], [Species.REGIGIGAS]: [ Moves.SKILL_SWAP, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.GIGATON_HAMMER ],
[Species.GIRATINA]: [ Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH, Moves.RECOVER, Moves.SPECTRAL_THIEF ], [Species.GIRATINA]: [ Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH, Moves.RECOVER, Moves.SPECTRAL_THIEF ],
[Species.CRESSELIA]: [ Moves.COSMIC_POWER, Moves.SECRET_SWORD, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ], [Species.CRESSELIA]: [ Moves.COSMIC_POWER, Moves.SECRET_SWORD, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ],
@ -274,18 +274,18 @@ export const speciesEggMoves = {
[Species.THROH]: [ Moves.MACH_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.RAGE_FIST ], [Species.THROH]: [ Moves.MACH_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.RAGE_FIST ],
[Species.SAWK]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.VICTORY_DANCE ], [Species.SAWK]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.VICTORY_DANCE ],
[Species.SEWADDLE]: [ Moves.STONE_AXE, Moves.PSYCHO_CUT, Moves.BITTER_BLADE, Moves.VICTORY_DANCE ], [Species.SEWADDLE]: [ Moves.STONE_AXE, Moves.PSYCHO_CUT, Moves.BITTER_BLADE, Moves.VICTORY_DANCE ],
[Species.VENIPEDE]: [ Moves.SWORDS_DANCE, Moves.LEECH_LIFE, Moves.NOXIOUS_TORQUE, Moves.POWER_TRIP ], [Species.VENIPEDE]: [ Moves.BANEFUL_BUNKER, Moves.LEECH_LIFE, Moves.NOXIOUS_TORQUE, Moves.POWER_TRIP ],
[Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ], [Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ],
[Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ], [Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ],
[Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ], [Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ],
[Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.WICKED_BLOW ], [Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.WICKED_BLOW ],
[Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.EARTHQUAKE, Moves.PYRO_BALL ], [Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.EARTHQUAKE, Moves.PYRO_BALL ],
[Species.MARACTUS]: [ Moves.SCORCHING_SANDS, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ], [Species.MARACTUS]: [ Moves.EARTH_POWER, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ],
[Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ], [Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ],
[Species.SCRAGGY]: [ Moves.SUCKER_PUNCH, Moves.BULLET_PUNCH, Moves.DRAGON_DANCE, Moves.COLLISION_COURSE ], [Species.SCRAGGY]: [ Moves.SUCKER_PUNCH, Moves.BULLET_PUNCH, Moves.NOXIOUS_TORQUE, Moves.VICTORY_DANCE ],
[Species.SIGILYPH]: [ Moves.MOONBLAST, Moves.CALM_MIND, Moves.FREEZING_GLARE, Moves.OBLIVION_WING ], [Species.SIGILYPH]: [ Moves.MOONBLAST, Moves.CALM_MIND, Moves.FREEZING_GLARE, Moves.OBLIVION_WING ],
[Species.YAMASK]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], [Species.YAMASK]: [ Moves.STRENGTH_SAP, Moves.GLARE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ],
[Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ], [Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.AQUA_STEP, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ],
[Species.ARCHEN]: [ Moves.ROOST, Moves.EARTHQUAKE, Moves.FLOATY_FALL, Moves.MIGHTY_CLEAVE ], [Species.ARCHEN]: [ Moves.ROOST, Moves.EARTHQUAKE, Moves.FLOATY_FALL, Moves.MIGHTY_CLEAVE ],
[Species.TRUBBISH]: [ Moves.COIL, Moves.RECOVER, Moves.DIRE_CLAW, Moves.GIGATON_HAMMER ], [Species.TRUBBISH]: [ Moves.COIL, Moves.RECOVER, Moves.DIRE_CLAW, Moves.GIGATON_HAMMER ],
[Species.ZORUA]: [ Moves.FLAMETHROWER, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.FIERY_WRATH ], [Species.ZORUA]: [ Moves.FLAMETHROWER, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.FIERY_WRATH ],
@ -295,19 +295,19 @@ export const speciesEggMoves = {
[Species.DUCKLETT]: [ Moves.SPLISHY_SPLASH, Moves.EARTH_POWER, Moves.WILDBOLT_STORM, Moves.QUIVER_DANCE ], [Species.DUCKLETT]: [ Moves.SPLISHY_SPLASH, Moves.EARTH_POWER, Moves.WILDBOLT_STORM, Moves.QUIVER_DANCE ],
[Species.VANILLITE]: [ Moves.EARTH_POWER, Moves.AURORA_VEIL, Moves.CALM_MIND, Moves.SPARKLY_SWIRL ], [Species.VANILLITE]: [ Moves.EARTH_POWER, Moves.AURORA_VEIL, Moves.CALM_MIND, Moves.SPARKLY_SWIRL ],
[Species.DEERLING]: [ Moves.TIDY_UP, Moves.FLOWER_TRICK, Moves.BODY_SLAM, Moves.COMBAT_TORQUE ], [Species.DEERLING]: [ Moves.TIDY_UP, Moves.FLOWER_TRICK, Moves.BODY_SLAM, Moves.COMBAT_TORQUE ],
[Species.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.TAILWIND, Moves.ZIPPY_ZAP ], [Species.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.AURA_WHEEL ],
[Species.KARRABLAST]: [ Moves.LEECH_LIFE, Moves.HEAL_ORDER, Moves.HIGH_HORSEPOWER, Moves.DOUBLE_IRON_BASH ], [Species.KARRABLAST]: [ Moves.LEECH_LIFE, Moves.BITTER_BLADE, Moves.HIGH_HORSEPOWER, Moves.DOUBLE_IRON_BASH ],
[Species.FOONGUS]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.FOUL_PLAY, Moves.SAPPY_SEED ], [Species.FOONGUS]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.FOUL_PLAY, Moves.SAPPY_SEED ],
[Species.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], [Species.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.BUZZY_BUZZ, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
[Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.TOXIC, Moves.GLITZY_GLOW ], [Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ],
[Species.JOLTIK]: [ Moves.THUNDER, Moves.PARABOLIC_CHARGE, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], [Species.JOLTIK]: [ Moves.WILDBOLT_STORM, Moves.PARABOLIC_CHARGE, Moves.EARTH_POWER, Moves.QUIVER_DANCE ],
[Species.FERROSEED]: [ Moves.STRENGTH_SAP, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.SAPPY_SEED ], [Species.FERROSEED]: [ Moves.STRENGTH_SAP, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.SAPPY_SEED ],
[Species.KLINK]: [ Moves.TRIPLE_AXEL, Moves.HIGH_HORSEPOWER, Moves.FUSION_BOLT, Moves.DOUBLE_IRON_BASH ], [Species.KLINK]: [ Moves.TRIPLE_AXEL, Moves.HIGH_HORSEPOWER, Moves.FUSION_BOLT, Moves.DOUBLE_IRON_BASH ],
[Species.TYNAMO]: [ Moves.SCALD, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.PLASMA_FISTS ], [Species.TYNAMO]: [ Moves.SCALD, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.AURA_WHEEL ],
[Species.ELGYEM]: [ Moves.LUSTER_PURGE, Moves.TRICK_ROOM, Moves.AURA_SPHERE, Moves.TAIL_GLOW ], [Species.ELGYEM]: [ Moves.LUSTER_PURGE, Moves.BADDY_BAD, Moves.AURA_SPHERE, Moves.TAIL_GLOW ],
[Species.LITWICK]: [ Moves.FIERY_DANCE, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE ], [Species.LITWICK]: [ Moves.FIERY_DANCE, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE ],
[Species.AXEW]: [ Moves.STONE_AXE, Moves.DIRE_CLAW, Moves.BITTER_BLADE, Moves.GLAIVE_RUSH ], [Species.AXEW]: [ Moves.STONE_AXE, Moves.DIRE_CLAW, Moves.BITTER_BLADE, Moves.GLAIVE_RUSH ],
[Species.CUBCHOO]: [ Moves.TRIPLE_AXEL, Moves.LIQUIDATION, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.CUBCHOO]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.ICE_SHARD, Moves.COLLISION_COURSE ],
[Species.CRYOGONAL]: [ Moves.FREEZING_GLARE, Moves.AURORA_VEIL, Moves.NASTY_PLOT, Moves.ORIGIN_PULSE ], [Species.CRYOGONAL]: [ Moves.FREEZING_GLARE, Moves.AURORA_VEIL, Moves.NASTY_PLOT, Moves.ORIGIN_PULSE ],
[Species.SHELMET]: [ Moves.POWER_GEM, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ], [Species.SHELMET]: [ Moves.POWER_GEM, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ],
[Species.STUNFISK]: [ Moves.BANEFUL_BUNKER, Moves.SANDSEAR_STORM, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ], [Species.STUNFISK]: [ Moves.BANEFUL_BUNKER, Moves.SANDSEAR_STORM, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ],
@ -315,24 +315,24 @@ export const speciesEggMoves = {
[Species.DRUDDIGON]: [ Moves.FIRE_LASH, Moves.ROOST, Moves.DRAGON_DARTS, Moves.CLANGOROUS_SOUL ], [Species.DRUDDIGON]: [ Moves.FIRE_LASH, Moves.ROOST, Moves.DRAGON_DARTS, Moves.CLANGOROUS_SOUL ],
[Species.GOLETT]: [ Moves.SHIFT_GEAR, Moves.DRAIN_PUNCH, Moves.HEADLONG_RUSH, Moves.RAGE_FIST ], [Species.GOLETT]: [ Moves.SHIFT_GEAR, Moves.DRAIN_PUNCH, Moves.HEADLONG_RUSH, Moves.RAGE_FIST ],
[Species.PAWNIARD]: [ Moves.SUCKER_PUNCH, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE, Moves.LAST_RESPECTS ], [Species.PAWNIARD]: [ Moves.SUCKER_PUNCH, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE, Moves.LAST_RESPECTS ],
[Species.BOUFFALANT]: [ Moves.SLACK_OFF, Moves.JUMP_KICK, Moves.HEAD_SMASH, Moves.FLARE_BLITZ ], [Species.BOUFFALANT]: [ Moves.SLACK_OFF, Moves.HIGH_JUMP_KICK, Moves.HEAD_SMASH, Moves.FLARE_BLITZ ],
[Species.RUFFLET]: [ Moves.FLOATY_FALL, Moves.AURA_SPHERE, Moves.NO_RETREAT, Moves.BOLT_BEAK ], [Species.RUFFLET]: [ Moves.FLOATY_FALL, Moves.AURA_SPHERE, Moves.NO_RETREAT, Moves.BOLT_BEAK ],
[Species.VULLABY]: [ Moves.FOUL_PLAY, Moves.BODY_PRESS, Moves.ROOST, Moves.RUINATION ], [Species.VULLABY]: [ Moves.FOUL_PLAY, Moves.BODY_PRESS, Moves.ROOST, Moves.RUINATION ],
[Species.HEATMOR]: [ Moves.EARTH_POWER, Moves.OVERHEAT, Moves.THUNDERBOLT, Moves.V_CREATE ], [Species.HEATMOR]: [ Moves.EARTH_POWER, Moves.OVERHEAT, Moves.THUNDERBOLT, Moves.V_CREATE ],
[Species.DURANT]: [ Moves.HIGH_HORSEPOWER, Moves.FIRST_IMPRESSION, Moves.SWORDS_DANCE, Moves.BEHEMOTH_BASH ], [Species.DURANT]: [ Moves.HIGH_HORSEPOWER, Moves.FIRST_IMPRESSION, Moves.SWORDS_DANCE, Moves.BEHEMOTH_BASH ],
[Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ], [Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ],
[Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.MATCHA_GOTCHA, Moves.EARTH_POWER, Moves.TORCH_SONG ], [Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.MATCHA_GOTCHA, Moves.EARTH_POWER, Moves.TORCH_SONG ],
[Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.BODY_PRESS, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.VIRIZION]: [ Moves.PSYBLADE, Moves.SAPPY_SEED, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.VIRIZION]: [ Moves.PSYBLADE, Moves.SAPPY_SEED, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.TORNADUS]: [ Moves.EARTH_POWER, Moves.PARTING_SHOT, Moves.ICE_BEAM, Moves.OBLIVION_WING ], [Species.TORNADUS]: [ Moves.EARTH_POWER, Moves.PARTING_SHOT, Moves.ICE_BEAM, Moves.OBLIVION_WING ],
[Species.THUNDURUS]: [ Moves.EARTH_POWER, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ], [Species.THUNDURUS]: [ Moves.EARTH_POWER, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ],
[Species.RESHIRAM]: [ Moves.MORNING_SUN, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ], [Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ],
[Species.ZEKROM]: [ Moves.DRAGON_DANCE, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ], [Species.ZEKROM]: [ Moves.TRIPLE_AXEL, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ],
[Species.LANDORUS]: [ Moves.STONE_AXE, Moves.FLOATY_FALL, Moves.ROOST, Moves.BLEAKWIND_STORM ], [Species.LANDORUS]: [ Moves.STONE_AXE, Moves.FLOATY_FALL, Moves.ROOST, Moves.BLEAKWIND_STORM ],
[Species.KYUREM]: [ Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.NO_RETREAT, Moves.GLACIAL_LANCE ], [Species.KYUREM]: [ Moves.DRAGON_DARTS, Moves.GLACIAL_LANCE, Moves.NO_RETREAT, Moves.DRAGON_ENERGY ],
[Species.KELDEO]: [ Moves.BOUNCY_BUBBLE, Moves.THUNDERBOLT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], [Species.KELDEO]: [ Moves.BOUNCY_BUBBLE, Moves.THUNDERBOLT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
[Species.MELOETTA]: [ Moves.VICTORY_DANCE, Moves.QUIVER_DANCE, Moves.TRIPLE_ARROWS, Moves.TORCH_SONG ], [Species.MELOETTA]: [ Moves.BODY_SLAM, Moves.TORCH_SONG, Moves.TRIPLE_ARROWS, Moves.BOOMBURST ],
[Species.GENESECT]: [ Moves.EXTREME_SPEED, Moves.U_TURN, Moves.TACHYON_CUTTER, Moves.TAIL_GLOW ], [Species.GENESECT]: [ Moves.EXTREME_SPEED, Moves.U_TURN, Moves.TACHYON_CUTTER, Moves.TAIL_GLOW ],
[Species.CHESPIN]: [ Moves.BODY_PRESS, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ], [Species.CHESPIN]: [ Moves.BODY_PRESS, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ],
[Species.FENNEKIN]: [ Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.THUNDERBOLT, Moves.TORCH_SONG ], [Species.FENNEKIN]: [ Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.THUNDERBOLT, Moves.TORCH_SONG ],
@ -344,13 +344,13 @@ export const speciesEggMoves = {
[Species.FLABEBE]: [ Moves.GLITZY_GLOW, Moves.MYSTICAL_FIRE, Moves.TAKE_HEART, Moves.SEED_FLARE ], [Species.FLABEBE]: [ Moves.GLITZY_GLOW, Moves.MYSTICAL_FIRE, Moves.TAKE_HEART, Moves.SEED_FLARE ],
[Species.SKIDDO]: [ Moves.HIGH_HORSEPOWER, Moves.GRASSY_GLIDE, Moves.STONE_AXE, Moves.SAPPY_SEED ], [Species.SKIDDO]: [ Moves.HIGH_HORSEPOWER, Moves.GRASSY_GLIDE, Moves.STONE_AXE, Moves.SAPPY_SEED ],
[Species.PANCHAM]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.WICKED_BLOW ], [Species.PANCHAM]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.WICKED_BLOW ],
[Species.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COVET, Moves.MULTI_ATTACK ], [Species.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COMBAT_TORQUE, Moves.MULTI_ATTACK ],
[Species.ESPURR]: [ Moves.GLARE, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PSYSTRIKE ], [Species.ESPURR]: [ Moves.LUSTER_PURGE, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.DARK_VOID ],
[Species.HONEDGE]: [ Moves.TACHYON_CUTTER, Moves.SHADOW_BONE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.HONEDGE]: [ Moves.TACHYON_CUTTER, Moves.SHADOW_BONE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ],
[Species.SPRITZEE]: [ Moves.TRICK_ROOM, Moves.FOUL_PLAY, Moves.WISH, Moves.REVIVAL_BLESSING ], [Species.SPRITZEE]: [ Moves.SPEED_SWAP, Moves.TORCH_SONG, Moves.ROOST, Moves.REVIVAL_BLESSING ],
[Species.SWIRLIX]: [ Moves.BELLY_DRUM, Moves.HEADLONG_RUSH, Moves.MAGICAL_TORQUE, Moves.REVIVAL_BLESSING ], [Species.SWIRLIX]: [ Moves.BELLY_DRUM, Moves.HEADLONG_RUSH, Moves.MAGICAL_TORQUE, Moves.REVIVAL_BLESSING ],
[Species.INKAY]: [ Moves.POWER_TRIP, Moves.SPIN_OUT, Moves.RECOVER, Moves.PSYCHO_BOOST ], [Species.INKAY]: [ Moves.POWER_TRIP, Moves.SPIN_OUT, Moves.RECOVER, Moves.PSYCHO_BOOST ],
[Species.BINACLE]: [ Moves.TRIPLE_AXEL, Moves.ACCELEROCK, Moves.DIRE_CLAW, Moves.MIGHTY_CLEAVE ], [Species.BINACLE]: [ Moves.TRIPLE_AXEL, Moves.CRABHAMMER, Moves.DIRE_CLAW, Moves.MIGHTY_CLEAVE ],
[Species.SKRELP]: [ Moves.STRENGTH_SAP, Moves.TRICK_ROOM, Moves.CALM_MIND, Moves.CORE_ENFORCER ], [Species.SKRELP]: [ Moves.STRENGTH_SAP, Moves.TRICK_ROOM, Moves.CALM_MIND, Moves.CORE_ENFORCER ],
[Species.CLAUNCHER]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.WATER_SHURIKEN, Moves.ORIGIN_PULSE ], [Species.CLAUNCHER]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.WATER_SHURIKEN, Moves.ORIGIN_PULSE ],
[Species.HELIOPTILE]: [ Moves.WEATHER_BALL, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.BOOMBURST ], [Species.HELIOPTILE]: [ Moves.WEATHER_BALL, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.BOOMBURST ],
@ -360,15 +360,15 @@ export const speciesEggMoves = {
[Species.DEDENNE]: [ Moves.BOOMBURST, Moves.FAKE_OUT, Moves.NASTY_PLOT, Moves.REVIVAL_BLESSING ], [Species.DEDENNE]: [ Moves.BOOMBURST, Moves.FAKE_OUT, Moves.NASTY_PLOT, Moves.REVIVAL_BLESSING ],
[Species.CARBINK]: [ Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SPARKLY_SWIRL, Moves.DIAMOND_STORM ], [Species.CARBINK]: [ Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SPARKLY_SWIRL, Moves.DIAMOND_STORM ],
[Species.GOOMY]: [ Moves.SCALD, Moves.RECOVER, Moves.CALM_MIND, Moves.MAKE_IT_RAIN ], [Species.GOOMY]: [ Moves.SCALD, Moves.RECOVER, Moves.CALM_MIND, Moves.MAKE_IT_RAIN ],
[Species.KLEFKI]: [ Moves.HEAL_BELL, Moves.ENCORE, Moves.TOPSY_TURVY, Moves.INSTRUCT ], [Species.KLEFKI]: [ Moves.HEAL_BELL, Moves.ENCORE, Moves.INSTRUCT, Moves.TOPSY_TURVY ],
[Species.PHANTUMP]: [ Moves.RAGE_FIST, Moves.TRICK_ROOM, Moves.SYNTHESIS, Moves.SAPPY_SEED ], [Species.PHANTUMP]: [ Moves.RAGE_FIST, Moves.TRICK_ROOM, Moves.SYNTHESIS, Moves.SAPPY_SEED ],
[Species.PUMPKABOO]: [ Moves.SPIRIT_SHACKLE, Moves.FIRE_LASH, Moves.DIRE_CLAW, Moves.SAPPY_SEED ], [Species.PUMPKABOO]: [ Moves.SPIRIT_SHACKLE, Moves.FIRE_LASH, Moves.DIRE_CLAW, Moves.SAPPY_SEED ],
[Species.BERGMITE]: [ Moves.STONE_AXE, Moves.METAL_BURST, Moves.BODY_PRESS, Moves.GLACIAL_LANCE ], [Species.BERGMITE]: [ Moves.STONE_AXE, Moves.METAL_BURST, Moves.BODY_PRESS, Moves.GLACIAL_LANCE ],
[Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ], [Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ],
[Species.XERNEAS]: [ Moves.SEARING_SHOT, Moves.LUMINA_CRASH, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ], [Species.XERNEAS]: [ Moves.EARTH_POWER, Moves.SPRINGTIDE_STORM, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ],
[Species.YVELTAL]: [ Moves.SHELL_SIDE_ARM, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ], [Species.YVELTAL]: [ Moves.SHELL_SIDE_ARM, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ],
[Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.CLANGOROUS_SOUL, Moves.DOUBLE_IRON_BASH ], [Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.CLANGOROUS_SOUL, Moves.DOUBLE_IRON_BASH ],
[Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.GEOMANCY ], [Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.AURA_SPHERE, Moves.SHORE_UP, Moves.GEOMANCY ],
[Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ], [Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ],
[Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.ENERGY_BALL, Moves.MAGMA_STORM ], [Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.ENERGY_BALL, Moves.MAGMA_STORM ],
[Species.ROWLET]: [ Moves.THOUSAND_ARROWS, Moves.SHADOW_BONE, Moves.FIRST_IMPRESSION, Moves.VICTORY_DANCE ], [Species.ROWLET]: [ Moves.THOUSAND_ARROWS, Moves.SHADOW_BONE, Moves.FIRST_IMPRESSION, Moves.VICTORY_DANCE ],
@ -379,7 +379,7 @@ export const speciesEggMoves = {
[Species.GRUBBIN]: [ Moves.ICE_BEAM, Moves.EARTH_POWER, Moves.THUNDERCLAP, Moves.QUIVER_DANCE ], [Species.GRUBBIN]: [ Moves.ICE_BEAM, Moves.EARTH_POWER, Moves.THUNDERCLAP, Moves.QUIVER_DANCE ],
[Species.CRABRAWLER]: [ Moves.JET_PUNCH, Moves.SHORE_UP, Moves.SUCKER_PUNCH, Moves.SURGING_STRIKES ], [Species.CRABRAWLER]: [ Moves.JET_PUNCH, Moves.SHORE_UP, Moves.SUCKER_PUNCH, Moves.SURGING_STRIKES ],
[Species.ORICORIO]: [ Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.THUNDERCLAP, Moves.OBLIVION_WING ], [Species.ORICORIO]: [ Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.THUNDERCLAP, Moves.OBLIVION_WING ],
[Species.CUTIEFLY]: [ Moves.STICKY_WEB, Moves.MOONBLAST, Moves.HEAT_WAVE, Moves.SPORE ], [Species.CUTIEFLY]: [ Moves.STICKY_WEB, Moves.SLEEP_POWDER, Moves.HEAT_WAVE, Moves.SPARKLY_SWIRL ],
[Species.ROCKRUFF]: [ Moves.HIGH_HORSEPOWER, Moves.TIDY_UP, Moves.ICE_SPINNER, Moves.MIGHTY_CLEAVE ], [Species.ROCKRUFF]: [ Moves.HIGH_HORSEPOWER, Moves.TIDY_UP, Moves.ICE_SPINNER, Moves.MIGHTY_CLEAVE ],
[Species.WISHIWASHI]: [ Moves.HEAL_ORDER, Moves.ICE_SPINNER, Moves.DRAGON_DANCE, Moves.JET_PUNCH ], [Species.WISHIWASHI]: [ Moves.HEAL_ORDER, Moves.ICE_SPINNER, Moves.DRAGON_DANCE, Moves.JET_PUNCH ],
[Species.MAREANIE]: [ Moves.CEASELESS_EDGE, Moves.SIZZLY_SLIDE, Moves.BODY_PRESS, Moves.LEECH_SEED ], [Species.MAREANIE]: [ Moves.CEASELESS_EDGE, Moves.SIZZLY_SLIDE, Moves.BODY_PRESS, Moves.LEECH_SEED ],
@ -394,31 +394,31 @@ export const speciesEggMoves = {
[Species.ORANGURU]: [ Moves.JUNGLE_HEALING, Moves.YAWN, Moves.FOLLOW_ME, Moves.LUMINA_CRASH ], [Species.ORANGURU]: [ Moves.JUNGLE_HEALING, Moves.YAWN, Moves.FOLLOW_ME, Moves.LUMINA_CRASH ],
[Species.PASSIMIAN]: [ Moves.FAKE_OUT, Moves.SUCKER_PUNCH, Moves.ZING_ZAP, Moves.PYRO_BALL ], [Species.PASSIMIAN]: [ Moves.FAKE_OUT, Moves.SUCKER_PUNCH, Moves.ZING_ZAP, Moves.PYRO_BALL ],
[Species.WIMPOD]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.JET_PUNCH, Moves.SURGING_STRIKES ], [Species.WIMPOD]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.JET_PUNCH, Moves.SURGING_STRIKES ],
[Species.SANDYGAST]: [ Moves.SCORCHING_SANDS, Moves.SPLISHY_SPLASH, Moves.TAKE_HEART, Moves.SALT_CURE ], [Species.SANDYGAST]: [ Moves.SANDSEAR_STORM, Moves.SPLISHY_SPLASH, Moves.TAKE_HEART, Moves.SALT_CURE ],
[Species.PYUKUMUKU]: [ Moves.COMEUPPANCE, Moves.BANEFUL_BUNKER, Moves.TOXIC_SPIKES, Moves.SALT_CURE ], [Species.PYUKUMUKU]: [ Moves.COMEUPPANCE, Moves.BANEFUL_BUNKER, Moves.TOXIC_SPIKES, Moves.SALT_CURE ],
[Species.TYPE_NULL]: [ Moves.DIRE_CLAW, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.SHELL_SMASH ], [Species.TYPE_NULL]: [ Moves.DIRE_CLAW, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.SHELL_SMASH ],
[Species.MINIOR]: [ Moves.EARTH_POWER, Moves.FLOATY_FALL, Moves.ZING_ZAP, Moves.DIAMOND_STORM ], [Species.MINIOR]: [ Moves.EARTH_POWER, Moves.FLOATY_FALL, Moves.ZING_ZAP, Moves.DIAMOND_STORM ],
[Species.KOMALA]: [ Moves.SLACK_OFF, Moves.EXTREME_SPEED, Moves.KNOCK_OFF, Moves.COLLISION_COURSE ], [Species.KOMALA]: [ Moves.SLACK_OFF, Moves.EXTREME_SPEED, Moves.KNOCK_OFF, Moves.COLLISION_COURSE ],
[Species.TURTONATOR]: [ Moves.BURNING_BULWARK, Moves.MORNING_SUN, Moves.BODY_PRESS, Moves.CORE_ENFORCER ], [Species.TURTONATOR]: [ Moves.BURNING_BULWARK, Moves.MORNING_SUN, Moves.BODY_PRESS, Moves.CORE_ENFORCER ],
[Species.TOGEDEMARU]: [ Moves.FAKE_OUT, Moves.METAL_BURST, Moves.METEOR_MASH, Moves.BOLT_STRIKE ], [Species.TOGEDEMARU]: [ Moves.FAKE_OUT, Moves.METAL_BURST, Moves.METEOR_MASH, Moves.AURA_WHEEL ],
[Species.MIMIKYU]: [ Moves.SPIRIT_BREAK, Moves.TIDY_UP, Moves.BITTER_BLADE, Moves.SPECTRAL_THIEF ], [Species.MIMIKYU]: [ Moves.SPIRIT_BREAK, Moves.TIDY_UP, Moves.BITTER_BLADE, Moves.SPECTRAL_THIEF ],
[Species.BRUXISH]: [ Moves.PLAY_ROUGH, Moves.FIRE_FANG, Moves.DRAGON_DANCE, Moves.SURGING_STRIKES ], [Species.BRUXISH]: [ Moves.PLAY_ROUGH, Moves.FIRE_FANG, Moves.DRAGON_DANCE, Moves.SURGING_STRIKES ],
[Species.DRAMPA]: [ Moves.SLACK_OFF, Moves.TRICK_ROOM, Moves.CORE_ENFORCER, Moves.BOOMBURST ], [Species.DRAMPA]: [ Moves.SLACK_OFF, Moves.TRICK_ROOM, Moves.CORE_ENFORCER, Moves.BOOMBURST ],
[Species.DHELMISE]: [ Moves.SHADOW_BONE, Moves.STRENGTH_SAP, Moves.LIQUIDATION, Moves.SAPPY_SEED ], [Species.DHELMISE]: [ Moves.SHADOW_BONE, Moves.STRENGTH_SAP, Moves.LIQUIDATION, Moves.SAPPY_SEED ],
[Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ], [Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ],
[Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.RISING_VOLTAGE, Moves.PLASMA_FISTS ], [Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.RISING_VOLTAGE, Moves.BOLT_STRIKE ],
[Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ], [Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ],
[Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.DRAIN_PUNCH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ], [Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.DRAIN_PUNCH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ],
[Species.TAPU_FINI]: [ Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ], [Species.TAPU_FINI]: [ Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ],
[Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ], [Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ],
[Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], [Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ],
[Species.BUZZWOLE]: [ Moves.FIRST_IMPRESSION, Moves.COMBAT_TORQUE, Moves.ROCK_WRECKER, Moves.DOUBLE_IRON_BASH ], [Species.BUZZWOLE]: [ Moves.FIRST_IMPRESSION, Moves.COMBAT_TORQUE, Moves.ROCK_WRECKER, Moves.DOUBLE_IRON_BASH ],
[Species.PHEROMOSA]: [ Moves.AURA_SPHERE, Moves.MAKE_IT_RAIN, Moves.ATTACK_ORDER, Moves.DIAMOND_STORM ], [Species.PHEROMOSA]: [ Moves.SECRET_SWORD, Moves.MAKE_IT_RAIN, Moves.ATTACK_ORDER, Moves.DIAMOND_STORM ],
[Species.XURKITREE]: [ Moves.FLAMETHROWER, Moves.GIGA_DRAIN, Moves.TAIL_GLOW, Moves.THUNDERCLAP ], [Species.XURKITREE]: [ Moves.FLAMETHROWER, Moves.GIGA_DRAIN, Moves.TAIL_GLOW, Moves.THUNDERCLAP ],
[Species.CELESTEELA]: [ Moves.RECOVER, Moves.BUZZY_BUZZ, Moves.SANDSEAR_STORM, Moves.OBLIVION_WING ], [Species.CELESTEELA]: [ Moves.RECOVER, Moves.BUZZY_BUZZ, Moves.SANDSEAR_STORM, Moves.OBLIVION_WING ],
[Species.KARTANA]: [ Moves.MIGHTY_CLEAVE, Moves.PSYBLADE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.KARTANA]: [ Moves.MIGHTY_CLEAVE, Moves.PSYBLADE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ],
[Species.GUZZLORD]: [ Moves.SUCKER_PUNCH, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ], [Species.GUZZLORD]: [ Moves.SUCKER_PUNCH, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ],
[Species.NECROZMA]: [ Moves.CLANGOROUS_SOUL, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.DYNAMAX_CANNON ], [Species.NECROZMA]: [ Moves.DYNAMAX_CANNON, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.CLANGOROUS_SOUL ],
[Species.MAGEARNA]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.MAKE_IT_RAIN ], [Species.MAGEARNA]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.MAKE_IT_RAIN ],
[Species.MARSHADOW]: [ Moves.POWER_UP_PUNCH, Moves.TRIPLE_AXEL, Moves.METEOR_MASH, Moves.STORM_THROW ], [Species.MARSHADOW]: [ Moves.POWER_UP_PUNCH, Moves.TRIPLE_AXEL, Moves.METEOR_MASH, Moves.STORM_THROW ],
[Species.POIPOLE]: [ Moves.CORE_ENFORCER, Moves.ICE_BEAM, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN ], [Species.POIPOLE]: [ Moves.CORE_ENFORCER, Moves.ICE_BEAM, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN ],
@ -428,11 +428,11 @@ export const speciesEggMoves = {
[Species.MELTAN]: [ Moves.BULLET_PUNCH, Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS ], [Species.MELTAN]: [ Moves.BULLET_PUNCH, Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS ],
[Species.GROOKEY]: [ Moves.HIGH_HORSEPOWER, Moves.CLANGOROUS_SOUL, Moves.GRASSY_GLIDE, Moves.SAPPY_SEED ], [Species.GROOKEY]: [ Moves.HIGH_HORSEPOWER, Moves.CLANGOROUS_SOUL, Moves.GRASSY_GLIDE, Moves.SAPPY_SEED ],
[Species.SCORBUNNY]: [ Moves.EXTREME_SPEED, Moves.HIGH_JUMP_KICK, Moves.TRIPLE_AXEL, Moves.BOLT_STRIKE ], [Species.SCORBUNNY]: [ Moves.EXTREME_SPEED, Moves.HIGH_JUMP_KICK, Moves.TRIPLE_AXEL, Moves.BOLT_STRIKE ],
[Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.SCORCHING_SANDS, Moves.NASTY_PLOT ], [Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.ENERGY_BALL, Moves.NASTY_PLOT ],
[Species.SKWOVET]: [ Moves.KNOCK_OFF, Moves.SLACK_OFF, Moves.BODY_PRESS, Moves.POPULATION_BOMB ], [Species.SKWOVET]: [ Moves.SUCKER_PUNCH, Moves.SLACK_OFF, Moves.COIL, Moves.POPULATION_BOMB ],
[Species.ROOKIDEE]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.BEHEMOTH_BASH ], [Species.ROOKIDEE]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.BEHEMOTH_BASH ],
[Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.EXPANDING_FORCE, Moves.SPORE, Moves.TAIL_GLOW ], [Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.LUSTER_PURGE, Moves.SLEEP_POWDER, Moves.TAIL_GLOW ],
[Species.NICKIT]: [ Moves.BADDY_BAD, Moves.BURNING_JEALOUSY, Moves.SPARKLY_SWIRL, Moves.FIERY_WRATH ], [Species.NICKIT]: [ Moves.BADDY_BAD, Moves.FLAMETHROWER, Moves.SPARKLY_SWIRL, Moves.MAKE_IT_RAIN ],
[Species.GOSSIFLEUR]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SEED_FLARE ], [Species.GOSSIFLEUR]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SEED_FLARE ],
[Species.WOOLOO]: [ Moves.PSYSHIELD_BASH, Moves.MILK_DRINK, Moves.BODY_PRESS, Moves.MULTI_ATTACK ], [Species.WOOLOO]: [ Moves.PSYSHIELD_BASH, Moves.MILK_DRINK, Moves.BODY_PRESS, Moves.MULTI_ATTACK ],
[Species.CHEWTLE]: [ Moves.ICE_FANG, Moves.ACCELEROCK, Moves.SHELL_SMASH, Moves.FISHIOUS_REND ], [Species.CHEWTLE]: [ Moves.ICE_FANG, Moves.ACCELEROCK, Moves.SHELL_SMASH, Moves.FISHIOUS_REND ],
@ -446,25 +446,25 @@ export const speciesEggMoves = {
[Species.SIZZLIPEDE]: [ Moves.BURNING_BULWARK, Moves.ZING_ZAP, Moves.FIRST_IMPRESSION, Moves.BITTER_BLADE ], [Species.SIZZLIPEDE]: [ Moves.BURNING_BULWARK, Moves.ZING_ZAP, Moves.FIRST_IMPRESSION, Moves.BITTER_BLADE ],
[Species.CLOBBOPUS]: [ Moves.STORM_THROW, Moves.JET_PUNCH, Moves.MACH_PUNCH, Moves.SURGING_STRIKES ], [Species.CLOBBOPUS]: [ Moves.STORM_THROW, Moves.JET_PUNCH, Moves.MACH_PUNCH, Moves.SURGING_STRIKES ],
[Species.SINISTEA]: [ Moves.SCALD, Moves.TAKE_HEART, Moves.SPARKLY_SWIRL, Moves.MATCHA_GOTCHA ], [Species.SINISTEA]: [ Moves.SCALD, Moves.TAKE_HEART, Moves.SPARKLY_SWIRL, Moves.MATCHA_GOTCHA ],
[Species.HATENNA]: [ Moves.RECOVER, Moves.MOONBLAST, Moves.BUZZY_BUZZ, Moves.SEARING_SHOT ], [Species.HATENNA]: [ Moves.RECOVER, Moves.MOONBLAST, Moves.BUZZY_BUZZ, Moves.TORCH_SONG ],
[Species.IMPIDIMP]: [ Moves.ENCORE, Moves.PARTING_SHOT, Moves.TOPSY_TURVY, Moves.WICKED_BLOW ], [Species.IMPIDIMP]: [ Moves.ENCORE, Moves.PARTING_SHOT, Moves.TOPSY_TURVY, Moves.WICKED_BLOW ],
[Species.MILCERY]: [ Moves.MOONBLAST, Moves.CHILLY_RECEPTION, Moves.EARTH_POWER, Moves.GEOMANCY ], [Species.MILCERY]: [ Moves.MOONBLAST, Moves.CHILLY_RECEPTION, Moves.EARTH_POWER, Moves.GEOMANCY ],
[Species.FALINKS]: [ Moves.COMBAT_TORQUE, Moves.PSYSHIELD_BASH, Moves.HEAL_ORDER, Moves.POPULATION_BOMB ], [Species.FALINKS]: [ Moves.COMBAT_TORQUE, Moves.PSYSHIELD_BASH, Moves.HEAL_ORDER, Moves.POPULATION_BOMB ],
[Species.PINCURCHIN]: [ Moves.TRICK_ROOM, Moves.RISING_VOLTAGE, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ], [Species.PINCURCHIN]: [ Moves.TRICK_ROOM, Moves.RISING_VOLTAGE, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ],
[Species.SNOM]: [ Moves.MOONBLAST, Moves.SURF, Moves.EARTH_POWER, Moves.FIERY_DANCE ], [Species.SNOM]: [ Moves.FROST_BREATH, Moves.HEAL_ORDER, Moves.EARTH_POWER, Moves.SPORE ],
[Species.STONJOURNER]: [ Moves.BODY_PRESS, Moves.HELPING_HAND, Moves.ACCELEROCK, Moves.DIAMOND_STORM ], [Species.STONJOURNER]: [ Moves.BODY_PRESS, Moves.HELPING_HAND, Moves.ACCELEROCK, Moves.DIAMOND_STORM ],
[Species.EISCUE]: [ Moves.TRIPLE_AXEL, Moves.AQUA_STEP, Moves.SHELL_SMASH, Moves.GLACIAL_LANCE ], [Species.EISCUE]: [ Moves.TRIPLE_AXEL, Moves.AQUA_STEP, Moves.SHELL_SMASH, Moves.GLACIAL_LANCE ],
[Species.INDEEDEE]: [ Moves.MATCHA_GOTCHA, Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.REVIVAL_BLESSING ], [Species.INDEEDEE]: [ Moves.MATCHA_GOTCHA, Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.REVIVAL_BLESSING ],
[Species.MORPEKO]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.MORPEKO]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ],
[Species.CUFANT]: [ Moves.LIQUIDATION, Moves.CURSE, Moves.COMBAT_TORQUE, Moves.GIGATON_HAMMER ], [Species.CUFANT]: [ Moves.LIQUIDATION, Moves.CURSE, Moves.COMBAT_TORQUE, Moves.GIGATON_HAMMER ],
[Species.DRACOZOLT]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.FIRE_LASH, Moves.DRAGON_DANCE ], [Species.DRACOZOLT]: [ Moves.TRIPLE_AXEL, Moves.SCALE_SHOT, Moves.FIRE_LASH, Moves.DRAGON_DANCE ],
[Species.ARCTOZOLT]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.ARCTOZOLT]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ],
[Species.DRACOVISH]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.THUNDER_FANG, Moves.DRAGON_DANCE ], [Species.DRACOVISH]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.THUNDER_FANG, Moves.DRAGON_DANCE ],
[Species.ARCTOVISH]: [ Moves.ICE_FANG, Moves.THUNDER_FANG, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.ARCTOVISH]: [ Moves.ICE_FANG, Moves.THUNDER_FANG, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ],
[Species.DURALUDON]: [ Moves.CORE_ENFORCER, Moves.BODY_PRESS, Moves.RECOVER, Moves.TACHYON_CUTTER ], [Species.DURALUDON]: [ Moves.CORE_ENFORCER, Moves.BODY_PRESS, Moves.RECOVER, Moves.TACHYON_CUTTER ],
[Species.DREEPY]: [ Moves.SHADOW_BONE, Moves.POWER_UP_PUNCH, Moves.BLAZING_TORQUE, Moves.GLAIVE_RUSH ], [Species.DREEPY]: [ Moves.SHADOW_BONE, Moves.NASTY_PLOT, Moves.FIRE_LASH, Moves.COLLISION_COURSE ],
[Species.ZACIAN]: [ Moves.MAGICAL_TORQUE, Moves.MIGHTY_CLEAVE, Moves.BITTER_BLADE, Moves.PRECIPICE_BLADES ], [Species.ZACIAN]: [ Moves.MAGICAL_TORQUE, Moves.MIGHTY_CLEAVE, Moves.BITTER_BLADE, Moves.PRECIPICE_BLADES ],
[Species.ZAMAZENTA]: [ Moves.PSYSHIELD_BASH, Moves.BODY_PRESS, Moves.SLACK_OFF, Moves.VICTORY_DANCE ], [Species.ZAMAZENTA]: [ Moves.BULK_UP, Moves.BODY_PRESS, Moves.SLACK_OFF, Moves.DIAMOND_STORM ],
[Species.ETERNATUS]: [ Moves.BODY_PRESS, Moves.NASTY_PLOT, Moves.MALIGNANT_CHAIN, Moves.DRAGON_ENERGY ], [Species.ETERNATUS]: [ Moves.BODY_PRESS, Moves.NASTY_PLOT, Moves.MALIGNANT_CHAIN, Moves.DRAGON_ENERGY ],
[Species.KUBFU]: [ Moves.METEOR_MASH, Moves.DRAIN_PUNCH, Moves.JET_PUNCH, Moves.DRAGON_DANCE ], [Species.KUBFU]: [ Moves.METEOR_MASH, Moves.DRAIN_PUNCH, Moves.JET_PUNCH, Moves.DRAGON_DANCE ],
[Species.ZARUDE]: [ Moves.SAPPY_SEED, Moves.MIGHTY_CLEAVE, Moves.WICKED_BLOW, Moves.VICTORY_DANCE ], [Species.ZARUDE]: [ Moves.SAPPY_SEED, Moves.MIGHTY_CLEAVE, Moves.WICKED_BLOW, Moves.VICTORY_DANCE ],
@ -482,7 +482,7 @@ export const speciesEggMoves = {
[Species.NYMBLE]: [ Moves.KNOCK_OFF, Moves.FELL_STINGER, Moves.ATTACK_ORDER, Moves.WICKED_BLOW ], [Species.NYMBLE]: [ Moves.KNOCK_OFF, Moves.FELL_STINGER, Moves.ATTACK_ORDER, Moves.WICKED_BLOW ],
[Species.PAWMI]: [ Moves.DRAIN_PUNCH, Moves.ICE_PUNCH, Moves.MACH_PUNCH, Moves.PLASMA_FISTS ], [Species.PAWMI]: [ Moves.DRAIN_PUNCH, Moves.ICE_PUNCH, Moves.MACH_PUNCH, Moves.PLASMA_FISTS ],
[Species.TANDEMAUS]: [ Moves.BATON_PASS, Moves.THIEF, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ], [Species.TANDEMAUS]: [ Moves.BATON_PASS, Moves.THIEF, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ],
[Species.FIDOUGH]: [ Moves.WISH, Moves.BODY_PRESS, Moves.SIZZLY_SLIDE, Moves.TIDY_UP ], [Species.FIDOUGH]: [ Moves.SOFT_BOILED, Moves.HIGH_HORSEPOWER, Moves.SIZZLY_SLIDE, Moves.TIDY_UP ],
[Species.SMOLIV]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.BOOMBURST ], [Species.SMOLIV]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.BOOMBURST ],
[Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ], [Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ],
[Species.NACLI]: [ Moves.BODY_PRESS, Moves.TOXIC, Moves.CURSE, Moves.DIAMOND_STORM ], [Species.NACLI]: [ Moves.BODY_PRESS, Moves.TOXIC, Moves.CURSE, Moves.DIAMOND_STORM ],
@ -492,7 +492,7 @@ export const speciesEggMoves = {
[Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.CLOSE_COMBAT, Moves.PSYCHIC_FANGS, Moves.NO_RETREAT ], [Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.CLOSE_COMBAT, Moves.PSYCHIC_FANGS, Moves.NO_RETREAT ],
[Species.SHROODLE]: [ Moves.GASTRO_ACID, Moves.PARTING_SHOT, Moves.TOXIC, Moves.SKETCH ], [Species.SHROODLE]: [ Moves.GASTRO_ACID, Moves.PARTING_SHOT, Moves.TOXIC, Moves.SKETCH ],
[Species.BRAMBLIN]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.FLOWER_TRICK, Moves.LAST_RESPECTS ], [Species.BRAMBLIN]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.FLOWER_TRICK, Moves.LAST_RESPECTS ],
[Species.TOEDSCOOL]: [ Moves.STRENGTH_SAP, Moves.TOPSY_TURVY, Moves.PARTING_SHOT, Moves.SAPPY_SEED ], [Species.TOEDSCOOL]: [ Moves.STRENGTH_SAP, Moves.TOPSY_TURVY, Moves.SAPPY_SEED, Moves.TAIL_GLOW ],
[Species.KLAWF]: [ Moves.CRABHAMMER, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH ], [Species.KLAWF]: [ Moves.CRABHAMMER, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH ],
[Species.CAPSAKID]: [ Moves.STRENGTH_SAP, Moves.APPLE_ACID, Moves.FROST_BREATH, Moves.TORCH_SONG ], [Species.CAPSAKID]: [ Moves.STRENGTH_SAP, Moves.APPLE_ACID, Moves.FROST_BREATH, Moves.TORCH_SONG ],
[Species.RELLOR]: [ Moves.HEAL_BLOCK, Moves.RECOVER, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ], [Species.RELLOR]: [ Moves.HEAL_BLOCK, Moves.RECOVER, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ],
@ -509,7 +509,7 @@ export const speciesEggMoves = {
[Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ], [Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ],
[Species.CETODDLE]: [ Moves.MOUNTAIN_GALE, Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.DRAGON_DANCE ], [Species.CETODDLE]: [ Moves.MOUNTAIN_GALE, Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.DRAGON_DANCE ],
[Species.VELUZA]: [ Moves.PSYBLADE, Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.BITTER_BLADE ], [Species.VELUZA]: [ Moves.PSYBLADE, Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.BITTER_BLADE ],
[Species.DONDOZO]: [ Moves.SOFT_BOILED, Moves.ICE_SPINNER, Moves.TOXIC, Moves.SALT_CURE ], [Species.DONDOZO]: [ Moves.SOFT_BOILED, Moves.SIZZLY_SLIDE, Moves.TOXIC, Moves.SALT_CURE ],
[Species.TATSUGIRI]: [ Moves.ICE_BEAM, Moves.FILLET_AWAY, Moves.CORE_ENFORCER, Moves.STEAM_ERUPTION ], [Species.TATSUGIRI]: [ Moves.ICE_BEAM, Moves.FILLET_AWAY, Moves.CORE_ENFORCER, Moves.STEAM_ERUPTION ],
[Species.GREAT_TUSK]: [ Moves.STONE_AXE, Moves.MORNING_SUN, Moves.COLLISION_COURSE, Moves.SHIFT_GEAR ], [Species.GREAT_TUSK]: [ Moves.STONE_AXE, Moves.MORNING_SUN, Moves.COLLISION_COURSE, Moves.SHIFT_GEAR ],
[Species.SCREAM_TAIL]: [ Moves.TORCH_SONG, Moves.GLITZY_GLOW, Moves.MOONLIGHT, Moves.SPARKLY_SWIRL ], [Species.SCREAM_TAIL]: [ Moves.TORCH_SONG, Moves.GLITZY_GLOW, Moves.MOONLIGHT, Moves.SPARKLY_SWIRL ],
@ -521,7 +521,7 @@ export const speciesEggMoves = {
[Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.BOUNCY_BUBBLE, Moves.NASTY_PLOT, Moves.STEAM_ERUPTION ], [Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.BOUNCY_BUBBLE, Moves.NASTY_PLOT, Moves.STEAM_ERUPTION ],
[Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ], [Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ],
[Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], [Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
[Species.IRON_MOTH]: [ Moves.EARTH_POWER, Moves.SEARING_SHOT, Moves.QUIVER_DANCE, Moves.MALIGNANT_CHAIN ], [Species.IRON_MOTH]: [ Moves.EARTH_POWER, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN, Moves.QUIVER_DANCE ],
[Species.IRON_THORNS]: [ Moves.DIAMOND_STORM, Moves.SHORE_UP, Moves.SHIFT_GEAR, Moves.PLASMA_FISTS ], [Species.IRON_THORNS]: [ Moves.DIAMOND_STORM, Moves.SHORE_UP, Moves.SHIFT_GEAR, Moves.PLASMA_FISTS ],
[Species.FRIGIBAX]: [ Moves.DRAGON_DARTS, Moves.DRAGON_DANCE, Moves.EARTHQUAKE, Moves.GLACIAL_LANCE ], [Species.FRIGIBAX]: [ Moves.DRAGON_DARTS, Moves.DRAGON_DANCE, Moves.EARTHQUAKE, Moves.GLACIAL_LANCE ],
[Species.GIMMIGHOUL]: [ Moves.HAPPY_HOUR, Moves.AURA_SPHERE, Moves.SURF, Moves.ASTRAL_BARRAGE ], [Species.GIMMIGHOUL]: [ Moves.HAPPY_HOUR, Moves.AURA_SPHERE, Moves.SURF, Moves.ASTRAL_BARRAGE ],
@ -540,7 +540,7 @@ export const speciesEggMoves = {
[Species.MUNKIDORI]: [ Moves.PSYSTRIKE, Moves.HEAT_WAVE, Moves.EARTH_POWER, Moves.MALIGNANT_CHAIN ], [Species.MUNKIDORI]: [ Moves.PSYSTRIKE, Moves.HEAT_WAVE, Moves.EARTH_POWER, Moves.MALIGNANT_CHAIN ],
[Species.FEZANDIPITI]: [ Moves.BARB_BARRAGE, Moves.VICTORY_DANCE, Moves.TRIPLE_AXEL, Moves.MAGICAL_TORQUE ], [Species.FEZANDIPITI]: [ Moves.BARB_BARRAGE, Moves.VICTORY_DANCE, Moves.TRIPLE_AXEL, Moves.MAGICAL_TORQUE ],
[Species.OGERPON]: [ Moves.FLOWER_TRICK, Moves.BONEMERANG, Moves.TRIPLE_AXEL, Moves.GIGATON_HAMMER ], [Species.OGERPON]: [ Moves.FLOWER_TRICK, Moves.BONEMERANG, Moves.TRIPLE_AXEL, Moves.GIGATON_HAMMER ],
[Species.GOUGING_FIRE]: [ Moves.SUPERCELL_SLAM, Moves.BULK_UP, Moves.SACRED_FIRE, Moves.GLAIVE_RUSH ], [Species.GOUGING_FIRE]: [ Moves.EXTREME_SPEED, Moves.BULK_UP, Moves.SACRED_FIRE, Moves.GLAIVE_RUSH ],
[Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ], [Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ],
[Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ], [Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ],
[Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ], [Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ],
@ -566,7 +566,7 @@ export const speciesEggMoves = {
[Species.GALAR_DARUMAKA]: [ Moves.ICE_SPINNER, Moves.ENDURE, Moves.DRAIN_PUNCH, Moves.V_CREATE ], [Species.GALAR_DARUMAKA]: [ Moves.ICE_SPINNER, Moves.ENDURE, Moves.DRAIN_PUNCH, Moves.V_CREATE ],
[Species.GALAR_YAMASK]: [ Moves.STRENGTH_SAP, Moves.DIRE_CLAW, Moves.THOUSAND_WAVES, Moves.SPECTRAL_THIEF ], [Species.GALAR_YAMASK]: [ Moves.STRENGTH_SAP, Moves.DIRE_CLAW, Moves.THOUSAND_WAVES, Moves.SPECTRAL_THIEF ],
[Species.GALAR_STUNFISK]: [ Moves.SPIKY_SHIELD, Moves.THOUSAND_ARROWS, Moves.STRENGTH_SAP, Moves.DOUBLE_IRON_BASH ], [Species.GALAR_STUNFISK]: [ Moves.SPIKY_SHIELD, Moves.THOUSAND_ARROWS, Moves.STRENGTH_SAP, Moves.DOUBLE_IRON_BASH ],
[Species.HISUI_GROWLITHE]: [ Moves.WOOD_HAMMER, Moves.HEAD_SMASH, Moves.MORNING_SUN, Moves.DRAGON_DANCE ], [Species.HISUI_GROWLITHE]: [ Moves.WAVE_CRASH, Moves.HEAD_SMASH, Moves.VOLT_TACKLE, Moves.DRAGON_DANCE ],
[Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ], [Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ],
[Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.STRENGTH_SAP, Moves.FISHIOUS_REND ], [Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.STRENGTH_SAP, Moves.FISHIOUS_REND ],
[Species.HISUI_SNEASEL]: [ Moves.THUNDEROUS_KICK, Moves.KNOCK_OFF, Moves.TRIPLE_AXEL, Moves.VICTORY_DANCE ], [Species.HISUI_SNEASEL]: [ Moves.THUNDEROUS_KICK, Moves.KNOCK_OFF, Moves.TRIPLE_AXEL, Moves.VICTORY_DANCE ],

View File

@ -0,0 +1,574 @@
import { Abilities } from "#app/enums/abilities";
import { Species } from "#app/enums/species";
export const starterPassiveAbilities = {
[Species.BULBASAUR]: Abilities.GRASSY_SURGE,
[Species.CHARMANDER]: Abilities.BEAST_BOOST,
[Species.SQUIRTLE]: Abilities.STURDY,
[Species.CATERPIE]: Abilities.MAGICIAN,
[Species.WEEDLE]: Abilities.TINTED_LENS,
[Species.PIDGEY]: Abilities.SHEER_FORCE,
[Species.RATTATA]: Abilities.STRONG_JAW,
[Species.SPEAROW]: Abilities.MOXIE,
[Species.EKANS]: Abilities.REGENERATOR,
[Species.SANDSHREW]: Abilities.TOUGH_CLAWS,
[Species.NIDORAN_F]: Abilities.FLARE_BOOST,
[Species.NIDORAN_M]: Abilities.GUTS,
[Species.VULPIX]: Abilities.FUR_COAT,
[Species.ZUBAT]: Abilities.INTIMIDATE,
[Species.ODDISH]: Abilities.TRIAGE,
[Species.PARAS]: Abilities.TRIAGE,
[Species.VENONAT]: Abilities.SIMPLE,
[Species.DIGLETT]: Abilities.STURDY,
[Species.MEOWTH]: Abilities.TOUGH_CLAWS,
[Species.PSYDUCK]: Abilities.SIMPLE,
[Species.MANKEY]: Abilities.IRON_FIST,
[Species.GROWLITHE]: Abilities.SPEED_BOOST,
[Species.POLIWAG]: Abilities.NO_GUARD,
[Species.ABRA]: Abilities.PSYCHIC_SURGE,
[Species.MACHOP]: Abilities.QUICK_FEET,
[Species.BELLSPROUT]: Abilities.FLOWER_GIFT,
[Species.TENTACOOL]: Abilities.TOXIC_CHAIN,
[Species.GEODUDE]: Abilities.DRY_SKIN,
[Species.PONYTA]: Abilities.MAGIC_GUARD,
[Species.SLOWPOKE]: Abilities.UNAWARE,
[Species.MAGNEMITE]: Abilities.LEVITATE,
[Species.FARFETCHD]: Abilities.SNIPER,
[Species.DODUO]: Abilities.PARENTAL_BOND,
[Species.SEEL]: Abilities.WATER_BUBBLE,
[Species.GRIMER]: Abilities.WATER_ABSORB,
[Species.SHELLDER]: Abilities.ICE_SCALES,
[Species.GASTLY]: Abilities.SHADOW_SHIELD,
[Species.ONIX]: Abilities.ROCKY_PAYLOAD,
[Species.DROWZEE]: Abilities.MAGICIAN,
[Species.KRABBY]: Abilities.THERMAL_EXCHANGE,
[Species.VOLTORB]: Abilities.TRANSISTOR,
[Species.EXEGGCUTE]: Abilities.RIPEN,
[Species.CUBONE]: Abilities.PARENTAL_BOND,
[Species.LICKITUNG]: Abilities.CHEEK_POUCH,
[Species.KOFFING]: Abilities.PARENTAL_BOND,
[Species.RHYHORN]: Abilities.FILTER,
[Species.TANGELA]: Abilities.SEED_SOWER,
[Species.KANGASKHAN]: Abilities.GUTS,
[Species.HORSEA]: Abilities.DRAGONS_MAW,
[Species.GOLDEEN]: Abilities.MULTISCALE,
[Species.STARYU]: Abilities.REGENERATOR,
[Species.SCYTHER]: Abilities.TINTED_LENS,
[Species.PINSIR]: Abilities.TINTED_LENS,
[Species.TAUROS]: Abilities.STAMINA,
[Species.MAGIKARP]: Abilities.MULTISCALE,
[Species.LAPRAS]: Abilities.LIGHTNING_ROD,
[Species.DITTO]: Abilities.ADAPTABILITY,
[Species.EEVEE]: Abilities.PICKUP,
[Species.PORYGON]: Abilities.PROTEAN,
[Species.OMANYTE]: Abilities.STURDY,
[Species.KABUTO]: Abilities.TOUGH_CLAWS,
[Species.AERODACTYL]: Abilities.ORICHALCUM_PULSE,
[Species.ARTICUNO]: Abilities.SNOW_WARNING,
[Species.ZAPDOS]: Abilities.DRIZZLE,
[Species.MOLTRES]: Abilities.DROUGHT,
[Species.DRATINI]: Abilities.AERILATE,
[Species.MEWTWO]: Abilities.NEUROFORCE,
[Species.MEW]: Abilities.PROTEAN,
[Species.CHIKORITA]: Abilities.THICK_FAT,
[Species.CYNDAQUIL]: Abilities.DROUGHT,
[Species.TOTODILE]: Abilities.TOUGH_CLAWS,
[Species.SENTRET]: Abilities.PICKUP,
[Species.HOOTHOOT]: Abilities.AERILATE,
[Species.LEDYBA]: Abilities.PRANKSTER,
[Species.SPINARAK]: Abilities.PRANKSTER,
[Species.CHINCHOU]: Abilities.WATER_BUBBLE,
[Species.PICHU]: Abilities.ELECTRIC_SURGE,
[Species.CLEFFA]: Abilities.ANALYTIC,
[Species.IGGLYBUFF]: Abilities.HUGE_POWER,
[Species.TOGEPI]: Abilities.PIXILATE,
[Species.NATU]: Abilities.TINTED_LENS,
[Species.MAREEP]: Abilities.ELECTROMORPHOSIS,
[Species.HOPPIP]: Abilities.FLUFFY,
[Species.AIPOM]: Abilities.SCRAPPY,
[Species.SUNKERN]: Abilities.DROUGHT,
[Species.YANMA]: Abilities.SHEER_FORCE,
[Species.WOOPER]: Abilities.COMATOSE,
[Species.MURKROW]: Abilities.DARK_AURA,
[Species.MISDREAVUS]: Abilities.BEADS_OF_RUIN,
[Species.UNOWN]: Abilities.PICKUP,
[Species.GIRAFARIG]: Abilities.PARENTAL_BOND,
[Species.PINECO]: Abilities.IRON_BARBS,
[Species.DUNSPARCE]: Abilities.UNAWARE,
[Species.GLIGAR]: Abilities.TOXIC_BOOST,
[Species.SNUBBULL]: Abilities.PIXILATE,
[Species.QWILFISH]: Abilities.TOXIC_DEBRIS,
[Species.SHUCKLE]: Abilities.HARVEST,
[Species.HERACROSS]: Abilities.TECHNICIAN,
[Species.SNEASEL]: Abilities.TOUGH_CLAWS,
[Species.TEDDIURSA]: Abilities.THICK_FAT,
[Species.SLUGMA]: Abilities.DESOLATE_LAND,
[Species.SWINUB]: Abilities.SLUSH_RUSH,
[Species.CORSOLA]: Abilities.STORM_DRAIN,
[Species.REMORAID]: Abilities.SIMPLE,
[Species.DELIBIRD]: Abilities.HUGE_POWER,
[Species.SKARMORY]: Abilities.LIGHTNING_ROD,
[Species.HOUNDOUR]: Abilities.DROUGHT,
[Species.PHANPY]: Abilities.SPEED_BOOST,
[Species.STANTLER]: Abilities.SPEED_BOOST,
[Species.SMEARGLE]: Abilities.PRANKSTER,
[Species.TYROGUE]: Abilities.MOXIE,
[Species.SMOOCHUM]: Abilities.PSYCHIC_SURGE,
[Species.ELEKID]: Abilities.SHEER_FORCE,
[Species.MAGBY]: Abilities.CONTRARY,
[Species.MILTANK]: Abilities.STAMINA,
[Species.RAIKOU]: Abilities.TRANSISTOR,
[Species.ENTEI]: Abilities.MOXIE,
[Species.SUICUNE]: Abilities.UNAWARE,
[Species.LARVITAR]: Abilities.SAND_RUSH,
[Species.LUGIA]: Abilities.DELTA_STREAM,
[Species.HO_OH]: Abilities.MAGIC_GUARD,
[Species.CELEBI]: Abilities.PSYCHIC_SURGE,
[Species.TREECKO]: Abilities.TINTED_LENS,
[Species.TORCHIC]: Abilities.RECKLESS,
[Species.MUDKIP]: Abilities.DRIZZLE,
[Species.POOCHYENA]: Abilities.TOUGH_CLAWS,
[Species.ZIGZAGOON]: Abilities.RUN_AWAY,
[Species.WURMPLE]: Abilities.SIMPLE,
[Species.LOTAD]: Abilities.DRIZZLE,
[Species.SEEDOT]: Abilities.SHARPNESS,
[Species.TAILLOW]: Abilities.AERILATE,
[Species.WINGULL]: Abilities.SWIFT_SWIM,
[Species.RALTS]: Abilities.PSYCHIC_SURGE,
[Species.SURSKIT]: Abilities.WATER_BUBBLE,
[Species.SHROOMISH]: Abilities.GUTS,
[Species.SLAKOTH]: Abilities.GUTS,
[Species.NINCADA]: Abilities.MAGIC_GUARD,
[Species.WHISMUR]: Abilities.PUNK_ROCK,
[Species.MAKUHITA]: Abilities.STAMINA,
[Species.AZURILL]: Abilities.MISTY_SURGE,
[Species.NOSEPASS]: Abilities.LEVITATE,
[Species.SKITTY]: Abilities.SCRAPPY,
[Species.SABLEYE]: Abilities.UNNERVE,
[Species.MAWILE]: Abilities.UNNERVE,
[Species.ARON]: Abilities.EARTH_EATER,
[Species.MEDITITE]: Abilities.MINDS_EYE,
[Species.ELECTRIKE]: Abilities.ELECTRIC_SURGE,
[Species.PLUSLE]: Abilities.POWER_SPOT,
[Species.MINUN]: Abilities.POWER_SPOT,
[Species.VOLBEAT]: Abilities.HONEY_GATHER,
[Species.ILLUMISE]: Abilities.HONEY_GATHER,
[Species.GULPIN]: Abilities.EARTH_EATER,
[Species.CARVANHA]: Abilities.SHEER_FORCE,
[Species.WAILMER]: Abilities.LEVITATE,
[Species.NUMEL]: Abilities.FUR_COAT,
[Species.TORKOAL]: Abilities.ANALYTIC,
[Species.SPOINK]: Abilities.PSYCHIC_SURGE,
[Species.SPINDA]: Abilities.SIMPLE,
[Species.TRAPINCH]: Abilities.ADAPTABILITY,
[Species.CACNEA]: Abilities.SAND_RUSH,
[Species.SWABLU]: Abilities.ADAPTABILITY,
[Species.ZANGOOSE]: Abilities.POISON_HEAL,
[Species.SEVIPER]: Abilities.MULTISCALE,
[Species.LUNATONE]: Abilities.SHADOW_SHIELD,
[Species.SOLROCK]: Abilities.DROUGHT,
[Species.BARBOACH]: Abilities.SIMPLE,
[Species.CORPHISH]: Abilities.TOUGH_CLAWS,
[Species.BALTOY]: Abilities.WELL_BAKED_BODY,
[Species.LILEEP]: Abilities.SEED_SOWER,
[Species.ANORITH]: Abilities.WATER_ABSORB,
[Species.FEEBAS]: Abilities.MAGIC_GUARD,
[Species.CASTFORM]: Abilities.ADAPTABILITY,
[Species.KECLEON]: Abilities.ADAPTABILITY,
[Species.SHUPPET]: Abilities.SHADOW_SHIELD,
[Species.DUSKULL]: Abilities.UNNERVE,
[Species.TROPIUS]: Abilities.RIPEN,
[Species.ABSOL]: Abilities.SHARPNESS,
[Species.WYNAUT]: Abilities.STURDY,
[Species.SNORUNT]: Abilities.SNOW_WARNING,
[Species.SPHEAL]: Abilities.UNAWARE,
[Species.CLAMPERL]: Abilities.DRIZZLE,
[Species.RELICANTH]: Abilities.PRIMORDIAL_SEA,
[Species.LUVDISC]: Abilities.MULTISCALE,
[Species.BAGON]: Abilities.DRAGONS_MAW,
[Species.BELDUM]: Abilities.LEVITATE,
[Species.REGIROCK]: Abilities.SAND_STREAM,
[Species.REGICE]: Abilities.SNOW_WARNING,
[Species.REGISTEEL]: Abilities.FILTER,
[Species.LATIAS]: Abilities.PRISM_ARMOR,
[Species.LATIOS]: Abilities.TINTED_LENS,
[Species.KYOGRE]: Abilities.MOLD_BREAKER,
[Species.GROUDON]: Abilities.TURBOBLAZE,
[Species.RAYQUAZA]: Abilities.UNNERVE,
[Species.JIRACHI]: Abilities.COMATOSE,
[Species.DEOXYS]: Abilities.PROTEAN,
[Species.TURTWIG]: Abilities.THICK_FAT,
[Species.CHIMCHAR]: Abilities.BEAST_BOOST,
[Species.PIPLUP]: Abilities.DRIZZLE,
[Species.STARLY]: Abilities.ROCK_HEAD,
[Species.BIDOOF]: Abilities.SAP_SIPPER,
[Species.KRICKETOT]: Abilities.SHARPNESS,
[Species.SHINX]: Abilities.SPEED_BOOST,
[Species.BUDEW]: Abilities.GRASSY_SURGE,
[Species.CRANIDOS]: Abilities.ROCK_HEAD,
[Species.SHIELDON]: Abilities.EARTH_EATER,
[Species.BURMY]: Abilities.STURDY,
[Species.COMBEE]: Abilities.INTIMIDATE,
[Species.PACHIRISU]: Abilities.HONEY_GATHER,
[Species.BUIZEL]: Abilities.MOXIE,
[Species.CHERUBI]: Abilities.ORICHALCUM_PULSE,
[Species.SHELLOS]: Abilities.REGENERATOR,
[Species.DRIFLOON]: Abilities.MAGIC_GUARD,
[Species.BUNEARY]: Abilities.ADAPTABILITY,
[Species.GLAMEOW]: Abilities.INTIMIDATE,
[Species.CHINGLING]: Abilities.PUNK_ROCK,
[Species.STUNKY]: Abilities.NEUTRALIZING_GAS,
[Species.BRONZOR]: Abilities.BULLETPROOF,
[Species.BONSLY]: Abilities.SAP_SIPPER,
[Species.MIME_JR]: Abilities.OPPORTUNIST,
[Species.HAPPINY]: Abilities.FUR_COAT,
[Species.CHATOT]: Abilities.PUNK_ROCK,
[Species.SPIRITOMB]: Abilities.VESSEL_OF_RUIN,
[Species.GIBLE]: Abilities.SAND_STREAM,
[Species.MUNCHLAX]: Abilities.RIPEN,
[Species.RIOLU]: Abilities.MINDS_EYE,
[Species.HIPPOPOTAS]: Abilities.UNAWARE,
[Species.SKORUPI]: Abilities.SUPER_LUCK,
[Species.CROAGUNK]: Abilities.MOXIE,
[Species.CARNIVINE]: Abilities.ARENA_TRAP,
[Species.FINNEON]: Abilities.WATER_BUBBLE,
[Species.MANTYKE]: Abilities.UNAWARE,
[Species.SNOVER]: Abilities.THICK_FAT,
[Species.ROTOM]: Abilities.HADRON_ENGINE,
[Species.UXIE]: Abilities.UNAWARE,
[Species.MESPRIT]: Abilities.MOODY,
[Species.AZELF]: Abilities.NEUROFORCE,
[Species.DIALGA]: Abilities.LEVITATE,
[Species.PALKIA]: Abilities.SPEED_BOOST,
[Species.HEATRAN]: Abilities.EARTH_EATER,
[Species.REGIGIGAS]: Abilities.SCRAPPY,
[Species.GIRATINA]: Abilities.SHADOW_SHIELD,
[Species.CRESSELIA]: Abilities.SHADOW_SHIELD,
[Species.PHIONE]: Abilities.SIMPLE,
[Species.MANAPHY]: Abilities.PRIMORDIAL_SEA,
[Species.DARKRAI]: Abilities.UNNERVE,
[Species.SHAYMIN]: Abilities.WIND_RIDER,
[Species.ARCEUS]: Abilities.ADAPTABILITY,
[Species.VICTINI]: Abilities.SHEER_FORCE,
[Species.SNIVY]: Abilities.MULTISCALE,
[Species.TEPIG]: Abilities.ROCK_HEAD,
[Species.OSHAWOTT]: Abilities.INTREPID_SWORD,
[Species.PATRAT]: Abilities.NO_GUARD,
[Species.LILLIPUP]: Abilities.FUR_COAT,
[Species.PURRLOIN]: Abilities.PICKUP,
[Species.PANSAGE]: Abilities.WELL_BAKED_BODY,
[Species.PANSEAR]: Abilities.WATER_ABSORB,
[Species.PANPOUR]: Abilities.SAP_SIPPER,
[Species.MUNNA]: Abilities.NEUTRALIZING_GAS,
[Species.PIDOVE]: Abilities.SNIPER,
[Species.BLITZLE]: Abilities.ELECTRIC_SURGE,
[Species.ROGGENROLA]: Abilities.SOLID_ROCK,
[Species.WOOBAT]: Abilities.OPPORTUNIST,
[Species.DRILBUR]: Abilities.SAND_STREAM,
[Species.AUDINO]: Abilities.FRIEND_GUARD,
[Species.TIMBURR]: Abilities.ROCKY_PAYLOAD,
[Species.TYMPOLE]: Abilities.POISON_HEAL,
[Species.THROH]: Abilities.STAMINA,
[Species.SAWK]: Abilities.SCRAPPY,
[Species.SEWADDLE]: Abilities.SHARPNESS,
[Species.VENIPEDE]: Abilities.STAMINA,
[Species.COTTONEE]: Abilities.FLUFFY,
[Species.PETILIL]: Abilities.SIMPLE,
[Species.BASCULIN]: Abilities.SUPREME_OVERLORD,
[Species.SANDILE]: Abilities.TOUGH_CLAWS,
[Species.DARUMAKA]: Abilities.GORILLA_TACTICS,
[Species.MARACTUS]: Abilities.WELL_BAKED_BODY,
[Species.DWEBBLE]: Abilities.ROCKY_PAYLOAD,
[Species.SCRAGGY]: Abilities.PROTEAN,
[Species.SIGILYPH]: Abilities.FLARE_BOOST,
[Species.YAMASK]: Abilities.PURIFYING_SALT,
[Species.TIRTOUGA]: Abilities.WATER_ABSORB,
[Species.ARCHEN]: Abilities.MULTISCALE,
[Species.TRUBBISH]: Abilities.NEUTRALIZING_GAS,
[Species.ZORUA]: Abilities.DARK_AURA,
[Species.MINCCINO]: Abilities.FUR_COAT,
[Species.GOTHITA]: Abilities.UNNERVE,
[Species.SOLOSIS]: Abilities.PSYCHIC_SURGE,
[Species.DUCKLETT]: Abilities.DRIZZLE,
[Species.VANILLITE]: Abilities.SLUSH_RUSH,
[Species.DEERLING]: Abilities.FUR_COAT,
[Species.EMOLGA]: Abilities.TRANSISTOR,
[Species.KARRABLAST]: Abilities.QUICK_DRAW,
[Species.FOONGUS]: Abilities.THICK_FAT,
[Species.FRILLISH]: Abilities.POISON_HEAL,
[Species.ALOMOMOLA]: Abilities.MULTISCALE,
[Species.JOLTIK]: Abilities.TRANSISTOR,
[Species.FERROSEED]: Abilities.ROUGH_SKIN,
[Species.KLINK]: Abilities.STEELY_SPIRIT,
[Species.TYNAMO]: Abilities.POISON_HEAL,
[Species.ELGYEM]: Abilities.PRISM_ARMOR,
[Species.LITWICK]: Abilities.SOUL_HEART,
[Species.AXEW]: Abilities.DRAGONS_MAW,
[Species.CUBCHOO]: Abilities.TOUGH_CLAWS,
[Species.CRYOGONAL]: Abilities.SNOW_WARNING,
[Species.SHELMET]: Abilities.PROTEAN,
[Species.STUNFISK]: Abilities.STORM_DRAIN,
[Species.MIENFOO]: Abilities.NO_GUARD,
[Species.DRUDDIGON]: Abilities.INTIMIDATE,
[Species.GOLETT]: Abilities.SHADOW_SHIELD,
[Species.PAWNIARD]: Abilities.SWORD_OF_RUIN,
[Species.BOUFFALANT]: Abilities.ROCK_HEAD,
[Species.RUFFLET]: Abilities.SPEED_BOOST,
[Species.VULLABY]: Abilities.THICK_FAT,
[Species.HEATMOR]: Abilities.CONTRARY,
[Species.DURANT]: Abilities.COMPOUND_EYES,
[Species.DEINO]: Abilities.PARENTAL_BOND,
[Species.LARVESTA]: Abilities.DROUGHT,
[Species.COBALION]: Abilities.INTREPID_SWORD,
[Species.TERRAKION]: Abilities.ROCKY_PAYLOAD,
[Species.VIRIZION]: Abilities.SHARPNESS,
[Species.TORNADUS]: Abilities.DRIZZLE,
[Species.THUNDURUS]: Abilities.DRIZZLE,
[Species.RESHIRAM]: Abilities.ORICHALCUM_PULSE,
[Species.ZEKROM]: Abilities.HADRON_ENGINE,
[Species.LANDORUS]: Abilities.STORM_DRAIN,
[Species.KYUREM]: Abilities.SNOW_WARNING,
[Species.KELDEO]: Abilities.GRIM_NEIGH,
[Species.MELOETTA]: Abilities.MINDS_EYE,
[Species.GENESECT]: Abilities.PROTEAN,
[Species.CHESPIN]: Abilities.DAUNTLESS_SHIELD,
[Species.FENNEKIN]: Abilities.PSYCHIC_SURGE,
[Species.FROAKIE]: Abilities.STAKEOUT,
[Species.BUNNELBY]: Abilities.GUTS,
[Species.FLETCHLING]: Abilities.MAGIC_GUARD,
[Species.SCATTERBUG]: Abilities.PRANKSTER,
[Species.LITLEO]: Abilities.BEAST_BOOST,
[Species.FLABEBE]: Abilities.GRASSY_SURGE,
[Species.SKIDDO]: Abilities.SEED_SOWER,
[Species.PANCHAM]: Abilities.FUR_COAT,
[Species.FURFROU]: Abilities.FLUFFY,
[Species.ESPURR]: Abilities.FUR_COAT,
[Species.HONEDGE]: Abilities.SHARPNESS,
[Species.SPRITZEE]: Abilities.FUR_COAT,
[Species.SWIRLIX]: Abilities.WELL_BAKED_BODY,
[Species.INKAY]: Abilities.UNNERVE,
[Species.BINACLE]: Abilities.SAP_SIPPER,
[Species.SKRELP]: Abilities.DRAGONS_MAW,
[Species.CLAUNCHER]: Abilities.SWIFT_SWIM,
[Species.HELIOPTILE]: Abilities.PROTEAN,
[Species.TYRUNT]: Abilities.RECKLESS,
[Species.AMAURA]: Abilities.ICE_SCALES,
[Species.HAWLUCHA]: Abilities.MOXIE,
[Species.DEDENNE]: Abilities.PIXILATE,
[Species.CARBINK]: Abilities.SOLID_ROCK,
[Species.GOOMY]: Abilities.REGENERATOR,
[Species.KLEFKI]: Abilities.LEVITATE,
[Species.PHANTUMP]: Abilities.SHADOW_TAG,
[Species.PUMPKABOO]: Abilities.WELL_BAKED_BODY,
[Species.BERGMITE]: Abilities.ICE_SCALES,
[Species.NOIBAT]: Abilities.PUNK_ROCK,
[Species.XERNEAS]: Abilities.HARVEST,
[Species.YVELTAL]: Abilities.SOUL_HEART,
[Species.ZYGARDE]: Abilities.HUGE_POWER,
[Species.DIANCIE]: Abilities.LEVITATE,
[Species.HOOPA]: Abilities.OPPORTUNIST,
[Species.VOLCANION]: Abilities.FILTER,
[Species.ROWLET]: Abilities.SNIPER,
[Species.LITTEN]: Abilities.OPPORTUNIST,
[Species.POPPLIO]: Abilities.PUNK_ROCK,
[Species.PIKIPEK]: Abilities.TECHNICIAN,
[Species.YUNGOOS]: Abilities.TOUGH_CLAWS,
[Species.GRUBBIN]: Abilities.SPEED_BOOST,
[Species.CRABRAWLER]: Abilities.WATER_BUBBLE,
[Species.ORICORIO]: Abilities.ADAPTABILITY,
[Species.CUTIEFLY]: Abilities.TINTED_LENS,
[Species.ROCKRUFF]: Abilities.ROCKY_PAYLOAD,
[Species.WISHIWASHI]: Abilities.REGENERATOR,
[Species.MAREANIE]: Abilities.TOXIC_DEBRIS,
[Species.MUDBRAY]: Abilities.CUD_CHEW,
[Species.DEWPIDER]: Abilities.TINTED_LENS,
[Species.FOMANTIS]: Abilities.SHARPNESS,
[Species.MORELULL]: Abilities.TRIAGE,
[Species.SALANDIT]: Abilities.DRAGONS_MAW,
[Species.STUFFUL]: Abilities.SCRAPPY,
[Species.BOUNSWEET]: Abilities.MOXIE,
[Species.COMFEY]: Abilities.FRIEND_GUARD,
[Species.ORANGURU]: Abilities.POWER_SPOT,
[Species.PASSIMIAN]: Abilities.LIBERO,
[Species.WIMPOD]: Abilities.REGENERATOR,
[Species.SANDYGAST]: Abilities.SAND_SPIT,
[Species.PYUKUMUKU]: Abilities.PURIFYING_SALT,
[Species.TYPE_NULL]: Abilities.ADAPTABILITY,
[Species.MINIOR]: Abilities.STURDY,
[Species.KOMALA]: Abilities.GUTS,
[Species.TURTONATOR]: Abilities.DAUNTLESS_SHIELD,
[Species.TOGEDEMARU]: Abilities.ROUGH_SKIN,
[Species.MIMIKYU]: Abilities.TOUGH_CLAWS,
[Species.BRUXISH]: Abilities.MULTISCALE,
[Species.DRAMPA]: Abilities.THICK_FAT,
[Species.DHELMISE]: Abilities.WATER_BUBBLE,
[Species.JANGMO_O]: Abilities.DAUNTLESS_SHIELD,
[Species.TAPU_KOKO]: Abilities.TRANSISTOR,
[Species.TAPU_LELE]: Abilities.SHEER_FORCE,
[Species.TAPU_BULU]: Abilities.TRIAGE,
[Species.TAPU_FINI]: Abilities.FAIRY_AURA,
[Species.COSMOG]: Abilities.BEAST_BOOST,
[Species.NIHILEGO]: Abilities.LEVITATE,
[Species.BUZZWOLE]: Abilities.MOXIE,
[Species.PHEROMOSA]: Abilities.TINTED_LENS,
[Species.XURKITREE]: Abilities.TRANSISTOR,
[Species.CELESTEELA]: Abilities.HEATPROOF,
[Species.KARTANA]: Abilities.SHARPNESS,
[Species.GUZZLORD]: Abilities.POISON_HEAL,
[Species.NECROZMA]: Abilities.BEAST_BOOST,
[Species.MAGEARNA]: Abilities.STEELY_SPIRIT,
[Species.MARSHADOW]: Abilities.IRON_FIST,
[Species.POIPOLE]: Abilities.SHEER_FORCE,
[Species.STAKATAKA]: Abilities.SOLID_ROCK,
[Species.BLACEPHALON]: Abilities.MAGIC_GUARD,
[Species.ZERAORA]: Abilities.TOUGH_CLAWS,
[Species.MELTAN]: Abilities.STEELY_SPIRIT,
[Species.GROOKEY]: Abilities.GRASS_PELT,
[Species.SCORBUNNY]: Abilities.NO_GUARD,
[Species.SOBBLE]: Abilities.SUPER_LUCK,
[Species.SKWOVET]: Abilities.HARVEST,
[Species.ROOKIDEE]: Abilities.IRON_BARBS,
[Species.BLIPBUG]: Abilities.PSYCHIC_SURGE,
[Species.NICKIT]: Abilities.MAGICIAN,
[Species.GOSSIFLEUR]: Abilities.GRASSY_SURGE,
[Species.WOOLOO]: Abilities.SIMPLE,
[Species.CHEWTLE]: Abilities.ROCKY_PAYLOAD,
[Species.YAMPER]: Abilities.SHEER_FORCE,
[Species.ROLYCOLY]: Abilities.SOLID_ROCK,
[Species.APPLIN]: Abilities.DRAGONS_MAW,
[Species.SILICOBRA]: Abilities.SAND_RUSH,
[Species.CRAMORANT]: Abilities.LIGHTNING_ROD,
[Species.ARROKUDA]: Abilities.INTIMIDATE,
[Species.TOXEL]: Abilities.ELECTRIC_SURGE,
[Species.SIZZLIPEDE]: Abilities.SPEED_BOOST,
[Species.CLOBBOPUS]: Abilities.WATER_BUBBLE,
[Species.SINISTEA]: Abilities.SHADOW_SHIELD,
[Species.HATENNA]: Abilities.FAIRY_AURA,
[Species.IMPIDIMP]: Abilities.FUR_COAT,
[Species.MILCERY]: Abilities.REGENERATOR,
[Species.FALINKS]: Abilities.PARENTAL_BOND,
[Species.PINCURCHIN]: Abilities.ELECTROMORPHOSIS,
[Species.SNOM]: Abilities.SNOW_WARNING,
[Species.STONJOURNER]: Abilities.STURDY,
[Species.EISCUE]: Abilities.ICE_SCALES,
[Species.INDEEDEE]: Abilities.FRIEND_GUARD,
[Species.MORPEKO]: Abilities.MOODY,
[Species.CUFANT]: Abilities.EARTH_EATER,
[Species.DRACOZOLT]: Abilities.NO_GUARD,
[Species.ARCTOZOLT]: Abilities.TRANSISTOR,
[Species.DRACOVISH]: Abilities.SWIFT_SWIM,
[Species.ARCTOVISH]: Abilities.STRONG_JAW,
[Species.DURALUDON]: Abilities.STEELWORKER,
[Species.DREEPY]: Abilities.PARENTAL_BOND,
[Species.ZACIAN]: Abilities.UNNERVE,
[Species.ZAMAZENTA]: Abilities.UNNERVE,
[Species.ETERNATUS]: Abilities.NEUTRALIZING_GAS,
[Species.KUBFU]: Abilities.IRON_FIST,
[Species.ZARUDE]: Abilities.TOUGH_CLAWS,
[Species.REGIELEKI]: Abilities.ELECTRIC_SURGE,
[Species.REGIDRAGO]: Abilities.MULTISCALE,
[Species.GLASTRIER]: Abilities.FILTER,
[Species.SPECTRIER]: Abilities.SHADOW_SHIELD,
[Species.CALYREX]: Abilities.HARVEST,
[Species.ENAMORUS]: Abilities.FAIRY_AURA,
[Species.SPRIGATITO]: Abilities.MAGICIAN,
[Species.FUECOCO]: Abilities.PUNK_ROCK,
[Species.QUAXLY]: Abilities.OPPORTUNIST,
[Species.LECHONK]: Abilities.SIMPLE,
[Species.TAROUNTULA]: Abilities.HONEY_GATHER,
[Species.NYMBLE]: Abilities.GUTS,
[Species.PAWMI]: Abilities.TRANSISTOR,
[Species.TANDEMAUS]: Abilities.SCRAPPY,
[Species.FIDOUGH]: Abilities.WATER_ABSORB,
[Species.SMOLIV]: Abilities.RIPEN,
[Species.SQUAWKABILLY]: Abilities.MOXIE,
[Species.NACLI]: Abilities.SOLID_ROCK,
[Species.CHARCADET]: Abilities.PRISM_ARMOR,
[Species.TADBULB]: Abilities.STAMINA,
[Species.WATTREL]: Abilities.SHEER_FORCE,
[Species.MASCHIFF]: Abilities.STRONG_JAW,
[Species.SHROODLE]: Abilities.CORROSION,
[Species.BRAMBLIN]: Abilities.SHADOW_SHIELD,
[Species.TOEDSCOOL]: Abilities.PRANKSTER,
[Species.KLAWF]: Abilities.WATER_ABSORB,
[Species.CAPSAKID]: Abilities.PARENTAL_BOND,
[Species.RELLOR]: Abilities.PRANKSTER,
[Species.FLITTLE]: Abilities.DAZZLING,
[Species.TINKATINK]: Abilities.STEELWORKER,
[Species.WIGLETT]: Abilities.STURDY,
[Species.BOMBIRDIER]: Abilities.UNBURDEN,
[Species.FINIZEN]: Abilities.IRON_FIST,
[Species.VAROOM]: Abilities.LEVITATE,
[Species.CYCLIZAR]: Abilities.PROTEAN,
[Species.ORTHWORM]: Abilities.REGENERATOR,
[Species.GLIMMET]: Abilities.LEVITATE,
[Species.GREAVARD]: Abilities.FUR_COAT,
[Species.FLAMIGO]: Abilities.MOXIE,
[Species.CETODDLE]: Abilities.ICE_SCALES,
[Species.VELUZA]: Abilities.SUPER_LUCK,
[Species.DONDOZO]: Abilities.PARENTAL_BOND,
[Species.TATSUGIRI]: Abilities.ADAPTABILITY,
[Species.GREAT_TUSK]: Abilities.INTIMIDATE,
[Species.SCREAM_TAIL]: Abilities.UNAWARE,
[Species.BRUTE_BONNET]: Abilities.CHLOROPHYLL,
[Species.FLUTTER_MANE]: Abilities.DAZZLING,
[Species.SLITHER_WING]: Abilities.SCRAPPY,
[Species.SANDY_SHOCKS]: Abilities.EARTH_EATER,
[Species.IRON_TREADS]: Abilities.STEELY_SPIRIT,
[Species.IRON_BUNDLE]: Abilities.SNOW_WARNING,
[Species.IRON_HANDS]: Abilities.IRON_FIST,
[Species.IRON_JUGULIS]: Abilities.LIGHTNING_ROD,
[Species.IRON_MOTH]: Abilities.LEVITATE,
[Species.IRON_THORNS]: Abilities.SAND_STREAM,
[Species.FRIGIBAX]: Abilities.SNOW_WARNING,
[Species.GIMMIGHOUL]: Abilities.HONEY_GATHER,
[Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN,
[Species.CHIEN_PAO]: Abilities.INTIMIDATE,
[Species.TING_LU]: Abilities.STAMINA,
[Species.CHI_YU]: Abilities.BERSERK,
[Species.ROARING_MOON]: Abilities.TOUGH_CLAWS,
[Species.IRON_VALIANT]: Abilities.ADAPTABILITY,
[Species.KORAIDON]: Abilities.OPPORTUNIST,
[Species.MIRAIDON]: Abilities.OPPORTUNIST,
[Species.WALKING_WAKE]: Abilities.BEAST_BOOST,
[Species.IRON_LEAVES]: Abilities.SHARPNESS,
[Species.POLTCHAGEIST]: Abilities.TRIAGE,
[Species.OKIDOGI]: Abilities.FUR_COAT,
[Species.MUNKIDORI]: Abilities.NEUROFORCE,
[Species.FEZANDIPITI]: Abilities.LEVITATE,
[Species.OGERPON]: Abilities.OPPORTUNIST,
[Species.GOUGING_FIRE]: Abilities.BEAST_BOOST,
[Species.RAGING_BOLT]: Abilities.BEAST_BOOST,
[Species.IRON_BOULDER]: Abilities.SHARPNESS,
[Species.IRON_CROWN]: Abilities.SHARPNESS,
[Species.TERAPAGOS]: Abilities.SOUL_HEART,
[Species.PECHARUNT]: Abilities.TOXIC_CHAIN,
[Species.ALOLA_RATTATA]: Abilities.ADAPTABILITY,
[Species.ALOLA_SANDSHREW]: Abilities.ICE_SCALES,
[Species.ALOLA_VULPIX]: Abilities.SHEER_FORCE,
[Species.ALOLA_DIGLETT]: Abilities.STURDY,
[Species.ALOLA_MEOWTH]: Abilities.DARK_AURA,
[Species.ALOLA_GEODUDE]: Abilities.DRY_SKIN,
[Species.ALOLA_GRIMER]: Abilities.TOXIC_DEBRIS,
[Species.ETERNAL_FLOETTE]: Abilities.MAGIC_GUARD,
[Species.GALAR_MEOWTH]: Abilities.STEELWORKER,
[Species.GALAR_PONYTA]: Abilities.MOXIE,
[Species.GALAR_SLOWPOKE]: Abilities.UNAWARE,
[Species.GALAR_FARFETCHD]: Abilities.INTREPID_SWORD,
[Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE,
[Species.GALAR_ZAPDOS]: Abilities.TOUGH_CLAWS,
[Species.GALAR_MOLTRES]: Abilities.DARK_AURA,
[Species.GALAR_CORSOLA]: Abilities.SHADOW_SHIELD,
[Species.GALAR_ZIGZAGOON]: Abilities.POISON_HEAL,
[Species.GALAR_DARUMAKA]: Abilities.FLASH_FIRE,
[Species.GALAR_YAMASK]: Abilities.TABLETS_OF_RUIN,
[Species.GALAR_STUNFISK]: Abilities.ARENA_TRAP,
[Species.HISUI_GROWLITHE]: Abilities.RECKLESS,
[Species.HISUI_VOLTORB]: Abilities.TRANSISTOR,
[Species.HISUI_QWILFISH]: Abilities.MERCILESS,
[Species.HISUI_SNEASEL]: Abilities.SCRAPPY,
[Species.HISUI_ZORUA]: Abilities.ADAPTABILITY,
[Species.PALDEA_TAUROS]: Abilities.ADAPTABILITY,
[Species.PALDEA_WOOPER]: Abilities.THICK_FAT,
[Species.BLOODMOON_URSALUNA]: Abilities.BERSERK
};

View File

@ -1,16 +1,18 @@
import { Gender } from "./gender"; import { Gender } from "#app/data/gender";
import { PokeballType } from "./pokeball"; import { PokeballType } from "#app/data/pokeball";
import Pokemon from "../field/pokemon"; import Pokemon from "#app/field/pokemon";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { Type } from "./type"; import { Type } from "#app/data/type";
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import { SpeciesFormKey } from "./pokemon-species"; import { WeatherType } from "#app/data/weather";
import { WeatherType } from "./weather"; import { Nature } from "#app/data/nature";
import { Nature } from "./nature";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier } from "#app/modifier/modifier";
import { SpeciesFormKey } from "#enums/species-form-key";
export enum SpeciesWildEvolutionDelay { export enum SpeciesWildEvolutionDelay {
NONE, NONE,
@ -1647,8 +1649,14 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.GIMMIGHOUL]: [ [Species.GIMMIGHOUL]: [
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG), new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG) + p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG)
] ]
}; };

53
src/data/balance/rates.ts Normal file
View File

@ -0,0 +1,53 @@
/**
* Rates for shinies and other random properties are defined in this file.
* CHANCE is defined as x/65536
* RATE is defined as 1/x
*/
// #region Encounterable properties
/** `64/65536 -> 1/1024` */
export const BASE_SHINY_CHANCE = 64;
/** `256/65536 -> 1/256` */
export const BASE_HIDDEN_ABILITY_CHANCE = 256;
// #region Egg properties
// Threshold x at which a gacha egg is determined to be a certain tier
// Specifically, the tier is determined by the highest threshold a random value between 0-255 meets or exceeds
// Legendary Up Gacha raises these thresholds by 1, thereby giving Legendary eggs 2/256 chance
export const GACHA_DEFAULT_COMMON_EGG_THRESHOLD = 52; // Default 204/256 chance, 203/256 chance in Legendary Up Gacha
export const GACHA_DEFAULT_RARE_EGG_THRESHOLD = 8; // Default 44/256 chance
export const GACHA_DEFAULT_EPIC_EGG_THRESHOLD = 1; // Default 7/256 chance, leaving Legendary as 1/256 chance
export const GACHA_LEGENDARY_UP_THRESHOLD_OFFSET = 1; // The offset to threshold for Legendary Up gacha eggs. +x/256 Legendary Egg chance, -x/256 Common Egg chance
// The number of eggs without finding a certain tier egg it takes for egg pity to kick in and that tier to be forced
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
export const EGG_PITY_LEGENDARY_THRESHOLD = 412;
export const EGG_PITY_EPIC_THRESHOLD = 59;
export const EGG_PITY_RARE_THRESHOLD = 9;
// Waves to hatch an egg of a given tier
export const HATCH_WAVES_COMMON_EGG = 10;
export const HATCH_WAVES_RARE_EGG = 25;
export const HATCH_WAVES_EPIC_EGG = 50;
export const HATCH_WAVES_LEGENDARY_EGG = 100;
export const HATCH_WAVES_MANAPHY_EGG = 50;
// Rates for specific random properties in 1/x
export const GACHA_DEFAULT_SHINY_RATE = 128;
export const GACHA_SHINY_UP_SHINY_RATE = 64;
export const SAME_SPECIES_EGG_SHINY_RATE = 12;
export const SAME_SPECIES_EGG_HA_RATE = 8;
export const MANAPHY_EGG_MANAPHY_RATE = 8;
export const GACHA_EGG_HA_RATE = 192;
// 1/x for legendary eggs, 1/x*2 for epic eggs, 1/x*4 for rare eggs, and 1/x*8 for common eggs
export const GACHA_DEFAULT_RARE_EGGMOVE_RATE = 6;
export const SAME_SPECIES_EGG_RARE_EGGMOVE_RATE = 3;
export const GACHA_MOVE_UP_RARE_EGGMOVE_RATE = 3;
// #region Variant properties
// The chance x/10 of a shiny being a variant, then of being specifically an epic variant
export const SHINY_VARIANT_CHANCE = 4;
export const SHINY_EPIC_CHANCE = 1;

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

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

View File

@ -1,4 +1,4 @@
import { ModifierTier } from "../modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -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

@ -1376,7 +1376,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));
} }
} }
@ -2123,7 +2123,40 @@ export class StockpilingTag extends BattlerTag {
*/ */
export class GulpMissileTag extends BattlerTag { export class GulpMissileTag extends BattlerTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) { constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.CUSTOM, 0, sourceMove); super(tagType, BattlerTagLapseType.HIT, 0, sourceMove);
}
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (pokemon.getTag(BattlerTagType.UNDERWATER)) {
return true;
}
const moveEffectPhase = pokemon.scene.getCurrentPhase();
if (moveEffectPhase instanceof MoveEffectPhase) {
const attacker = moveEffectPhase.getUserPokemon();
if (!attacker) {
return false;
}
if (moveEffectPhase.move.getMove().hitsSubstitute(attacker, pokemon)) {
return true;
}
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
if (!cancelled.value) {
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER);
}
if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1));
} else {
attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon);
}
}
return false;
} }
/** /**
@ -2238,7 +2271,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
* Uses DisabledTag's selectionDeniedText() message * Uses DisabledTag's selectionDeniedText() message
*/ */
override selectionDeniedText(pokemon: Pokemon, move: Moves): string { override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledHealBlock", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name, healBlockName: allMoves[Moves.HEAL_BLOCK].name });
} }
/** /**
@ -2248,7 +2281,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
* @returns {string} text to display when the move is interrupted * @returns {string} text to display when the move is interrupted
*/ */
override interruptedText(pokemon: Pokemon, move: Moves): string { override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledHealBlock", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name, healBlockName: allMoves[Moves.HEAL_BLOCK].name });
} }
override onRemove(pokemon: Pokemon): void { override onRemove(pokemon: Pokemon): void {
@ -2281,6 +2314,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
@ -2501,8 +2549,8 @@ export class TormentTag extends MoveRestrictionBattlerTag {
return false; return false;
} }
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string { override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledTorment", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) });
} }
} }
@ -2530,12 +2578,12 @@ export class TauntTag extends MoveRestrictionBattlerTag {
return allMoves[move].category === MoveCategory.STATUS; return allMoves[move].category === MoveCategory.STATUS;
} }
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string { override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledTaunt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
} }
override interruptedText(pokemon: Pokemon, move: Moves): string { override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledTaunt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
} }
} }
@ -2580,24 +2628,58 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
return false; return false;
} }
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string { override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledImprison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
} }
override interruptedText(pokemon: Pokemon, move: Moves): string { override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name }); return i18next.t("battle:moveDisabledImprison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
} }
} }
/**
* 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.
* 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 {
constructor(sourceId: number) {
super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB, sourceId);
}
/**
* Adds the Syrup Bomb battler tag to the target Pokemon.
* @param pokemon - The target {@linkcode Pokemon}
*/
override onAdd(pokemon: Pokemon) {
super.onAdd(pokemon);
pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
/**
* Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count
* @param pokemon - The target {@linkcode Pokemon}
* @param _lapseType - N/A
* @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 {
if (this.sourceId && !pokemon.scene.getPokemonById(this.sourceId)?.isActive(true)) {
return false;
}
// 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, pokemon.getBattlerIndex(), true,
[ Stat.SPD ], -1, true, false, true
));
return --this.turnCount > 0;
}
}
/** /**
* 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) {
@ -2745,6 +2827,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:
@ -2763,6 +2847,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new TauntTag(); return new TauntTag();
case BattlerTagType.IMPRISON: case BattlerTagType.IMPRISON:
return new ImprisonTag(sourceId); return new ImprisonTag(sourceId);
case BattlerTagType.SYRUP_BOMB:
return new SyrupBombTag(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

@ -1,20 +1,21 @@
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data"; import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "./pokemon-species"; import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import Pokemon, { PokemonMove } from "#app/field/pokemon";
import { BattleType, FixedBattleConfig } from "#app/battle"; import { BattleType, FixedBattleConfig } from "#app/battle";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import { GameMode } from "#app/game-mode"; import { GameMode } from "#app/game-mode";
import { Type } from "./type"; import { Type } from "#app/data/type";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Nature } from "./nature"; import { Nature } from "#app/data/nature";
import { Moves } from "#app/enums/moves"; import { Moves } from "#enums/moves";
import { TypeColor, TypeShadow } from "#app/enums/color"; import { TypeColor, TypeShadow } from "#enums/color";
import { pokemonEvolutions } from "./pokemon-evolutions"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { pokemonFormChanges } from "./pokemon-forms"; import { pokemonFormChanges } from "#app/data/pokemon-forms";
/** A constant for the default max cost of the starting party before a run */ /** A constant for the default max cost of the starting party before a run */
const DEFAULT_PARTY_MAX_COST = 10; const DEFAULT_PARTY_MAX_COST = 10;
@ -527,18 +528,19 @@ interface monotypeOverride {
*/ */
export class SingleTypeChallenge extends Challenge { export class SingleTypeChallenge extends Challenge {
private static TYPE_OVERRIDES: monotypeOverride[] = [ private static TYPE_OVERRIDES: monotypeOverride[] = [
{species: Species.MELOETTA, type: Type.PSYCHIC, fusion: true},
{ species: Species.CASTFORM, type: Type.NORMAL, fusion: false }, { species: Species.CASTFORM, type: Type.NORMAL, fusion: false },
]; ];
// TODO: Find a solution for all Pokemon with this ssui issue, including Basculin and Burmy
private static SPECIES_OVERRIDES: Species[] = [ Species.MELOETTA ];
constructor() { constructor() {
super(Challenges.SINGLE_TYPE, 18); super(Challenges.SINGLE_TYPE, 18);
} }
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { override applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean {
const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex); const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex);
const types = [ speciesForm.type1, speciesForm.type2 ]; const types = [ speciesForm.type1, speciesForm.type2 ];
if (soft) { if (soft && !SingleTypeChallenge.SPECIES_OVERRIDES.includes(pokemon.speciesId)) {
const speciesToCheck = [ pokemon.speciesId ]; const speciesToCheck = [ pokemon.speciesId ];
while (speciesToCheck.length) { while (speciesToCheck.length) {
const checking = speciesToCheck.pop(); const checking = speciesToCheck.pop();
@ -636,7 +638,7 @@ export class FreshStartChallenge extends Challenge {
applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean { applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean {
if (defaultStarterSpecies.includes(species)) { if (defaultStarterSpecies.includes(species)) {
cost.value = speciesStarters[species]; cost.value = speciesStarterCosts[species];
return true; return true;
} }
return false; return false;
@ -718,7 +720,7 @@ export class LowerStarterMaxCostChallenge extends Challenge {
} }
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean {
if (speciesStarters[pokemon.speciesId] > DEFAULT_PARTY_MAX_COST - this.value) { if (speciesStarterCosts[pokemon.speciesId] > DEFAULT_PARTY_MAX_COST - this.value) {
valid.value = false; valid.value = false;
return true; return true;
} }

View File

@ -1,10 +1,11 @@
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "../battle-scene"; import BattleScene from "#app/battle-scene";
import { PlayerPokemon } from "../field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
import { Starter } from "../ui/starter-select-ui-handler"; import { Starter } from "#app/ui/starter-select-ui-handler";
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import PokemonSpecies, { PokemonSpeciesForm, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "./pokemon-species"; import PokemonSpecies, { PokemonSpeciesForm, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
export interface DailyRunConfig { export interface DailyRunConfig {
seed: integer; seed: integer;
@ -46,9 +47,9 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
for (let c = 0; c < starterCosts.length; c++) { for (let c = 0; c < starterCosts.length; c++) {
const cost = starterCosts[c]; const cost = starterCosts[c];
const costSpecies = Object.keys(speciesStarters) const costSpecies = Object.keys(speciesStarterCosts)
.map(s => parseInt(s) as Species) .map(s => parseInt(s) as Species)
.filter(s => speciesStarters[s] === cost); .filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies)); const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER)); const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER));
starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel)); starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel));

View File

@ -1,30 +1,20 @@
import BattleScene from "../battle-scene"; import BattleScene from "#app/battle-scene";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species"; import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
import { VariantTier } from "../enums/variant-tier"; import { speciesStarterCosts } from "#app/data/balance/starters";
import * as Utils from "../utils"; import { VariantTier } from "#enums/variant-tier";
import * as Utils from "#app/utils";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { pokemonPrevolutions } from "./pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { PlayerPokemon } from "#app/field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { EggSourceType } from "#app/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 { speciesEggTiers } from "#app/data/balance/species-egg-tiers";
export const EGG_SEED = 1073741824; export const EGG_SEED = 1073741824;
// Rates for specific random properties in 1/x
const DEFAULT_SHINY_RATE = 128;
const GACHA_SHINY_UP_SHINY_RATE = 64;
const SAME_SPECIES_EGG_SHINY_RATE = 12;
const SAME_SPECIES_EGG_HA_RATE = 8;
const MANAPHY_EGG_MANAPHY_RATE = 8;
const GACHA_EGG_HA_RATE = 192;
// 1/x for legendary eggs, 1/x*2 for epic eggs, 1/x*4 for rare eggs, and 1/x*8 for common eggs
const DEFAULT_RARE_EGGMOVE_RATE = 6;
const SAME_SPECIES_EGG_RARE_EGGMOVE_RATE = 3;
const GACHA_MOVE_UP_RARE_EGGMOVE_RATE = 3;
/** Egg options to override egg properties */ /** Egg options to override egg properties */
export interface IEggOptions { export interface IEggOptions {
/** Id. Used to check if egg type will be manaphy (id % 204 === 0) */ /** Id. Used to check if egg type will be manaphy (id % 204 === 0) */
@ -171,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
@ -272,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");
@ -323,7 +313,7 @@ export class Egg {
//// ////
private rollEggMoveIndex() { private rollEggMoveIndex() {
let baseChance = DEFAULT_RARE_EGGMOVE_RATE; let baseChance = GACHA_DEFAULT_RARE_EGGMOVE_RATE;
switch (this._sourceType) { switch (this._sourceType) {
case EggSourceType.SAME_SPECIES_EGG: case EggSourceType.SAME_SPECIES_EGG:
baseChance = SAME_SPECIES_EGG_RARE_EGGMOVE_RATE; baseChance = SAME_SPECIES_EGG_RARE_EGGMOVE_RATE;
@ -341,24 +331,24 @@ export class Egg {
private getEggTierDefaultHatchWaves(eggTier?: EggTier): number { private getEggTierDefaultHatchWaves(eggTier?: EggTier): number {
if (this._species === Species.PHIONE || this._species === Species.MANAPHY) { if (this._species === Species.PHIONE || this._species === Species.MANAPHY) {
return 50; return HATCH_WAVES_MANAPHY_EGG;
} }
switch (eggTier ?? this._tier) { switch (eggTier ?? this._tier) {
case EggTier.COMMON: case EggTier.COMMON:
return 10; return HATCH_WAVES_COMMON_EGG;
case EggTier.GREAT: case EggTier.RARE:
return 25; return HATCH_WAVES_RARE_EGG;
case EggTier.ULTRA: case EggTier.EPIC:
return 50; return HATCH_WAVES_EPIC_EGG;
} }
return 100; return HATCH_WAVES_LEGENDARY_EGG;
} }
private rollEggTier(): EggTier { private rollEggTier(): EggTier {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? 1 : 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 >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + 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 {
@ -378,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);
@ -389,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;
@ -409,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(speciesStarters) let speciesPool = Object.keys(speciesEggTiers)
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[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);
@ -441,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 - speciesStarters[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);
@ -475,7 +467,7 @@ export class Egg {
* @returns True if the egg is shiny * @returns True if the egg is shiny
**/ **/
private rollShiny(): boolean { private rollShiny(): boolean {
let shinyChance = DEFAULT_SHINY_RATE; let shinyChance = GACHA_DEFAULT_SHINY_RATE;
switch (this._sourceType) { switch (this._sourceType) {
case EggSourceType.GACHA_SHINY: case EggSourceType.GACHA_SHINY:
shinyChance = GACHA_SHINY_UP_SHINY_RATE; shinyChance = GACHA_SHINY_UP_SHINY_RATE;
@ -498,9 +490,9 @@ export class Egg {
} }
const rand = Utils.randSeedInt(10); const rand = Utils.randSeedInt(10);
if (rand >= 4) { if (rand >= SHINY_VARIANT_CHANCE) {
return VariantTier.STANDARD; // 6/10 return VariantTier.STANDARD; // 6/10
} else if (rand >= 1) { } else if (rand >= SHINY_EPIC_CHANCE) {
return VariantTier.RARE; // 3/10 return VariantTier.RARE; // 3/10
} else { } else {
return VariantTier.EPIC; // 1/10 return VariantTier.EPIC; // 1/10
@ -508,17 +500,17 @@ export class Egg {
} }
private checkForPityTierOverrides(scene: BattleScene): void { private checkForPityTierOverrides(scene: BattleScene): void {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? 1 : 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] >= 412 && 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] >= 59 && 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] >= 9 && 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;
} }
@ -527,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 = speciesStarters[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;
} }
//// ////
@ -567,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(speciesStarters) 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());
@ -590,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 = speciesStarters[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;
} }

View File

@ -25,7 +25,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { MoveUsedEvent } from "#app/events/battle-scene"; import { MoveUsedEvent } from "#app/events/battle-scene";
import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat"; import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat";
import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase";
import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase";
import { MovePhase } from "#app/phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
@ -34,6 +33,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchPhase } from "#app/phases/switch-phase";
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
import { GameMode } from "#app/game-mode"; import { GameMode } from "#app/game-mode";
import { applyChallenges, ChallengeType } from "./challenge"; import { applyChallenges, ChallengeType } from "./challenge";
@ -341,7 +341,8 @@ export default class Move implements Localizable {
* @returns `true` if the move can bypass the target's Substitute; `false` otherwise. * @returns `true` if the move can bypass the target's Substitute; `false` otherwise.
*/ */
hitsSubstitute(user: Pokemon, target: Pokemon | null): boolean { hitsSubstitute(user: Pokemon, target: Pokemon | null): boolean {
if (this.moveTarget === MoveTarget.USER || !target?.getTag(BattlerTagType.SUBSTITUTE)) { if ([ MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES ].includes(this.moveTarget)
|| !target?.getTag(BattlerTagType.SUBSTITUTE)) {
return false; return false;
} }
@ -398,220 +399,202 @@ export default class Move implements Localizable {
/** /**
* Sets the {@linkcode MoveFlags.MAKES_CONTACT} flag for the calling Move * Sets the {@linkcode MoveFlags.MAKES_CONTACT} flag for the calling Move
* @param makesContact The value (boolean) to set the flag to * @param setFlag Default `true`, set to `false` if the move doesn't make contact
* @see {@linkcode Abilities.STATIC}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
makesContact(makesContact: boolean = true): this { makesContact(setFlag: boolean = true): this {
this.setFlag(MoveFlags.MAKES_CONTACT, makesContact); this.setFlag(MoveFlags.MAKES_CONTACT, setFlag);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.IGNORE_PROTECT} flag for the calling Move * Sets the {@linkcode MoveFlags.IGNORE_PROTECT} flag for the calling Move
* @param ignoresProtect The value (boolean) to set the flag to * @see {@linkcode Moves.CURSE}
* example: @see {@linkcode Moves.CURSE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
ignoresProtect(ignoresProtect: boolean = true): this { ignoresProtect(): this {
this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect); this.setFlag(MoveFlags.IGNORE_PROTECT, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.IGNORE_VIRTUAL} flag for the calling Move * Sets the {@linkcode MoveFlags.IGNORE_VIRTUAL} flag for the calling Move
* @param ignoresVirtual The value (boolean) to set the flag to * @see {@linkcode Moves.NATURE_POWER}
* example: @see {@linkcode Moves.NATURE_POWER}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
ignoresVirtual(ignoresVirtual: boolean = true): this { ignoresVirtual(): this {
this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual); this.setFlag(MoveFlags.IGNORE_VIRTUAL, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.SOUND_BASED} flag for the calling Move * Sets the {@linkcode MoveFlags.SOUND_BASED} flag for the calling Move
* @param soundBased The value (boolean) to set the flag to * @see {@linkcode Moves.UPROAR}
* example: @see {@linkcode Moves.UPROAR}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
soundBased(soundBased: boolean = true): this { soundBased(): this {
this.setFlag(MoveFlags.SOUND_BASED, soundBased); this.setFlag(MoveFlags.SOUND_BASED, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.HIDE_USER} flag for the calling Move * Sets the {@linkcode MoveFlags.HIDE_USER} flag for the calling Move
* @param hidesUser The value (boolean) to set the flag to * @see {@linkcode Moves.TELEPORT}
* example: @see {@linkcode Moves.TELEPORT}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
hidesUser(hidesUser: boolean = true): this { hidesUser(): this {
this.setFlag(MoveFlags.HIDE_USER, hidesUser); this.setFlag(MoveFlags.HIDE_USER, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.HIDE_TARGET} flag for the calling Move * Sets the {@linkcode MoveFlags.HIDE_TARGET} flag for the calling Move
* @param hidesTarget The value (boolean) to set the flag to * @see {@linkcode Moves.WHIRLWIND}
* example: @see {@linkcode Moves.WHIRLWIND}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
hidesTarget(hidesTarget: boolean = true): this { hidesTarget(): this {
this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget); this.setFlag(MoveFlags.HIDE_TARGET, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.BITING_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.BITING_MOVE} flag for the calling Move
* @param bitingMove The value (boolean) to set the flag to * @see {@linkcode Moves.BITE}
* example: @see {@linkcode Moves.BITE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
bitingMove(bitingMove: boolean = true): this { bitingMove(): this {
this.setFlag(MoveFlags.BITING_MOVE, bitingMove); this.setFlag(MoveFlags.BITING_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.PULSE_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.PULSE_MOVE} flag for the calling Move
* @param pulseMove The value (boolean) to set the flag to * @see {@linkcode Moves.WATER_PULSE}
* example: @see {@linkcode Moves.WATER_PULSE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
pulseMove(pulseMove: boolean = true): this { pulseMove(): this {
this.setFlag(MoveFlags.PULSE_MOVE, pulseMove); this.setFlag(MoveFlags.PULSE_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.PUNCHING_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.PUNCHING_MOVE} flag for the calling Move
* @param punchingMove The value (boolean) to set the flag to * @see {@linkcode Moves.DRAIN_PUNCH}
* example: @see {@linkcode Moves.DRAIN_PUNCH}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
punchingMove(punchingMove: boolean = true): this { punchingMove(): this {
this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove); this.setFlag(MoveFlags.PUNCHING_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.SLICING_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.SLICING_MOVE} flag for the calling Move
* @param slicingMove The value (boolean) to set the flag to * @see {@linkcode Moves.X_SCISSOR}
* example: @see {@linkcode Moves.X_SCISSOR}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
slicingMove(slicingMove: boolean = true): this { slicingMove(): this {
this.setFlag(MoveFlags.SLICING_MOVE, slicingMove); this.setFlag(MoveFlags.SLICING_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.RECKLESS_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.RECKLESS_MOVE} flag for the calling Move
* @see {@linkcode Abilities.RECKLESS} * @see {@linkcode Abilities.RECKLESS}
* @param recklessMove The value to set the flag to
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
recklessMove(recklessMove: boolean = true): this { recklessMove(): this {
this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove); this.setFlag(MoveFlags.RECKLESS_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.BALLBOMB_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.BALLBOMB_MOVE} flag for the calling Move
* @param ballBombMove The value (boolean) to set the flag to * @see {@linkcode Moves.ELECTRO_BALL}
* example: @see {@linkcode Moves.ELECTRO_BALL}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
ballBombMove(ballBombMove: boolean = true): this { ballBombMove(): this {
this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove); this.setFlag(MoveFlags.BALLBOMB_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.POWDER_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.POWDER_MOVE} flag for the calling Move
* @param powderMove The value (boolean) to set the flag to * @see {@linkcode Moves.STUN_SPORE}
* example: @see {@linkcode Moves.STUN_SPORE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
powderMove(powderMove: boolean = true): this { powderMove(): this {
this.setFlag(MoveFlags.POWDER_MOVE, powderMove); this.setFlag(MoveFlags.POWDER_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.DANCE_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.DANCE_MOVE} flag for the calling Move
* @param danceMove The value (boolean) to set the flag to * @see {@linkcode Moves.PETAL_DANCE}
* example: @see {@linkcode Moves.PETAL_DANCE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
danceMove(danceMove: boolean = true): this { danceMove(): this {
this.setFlag(MoveFlags.DANCE_MOVE, danceMove); this.setFlag(MoveFlags.DANCE_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.WIND_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.WIND_MOVE} flag for the calling Move
* @param windMove The value (boolean) to set the flag to * @see {@linkcode Moves.HURRICANE}
* example: @see {@linkcode Moves.HURRICANE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
windMove(windMove: boolean = true): this { windMove(): this {
this.setFlag(MoveFlags.WIND_MOVE, windMove); this.setFlag(MoveFlags.WIND_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.TRIAGE_MOVE} flag for the calling Move * Sets the {@linkcode MoveFlags.TRIAGE_MOVE} flag for the calling Move
* @param triageMove The value (boolean) to set the flag to * @see {@linkcode Moves.ABSORB}
* example: @see {@linkcode Moves.ABSORB}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
triageMove(triageMove: boolean = true): this { triageMove(): this {
this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove); this.setFlag(MoveFlags.TRIAGE_MOVE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.IGNORE_ABILITIES} flag for the calling Move * Sets the {@linkcode MoveFlags.IGNORE_ABILITIES} flag for the calling Move
* @param ignoresAbilities sThe value (boolean) to set the flag to * @see {@linkcode Moves.SUNSTEEL_STRIKE}
* example: @see {@linkcode Moves.SUNSTEEL_STRIKE}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
ignoresAbilities(ignoresAbilities: boolean = true): this { ignoresAbilities(): this {
this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities); this.setFlag(MoveFlags.IGNORE_ABILITIES, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.CHECK_ALL_HITS} flag for the calling Move * Sets the {@linkcode MoveFlags.CHECK_ALL_HITS} flag for the calling Move
* @param checkAllHits The value (boolean) to set the flag to * @see {@linkcode Moves.TRIPLE_AXEL}
* example: @see {@linkcode Moves.TRIPLE_AXEL}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
checkAllHits(checkAllHits: boolean = true): this { checkAllHits(): this {
this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits); this.setFlag(MoveFlags.CHECK_ALL_HITS, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.IGNORE_SUBSTITUTE} flag for the calling Move * Sets the {@linkcode MoveFlags.IGNORE_SUBSTITUTE} flag for the calling Move
* @param ignoresSubstitute The value (boolean) to set the flag to * @see {@linkcode Moves.WHIRLWIND}
* example: @see {@linkcode Moves.WHIRLWIND}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
ignoresSubstitute(ignoresSubstitute: boolean = true): this { ignoresSubstitute(): this {
this.setFlag(MoveFlags.IGNORE_SUBSTITUTE, ignoresSubstitute); this.setFlag(MoveFlags.IGNORE_SUBSTITUTE, true);
return this; return this;
} }
/** /**
* Sets the {@linkcode MoveFlags.REDIRECT_COUNTER} flag for the calling Move * Sets the {@linkcode MoveFlags.REDIRECT_COUNTER} flag for the calling Move
* @param redirectCounter The value (boolean) to set the flag to * @see {@linkcode Moves.METAL_BURST}
* example: @see {@linkcode Moves.METAL_BURST}
* @returns The {@linkcode Move} that called this function * @returns The {@linkcode Move} that called this function
*/ */
redirectCounter(redirectCounter: boolean = true): this { redirectCounter(): this {
this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter); this.setFlag(MoveFlags.REDIRECT_COUNTER, true);
return this; return this;
} }
@ -987,13 +970,16 @@ export class MoveEffectAttr extends MoveAttr {
public lastHitOnly: boolean; public lastHitOnly: boolean;
/** Should this effect only apply on the first target hit? */ /** Should this effect only apply on the first target hit? */
public firstTargetOnly: boolean; public firstTargetOnly: boolean;
/** Overrides the secondary effect chance for this attr if set. */
public effectChanceOverride?: number;
constructor(selfTarget?: boolean, trigger?: MoveEffectTrigger, firstHitOnly: boolean = false, lastHitOnly: boolean = false, firstTargetOnly: boolean = false) { constructor(selfTarget?: boolean, trigger?: MoveEffectTrigger, firstHitOnly: boolean = false, lastHitOnly: boolean = false, firstTargetOnly: boolean = false, effectChanceOverride?: number) {
super(selfTarget); super(selfTarget);
this.trigger = trigger !== undefined ? trigger : MoveEffectTrigger.POST_APPLY; this.trigger = trigger ?? MoveEffectTrigger.POST_APPLY;
this.firstHitOnly = firstHitOnly; this.firstHitOnly = firstHitOnly;
this.lastHitOnly = lastHitOnly; this.lastHitOnly = lastHitOnly;
this.firstTargetOnly = firstTargetOnly; this.firstTargetOnly = firstTargetOnly;
this.effectChanceOverride = effectChanceOverride;
} }
/** /**
@ -1018,16 +1004,23 @@ export class MoveEffectAttr extends MoveAttr {
/** /**
* Gets the used move's additional effect chance. * Gets the used move's additional effect chance.
* If user's ability has MoveEffectChanceMultiplierAbAttr or IgnoreMoveEffectsAbAttr modifies the base chance. * Chance is modified by {@linkcode MoveEffectChanceMultiplierAbAttr} and {@linkcode IgnoreMoveEffectsAbAttr}.
* @param user {@linkcode Pokemon} using this move * @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move * @param target {@linkcode Pokemon | Target} of this move
* @param move {@linkcode Move} being used * @param move {@linkcode Move} being used
* @param selfEffect {@linkcode Boolean} if move targets user. * @param selfEffect `true` if move targets user.
* @returns Move chance value. * @returns Move effect chance value.
*/ */
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer { getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer {
const moveChance = new Utils.NumberHolder(move.chance); const moveChance = new Utils.NumberHolder(this.effectChanceOverride ?? move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility); applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility);
if (!move.hasAttr(FlinchAttr) || moveChance.value <= move.chance) {
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
user.scene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, moveChance);
}
if (!selfEffect) { if (!selfEffect) {
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance); applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance);
} }
@ -1602,12 +1595,31 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
if (!this.canApply(user, target, move, args)) { if (!this.canApply(user, target, move, args)) {
return false; return false;
} }
this.addPartyCurePhase(user); const partyPokemon = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
partyPokemon.forEach(p => this.cureStatus(p, user.id));
if (this.message) {
user.scene.queueMessage(this.message);
}
return true; return true;
} }
addPartyCurePhase(user: Pokemon) { /**
user.scene.unshiftPhase(new PartyStatusCurePhase(user.scene, user, this.message, this.abilityCondition)); * Tries to cure the status of the given {@linkcode Pokemon}
* @param pokemon The {@linkcode Pokemon} to cure.
* @param userId The ID of the (move) {@linkcode Pokemon | user}.
*/
public cureStatus(pokemon: Pokemon, userId: number) {
if (!pokemon.isOnField() || pokemon.id === userId) { // user always cures its own status, regardless of ability
pokemon.resetStatus(false);
pokemon.updateInfo();
} else if (!pokemon.hasAbility(this.abilityCondition)) {
pokemon.resetStatus();
pokemon.updateInfo();
} else {
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition));
}
} }
} }
@ -1926,12 +1938,21 @@ export class IncrementMovePriorityAttr extends MoveAttr {
* @see {@linkcode apply} * @see {@linkcode apply}
*/ */
export class MultiHitAttr extends MoveAttr { export class MultiHitAttr extends MoveAttr {
/** This move's intrinsic multi-hit type. It should never be modified. */
private readonly intrinsicMultiHitType: MultiHitType;
/** This move's current multi-hit type. It may be temporarily modified by abilities (e.g., Battle Bond). */
private multiHitType: MultiHitType; private multiHitType: MultiHitType;
constructor(multiHitType?: MultiHitType) { constructor(multiHitType?: MultiHitType) {
super(); super();
this.multiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5; this.intrinsicMultiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5;
this.multiHitType = this.intrinsicMultiHitType;
}
// Currently used by `battle_bond.test.ts`
getMultiHitType(): MultiHitType {
return this.multiHitType;
} }
/** /**
@ -1945,7 +1966,7 @@ export class MultiHitAttr extends MoveAttr {
* @returns True * @returns True
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const hitType = new Utils.NumberHolder(this.multiHitType); const hitType = new Utils.NumberHolder(this.intrinsicMultiHitType);
applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType); applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType);
this.multiHitType = hitType.value; this.multiHitType = hitType.value;
@ -2685,20 +2706,101 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
} }
} }
/**
* Attribute that cancels the associated move's effects when set to be combined with the user's ally's
* subsequent move this turn. Used for Grass Pledge, Water Pledge, and Fire Pledge.
* @extends OverrideMoveEffectAttr
*/
export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
constructor() {
super(true);
}
/**
* If the user's ally is set to use a different move with this attribute,
* defer this move's effects for a combined move on the ally's turn.
* @param user the {@linkcode Pokemon} using this move
* @param target n/a
* @param move the {@linkcode Move} being used
* @param args
* - [0] a {@linkcode Utils.BooleanHolder} indicating whether the move's base
* effects should be overridden this turn.
* @returns `true` if base move effects were overridden; `false` otherwise
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.turnData.combiningPledge) {
// "The two moves have become one!\nIt's a combined move!"
user.scene.queueMessage(i18next.t("moveTriggers:combiningPledge"));
return false;
}
const overridden = args[0] as Utils.BooleanHolder;
const allyMovePhase = user.scene.findPhase<MovePhase>((phase) => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer());
if (allyMovePhase) {
const allyMove = allyMovePhase.move.getMove();
if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) {
[ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id);
// "{userPokemonName} is waiting for {allyPokemonName}'s move..."
user.scene.queueMessage(i18next.t("moveTriggers:awaitingPledge", {
userPokemonName: getPokemonNameWithAffix(user),
allyPokemonName: getPokemonNameWithAffix(allyMovePhase.pokemon)
}));
// Move the ally's MovePhase (if needed) so that the ally moves next
const allyMovePhaseIndex = user.scene.phaseQueue.indexOf(allyMovePhase);
const firstMovePhaseIndex = user.scene.phaseQueue.findIndex((phase) => phase instanceof MovePhase);
if (allyMovePhaseIndex !== firstMovePhaseIndex) {
user.scene.prependToPhase(user.scene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase);
}
overridden.value = true;
return true;
}
}
return false;
}
}
/**
* Attribute used for moves that change stat stages
*
* @param stats {@linkcode BattleStat} Array of stat(s) to change
* @param stages How many stages to change the stat(s) by, [-6, 6]
* @param selfTarget `true` if the move is self-targetting
* @param condition {@linkcode MoveConditionFunc} Optional condition to be checked in order to apply the changes
* @param showMessage `true` to display a message; default `true`
* @param firstHitOnly `true` if only the first hit of a multi hit move should cause a stat stage change; default `false`
* @param moveEffectTrigger {@linkcode MoveEffectTrigger} When the stat change should trigger; default {@linkcode MoveEffectTrigger.HIT}
* @param firstTargetOnly `true` if a move that hits multiple pokemon should only trigger the stat change if it hits at least one pokemon, rather than once per hit pokemon; default `false`
* @param lastHitOnly `true` if the effect should only apply after the last hit of a multi hit move; default `false`
* @param effectChanceOverride Will override the move's normal secondary effect chance if specified
*
* @extends MoveEffectAttr
* @see {@linkcode apply}
*/
export class StatStageChangeAttr extends MoveEffectAttr { export class StatStageChangeAttr extends MoveEffectAttr {
public stats: BattleStat[]; public stats: BattleStat[];
public stages: integer; public stages: integer;
private condition: MoveConditionFunc | null; private condition?: MoveConditionFunc | null;
private showMessage: boolean; private showMessage: boolean;
constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false, lastHitOnly: boolean = false, effectChanceOverride?: number) {
super(selfTarget, moveEffectTrigger, firstHitOnly, false, firstTargetOnly); super(selfTarget, moveEffectTrigger, firstHitOnly, lastHitOnly, firstTargetOnly, effectChanceOverride);
this.stats = stats; this.stats = stats;
this.stages = stages; this.stages = stages;
this.condition = condition!; // TODO: is this bang correct? this.condition = condition;
this.showMessage = showMessage; this.showMessage = showMessage;
} }
/**
* Attempts to change stats of the user or target (depending on value of selfTarget) if conditions are met
* @param user {@linkcode Pokemon} the user of the move
* @param target {@linkcode Pokemon} the target of the move
* @param move {@linkcode Move} the move
* @param args unused
* @returns whether stat stages were changed
*/
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> {
if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) { if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) {
return false; return false;
@ -3738,6 +3840,45 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr {
} }
} }
/**
* Changes a Pledge move's power to 150 when combined with another unique Pledge
* move from an ally.
*/
export class CombinedPledgePowerAttr extends VariablePowerAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0];
if (!(power instanceof Utils.NumberHolder)) {
return false;
}
const combinedPledgeMove = user.turnData.combiningPledge;
if (combinedPledgeMove && combinedPledgeMove !== move.id) {
power.value *= 150 / 80;
return true;
}
return false;
}
}
/**
* Applies STAB to the given Pledge move if the move is part of a combined attack.
*/
export class CombinedPledgeStabBoostAttr extends MoveAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const stabMultiplier = args[0];
if (!(stabMultiplier instanceof Utils.NumberHolder)) {
return false;
}
const combinedPledgeMove = user.turnData.combiningPledge;
if (combinedPledgeMove && combinedPledgeMove !== move.id) {
stabMultiplier.value = 1.5;
return true;
}
return false;
}
}
export class VariableAtkAttr extends MoveAttr { export class VariableAtkAttr extends MoveAttr {
constructor() { constructor() {
super(); super();
@ -3918,7 +4059,14 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr {
} }
} }
export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr { /**
* Attribute used for tera moves that change category based on the user's Atk and SpAtk stats
* Note: Currently, `getEffectiveStat` does not ignore all abilities that affect stats except those
* with the attribute of `StatMultiplierAbAttr`
* TODO: Remove the `.partial()` tag from Tera Blast and Tera Starstorm when the above issue is resolved
* @extends VariableMoveCategoryAttr
*/
export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const category = (args[0] as Utils.NumberHolder); const category = (args[0] as Utils.NumberHolder);
@ -3968,11 +4116,11 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr {
* @param user {@linkcode Pokemon} using the move * @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} target of the move * @param target {@linkcode Pokemon} target of the move
* @param move {@linkcode Move} with this attribute * @param move {@linkcode Move} with this attribute
* @param args [0] {@linkcode Utils.IntegerHolder} The category of the move * @param args [0] {@linkcode Utils.NumberHolder} The category of the move
* @returns true if the function succeeds * @returns true if the function succeeds
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const category = (args[0] as Utils.IntegerHolder); const category = (args[0] as Utils.NumberHolder);
if (user.getAlly() === target) { if (user.getAlly() === target) {
category.value = MoveCategory.STATUS; category.value = MoveCategory.STATUS;
@ -4007,6 +4155,30 @@ export class VariableMoveTypeAttr extends MoveAttr {
} }
} }
/**
* Attribute used for Tera Starstorm that changes the move type to Stellar
* @extends VariableMoveTypeAttr
*/
export class TeraStarstormTypeAttr extends VariableMoveTypeAttr {
/**
*
* @param user the {@linkcode Pokemon} using the move
* @param target n/a
* @param move n/a
* @param args[0] {@linkcode Utils.NumberHolder} the move type
* @returns `true` if the move type is changed to {@linkcode Type.STELLAR}, `false` otherwise
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.isTerastallized() && (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS)) {
const moveType = args[0] as Utils.NumberHolder;
moveType.value = Type.STELLAR;
return true;
}
return false;
}
}
export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0]; const moveType = args[0];
@ -4303,6 +4475,47 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr {
} }
} }
/**
* Changes the type of a Pledge move based on the Pledge move combined with it.
* @extends VariableMoveTypeAttr
*/
export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveType = args[0];
if (!(moveType instanceof Utils.NumberHolder)) {
return false;
}
const combinedPledgeMove = user.turnData.combiningPledge;
if (!combinedPledgeMove) {
return false;
}
switch (move.id) {
case Moves.FIRE_PLEDGE:
if (combinedPledgeMove === Moves.WATER_PLEDGE) {
moveType.value = Type.WATER;
return true;
}
return false;
case Moves.WATER_PLEDGE:
if (combinedPledgeMove === Moves.GRASS_PLEDGE) {
moveType.value = Type.GRASS;
return true;
}
return false;
case Moves.GRASS_PLEDGE:
if (combinedPledgeMove === Moves.FIRE_PLEDGE) {
moveType.value = Type.FIRE;
return true;
}
return false;
default:
return false;
}
}
}
export class VariableMoveTypeMultiplierAttr extends MoveAttr { export class VariableMoveTypeMultiplierAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
return false; return false;
@ -4450,7 +4663,15 @@ export class TypelessAttr extends MoveAttr { }
* Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot * Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot
* Bypasses Storm Drain, Follow Me, Ally Switch, and the like. * Bypasses Storm Drain, Follow Me, Ally Switch, and the like.
*/ */
export class BypassRedirectAttr extends MoveAttr { } export class BypassRedirectAttr extends MoveAttr {
/** `true` if this move only bypasses redirection from Abilities */
public readonly abilitiesOnly: boolean;
constructor(abilitiesOnly: boolean = false) {
super();
this.abilitiesOnly = abilitiesOnly;
}
}
export class FrenzyAttr extends MoveEffectAttr { export class FrenzyAttr extends MoveEffectAttr {
constructor() { constructor() {
@ -4544,6 +4765,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
case BattlerTagType.DROWSY: case BattlerTagType.DROWSY:
case BattlerTagType.DISABLED: case BattlerTagType.DISABLED:
case BattlerTagType.HEAL_BLOCK: case BattlerTagType.HEAL_BLOCK:
case BattlerTagType.RECEIVE_DOUBLE_DAMAGE:
return -5; return -5;
case BattlerTagType.SEEDED: case BattlerTagType.SEEDED:
case BattlerTagType.SALT_CURED: case BattlerTagType.SALT_CURED:
@ -4564,6 +4786,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
case BattlerTagType.ENCORE: case BattlerTagType.ENCORE:
return -2; return -2;
case BattlerTagType.MINIMIZED: case BattlerTagType.MINIMIZED:
case BattlerTagType.ALWAYS_GET_HIT:
return 0; return 0;
case BattlerTagType.INGRAIN: case BattlerTagType.INGRAIN:
case BattlerTagType.IGNORE_ACCURACY: case BattlerTagType.IGNORE_ACCURACY:
@ -4621,14 +4844,14 @@ export class GulpMissileTagAttr extends MoveEffectAttr {
/** /**
* Adds BattlerTagType from GulpMissileTag based on the Pokemon's HP ratio. * Adds BattlerTagType from GulpMissileTag based on the Pokemon's HP ratio.
* @param {Pokemon} user The Pokemon using the move. * @param user The Pokemon using the move.
* @param {Pokemon} target The Pokemon being targeted by the move. * @param _target N/A
* @param {Move} move The move being used. * @param move The move being used.
* @param {any[]} args Additional arguments, if any. * @param _args N/A
* @returns Whether the BattlerTag is applied. * @returns Whether the BattlerTag is applied.
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> { apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean {
if (!super.apply(user, target, move, args)) { if (!super.apply(user, _target, move, _args)) {
return false; return false;
} }
@ -5139,6 +5362,32 @@ export class SwapArenaTagsAttr extends MoveEffectAttr {
} }
} }
/**
* Attribute that adds a secondary effect to the field when two unique Pledge moves
* are combined. The effect added varies based on the two Pledge moves combined.
*/
export class AddPledgeEffectAttr extends AddArenaTagAttr {
private readonly requiredPledge: Moves;
constructor(tagType: ArenaTagType, requiredPledge: Moves, selfSideTarget: boolean = false) {
super(tagType, 4, false, selfSideTarget);
this.requiredPledge = requiredPledge;
}
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// TODO: add support for `HIT` effect triggering in AddArenaTagAttr to remove the need for this check
if (user.getLastXMoves(1)[0].result !== MoveResult.SUCCESS) {
return false;
}
if (user.turnData.combiningPledge === this.requiredPledge) {
return super.apply(user, target, move, args);
}
return false;
}
}
/** /**
* Attribute used for Revival Blessing. * Attribute used for Revival Blessing.
* @extends MoveEffectAttr * @extends MoveEffectAttr
@ -5226,6 +5475,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
*/ */
const switchOutTarget = this.selfSwitch ? user : target; const switchOutTarget = this.selfSwitch ? user : target;
if (switchOutTarget instanceof PlayerPokemon) { if (switchOutTarget instanceof PlayerPokemon) {
if (switchOutTarget.scene.getParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false;
}
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
@ -5234,6 +5486,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
} }
return false; return false;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) { } else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false;
}
// Switch out logic for trainer battles // Switch out logic for trainer battles
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
@ -5244,6 +5499,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
false, false), MoveEndPhase); false, false), MoveEndPhase);
} }
} else { } else {
if (user.scene.currentBattle.waveIndex % 10 === 0) {
return false;
}
// Switch out logic for everything else (eg: WILD battles) // Switch out logic for everything else (eg: WILD battles)
switchOutTarget.leaveField(false); switchOutTarget.leaveField(false);
@ -5290,6 +5548,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
return false; return false;
} }
if (!player && user.scene.currentBattle.isBattleMysteryEncounter() && !user.scene.currentBattle.mysteryEncounter?.fleeAllowed) {
// Don't allow wild opponents to be force switched during MEs with flee disabled
return false;
}
const blockedByAbility = new Utils.BooleanHolder(false); const blockedByAbility = new Utils.BooleanHolder(false);
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
return !blockedByAbility.value; return !blockedByAbility.value;
@ -5334,7 +5597,7 @@ export class ChillyReceptionAttr extends ForceSwitchOutAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
// chilly reception move will go through if the weather is change-able to snow, or the user can switch out, else move will fail // chilly reception move will go through if the weather is change-able to snow, or the user can switch out, else move will fail
return (user, target, move) => user.scene.arena.trySetWeather(WeatherType.SNOW, true) || super.getSwitchOutCondition()(user, target, move); return (user, target, move) => user.scene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move);
} }
} }
export class RemoveTypeAttr extends MoveEffectAttr { export class RemoveTypeAttr extends MoveEffectAttr {
@ -6575,7 +6838,7 @@ export class MoveCondition {
export class FirstMoveCondition extends MoveCondition { export class FirstMoveCondition extends MoveCondition {
constructor() { constructor() {
super((user, target, move) => user.battleSummonData?.turnCount === 1); super((user, target, move) => user.battleSummonData?.waveTurnCount === 1);
} }
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
@ -7252,7 +7515,7 @@ export function initMoves() {
new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2) new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2)
.attr(CurseAttr) .attr(CurseAttr)
.ignoresSubstitute() .ignoresSubstitute()
.ignoresProtect(true) .ignoresProtect()
.target(MoveTarget.CURSE), .target(MoveTarget.CURSE),
new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2)
.attr(LowHpPowerAttr), .attr(LowHpPowerAttr),
@ -8080,7 +8343,7 @@ export function initMoves() {
.makesContact(false), .makesContact(false),
new SelfStatusMove(Moves.DEFEND_ORDER, Type.BUG, -1, 10, -1, 0, 4) new SelfStatusMove(Moves.DEFEND_ORDER, Type.BUG, -1, 10, -1, 0, 4)
.attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true),
new SelfStatusMove(Moves.HEAL_ORDER, Type.BUG, -1, 10, -1, 0, 4) new SelfStatusMove(Moves.HEAL_ORDER, Type.BUG, -1, 5, -1, 0, 4)
.attr(HealAttr, 0.5) .attr(HealAttr, 0.5)
.triageMove(), .triageMove(),
new AttackMove(Moves.HEAD_SMASH, Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, 0, 4) new AttackMove(Moves.HEAD_SMASH, Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, 0, 4)
@ -8270,11 +8533,29 @@ export function initMoves() {
new AttackMove(Moves.INFERNO, Type.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5) new AttackMove(Moves.INFERNO, Type.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5)
.attr(StatusEffectAttr, StatusEffect.BURN), .attr(StatusEffectAttr, StatusEffect.BURN),
new AttackMove(Moves.WATER_PLEDGE, Type.WATER, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) new AttackMove(Moves.WATER_PLEDGE, Type.WATER, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
.partial(), .attr(AwaitCombinedPledgeAttr)
.attr(CombinedPledgeTypeAttr)
.attr(CombinedPledgePowerAttr)
.attr(CombinedPledgeStabBoostAttr)
.attr(AddPledgeEffectAttr, ArenaTagType.WATER_FIRE_PLEDGE, Moves.FIRE_PLEDGE, true)
.attr(AddPledgeEffectAttr, ArenaTagType.GRASS_WATER_PLEDGE, Moves.GRASS_PLEDGE)
.attr(BypassRedirectAttr, true),
new AttackMove(Moves.FIRE_PLEDGE, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) new AttackMove(Moves.FIRE_PLEDGE, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
.partial(), .attr(AwaitCombinedPledgeAttr)
.attr(CombinedPledgeTypeAttr)
.attr(CombinedPledgePowerAttr)
.attr(CombinedPledgeStabBoostAttr)
.attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.GRASS_PLEDGE)
.attr(AddPledgeEffectAttr, ArenaTagType.WATER_FIRE_PLEDGE, Moves.WATER_PLEDGE, true)
.attr(BypassRedirectAttr, true),
new AttackMove(Moves.GRASS_PLEDGE, Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) new AttackMove(Moves.GRASS_PLEDGE, Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
.partial(), .attr(AwaitCombinedPledgeAttr)
.attr(CombinedPledgeTypeAttr)
.attr(CombinedPledgePowerAttr)
.attr(CombinedPledgeStabBoostAttr)
.attr(AddPledgeEffectAttr, ArenaTagType.GRASS_WATER_PLEDGE, Moves.WATER_PLEDGE)
.attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.FIRE_PLEDGE)
.attr(BypassRedirectAttr, true),
new AttackMove(Moves.VOLT_SWITCH, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5) new AttackMove(Moves.VOLT_SWITCH, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5)
.attr(ForceSwitchOutAttr, true), .attr(ForceSwitchOutAttr, true),
new AttackMove(Moves.STRUGGLE_BUG, Type.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5) new AttackMove(Moves.STRUGGLE_BUG, Type.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5)
@ -8422,8 +8703,8 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1)
.soundBased(), .soundBased(),
new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6) new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6)
.target(MoveTarget.BOTH_SIDES) .attr(AddArenaTagAttr, ArenaTagType.ION_DELUGE)
.unimplemented(), .target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.PARABOLIC_CHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 65, 100, 20, -1, 0, 6) new AttackMove(Moves.PARABOLIC_CHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 65, 100, 20, -1, 0, 6)
.attr(HitHealAttr) .attr(HitHealAttr)
.target(MoveTarget.ALL_NEAR_OTHERS) .target(MoveTarget.ALL_NEAR_OTHERS)
@ -8466,7 +8747,7 @@ export function initMoves() {
.attr(TerrainChangeAttr, TerrainType.MISTY) .attr(TerrainChangeAttr, TerrainType.MISTY)
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new StatusMove(Moves.ELECTRIFY, Type.ELECTRIC, -1, 20, -1, 0, 6) new StatusMove(Moves.ELECTRIFY, Type.ELECTRIC, -1, 20, -1, 0, 6)
.unimplemented(), .attr(AddBattlerTagAttr, BattlerTagType.ELECTRIFIED, false, true),
new AttackMove(Moves.PLAY_ROUGH, Type.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6) new AttackMove(Moves.PLAY_ROUGH, Type.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6)
.attr(StatStageChangeAttr, [ Stat.ATK ], -1), .attr(StatStageChangeAttr, [ Stat.ATK ], -1),
new AttackMove(Moves.FAIRY_WIND, Type.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6) new AttackMove(Moves.FAIRY_WIND, Type.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6)
@ -8490,7 +8771,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .attr(StatStageChangeAttr, [ Stat.SPATK ], -1)
.soundBased(), .soundBased(),
new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6) new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6)
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true, undefined, undefined, undefined, undefined, true)
.makesContact(false) .makesContact(false)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.STEAM_ERUPTION, Type.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6) new AttackMove(Moves.STEAM_ERUPTION, Type.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6)
@ -8892,8 +9173,8 @@ export function initMoves() {
.attr(HalfSacrificialAttr) .attr(HalfSacrificialAttr)
.target(MoveTarget.ALL_NEAR_OTHERS), .target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7) new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7)
.punchingMove() .attr(AddArenaTagAttr, ArenaTagType.ION_DELUGE, 1)
.partial(), .punchingMove(),
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
.attr(PhotonGeyserCategoryAttr) .attr(PhotonGeyserCategoryAttr)
.ignoresAbilities() .ignoresAbilities()
@ -8917,13 +9198,13 @@ export function initMoves() {
.makesContact(false) .makesContact(false)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.CLANGOROUS_SOULBLAZE, Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) new AttackMove(Moves.CLANGOROUS_SOULBLAZE, Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true)
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.partial() .partial()
.ignoresVirtual(), .ignoresVirtual(),
/* End Unused */ /* End Unused */
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, 100, 2, 7) //LGPE Implementation new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation
.attr(CritOnlyAttr), .attr(CritOnlyAttr),
new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7) new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS) .attr(StatusEffectAttr, StatusEffect.PARALYSIS)
@ -9150,16 +9431,15 @@ export function initMoves() {
.attr(HalfSacrificialAttr), .attr(HalfSacrificialAttr),
new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8)
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1)
.attr(VariableTargetAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 6 : 3), .attr(VariableTargetAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER),
new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8) new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8)
.attr(ClearTerrainAttr) .attr(ClearTerrainAttr)
.condition((user, target, move) => !!user.scene.arena.terrain), .condition((user, target, move) => !!user.scene.arena.terrain),
new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8) new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8)
//.attr(StatStageChangeAttr, Stat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true, null, true, false, MoveEffectTrigger.HIT, false, true)
//.attr(StatStageChangeAttr, Stat.DEF, -1, true) .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true, null, true, false, MoveEffectTrigger.HIT, false, true)
.attr(MultiHitAttr) .attr(MultiHitAttr)
.makesContact(false) .makesContact(false),
.partial(),
new AttackMove(Moves.METEOR_BEAM, Type.ROCK, MoveCategory.SPECIAL, 120, 90, 10, 100, 0, 8) new AttackMove(Moves.METEOR_BEAM, Type.ROCK, MoveCategory.SPECIAL, 120, 90, 10, 100, 0, 8)
.attr(ChargeAttr, ChargeAnim.METEOR_BEAM_CHARGING, i18next.t("moveTriggers:isOverflowingWithSpacePower", { pokemonName: "{USER}" }), null, true) .attr(ChargeAttr, ChargeAnim.METEOR_BEAM_CHARGING, i18next.t("moveTriggers:isOverflowingWithSpacePower", { pokemonName: "{USER}" }), null, true)
.attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true)
@ -9291,9 +9571,8 @@ export function initMoves() {
new AttackMove(Moves.TRIPLE_ARROWS, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) new AttackMove(Moves.TRIPLE_ARROWS, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8)
.makesContact(false) .makesContact(false)
.attr(HighCritAttr) .attr(HighCritAttr)
.attr(StatStageChangeAttr, [ Stat.DEF ], -1) .attr(StatStageChangeAttr, [ Stat.DEF ], -1, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 50)
.attr(FlinchAttr) .attr(FlinchAttr),
.partial(),
new AttackMove(Moves.INFERNAL_PARADE, Type.GHOST, MoveCategory.SPECIAL, 60, 100, 15, 30, 0, 8) new AttackMove(Moves.INFERNAL_PARADE, Type.GHOST, MoveCategory.SPECIAL, 60, 100, 15, 30, 0, 8)
.attr(StatusEffectAttr, StatusEffect.BURN) .attr(StatusEffectAttr, StatusEffect.BURN)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status ? 2 : 1), .attr(MovePowerMultiplierAttr, (user, target, move) => target.status ? 2 : 1),
@ -9425,10 +9704,11 @@ export function initMoves() {
.unimplemented(), .unimplemented(),
End Unused */ End Unused */
new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
.attr(TeraBlastCategoryAttr) .attr(TeraMoveCategoryAttr)
.attr(TeraBlastTypeAttr) .attr(TeraBlastTypeAttr)
.attr(TeraBlastPowerAttr) .attr(TeraBlastPowerAttr)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR))
.partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9) new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
.attr(ProtectAttr, BattlerTagType.SILK_TRAP) .attr(ProtectAttr, BattlerTagType.SILK_TRAP)
.condition(failIfLastCondition), .condition(failIfLastCondition),
@ -9608,9 +9888,8 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.triageMove(), .triageMove(),
new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9) new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9)
.attr(StatStageChangeAttr, [ Stat.SPD ], -1) //Temporary .attr(AddBattlerTagAttr, BattlerTagType.SYRUP_BOMB, false, false, 3)
.ballBombMove() .ballBombMove(),
.partial(),
new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9) new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9)
.attr(IvyCudgelTypeAttr) .attr(IvyCudgelTypeAttr)
.attr(HighCritAttr) .attr(HighCritAttr)
@ -9619,8 +9898,10 @@ export function initMoves() {
.attr(ElectroShotChargeAttr) .attr(ElectroShotChargeAttr)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
.attr(TeraBlastCategoryAttr) .attr(TeraMoveCategoryAttr)
.partial(), .attr(TeraStarstormTypeAttr)
.attr(VariableTargetAttr, (user, target, move) => (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS) && user.isTerastallized() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER)
.partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */
new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9) new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9)
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc) .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)
.attr(DoublePowerChanceAttr), .attr(DoublePowerChanceAttr),

View File

@ -18,7 +18,7 @@ import { modifierTypes } from "#app/modifier/modifier-type";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:aTrainersTest"; const namespace = "mysteryEncounters/aTrainersTest";
/** /**
* A Trainer's Test encounter. * A Trainer's Test encounter.
@ -32,7 +32,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // These are set in onInit() .withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
@ -76,24 +76,24 @@ export const ATrainersTestEncounter: MysteryEncounter =
encounter.dialogue.intro = [ encounter.dialogue.intro = [
{ {
speaker: `trainerNames:${trainerNameKey}`, speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.intro_dialogue` text: `${namespace}:${trainerNameKey}.intro_dialogue`
} }
]; ];
encounter.options[0].dialogue!.selected = [ encounter.options[0].dialogue!.selected = [
{ {
speaker: `trainerNames:${trainerNameKey}`, speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.accept` text: `${namespace}:${trainerNameKey}.accept`
} }
]; ];
encounter.options[1].dialogue!.selected = [ encounter.options[1].dialogue!.selected = [
{ {
speaker: `trainerNames:${trainerNameKey}`, speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.decline` text: `${namespace}:${trainerNameKey}.decline`
} }
]; ];
encounter.setDialogueToken("statTrainerName", i18next.t(`trainerNames:${trainerNameKey}`)); encounter.setDialogueToken("statTrainerName", i18next.t(`trainerNames:${trainerNameKey}`));
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(`trainerNames:${trainerNameKey}`); const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(`trainerNames:${trainerNameKey}`);
encounter.misc = { trainerType, trainerNameKey, trainerEggDescription: eggDescription }; encounter.misc = { trainerType, trainerNameKey, trainerEggDescription: eggDescription };
// Trainer config // Trainer config
@ -128,14 +128,15 @@ export const ATrainersTestEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withIntroDialogue() .withIntroDialogue()
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip` buttonTooltip: `${namespace}:option.1.tooltip`
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -149,17 +150,17 @@ 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); return initBattleWithEnemyConfig(scene, config);
} }
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -171,16 +172,16 @@ 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 ]);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} }
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro` text: `${namespace}:outro`
} }
]) ])
.build(); .build();

View File

@ -24,9 +24,10 @@ import { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:absoluteAvarice"; const namespace = "mysteryEncounters/absoluteAvarice";
/** /**
* Absolute Avarice encounter. * Absolute Avarice encounter.
@ -38,6 +39,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.GREAT) .withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
// This sprite has the shadow // This sprite has the shadow
@ -161,12 +163,13 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -217,7 +220,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
modifierConfigs: bossModifierConfigs, modifierConfigs: bossModifierConfigs,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`); queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
} }
@ -248,11 +251,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -262,19 +265,17 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Provides 1x Reviver Seed to each party member at end of battle // Provides 1x Reviver Seed to each party member at end of battle
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED); const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
const givePartyPokemonReviverSeeds = () => { const givePartyPokemonReviverSeeds = () => {
const party = scene.getParty(); const party = scene.getParty();
party.forEach(p => { party.forEach(p => {
const heldItems = p.getHeldItems(); const heldItems = p.getHeldItems();
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) { if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
const seedModifier = revSeed.newModifier(p); const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);
}
scene.addModifier(seedModifier, false, false, false, true); scene.addModifier(seedModifier, false, false, false, true);
} }
}); });
queueEncounterMessage(scene, `${namespace}.option.1.food_stash`); queueEncounterMessage(scene, `${namespace}:option.1.food_stash`);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds); setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds);
@ -294,11 +295,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
@ -336,11 +337,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}) })

View File

@ -8,14 +8,15 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:offerYouCantRefuse"; const namespace = "mysteryEncounters/anOfferYouCantRefuse";
/** /**
* Money offered starts at base value of Relic Gold, increasing linearly up to 3x Relic Gold based on the starter tier of the Pokemon being purchased * Money offered starts at base value of Relic Gold, increasing linearly up to 3x Relic Gold based on the starter tier of the Pokemon being purchased
@ -56,22 +57,23 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true);
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true); const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId();
const starterValue: number = speciesStarters[baseSpecies] ?? 1; const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1;
const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER); const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER);
const price = scene.getWaveMoneyAmount(multiplier); const price = scene.getWaveMoneyAmount(multiplier);
@ -104,12 +106,12 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
], ],
}) })
@ -135,13 +137,13 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
new AbilityRequirement(EXTORTION_ABILITIES)) new AbilityRequirement(EXTORTION_ABILITIES))
) )
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`, disabledButtonTooltip: `${namespace}:option.2.tooltip_disabled`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
@ -159,12 +161,12 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}, },

View File

@ -33,7 +33,7 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:berriesAbound"; const namespace = "mysteryEncounters/berriesAbound";
/** /**
* Berries Abound encounter. * Berries Abound encounter.
@ -46,10 +46,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true) .withCatchAllowed(true)
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit() .withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -109,16 +110,17 @@ export const BerriesAboundEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -128,7 +130,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
const numBerries = encounter.misc.numBerries; const numBerries = encounter.misc.numBerries;
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
@ -156,8 +158,8 @@ export const BerriesAboundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Pick race for berries // Pick race for berries
@ -179,7 +181,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
if (speedDiff < 1) { if (speedDiff < 1) {
// Caught and attacked by boss, gets +1 to all stats at start of fight // Caught and attacked by boss, gets +1 to all stats at start of fight
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
@ -198,11 +200,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ]; config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`); queueEncounterMessage(pokemon.scene, `${namespace}:option.2.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}; };
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected_bad`); await showEncounterText(scene, `${namespace}:option.2.selected_bad`);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
return; return;
} else { } else {
@ -210,7 +212,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1) / 0.08), numBerries), 2); const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1) / 0.08), numBerries), 2);
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed)); encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
const doFasterBerryRewards = () => { const doFasterBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed })); queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed }));
@ -223,7 +225,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards); setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected`); await showEncounterText(scene, `${namespace}:option.2.selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} }
}) })
@ -231,11 +233,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}, },

View File

@ -49,9 +49,10 @@ import MoveInfoOverlay from "#app/ui/move-info-overlay";
import { allMoves } from "#app/data/move"; import { allMoves } from "#app/data/move";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:bugTypeSuperfan"; const namespace = "mysteryEncounters/bugTypeSuperfan";
const POOL_1_POKEMON = [ const POOL_1_POKEMON = [
Species.PARASECT, Species.PARASECT,
@ -177,9 +178,14 @@ const MISC_TUTOR_MOVES = [
Moves.U_TURN Moves.U_TURN
]; ];
/**
* Wave breakpoints that determine how strong to make the Bug-Type Superfan's team
*/
const WAVE_LEVEL_BREAKPOINTS = [ 30, 50, 70, 100, 120, 140, 160 ];
/** /**
* Bug Type Superfan encounter. * Bug Type Superfan encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810} * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3820 | GitHub Issue #3820}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome} * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const BugTypeSuperfanEncounter: MysteryEncounter = export const BugTypeSuperfanEncounter: MysteryEncounter =
@ -197,11 +203,11 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -216,11 +222,46 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
female: true, female: true,
}); });
let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string };
if (scene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false);
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false);
} else {
// Mega Beedrill/Gmax Butterfree
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false, 1);
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false, 1);
}
encounter.spriteConfigs = [ encounter.spriteConfigs = [
{
spriteKey: beedrillKeys.spriteKey,
fileRoot: beedrillKeys.fileRoot,
hasShadow: true,
repeat: true,
isPokemon: true,
x: -30,
tint: 0.15,
y: -4,
yShadow: -4
},
{
spriteKey: butterfreeKeys.spriteKey,
fileRoot: butterfreeKeys.fileRoot,
hasShadow: true,
repeat: true,
isPokemon: true,
x: 30,
tint: 0.15,
y: -4,
yShadow: -4
},
{ {
spriteKey: spriteKey, spriteKey: spriteKey,
fileRoot: "trainer", fileRoot: "trainer",
hasShadow: true, hasShadow: true,
x: 4,
y: 7,
yShadow: 7
}, },
]; ];
@ -235,17 +276,18 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -276,9 +318,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new TypeRequirement(Type.BUG, false, 1)) // Must have 1 Bug type on team .withPrimaryPokemonRequirement(new TypeRequirement(Type.BUG, false, 1)) // Must have 1 Bug type on team
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip` disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async (scene: BattleScene) => {
// Player shows off their bug types // Player shows off their bug types
@ -286,31 +328,31 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
// Player gets different rewards depending on the number of bug types they have // Player gets different rewards depending on the number of bug types they have
const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length; const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length;
const numBugTypesText = i18next.t(`${namespace}.numBugTypes`, { count: numBugTypes }); const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
encounter.setDialogueToken("numBugTypes", numBugTypesText); encounter.setDialogueToken("numBugTypes", numBugTypesText);
if (numBugTypes < 2) { if (numBugTypes < 2) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected_0_to_1`, text: `${namespace}:option.2.selected_0_to_1`,
}, },
]; ];
} else if (numBugTypes < 4) { } else if (numBugTypes < 4) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected_2_to_3`, text: `${namespace}:option.2.selected_2_to_3`,
}, },
]; ];
} else if (numBugTypes < 6) { } else if (numBugTypes < 6) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected_4_to_5`, text: `${namespace}:option.2.selected_4_to_5`,
}, },
]; ];
} else { } else {
@ -330,6 +372,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
if (formChangeModifier) { if (formChangeModifier) {
specialOptions.push(formChangeModifier); specialOptions.push(formChangeModifier);
} }
const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeModifier) {
specialOptions.push(rareFormChangeModifier);
}
if (specialOptions.length > 0) { if (specialOptions.length > 0) {
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
} }
@ -337,8 +383,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected_6`, text: `${namespace}:option.2.selected_6`,
}, },
]; ];
} }
@ -356,19 +402,19 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1) new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1)
)) ))
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.3.selected_dialogue`, text: `${namespace}:option.3.selected_dialogue`,
}, },
], ],
secondOptionPrompt: `${namespace}.option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -407,7 +453,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG); (item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
}); });
if (!hasValidItem) { if (!hasValidItem) {
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
@ -435,7 +481,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.build()) .build())
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]) ])
.build(); .build();
@ -445,29 +491,29 @@ function getTrainerConfigForWave(waveIndex: number) {
const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone(); const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone();
config.name = i18next.t("trainerNames:bug_type_superfan"); config.name = i18next.t("trainerNames:bug_type_superfan");
const pool3Copy = POOL_3_POKEMON.slice(0); let pool3Copy = POOL_3_POKEMON.slice(0);
randSeedShuffle(pool3Copy); pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon = pool3Copy.pop()!; const pool3Mon = pool3Copy.pop()!;
if (waveIndex < 30) { if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) {
// Use default template (2 AVG) // Use default template (2 AVG)
config config
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)); .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true));
} else if (waveIndex < 50) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[1]) {
config config
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)); .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 70) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[2]) {
config config
.setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 100) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
config config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
@ -475,7 +521,7 @@ function getTrainerConfigForWave(waveIndex: number) {
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); .setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 120) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[4]) {
config config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -497,8 +543,8 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateName(); p.generateName();
} }
})); }));
} else if (waveIndex < 140) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[5]) {
randSeedShuffle(pool3Copy); pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon2 = pool3Copy.pop()!; const pool3Mon2 = pool3Copy.pop()!;
config config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
@ -527,7 +573,7 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateName(); p.generateName();
} }
})); }));
} else if (waveIndex < 160) { } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[6]) {
config config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG))) .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -550,6 +596,8 @@ function getTrainerConfigForWave(waveIndex: number) {
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true)); .setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true));
} else { } else {
pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon2 = pool3Copy.pop()!;
config config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG))) .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -571,9 +619,9 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateName(); p.generateName();
} }
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ pool3Mon2.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) { if (!isNullOrUndefined(pool3Mon2.formIndex)) {
p.formIndex = pool3Mon.formIndex; p.formIndex = pool3Mon2.formIndex;
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.generateName(); p.generateName();
} }
@ -587,7 +635,7 @@ function getTrainerConfigForWave(waveIndex: number) {
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> { function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions; const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
await showEncounterDialogue(scene, `${namespace}.battle_won`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:battle_won`, `${namespace}:speaker`);
const overlayScale = 1; const overlayScale = 1;
const moveInfoOverlay = new MoveInfoOverlay(scene, { const moveInfoOverlay = new MoveInfoOverlay(scene, {
@ -622,34 +670,14 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
moveInfoOverlay.setVisible(false); moveInfoOverlay.setVisible(false);
}; };
const result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}.teach_move_prompt`, undefined, onHoverOverCancel); const result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel);
// let forceExit = !!result; // let forceExit = !!result;
if (!result) { if (!result) {
moveInfoOverlay.active = false; moveInfoOverlay.active = false;
moveInfoOverlay.setVisible(false); moveInfoOverlay.setVisible(false);
} }
// TODO: add menu to confirm player doesn't want to teach a move // TODO: add menu to confirm player doesn't want to teach a move?
// while (!result && !forceExit) {
// // Didn't teach a move, ask the player to confirm they don't want to teach a move
// await showEncounterDialogue(scene, `${namespace}.confirm_no_teach`, `${namespace}.speaker`);
// const confirm = await new Promise<boolean>(confirmResolve => {
// scene.ui.setMode(Mode.CONFIRM, () => confirmResolve(true), () => confirmResolve(false));
// });
// scene.ui.clearText();
// await scene.ui.setMode(Mode.MESSAGE);
// if (confirm) {
// // No teach, break out of loop
// forceExit = true;
// } else {
// // Re-show learn menu
// result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}.teach_move_prompt`, undefined, onHoverOverCancel);
// if (!result) {
// moveInfoOverlay.active = false;
// moveInfoOverlay.setVisible(false);
// }
// }
// }
// Option select complete, handle if they are learning a move // Option select complete, handle if they are learning a move
if (result && result.selectedOptionIndex < moveOptions.length) { if (result && result.selectedOptionIndex < moveOptions.length) {

View File

@ -34,7 +34,7 @@ import { EncounterAnim } from "#enums/encounter-anims";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:clowningAround"; const namespace = "mysteryEncounters/clowningAround";
const RANDOM_ABILITY_POOL = [ const RANDOM_ABILITY_POOL = [
Abilities.STURDY, Abilities.STURDY,
@ -98,11 +98,11 @@ export const ClowningAroundEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -148,19 +148,20 @@ export const ClowningAroundEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
], ],
}) })
@ -199,7 +200,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
// After the battle, offer the player the opportunity to permanently swap ability // After the battle, offer the player the opportunity to permanently swap ability
const abilityWasSwapped = await handleSwapAbility(scene); const abilityWasSwapped = await handleSwapAbility(scene);
if (abilityWasSwapped) { if (abilityWasSwapped) {
await showEncounterText(scene, `${namespace}.option.1.ability_gained`); await showEncounterText(scene, `${namespace}:option.1.ability_gained`);
} }
// Play animations once ability swap is complete // Play animations once ability swap is complete
@ -222,19 +223,19 @@ export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
{ {
text: `${namespace}.option.2.selected_2`, text: `${namespace}:option.2.selected_2`,
}, },
{ {
text: `${namespace}.option.2.selected_3`, text: `${namespace}:option.2.selected_3`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
], ],
}) })
@ -308,19 +309,19 @@ export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
{ {
text: `${namespace}.option.3.selected_2`, text: `${namespace}:option.3.selected_2`,
}, },
{ {
text: `${namespace}.option.3.selected_3`, text: `${namespace}:option.3.selected_3`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
}, },
], ],
}) })
@ -336,8 +337,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
.filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS) .filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
.map(move => move!.getMove().type); .map(move => move!.getMove().type);
if (priorityTypes?.length > 0) { if (priorityTypes?.length > 0) {
priorityTypes = [...new Set(priorityTypes)]; priorityTypes = [ ...new Set(priorityTypes) ].sort();
randSeedShuffle(priorityTypes); priorityTypes = randSeedShuffle(priorityTypes);
} }
const newTypes = [ originalTypes[0] ]; const newTypes = [ originalTypes[0] ];
@ -377,15 +378,15 @@ export const ClowningAroundEncounter: MysteryEncounter =
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]) ])
.build(); .build();
async function handleSwapAbility(scene: BattleScene) { async function handleSwapAbility(scene: BattleScene) {
return new Promise<boolean>(async resolve => { return new Promise<boolean>(async resolve => {
await showEncounterDialogue(scene, `${namespace}.option.1.apply_ability_dialogue`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
await showEncounterText(scene, `${namespace}.option.1.apply_ability_message`); await showEncounterText(scene, `${namespace}:option.1.apply_ability_message`);
scene.ui.setMode(Mode.MESSAGE).then(() => { scene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve); displayYesNoOptions(scene, resolve);
@ -394,7 +395,7 @@ async function handleSwapAbility(scene: BattleScene) {
} }
function displayYesNoOptions(scene: BattleScene, resolve) { function displayYesNoOptions(scene: BattleScene, resolve) {
showEncounterText(scene, `${namespace}.option.1.ability_prompt`, null, 500, false); showEncounterText(scene, `${namespace}:option.1.ability_prompt`, null, 500, false);
const fullOptions = [ const fullOptions = [
{ {
label: i18next.t("menu:yes"), label: i18next.t("menu:yes"),
@ -494,9 +495,13 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
} }
for (let i = 0; i < numItems; i++) { for (let i = 0; i < numItems; i++) {
if (pool.length === 0) {
// Stop generating new items if somehow runs out of items to spawn
return;
}
const randIndex = randSeedInt(pool.length); const randIndex = randSeedInt(pool.length);
const newItemType = pool[randIndex]; const newItemType = pool[randIndex];
let newMod; let newMod: PokemonHeldItemModifierType;
if (tier === "Berries") { if (tier === "Berries") {
newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType; newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
} else { } else {

View File

@ -30,7 +30,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next"; import i18next from "i18next";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:dancingLessons"; const namespace = "mysteryEncounters/dancingLessons";
// Fire form // Fire form
const BAILE_STYLE_BIOMES = [ const BAILE_STYLE_BIOMES = [
@ -90,6 +90,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withCatchAllowed(true) .withCatchAllowed(true)
.withFleeAllowed(false)
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart((scene: BattleScene) => {
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]); const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]);
danceAnim.play(scene); danceAnim.play(scene);
@ -98,12 +99,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
}) })
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -154,7 +156,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
// Gets +1 to all stats except SPD on battle start // Gets +1 to all stats except SPD on battle start
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`); queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1));
} }
}], }],
@ -172,11 +174,11 @@ export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -201,11 +203,11 @@ export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
@ -236,13 +238,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(DANCING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically .withPrimaryPokemonRequirement(new MoveRequirement(DANCING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}) })
@ -277,7 +279,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
} }
return null; return null;

View File

@ -16,7 +16,7 @@ import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/mod
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */ /** i18n namespace for encounter */
const namespace = "mysteryEncounter:darkDeal"; const namespace = "mysteryEncounters/darkDeal";
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */ /** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */
const excludedBosses = [ const excludedBosses = [
@ -107,32 +107,33 @@ export const DarkDealEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.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)
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.1.selected_dialogue`, text: `${namespace}:option.1.selected_dialogue`,
}, },
{ {
text: `${namespace}.option.1.selected_message`, text: `${namespace}:option.1.selected_message`,
}, },
], ],
}) })
@ -187,12 +188,12 @@ export const DarkDealEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -204,7 +205,7 @@ export const DarkDealEncounter: MysteryEncounter =
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro` text: `${namespace}:outro`
} }
]) ])
.build(); .build();

View File

@ -19,7 +19,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy"; const namespace = "mysteryEncounters/delibirdy";
/** Berries only */ /** Berries only */
const OPTION_2_ALLOWED_MODIFIERS = [ "BerryModifier", "PokemonInstantReviveModifier" ]; const OPTION_2_ALLOWED_MODIFIERS = [ "BerryModifier", "PokemonInstantReviveModifier" ];
@ -81,15 +81,16 @@ export const DelibirdyEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
} }
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -108,11 +109,11 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER) // Must have money to spawn .withSceneMoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER) // Must have money to spawn
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -145,12 +146,12 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS)) .withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS))
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}.option.2.select_prompt`, secondOptionPrompt: `${namespace}:option.2.select_prompt`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
@ -183,7 +184,7 @@ export const DelibirdyEncounter: MysteryEncounter =
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
@ -193,7 +194,7 @@ export const DelibirdyEncounter: MysteryEncounter =
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier; const modifier: BerryModifier | HealingBoosterModifier = encounter.misc.chosenModifier;
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed // Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
if (modifier instanceof BerryModifier) { if (modifier instanceof BerryModifier) {
@ -239,12 +240,12 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)) .withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true))
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}) })
@ -277,7 +278,7 @@ export const DelibirdyEncounter: MysteryEncounter =
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
} }
return null; return null;

View File

@ -14,7 +14,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */ /** i18n namespace for encounter */
const namespace = "mysteryEncounter:departmentStoreSale"; const namespace = "mysteryEncounters/departmentStoreSale";
/** /**
* Department Store Sale encounter. * Department Store Sale encounter.
@ -43,21 +43,22 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Choose TMs // Choose TMs
@ -82,8 +83,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Choose Vitamins // Choose Vitamins
@ -106,8 +107,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Choose X Items // Choose X Items
@ -130,8 +131,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.4.label`, buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`, buttonTooltip: `${namespace}:option.4.tooltip`,
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Choose Pokeballs // Choose Pokeballs
@ -158,7 +159,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
} }
]) ])
.build(); .build();

View File

@ -14,7 +14,7 @@ import i18next from "i18next";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for the encounter */ /** i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieldTrip"; const namespace = "mysteryEncounters/fieldTrip";
/** /**
* Field Trip encounter. * Field Trip encounter.
@ -44,24 +44,25 @@ export const FieldTripEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -72,7 +73,7 @@ export const FieldTripEncounter: MysteryEncounter =
label: move.getName(), label: move.getName(),
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.physical`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL); pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL);
return true; return true;
}, },
@ -105,9 +106,9 @@ export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -118,7 +119,7 @@ export const FieldTripEncounter: MysteryEncounter =
label: move.getName(), label: move.getName(),
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.special`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL); pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL);
return true; return true;
}, },
@ -151,9 +152,9 @@ export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -164,7 +165,7 @@ export const FieldTripEncounter: MysteryEncounter =
label: move.getName(), label: move.getName(),
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.status`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS); pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS);
return true; return true;
}, },
@ -203,28 +204,28 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
if (!correctMove) { if (!correctMove) {
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
{ {
text: `${namespace}.incorrect`, text: `${namespace}:incorrect`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
{ {
text: `${namespace}.incorrect_exp`, text: `${namespace}:incorrect_exp`,
}, },
]; ];
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50); setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
} else { } else {
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
{ {
text: `${namespace}.correct`, text: `${namespace}:correct`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
{ {
text: `${namespace}.correct_exp`, text: `${namespace}:correct_exp`,
}, },
]; ];
setEncounterExp(scene, [ pokemon.id ], 100); setEncounterExp(scene, [ pokemon.id ], 100);

View File

@ -24,7 +24,7 @@ import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieryFallout"; const namespace = "mysteryEncounters/fieryFallout";
/** /**
* Damage percentage taken when suffering the heat. * Damage percentage taken when suffering the heat.
@ -46,9 +46,10 @@ export const FieryFalloutEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // Set in onInit() .withIntroSpriteConfigs([]) // Set in onInit()
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withFleeAllowed(false)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -121,16 +122,17 @@ export const FieryFalloutEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -169,11 +171,11 @@ export const FieryFalloutEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -196,7 +198,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) { if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
// Burn applied // Burn applied
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender()); encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}.option.2.target_burned`); queueEncounterMessage(scene, `${namespace}:option.2.target_burned`);
} }
} }
@ -210,12 +212,12 @@ export const FieryFalloutEncounter: MysteryEncounter =
.withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3PrimaryName dialogue token automatically .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3PrimaryName dialogue token automatically
.withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}) })
@ -250,6 +252,6 @@ function giveLeadPokemonCharcoal(scene: BattleScene) {
const charcoal = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType; const charcoal = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType;
applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal); applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal);
scene.currentBattle.mysteryEncounter!.setDialogueToken("leadPokemon", leadPokemon.getNameToRender()); scene.currentBattle.mysteryEncounter!.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}.found_charcoal`); queueEncounterMessage(scene, `${namespace}:found_charcoal`);
} }
} }

View File

@ -31,7 +31,7 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fightOrFlight"; const namespace = "mysteryEncounters/fightOrFlight";
/** /**
* Fight or Flight encounter. * Fight or Flight encounter.
@ -44,10 +44,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true) .withCatchAllowed(true)
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit() .withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -66,7 +67,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
isBoss: true, isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`); queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`);
// Randomly boost 1 stat 2 stages // Randomly boost 1 stat 2 stages
// Cannot boost Spd, Acc, or Evasion // Cannot boost Spd, Acc, or Evasion
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2));
@ -119,16 +120,17 @@ export const FightOrFlightEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -145,12 +147,12 @@ export const FightOrFlightEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected` text: `${namespace}:option.2.selected`
} }
] ]
}) })
@ -169,11 +171,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}, },

View File

@ -25,7 +25,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:funAndGames"; const namespace = "mysteryEncounters/funAndGames";
/** /**
* Fun and Games! encounter. * Fun and Games! encounter.
@ -44,6 +44,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withSkipEnemyBattleTurns(true) .withSkipEnemyBattleTurns(true)
// Will skip COMMAND selection menu and go straight to FIGHT (move select) menu // Will skip COMMAND selection menu and go straight to FIGHT (move select) menu
.withSkipToFightInput(true) .withSkipToFightInput(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "fun_and_games_game", spriteKey: "fun_and_games_game",
@ -71,13 +72,14 @@ export const FunAndGamesEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3"); scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
@ -92,11 +94,11 @@ export const FunAndGamesEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneRequirement(new MoneyRequirement(0, 1.5)) // Cost equal to 1 Max Potion .withSceneRequirement(new MoneyRequirement(0, 1.5)) // Cost equal to 1 Max Potion
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -111,7 +113,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be selected // Only Pokemon that are not KOed/legal can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -140,11 +142,11 @@ export const FunAndGamesEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -230,7 +232,7 @@ function handleLoseMinigame(scene: BattleScene) {
scene.currentBattle.enemyParty = []; scene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
await showEncounterText(scene, `${namespace}.ko`); await showEncounterText(scene, `${namespace}:ko`);
const reviveCost = scene.getWaveMoneyAmount(1.5); const reviveCost = scene.getWaveMoneyAmount(1.5);
updatePlayerMoney(scene, -reviveCost, true, false); updatePlayerMoney(scene, -reviveCost, true, false);
} }
@ -256,19 +258,19 @@ function handleNextTurn(scene: BattleScene) {
if (healthRatio < 0.03) { if (healthRatio < 0.03) {
// Grand prize // Grand prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}.best_result`; resultMessageKey = `${namespace}:best_result`;
} else if (healthRatio < 0.15) { } else if (healthRatio < 0.15) {
// 2nd prize // 2nd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}.great_result`; resultMessageKey = `${namespace}:great_result`;
} else if (healthRatio < 0.33) { } else if (healthRatio < 0.33) {
// 3rd prize // 3rd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}.good_result`; resultMessageKey = `${namespace}:good_result`;
} else { } else {
// No prize // No prize
isHealPhase = true; isHealPhase = true;
resultMessageKey = `${namespace}.bad_result`; resultMessageKey = `${namespace}:bad_result`;
} }
// End the battle // End the battle
@ -278,7 +280,7 @@ function handleNextTurn(scene: BattleScene) {
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, isHealPhase); leaveEncounterWithoutBattle(scene, isHealPhase);
// Must end the TurnInit phase prematurely so battle phases aren't added to queue // Must end the TurnInit phase prematurely so battle phases aren't added to queue
queueEncounterMessage(scene, `${namespace}.end_game`); queueEncounterMessage(scene, `${namespace}:end_game`);
queueEncounterMessage(scene, resultMessageKey); queueEncounterMessage(scene, resultMessageKey);
// Skip remainder of TurnInitPhase // Skip remainder of TurnInitPhase
@ -286,9 +288,9 @@ function handleNextTurn(scene: BattleScene) {
} else { } else {
if (encounter.misc.turnsRemaining < 3) { if (encounter.misc.turnsRemaining < 3) {
// Display charging messages on turns that aren't the initial turn // Display charging messages on turns that aren't the initial turn
queueEncounterMessage(scene, `${namespace}.charging_continue`); queueEncounterMessage(scene, `${namespace}:charging_continue`);
} }
queueEncounterMessage(scene, `${namespace}.turn_remaining_${encounter.misc.turnsRemaining}`); queueEncounterMessage(scene, `${namespace}:turn_remaining_${encounter.misc.turnsRemaining}`);
encounter.misc.turnsRemaining--; encounter.misc.turnsRemaining--;
} }

View File

@ -27,7 +27,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:globalTradeSystem"; const namespace = "mysteryEncounters/globalTradeSystem";
/** Base shiny chance of 512/65536 -> 1/128 odds, affected by events and Shiny Charms. Cannot exceed 1/16 odds. */ /** Base shiny chance of 512/65536 -> 1/128 odds, affected by events and Shiny Charms. Cannot exceed 1/16 odds. */
const WONDER_TRADE_SHINY_CHANCE = 512; const WONDER_TRADE_SHINY_CHANCE = 512;
@ -93,12 +93,13 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -133,9 +134,9 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}.option.1.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -159,7 +160,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return true; return true;
}, },
onHover: () => { onHover: () => {
const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[pokemon.formIndex].formName : null; const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null;
const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : ""); const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : "");
const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : ""); const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false); showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
@ -201,7 +202,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground(scene);
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000); await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); scene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground(scene);
@ -216,8 +217,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -309,7 +310,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground(scene);
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000); await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); scene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground(scene);
@ -323,9 +324,9 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -355,7 +356,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return it.isTransferable; return it.isTransferable;
}).length > 0; }).length > 0;
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
@ -403,18 +404,18 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Generate a trainer name // Generate a trainer name
const traderName = generateRandomTraderName(); const traderName = generateRandomTraderName();
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
await showEncounterText(scene, `${namespace}.item_trade_selected`); await showEncounterText(scene, `${namespace}:item_trade_selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build() .build()
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.4.label`, buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`, buttonTooltip: `${namespace}:option.4.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.4.selected`, text: `${namespace}:option.4.selected`,
}, },
], ],
}, },
@ -628,10 +629,10 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
duration: 500, duration: 500,
onComplete: async () => { onComplete: async () => {
scene.fadeOutBgm(1000, false); scene.fadeOutBgm(1000, false);
await showEncounterText(scene, `${namespace}.pokemon_trade_selected`); await showEncounterText(scene, `${namespace}:pokemon_trade_selected`);
tradedPokemon.cry(); tradedPokemon.cry();
scene.playBgm("evolution"); scene.playBgm("evolution");
await showEncounterText(scene, `${namespace}.pokemon_trade_goodbye`); await showEncounterText(scene, `${namespace}:pokemon_trade_goodbye`);
tradedPokeball.setAlpha(0); tradedPokeball.setAlpha(0);
tradedPokeball.setVisible(true); tradedPokeball.setVisible(true);

View File

@ -21,7 +21,7 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY;
*/ */
const DAMAGE_PERCENTAGE: number = 25; const DAMAGE_PERCENTAGE: number = 25;
/** The i18n namespace for the encounter */ /** The i18n namespace for the encounter */
const namespace = "mysteryEncounter:lostAtSea"; const namespace = "mysteryEncounters/lostAtSea";
/** /**
* Lost at sea encounter. * Lost at sea encounter.
@ -40,7 +40,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
y: 3, y: 3,
}, },
]) ])
.withIntroDialogue([{ text: `${namespace}.intro` }]) .withIntroDialogue([{ text: `${namespace}:intro` }])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -50,22 +50,23 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/ // Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE) .withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
disabledButtonLabel: `${namespace}.option.1.label_disabled`, disabledButtonLabel: `${namespace}:option.1.label_disabled`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
disabledButtonTooltip: `${namespace}.option.1.tooltip_disabled`, disabledButtonTooltip: `${namespace}:option.1.tooltip_disabled`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -78,13 +79,13 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE) .withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
disabledButtonLabel: `${namespace}.option.2.label_disabled`, disabledButtonLabel: `${namespace}:option.2.label_disabled`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`, disabledButtonTooltip: `${namespace}:option.2.tooltip_disabled`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
@ -94,11 +95,11 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
.withSimpleOption( .withSimpleOption(
// Option 3: Wander aimlessly // Option 3: Wander aimlessly
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}, },
@ -118,7 +119,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]) ])
.build(); .build();

View File

@ -20,7 +20,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:mysteriousChallengers"; const namespace = "mysteryEncounters/mysteriousChallengers";
/** /**
* Mysterious Challengers encounter. * Mysterious Challengers encounter.
@ -34,7 +34,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // These are set in onInit() .withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -125,16 +125,17 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}, },
@ -155,11 +156,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}, },
@ -180,11 +181,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}, },
@ -208,7 +209,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]) ])
.build(); .build();

View File

@ -16,7 +16,7 @@ import { GameOverPhase } from "#app/phases/game-over-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */ /** i18n namespace for encounter */
const namespace = "mysteryEncounter:mysteriousChest"; const namespace = "mysteryEncounters/mysteriousChest";
const RAND_LENGTH = 100; const RAND_LENGTH = 100;
const TRAP_PERCENT = 35; const TRAP_PERCENT = 35;
@ -58,12 +58,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -96,11 +97,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -141,7 +142,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.normal`); queueEncounterMessage(scene, `${namespace}:option.1.normal`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%) // Choose between 3 ULTRA tier items (30%)
@ -153,19 +154,19 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.good`); queueEncounterMessage(scene, `${namespace}:option.1.good`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// Choose between 2 ROGUE tier items (10%) // Choose between 2 ROGUE tier items (10%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]}); setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.great`); queueEncounterMessage(scene, `${namespace}:option.1.great`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
// Choose 1 MASTER tier item (5%) // Choose 1 MASTER tier item (5%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]}); setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.amazing`); queueEncounterMessage(scene, `${namespace}:option.1.amazing`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} else { } else {
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%) // Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
@ -173,7 +174,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
koPlayerPokemon(scene, highestLevelPokemon); koPlayerPokemon(scene, highestLevelPokemon);
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender()); encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
await showEncounterText(scene, `${namespace}.option.1.bad`); await showEncounterText(scene, `${namespace}:option.1.bad`);
// Handle game over edge case // Handle game over edge case
const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle()); const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle());
@ -193,11 +194,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },

View File

@ -15,7 +15,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:partTimer"; const namespace = "mysteryEncounters/partTimer";
/** /**
* Part Timer encounter. * Part Timer encounter.
@ -45,11 +45,11 @@ export const PartTimerEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -69,17 +69,18 @@ export const PartTimerEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected` text: `${namespace}:option.1.selected`
} }
] ]
}) })
@ -118,7 +119,7 @@ export const PartTimerEncounter: MysteryEncounter =
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -132,14 +133,14 @@ export const PartTimerEncounter: MysteryEncounter =
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(scene, moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}.pokemon_tired`); await showEncounterText(scene, `${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards(scene, { fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
@ -149,11 +150,11 @@ export const PartTimerEncounter: MysteryEncounter =
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected` text: `${namespace}:option.2.selected`
} }
] ]
}) })
@ -195,7 +196,7 @@ export const PartTimerEncounter: MysteryEncounter =
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -209,14 +210,14 @@ export const PartTimerEncounter: MysteryEncounter =
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(scene, moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}.pokemon_tired`); await showEncounterText(scene, `${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards(scene, { fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
@ -228,12 +229,12 @@ export const PartTimerEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically .withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}) })
@ -264,11 +265,11 @@ export const PartTimerEncounter: MysteryEncounter =
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(scene, false, false);
// Give money and do dialogue // Give money and do dialogue
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
const moneyChange = scene.getWaveMoneyAmount(2.5); const moneyChange = scene.getWaveMoneyAmount(2.5);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(scene, moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}.pokemon_tired`); await showEncounterText(scene, `${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards(scene, { fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
@ -277,8 +278,8 @@ export const PartTimerEncounter: MysteryEncounter =
) )
.withOutroDialogue([ .withOutroDialogue([
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.outro`, text: `${namespace}:outro`,
} }
]) ])
.build(); .build();

View File

@ -21,7 +21,7 @@ import { SummonPhase } from "#app/phases/summon-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:safariZone"; const namespace = "mysteryEncounters/safariZone";
const TRAINER_THROW_ANIMATION_TIMES = [ 512, 184, 768 ]; const TRAINER_THROW_ANIMATION_TIMES = [ 512, 184, 768 ];
@ -51,12 +51,13 @@ export const SafariZoneEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString()); scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
return true; return true;
@ -65,11 +66,11 @@ export const SafariZoneEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneRequirement(new MoneyRequirement(0, SAFARI_MONEY_MULTIPLIER)) // Cost equal to 1 Max Revive .withSceneRequirement(new MoneyRequirement(0, SAFARI_MONEY_MULTIPLIER)) // Cost equal to 1 Max Revive
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -98,11 +99,11 @@ export const SafariZoneEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -133,11 +134,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.safari.1.label`, buttonLabel: `${namespace}:safari.1.label`,
buttonTooltip: `${namespace}.safari.1.tooltip`, buttonTooltip: `${namespace}:safari.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.safari.1.selected`, text: `${namespace}:safari.1.selected`,
} }
], ],
}) })
@ -168,11 +169,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.safari.2.label`, buttonLabel: `${namespace}:safari.2.label`,
buttonTooltip: `${namespace}.safari.2.tooltip`, buttonTooltip: `${namespace}:safari.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.safari.2.selected`, text: `${namespace}:safari.2.selected`,
}, },
], ],
}) })
@ -186,9 +187,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
// 80% chance to increase flee stage +1 // 80% chance to increase flee stage +1
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8); const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
if (!fleeChangeResult) { if (!fleeChangeResult) {
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.busy_eating`) ?? "", null, 1000, false ); await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.busy_eating`) ?? "", null, 1000, false );
} else { } else {
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.eating`) ?? "", null, 1000, false); await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.eating`) ?? "", null, 1000, false);
} }
await doEndTurn(scene, 1); await doEndTurn(scene, 1);
@ -198,11 +199,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.safari.3.label`, buttonLabel: `${namespace}:safari.3.label`,
buttonTooltip: `${namespace}.safari.3.tooltip`, buttonTooltip: `${namespace}:safari.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.safari.3.selected`, text: `${namespace}:safari.3.selected`,
}, },
], ],
}) })
@ -215,9 +216,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
// 80% chance to decrease catch stage -1 // 80% chance to decrease catch stage -1
const catchChangeResult = tryChangeCatchStage(scene, -1, 8); const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
if (!catchChangeResult) { if (!catchChangeResult) {
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.beside_itself_angry`) ?? "", null, 1000, false ); await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.beside_itself_angry`) ?? "", null, 1000, false );
} else { } else {
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.angry`) ?? "", null, 1000, false ); await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.angry`) ?? "", null, 1000, false );
} }
await doEndTurn(scene, 2); await doEndTurn(scene, 2);
@ -227,8 +228,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.safari.4.label`, buttonLabel: `${namespace}:safari.4.label`,
buttonTooltip: `${namespace}.safari.4.tooltip`, buttonTooltip: `${namespace}:safari.4.tooltip`,
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Flee option // Flee option
@ -253,7 +254,7 @@ async function summonSafariPokemon(scene: BattleScene) {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
// Message pokemon remaining // Message pokemon remaining
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining); encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
scene.queueMessage(getEncounterText(scene, `${namespace}.safari.remaining_count`) ?? "", null, true); scene.queueMessage(getEncounterText(scene, `${namespace}:safari.remaining_count`) ?? "", null, true);
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken // Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal // Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
@ -518,7 +519,7 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number) {
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
} }
} else { } else {
scene.queueMessage(getEncounterText(scene, `${namespace}.safari.watching`) ?? "", 0, null, 1000); scene.queueMessage(getEncounterText(scene, `${namespace}:safari.watching`) ?? "", 0, null, 1000);
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
} }
} }

View File

@ -18,7 +18,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next"; import i18next from "i18next";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:shadyVitaminDealer"; const namespace = "mysteryEncounters/shadyVitaminDealer";
const VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER = 1.5; const VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER = 1.5;
const VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER = 3.5; const VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER = 3.5;
@ -55,26 +55,27 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER) .withSceneMoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -103,7 +104,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null; return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
} }
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) { if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
@ -139,7 +140,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
chosenPokemon.nature = newNature; chosenPokemon.nature = newNature;
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(); chosenPokemon.updateInfo();
}) })
@ -150,11 +151,11 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER) .withSceneMoneyRequirement(0, VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -178,7 +179,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
// Only Pokemon that can gain benefits are unfainted // Only Pokemon that can gain benefits are unfainted
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -200,7 +201,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
queueEncounterMessage(scene, `${namespace}.no_bad_effects`); queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo(); chosenPokemon.updateInfo();
@ -209,12 +210,12 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
speaker: `${namespace}.speaker` speaker: `${namespace}:speaker`
} }
] ]
}, },

View File

@ -21,7 +21,7 @@ import { BerryType } from "#enums/berry-type";
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
/** i18n namespace for the encounter */ /** i18n namespace for the encounter */
const namespace = "mysteryEncounter:slumberingSnorlax"; const namespace = "mysteryEncounters/slumberingSnorlax";
/** /**
* Sleeping Snorlax encounter. * Sleeping Snorlax encounter.
@ -34,6 +34,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true) .withCatchAllowed(true)
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: Species.SNORLAX.toString(), spriteKey: Species.SNORLAX.toString(),
@ -47,7 +48,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -87,16 +88,17 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -122,11 +124,11 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -134,7 +136,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
// Fall asleep waiting for Snorlax // Fall asleep waiting for Snorlax
// Full heal party // Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true)); scene.unshiftPhase(new PartyHealPhase(scene, true));
queueEncounterMessage(scene, `${namespace}.option.2.rest_result`); queueEncounterMessage(scene, `${namespace}:option.2.rest_result`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} }
) )
@ -143,12 +145,12 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES))
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected` text: `${namespace}:option.3.selected`
} }
] ]
}) })

View File

@ -23,7 +23,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:teleportingHijinks"; const namespace = "mysteryEncounters/teleportingHijinks";
const MONEY_COST_MULTIPLIER = 1.75; const MONEY_COST_MULTIPLIER = 1.75;
const BIOME_CANDIDATES = [ Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO ]; const BIOME_CANDIDATES = [ Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO ];
@ -42,6 +42,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost .withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withCatchAllowed(true) .withCatchAllowed(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "teleporting_hijinks_teleporter", spriteKey: "teleporting_hijinks_teleporter",
@ -54,12 +55,13 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER); const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
@ -75,11 +77,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost .withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
} }
], ],
}) })
@ -99,12 +101,12 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPokemonTypeRequirement(MACHINE_INTERFACING_TYPES, true, 1) // Must have Steel or Electric type .withPokemonTypeRequirement(MACHINE_INTERFACING_TYPES, true, 1) // Must have Steel or Electric type
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
} }
], ],
}) })
@ -118,11 +120,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected`, text: `${namespace}:option.3.selected`,
}, },
], ],
}, },
@ -161,10 +163,10 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)]; const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
// Show dialogue and transition biome // Show dialogue and transition biome
await showEncounterText(scene, `${namespace}.transport`); await showEncounterText(scene, `${namespace}:transport`);
await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]); await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]);
scene.playBgm(); scene.playBgm();
await showEncounterText(scene, `${namespace}.attacked`); await showEncounterText(scene, `${namespace}:attacked`);
// Init enemy // Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
@ -185,7 +187,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
isBoss: true, isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.boss_enraged`); queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
}], }],

View File

@ -10,7 +10,8 @@ import { Biome } from "#enums/biome";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import i18next from "i18next"; import i18next from "i18next";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PlayerPokemon } from "#app/field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
@ -26,7 +27,7 @@ import { Type } from "#app/data/type";
import { getPokeballTintColor } from "#app/data/pokeball"; import { getPokeballTintColor } from "#app/data/pokeball";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:expertPokemonBreeder"; const namespace = "mysteryEncounters/theExpertPokemonBreeder";
const trainerNameKey = "trainerNames:expert_pokemon_breeder"; const trainerNameKey = "trainerNames:expert_pokemon_breeder";
@ -85,11 +86,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // These are set in onInit() .withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -138,45 +139,45 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
// Dialogue and egg calcs for Pokemon 1 // Dialogue and egg calcs for Pokemon 1
const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1); const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1);
let pokemon1Tooltip = getEncounterText(scene, `${namespace}.option.1.tooltip_base`)!; let pokemon1Tooltip = getEncounterText(scene, `${namespace}:option.1.tooltip_base`)!;
if (pokemon1RareEggs > 0) { if (pokemon1RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1RareEggs", eggsText); encounter.setDialogueToken("pokemon1RareEggs", eggsText);
} }
if (pokemon1CommonEggs > 0) { if (pokemon1CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1CommonEggs", eggsText); encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
} }
encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip; encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip;
// Dialogue and egg calcs for Pokemon 2 // Dialogue and egg calcs for Pokemon 2
const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2); const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2);
let pokemon2Tooltip = getEncounterText(scene, `${namespace}.option.2.tooltip_base`)!; let pokemon2Tooltip = getEncounterText(scene, `${namespace}:option.2.tooltip_base`)!;
if (pokemon2RareEggs > 0) { if (pokemon2RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon2RareEggs", eggsText); encounter.setDialogueToken("pokemon2RareEggs", eggsText);
} }
if (pokemon2CommonEggs > 0) { if (pokemon2CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1CommonEggs", eggsText); encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
} }
encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip; encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip;
// Dialogue and egg calcs for Pokemon 3 // Dialogue and egg calcs for Pokemon 3
const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3); const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3);
let pokemon3Tooltip = getEncounterText(scene, `${namespace}.option.3.tooltip_base`)!; let pokemon3Tooltip = getEncounterText(scene, `${namespace}:option.3.tooltip_base`)!;
if (pokemon3RareEggs > 0) { if (pokemon3RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon3RareEggs", eggsText); encounter.setDialogueToken("pokemon3RareEggs", eggsText);
} }
if (pokemon3CommonEggs > 0) { if (pokemon3CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon3CommonEggs", eggsText); encounter.setDialogueToken("pokemon3CommonEggs", eggsText);
} }
encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip; encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip;
@ -195,18 +196,19 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
selected: [ selected: [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -228,17 +230,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]; ];
if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1CommonEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1CommonEggs"] }),
}); });
} }
if (encounter.dialogueTokens.hasOwnProperty("pokemon1RareEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon1RareEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1RareEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1RareEggs"] }),
}); });
} }
@ -254,11 +256,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
selected: [ selected: [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -280,17 +282,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]; ];
if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2CommonEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2CommonEggs"] }),
}); });
} }
if (encounter.dialogueTokens.hasOwnProperty("pokemon2RareEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon2RareEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2RareEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2RareEggs"] }),
}); });
} }
@ -306,11 +308,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
selected: [ selected: [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -332,17 +334,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]; ];
if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3CommonEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3CommonEggs"] }),
}); });
} }
if (encounter.dialogueTokens.hasOwnProperty("pokemon3RareEggs")) { if (encounter.dialogueTokens.hasOwnProperty("pokemon3RareEggs")) {
encounter.dialogue.outro.push({ encounter.dialogue.outro.push({
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3RareEggs"] }), text: i18next.t(`${namespace}:gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3RareEggs"] }),
}); });
} }
@ -357,7 +359,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
.withOutroDialogue([ .withOutroDialogue([
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.outro`, text: `${namespace}:outro`,
}, },
]) ])
.build(); .build();
@ -374,7 +376,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
trainerType: TrainerType.EXPERT_POKEMON_BREEDER, trainerType: TrainerType.EXPERT_POKEMON_BREEDER,
pokemonConfigs: [ pokemonConfigs: [
{ {
nickname: i18next.t(`${namespace}.cleffa_1_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), nickname: i18next.t(`${namespace}:cleffa_1_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
species: getPokemonSpecies(cleffaSpecies), species: getPokemonSpecies(cleffaSpecies),
isBoss: false, isBoss: false,
abilityIndex: 1, // Magic Guard abilityIndex: 1, // Magic Guard
@ -394,7 +396,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
if (scene.arena.biomeType === Biome.SPACE) { if (scene.arena.biomeType === Biome.SPACE) {
// All 3 members always Cleffa line, but different configs // All 3 members always Cleffa line, but different configs
baseConfig.pokemonConfigs!.push({ baseConfig.pokemonConfigs!.push({
nickname: i18next.t(`${namespace}.cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), nickname: i18next.t(`${namespace}:cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
species: getPokemonSpecies(cleffaSpecies), species: getPokemonSpecies(cleffaSpecies),
isBoss: false, isBoss: false,
abilityIndex: 1, // Magic Guard abilityIndex: 1, // Magic Guard
@ -405,7 +407,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
ivs: [ 31, 31, 31, 31, 31, 31 ] ivs: [ 31, 31, 31, 31, 31, 31 ]
}, },
{ {
nickname: i18next.t(`${namespace}.cleffa_3_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), nickname: i18next.t(`${namespace}:cleffa_3_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
species: getPokemonSpecies(cleffaSpecies), species: getPokemonSpecies(cleffaSpecies),
isBoss: false, isBoss: false,
abilityIndex: 2, // Friend Guard / Unaware abilityIndex: 2, // Friend Guard / Unaware
@ -437,8 +439,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
} }
function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species { function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species {
const poolCopy = speciesPool.slice(0); const poolCopy = randSeedShuffle(speciesPool.slice(0));
randSeedShuffle(poolCopy);
const speciesEvolutions = poolCopy.pop()!.slice(0); const speciesEvolutions = poolCopy.pop()!.slice(0);
let speciesObject = speciesEvolutions.pop()!; let speciesObject = speciesEvolutions.pop()!;
while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) { while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) {
@ -452,11 +453,11 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
// 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1) // 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1)
const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18); const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18);
const rootSpecies = pokemon.species.getRootSpeciesId(true); const rootSpecies = pokemon.species.getRootSpeciesId();
let pointsFromStarterTier = 0; let pointsFromStarterTier = 0;
// 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0) // 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0)
if (speciesStarters.hasOwnProperty(rootSpecies)) { if (speciesStarterCosts.hasOwnProperty(rootSpecies)) {
const starterTier = speciesStarters[rootSpecies]; const starterTier = speciesStarterCosts[rootSpecies];
pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12); pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12);
} }
@ -472,7 +473,7 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
} }
function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) { function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) {
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(trainerNameKey); const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(trainerNameKey);
const eggOptions: IEggOptions[] = []; const eggOptions: IEggOptions[] = [];
if (commonEggs > 0) { if (commonEggs > 0) {
@ -493,7 +494,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
}); });
} }
} }
@ -541,7 +542,7 @@ function onGameOver(scene: BattleScene) {
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
text: `${namespace}.outro_failed`, text: `${namespace}:outro_failed`,
}, },
]; ];
@ -559,6 +560,10 @@ function onGameOver(scene: BattleScene) {
// Revert BGM // Revert BGM
scene.playBgm(scene.arena.bgm); scene.playBgm(scene.arena.bgm);
// Clear any leftover battle phases
scene.clearPhaseQueue();
scene.clearPhaseQueueSplice();
// Return enemy Pokemon // Return enemy Pokemon
const pokemon = scene.getEnemyPokemon(); const pokemon = scene.getEnemyPokemon();
if (pokemon) { if (pokemon) {

View File

@ -5,7 +5,8 @@ import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { PokeballType } from "#app/data/pokeball"; import { PokeballType } from "#app/data/pokeball";
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
@ -18,7 +19,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:pokemonSalesman"; const namespace = "mysteryEncounters/thePokemonSalesman";
const MAX_POKEMON_PRICE_MULTIPLIER = 4; const MAX_POKEMON_PRICE_MULTIPLIER = 4;
@ -45,16 +46,17 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -88,15 +90,15 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
isPokemon: true isPokemon: true
}); });
const starterTier = speciesStarters[species.speciesId]; const starterTier = speciesStarterCosts[species.speciesId];
// Prices decrease by starter tier less than 5, but only reduces cost by half at max // Prices decrease by starter tier less than 5, but only reduces cost by half at max
let priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER * (Math.max(starterTier, 2.5) / 5); let priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER * (Math.max(starterTier, 2.5) / 5);
if (pokemon.shiny) { if (pokemon.shiny) {
// Always max price for shiny (flip HA back to normal), and add special messaging // Always max price for shiny (flip HA back to normal), and add special messaging
priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER; priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER;
pokemon.abilityIndex = 0; pokemon.abilityIndex = 0;
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}.description_shiny`; encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}:description_shiny`;
encounter.options[0].dialogue!.buttonTooltip = `${namespace}.option.1.tooltip_shiny`; encounter.options[0].dialogue!.buttonTooltip = `${namespace}:option.1.tooltip_shiny`;
} }
const price = scene.getWaveMoneyAmount(priceMultiplier); const price = scene.getWaveMoneyAmount(priceMultiplier);
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender()); encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
@ -116,11 +118,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
.withHasDexProgress(true) .withHasDexProgress(true)
.withSceneMoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2 .withSceneMoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected_message`, text: `${namespace}:option.1.selected_message`,
} }
], ],
}) })
@ -133,7 +135,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
updatePlayerMoney(scene, -price, true, false); updatePlayerMoney(scene, -price, true, false);
// Show dialogue // Show dialogue
await showEncounterDialogue(scene, `${namespace}.option.1.selected_dialogue`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:option.1.selected_dialogue`, `${namespace}:speaker`);
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals(scene);
// "Catch" purchased pokemon // "Catch" purchased pokemon
@ -147,11 +149,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },

View File

@ -20,7 +20,7 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theStrongStuff"; const namespace = "mysteryEncounters/theStrongStuff";
// Halved for HP stat // Halved for HP stat
const HIGH_BST_REDUCTION_VALUE = 15; const HIGH_BST_REDUCTION_VALUE = 15;
@ -39,6 +39,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
.withMaxAllowedEncounters(1) .withMaxAllowedEncounters(1)
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "berry_juice", spriteKey: "berry_juice",
@ -63,7 +64,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
]) // Set in onInit() ]) // Set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -101,7 +102,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
], ],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.stat_boost`); queueEncounterMessage(pokemon.scene, `${namespace}:option.2.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2));
} }
} }
@ -116,16 +117,17 @@ export const TheStrongStuffEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected` text: `${namespace}:option.1.selected`
} }
] ]
}, },
@ -158,11 +160,11 @@ export const TheStrongStuffEncounter: MysteryEncounter =
encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString()); encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString());
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString()); encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, undefined, true); await showEncounterText(scene, `${namespace}:option.1.selected_2`, null, undefined, true);
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
text: `${namespace}.outro`, text: `${namespace}:outro`,
} }
]; ];
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards(scene, { fillRemaining: true });
@ -172,11 +174,11 @@ export const TheStrongStuffEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },

View File

@ -25,7 +25,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theWinstrateChallenge"; const namespace = "mysteryEncounters/theWinstrateChallenge";
/** /**
* The Winstrate Challenge encounter. * The Winstrate Challenge encounter.
@ -74,11 +74,11 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
@ -94,17 +94,18 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
return true; return true;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -119,12 +120,12 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -142,7 +143,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
const nextConfig = encounter.enemyPartyConfigs.pop(); const nextConfig = encounter.enemyPartyConfigs.pop();
if (!nextConfig) { if (!nextConfig) {
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(scene, false, false);
await showEncounterDialogue(scene, `${namespace}.victory`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:victory`, `${namespace}:speaker`);
// Give 10x Voucher // Give 10x Voucher
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier(); const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
@ -150,7 +151,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
scene.playSound("item_fanfare"); scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name })); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
await showEncounterDialogue(scene, `${namespace}.victory_2`, `${namespace}.speaker`); await showEncounterDialogue(scene, `${namespace}:victory_2`, `${namespace}:speaker`);
scene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in scene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in
const machoBrace = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!; const machoBrace = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
machoBrace.type.tier = ModifierTier.MASTER; machoBrace.type.tier = ModifierTier.MASTER;

View File

@ -1,7 +1,7 @@
import { Ability, allAbilities } from "#app/data/ability"; import { Ability, allAbilities } from "#app/data/ability";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getNatureName, Nature } from "#app/data/nature"; import { getNatureName, Nature } from "#app/data/nature";
import { speciesStarters } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { AbilityAttr } from "#app/system/game-data"; import { AbilityAttr } from "#app/system/game-data";
@ -23,7 +23,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** The i18n namespace for the encounter */ /** The i18n namespace for the encounter */
const namespace = "mysteryEncounter:trainingSession"; const namespace = "mysteryEncounters/trainingSession";
/** /**
* Training Session encounter. * Training Session encounter.
@ -49,22 +49,23 @@ export const TrainingSessionEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
} }
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -78,7 +79,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -101,7 +102,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
encounter.setDialogueToken("stat1", "-"); encounter.setDialogueToken("stat1", "-");
encounter.setDialogueToken("stat2", "-"); encounter.setDialogueToken("stat2", "-");
// Add the pokemon back to party with IV boost // Add the pokemon back to party with IV boost
const ivIndexes: any[] = []; let ivIndexes: any[] = [];
playerPokemon.ivs.forEach((iv, index) => { playerPokemon.ivs.forEach((iv, index) => {
if (iv < 31) { if (iv < 31) {
ivIndexes.push({ iv: iv, index: index }); ivIndexes.push({ iv: iv, index: index });
@ -117,7 +118,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
// 25-27 starting IV caps in 2 encounters // 25-27 starting IV caps in 2 encounters
let improvedCount = 0; let improvedCount = 0;
while (ivIndexes.length > 0 && improvedCount < 2) { while (ivIndexes.length > 0 && improvedCount < 2) {
randSeedShuffle(ivIndexes); ivIndexes = randSeedShuffle(ivIndexes);
const ivToChange = ivIndexes.pop(); const ivToChange = ivIndexes.pop();
let newVal = ivToChange.iv; let newVal = ivToChange.iv;
if (improvedCount === 0) { if (improvedCount === 0) {
@ -145,10 +146,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
if (improvedCount > 0) { if (improvedCount > 0) {
playerPokemon.calculateStats(); playerPokemon.calculateStats();
scene.gameData.updateSpeciesDexIvs( scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
playerPokemon.species.getRootSpeciesId(true),
playerPokemon.ivs
);
scene.gameData.setPokemonCaught(playerPokemon, false); scene.gameData.setPokemonCaught(playerPokemon, false);
} }
@ -159,7 +157,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.addModifier(mod, true, false, false, true); scene.addModifier(mod, true, false, false, true);
} }
scene.updateModifiers(true); scene.updateModifiers(true);
queueEncounterMessage(scene, `${namespace}.option.1.finished`); queueEncounterMessage(scene, `${namespace}:option.1.finished`);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
@ -173,12 +171,12 @@ export const TrainingSessionEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}.option.2.select_prompt`, secondOptionPrompt: `${namespace}:option.2.select_prompt`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -207,7 +205,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -224,7 +222,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.removePokemonFromPlayerParty(playerPokemon, false); scene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}.option.2.finished`); queueEncounterMessage(scene, `${namespace}:option.2.finished`);
// Add the pokemon back to party with Nature change // Add the pokemon back to party with Nature change
playerPokemon.setNature(encounter.misc.chosenNature); playerPokemon.setNature(encounter.misc.chosenNature);
scene.gameData.setPokemonCaught(playerPokemon, false); scene.gameData.setPokemonCaught(playerPokemon, false);
@ -249,12 +247,12 @@ export const TrainingSessionEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
selected: [ selected: [
{ {
text: `${namespace}.option.selected`, text: `${namespace}:option.selected`,
}, },
], ],
}) })
@ -298,7 +296,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -319,30 +317,26 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.removePokemonFromPlayerParty(playerPokemon, false); scene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}.option.3.finished`); queueEncounterMessage(scene, `${namespace}:option.3.finished`);
// Add the pokemon back to party with ability change // Add the pokemon back to party with ability change
const abilityIndex = encounter.misc.abilityIndex; const abilityIndex = encounter.misc.abilityIndex;
if (!!playerPokemon.getFusionSpeciesForm()) { if (!!playerPokemon.getFusionSpeciesForm()) {
playerPokemon.fusionAbilityIndex = abilityIndex; playerPokemon.fusionAbilityIndex = abilityIndex;
if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) {
scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId] // Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals)
.abilityAttr |= const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 if (!isNullOrUndefined(rootFusionSpecies)
? Math.pow(2, playerPokemon.fusionAbilityIndex) && speciesStarterCosts.hasOwnProperty(rootFusionSpecies)
&& !!scene.gameData.dexData[rootFusionSpecies].caughtAttr) {
scene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
? 1 << playerPokemon.fusionAbilityIndex
: AbilityAttr.ABILITY_HIDDEN; : AbilityAttr.ABILITY_HIDDEN;
} }
} else { } else {
playerPokemon.abilityIndex = abilityIndex; playerPokemon.abilityIndex = abilityIndex;
if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) {
scene.gameData.starterData[playerPokemon.species.speciesId]
.abilityAttr |=
abilityIndex !== 1 || playerPokemon.species.ability2
? Math.pow(2, playerPokemon.abilityIndex)
: AbilityAttr.ABILITY_HIDDEN;
}
} }
playerPokemon.getAbility();
playerPokemon.calculateStats(); playerPokemon.calculateStats();
scene.gameData.setPokemonCaught(playerPokemon, false); scene.gameData.setPokemonCaught(playerPokemon, false);
@ -363,11 +357,11 @@ export const TrainingSessionEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.4.label`, buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`, buttonTooltip: `${namespace}:option.4.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.4.selected`, text: `${namespace}:option.4.selected`,
}, },
], ],
}, },

View File

@ -19,7 +19,7 @@ import { PokemonMove } from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:trashToTreasure"; const namespace = "mysteryEncounters/trashToTreasure";
const SOUND_EFFECT_WAIT_TIME = 700; const SOUND_EFFECT_WAIT_TIME = 700;
@ -36,6 +36,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.ULTRA) .withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withMaxAllowedEncounters(1) .withMaxAllowedEncounters(1)
.withFleeAllowed(false)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: Species.GARBODOR.toString() + "-gigantamax", spriteKey: Species.GARBODOR.toString() + "-gigantamax",
@ -50,12 +51,13 @@ export const TrashToTreasureEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
@ -89,11 +91,11 @@ export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}) })
@ -122,18 +124,18 @@ export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// 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); transitionMysteryEncounterIntroVisuals(scene);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;

View File

@ -25,7 +25,7 @@ import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:uncommonBreed"; const namespace = "mysteryEncounters/uncommonBreed";
/** /**
* Uncommon Breed encounter. * Uncommon Breed encounter.
@ -38,10 +38,11 @@ export const UncommonBreedEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true) .withCatchAllowed(true)
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit() .withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
@ -59,16 +60,17 @@ export const UncommonBreedEncounter: MysteryEncounter =
const eggMoveIndex = randSeedInt(4); const eggMoveIndex = randSeedInt(4);
const randomEggMove: Moves = eggMoves[eggMoveIndex]; const randomEggMove: Moves = eggMoves[eggMoveIndex];
encounter.misc = { encounter.misc = {
eggMove: randomEggMove eggMove: randomEggMove,
pokemon: pokemon
}; };
if (pokemon.moveset.length < 4) { if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove)); pokemon.moveset.push(new PokemonMove(randomEggMove));
} else { } else {
pokemon.moveset[0] = new PokemonMove(randomEggMove); pokemon.moveset[0] = new PokemonMove(randomEggMove);
} }
} } else {
encounter.misc.pokemon = pokemon; encounter.misc.pokemon = pokemon;
}
// Defense/Spd buffs below wave 50, +1 to all stats otherwise // Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
@ -83,7 +85,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
isBoss: false, isBoss: false,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`); queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
}], }],
@ -123,16 +125,17 @@ 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;
}) })
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
}, },
], ],
}, },
@ -165,12 +168,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected` text: `${namespace}:option.2.selected`
} }
] ]
}) })
@ -209,12 +212,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`, disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.3.selected` text: `${namespace}:option.3.selected`
} }
] ]
}) })

View File

@ -23,7 +23,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
/** i18n namespace for encounter */ /** i18n namespace for encounter */
const namespace = "mysteryEncounter:weirdDream"; const namespace = "mysteryEncounters/weirdDream";
/** Exclude Ultra Beasts, Paradox, Eternatus, and all legendary/mythical/trio pokemon that are below 570 BST */ /** Exclude Ultra Beasts, Paradox, Eternatus, and all legendary/mythical/trio pokemon that are below 570 BST */
const EXCLUDED_TRANSFORMATION_SPECIES = [ const EXCLUDED_TRANSFORMATION_SPECIES = [
@ -118,16 +118,17 @@ export const WeirdDreamEncounter: MysteryEncounter =
]) ])
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}:intro`,
}, },
{ {
speaker: `${namespace}.speaker`, speaker: `${namespace}:speaker`,
text: `${namespace}.intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withTitle(`${namespace}.title`) .setLocalizationKey(`${namespace}`)
.withDescription(`${namespace}.description`) .withTitle(`${namespace}:title`)
.withQuery(`${namespace}.query`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3"); scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
return true; return true;
@ -141,11 +142,11 @@ export const WeirdDreamEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true) .withHasDexProgress(true)
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}.option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.1.selected`, text: `${namespace}:option.1.selected`,
} }
], ],
}) })
@ -165,7 +166,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue // Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue
const cutsceneDialoguePromise = showEncounterText(scene, `${namespace}.option.1.cutscene`); const cutsceneDialoguePromise = showEncounterText(scene, `${namespace}:option.1.cutscene`);
// Change the entire player's party // Change the entire player's party
// Wait for all new Pokemon assets to be loaded before showing transformation animations // Wait for all new Pokemon assets to be loaded before showing transformation animations
@ -189,7 +190,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
await cutsceneDialoguePromise; await cutsceneDialoguePromise;
doHideDreamBackground(scene); doHideDreamBackground(scene);
await showEncounterText(scene, `${namespace}.option.1.dream_complete`); await showEncounterText(scene, `${namespace}:option.1.dream_complete`);
await doNewTeamPostProcess(scene, transformations); await doNewTeamPostProcess(scene, transformations);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT ]}); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT ]});
@ -199,11 +200,11 @@ export const WeirdDreamEncounter: MysteryEncounter =
) )
.withSimpleOption( .withSimpleOption(
{ {
buttonLabel: `${namespace}.option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [ selected: [
{ {
text: `${namespace}.option.2.selected`, text: `${namespace}:option.2.selected`,
}, },
], ],
}, },
@ -368,7 +369,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm); const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm);
// Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset) // Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset)
addFavoredMoveToNewPokemonMoveset(scene, newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
// Randomize the second type of the pokemon // Randomize the second type of the pokemon
// If the pokemon does not normally have a second type, it will gain 1 // If the pokemon does not normally have a second type, it will gain 1
@ -553,8 +554,7 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla
let eggMoveIndex: null | number = null; let eggMoveIndex: null | number = null;
const eggMoves = newPokemon.getEggMoves()?.slice(0); const eggMoves = newPokemon.getEggMoves()?.slice(0);
if (eggMoves) { if (eggMoves) {
const eggMoveIndices = [0, 1, 2, 3]; const eggMoveIndices = randSeedShuffle([ 0, 1, 2, 3 ]);
randSeedShuffle(eggMoveIndices);
let randomEggMoveIndex = eggMoveIndices.pop(); let randomEggMoveIndex = eggMoveIndices.pop();
let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
let retries = 0; let retries = 0;
@ -587,12 +587,11 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla
/** /**
* Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`) * Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`)
* @param scene
* @param newPokemon * @param newPokemon
* @param newPokemonGeneratedMoveset * @param newPokemonGeneratedMoveset
* @param newEggMoveIndex * @param newEggMoveIndex
*/ */
function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) { function addFavoredMoveToNewPokemonMoveset(newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) {
let favoredMove: PokemonMove | null = null; let favoredMove: PokemonMove | null = null;
for (const move of newPokemonGeneratedMoveset) { for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced // Needs to match first type, second type will be replaced
@ -614,6 +613,9 @@ function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Playe
} }
// Finally, assign favored move to random index that isn't the new egg move index // Finally, assign favored move to random index that isn't the new egg move index
if (favoredMove) { if (favoredMove) {
if (newPokemon.moveset.length < 4) {
newPokemon.moveset.push(favoredMove);
} else {
let favoredMoveIndex = randSeedInt(4); let favoredMoveIndex = randSeedInt(4);
while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) { while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) {
favoredMoveIndex = randSeedInt(4); favoredMoveIndex = randSeedInt(4);
@ -622,3 +624,4 @@ function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Playe
newPokemon.moveset[favoredMoveIndex] = favoredMove; newPokemon.moveset[favoredMoveIndex] = favoredMove;
} }
} }
}

View File

@ -5,16 +5,16 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { Nature } from "../nature"; import { Nature } from "#app/data/nature";
import { EvolutionItem, pokemonEvolutions } from "../pokemon-evolutions"; import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "../pokemon-forms"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
import { SpeciesFormKey } from "../pokemon-species"; import { StatusEffect } from "#app/data/status-effect";
import { StatusEffect } from "../status-effect"; import { Type } from "#app/data/type";
import { Type } from "../type"; import { WeatherType } from "#app/data/weather";
import { WeatherType } from "../weather";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { AttackTypeBoosterModifier } from "#app/modifier/modifier"; import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { SpeciesFormKey } from "#enums/species-form-key";
export interface EncounterRequirement { export interface EncounterRequirement {
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met
@ -1033,4 +1033,3 @@ export class WeightRequirement extends EncounterPokemonRequirement {
} }
} }

View File

@ -190,7 +190,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
secondaryPokemon?: PlayerPokemon[]; secondaryPokemon?: PlayerPokemon[];
// #region Post-construct / Auto-populated params // #region Post-construct / Auto-populated params
localizationKey: string;
/** /**
* Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter * Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter
*/ */
@ -264,6 +264,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
Object.assign(this, encounter); Object.assign(this, encounter);
} }
this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON; this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON;
this.localizationKey = this.localizationKey ?? "";
this.dialogue = this.dialogue ?? {}; this.dialogue = this.dialogue ?? {};
this.spriteConfigs = this.spriteConfigs ? [ ...this.spriteConfigs ] : []; this.spriteConfigs = this.spriteConfigs ? [ ...this.spriteConfigs ] : [];
// Default max is 1 for ROGUE encounters, 2 for others // Default max is 1 for ROGUE encounters, 2 for others
@ -528,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]]; options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]];
enemyPartyConfigs: EnemyPartyConfig[] = []; enemyPartyConfigs: EnemyPartyConfig[] = [];
localizationKey: string = "";
dialogue: MysteryEncounterDialogue = {}; dialogue: MysteryEncounterDialogue = {};
requirements: EncounterSceneRequirement[] = []; requirements: EncounterSceneRequirement[] = [];
primaryPokemonRequirements: EncounterPokemonRequirement[] = []; primaryPokemonRequirements: EncounterPokemonRequirement[] = [];
@ -632,6 +634,16 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue); return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue);
} }
/**
* Sets the localization key used by the encounter
* @param localizationKey the string used as the key
* @returns `this`
*/
setLocalizationKey(localizationKey: string): this {
this.localizationKey = localizationKey;
return this;
}
/** /**
* OPTIONAL * OPTIONAL
*/ */

View File

@ -32,7 +32,7 @@ import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fu
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
import { getBiomeName } from "#app/data/biomes"; import { getBiomeName } from "#app/data/balance/biomes";
/** /**
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT * Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
@ -363,6 +363,7 @@ export function initMysteryEncounters() {
}); });
// Add ANY biome encounters to biome map // Add ANY biome encounters to biome map
// eslint-disable-next-line
let encounterBiomeTableLog = ""; let encounterBiomeTableLog = "";
mysteryEncountersByBiome.forEach((biomeEncounters, biome) => { mysteryEncountersByBiome.forEach((biomeEncounters, biome) => {
anyBiomeEncounters.forEach(encounter => { anyBiomeEncounters.forEach(encounter => {
@ -374,5 +375,5 @@ export function initMysteryEncounters() {
encounterBiomeTableLog += `${getBiomeName(biome).toUpperCase()}: [${biomeEncounters.map(type => MysteryEncounterType[type].toString().toLowerCase()).sort().join(", ")}]\n`; encounterBiomeTableLog += `${getBiomeName(biome).toUpperCase()}: [${biomeEncounters.map(type => MysteryEncounterType[type].toString().toLowerCase()).sort().join(", ")}]\n`;
}); });
console.debug("All Mystery Encounters by Biome:\n" + encounterBiomeTableLog); //console.debug("All Mystery Encounters by Biome:\n" + encounterBiomeTableLog);
} }

View File

@ -1,5 +1,5 @@
import Battle, { BattlerIndex, BattleType } from "#app/battle"; import Battle, { BattlerIndex, BattleType } from "#app/battle";
import { biomeLinks, BiomePoolTier } from "#app/data/biomes"; import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -832,7 +832,7 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide:
*/ */
export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
const encounter = scene.currentBattle.mysteryEncounter; const encounter = scene.currentBattle.mysteryEncounter;
if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) {
const effects = encounter.startOfBattleEffects; const effects = encounter.startOfBattleEffects;
effects.forEach(effect => { effects.forEach(effect => {
let source; let source;
@ -871,7 +871,7 @@ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
*/ */
export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): boolean { export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): boolean {
const encounter = scene.currentBattle.mysteryEncounter; const encounter = scene.currentBattle.mysteryEncounter;
if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.onTurnStart) { if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.onTurnStart) {
return encounter.onTurnStart(scene); return encounter.onTurnStart(scene);
} }

View File

@ -12,7 +12,8 @@ import { Mode } from "#app/ui/ui";
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Type } from "#app/data/type"; import { Type } from "#app/data/type";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
@ -206,14 +207,14 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu
let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers; let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers;
let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers; let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers;
let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters) let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarterCosts)
.map(s => [parseInt(s) as Species, speciesStarters[s] as number]) .map(s => [ parseInt(s) as Species, speciesStarterCosts[s] as number ])
.filter(s => { .filter(s => {
const pokemonSpecies = getPokemonSpecies(s[0]); const pokemonSpecies = getPokemonSpecies(s[0]);
return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0]) return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0]))
&& (allowSubLegendary || !pokemonSpecies.subLegendary) && (allowSubLegendary || !pokemonSpecies.subLegendary)
&& (allowLegendary || !pokemonSpecies.legendary) && (allowLegendary || !pokemonSpecies.legendary)
&& (allowMythical || !pokemonSpecies.mythical)); && (allowMythical || !pokemonSpecies.mythical);
}) })
.map(s => [ getPokemonSpecies(s[0]), s[1] ]); .map(s => [ getPokemonSpecies(s[0]), s[1] ]);
@ -224,7 +225,7 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu
// If no filtered mons exist at specified starter tiers, will expand starter search range until there are // If no filtered mons exist at specified starter tiers, will expand starter search range until there are
// Starts by decrementing starter tier min until it is 0, then increments tier max up to 10 // Starts by decrementing starter tier min until it is 0, then increments tier max up to 10
let tryFilterStarterTiers: [PokemonSpecies, number][] = filteredSpecies.filter(s => (s[1] >= min && s[1] <= max)); let tryFilterStarterTiers: [PokemonSpecies, number][] = filteredSpecies.filter(s => (s[1] >= min && s[1] <= max));
while (tryFilterStarterTiers.length === 0 && (min !== 0 && max !== 10)) { while (tryFilterStarterTiers.length === 0 && !(min === 0 && max === 10)) {
if (min > 0) { if (min > 0) {
min--; min--;
} else { } else {
@ -757,9 +758,10 @@ const GOLDEN_BUG_NET_SPECIES_POOL: [Species, number][] = [
]; ];
/** /**
* Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights * Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights.
* Will also check for and evolve pokemon based on level.
*/ */
export function getGoldenBugNetSpecies(): PokemonSpecies { export function getGoldenBugNetSpecies(level: number): PokemonSpecies {
const totalWeight = GOLDEN_BUG_NET_SPECIES_POOL.reduce((a, b) => a + b[1], 0); const totalWeight = GOLDEN_BUG_NET_SPECIES_POOL.reduce((a, b) => a + b[1], 0);
const roll = randSeedInt(totalWeight); const roll = randSeedInt(totalWeight);
@ -767,7 +769,8 @@ export function getGoldenBugNetSpecies(): PokemonSpecies {
for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) { for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) {
w += speciesWeightPair[1]; w += speciesWeightPair[1];
if (roll < w) { if (roll < w) {
return getPokemonSpecies(speciesWeightPair[0]); const initialSpecies = getPokemonSpecies(speciesWeightPair[0]);
return getPokemonSpecies(initialSpecies.getSpeciesForLevel(level, true));
} }
} }

View File

@ -1,10 +1,9 @@
import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier"; import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier";
import Pokemon from "../field/pokemon"; import Pokemon from "../field/pokemon";
import { SpeciesFormKey } from "./pokemon-species";
import { StatusEffect } from "./status-effect"; import { StatusEffect } from "./status-effect";
import { MoveCategory, allMoves } from "./move"; import { MoveCategory, allMoves } from "./move";
import { Type } from "./type"; import { Type } from "./type";
import { Constructor } from "#app/utils"; import { Constructor, nil } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -13,6 +12,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import i18next from "i18next"; import i18next from "i18next";
import { WeatherType } from "./weather"; import { WeatherType } from "./weather";
import { Challenges } from "#app/enums/challenges"; import { Challenges } from "#app/enums/challenges";
import { SpeciesFormKey } from "#enums/species-form-key";
export enum FormChangeItem { export enum FormChangeItem {
NONE, NONE,
@ -185,7 +185,7 @@ export class SpeciesFormChange {
return true; return true;
} }
findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger | null { findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger | nil {
if (!this.trigger.hasTriggerType(triggerType)) { if (!this.trigger.hasTriggerType(triggerType)) {
return null; return null;
} }
@ -193,7 +193,7 @@ export class SpeciesFormChange {
const trigger = this.trigger; const trigger = this.trigger;
if (trigger instanceof SpeciesFormChangeCompoundTrigger) { if (trigger instanceof SpeciesFormChangeCompoundTrigger) {
return trigger.triggers.find(t => t.hasTriggerType(triggerType))!; // TODO: is this bang correct? return trigger.triggers.find(t => t.hasTriggerType(triggerType));
} }
return trigger; return trigger;
@ -202,11 +202,11 @@ export class SpeciesFormChange {
export class SpeciesFormChangeCondition { export class SpeciesFormChangeCondition {
public predicate: SpeciesFormChangeConditionPredicate; public predicate: SpeciesFormChangeConditionPredicate;
public enforceFunc: SpeciesFormChangeConditionEnforceFunc | null; public enforceFunc: SpeciesFormChangeConditionEnforceFunc | nil;
constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) { constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) {
this.predicate = predicate; this.predicate = predicate;
this.enforceFunc = enforceFunc!; // TODO: is this bang correct? this.enforceFunc = enforceFunc;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,27 @@
import BattleScene, { startingWave } from "../battle-scene"; import BattleScene, { startingWave } from "#app/battle-scene";
import { ModifierTypeFunc, modifierTypes } from "../modifier/modifier-type"; import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type";
import { EnemyPokemon, PokemonMove } from "../field/pokemon"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import * as Utils from "../utils"; import * as Utils from "#app/utils";
import { PokeballType } from "./pokeball"; import { PokeballType } from "#app/data/pokeball";
import { pokemonEvolutions, pokemonPrevolutions } from "./pokemon-evolutions"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokemonSpecies, { getPokemonSpecies, PokemonSpeciesFilter } from "./pokemon-species"; import PokemonSpecies, { getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
import { tmSpecies } from "./tms"; import { tmSpecies } from "#app/data/balance/tms";
import { Type } from "./type"; import { Type } from "#app/data/type";
import { doubleBattleDialogue } from "./dialogue"; import { doubleBattleDialogue } from "#app/data/dialogue";
import { PersistentModifier } from "../modifier/modifier"; import { PersistentModifier } from "#app/modifier/modifier";
import { TrainerVariant } from "../field/trainer"; import { TrainerVariant } from "#app/field/trainer";
import { getIsInitialized, initI18n } from "#app/plugins/i18n"; import { getIsInitialized, initI18n } from "#app/plugins/i18n";
import i18next from "i18next"; import i18next from "i18next";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import {Gender} from "./gender"; import { Gender } from "#app/data/gender";
/** Minimum BST for Pokemon generated onto the Elite Four's teams */
const ELITE_FOUR_MINIMUM_BST = 460;
/** Minimum BST for Pokemon generated onto the E4 Champion's team */
const CHAMPION_MINIMUM_BST = 508;
export enum TrainerPoolTier { export enum TrainerPoolTier {
COMMON, COMMON,
@ -860,10 +865,10 @@ export class TrainerConfig {
// Set species filter and specialty types if provided, otherwise filter by base total. // Set species filter and specialty types if provided, otherwise filter by base total.
if (specialtyTypes.length) { if (specialtyTypes.length) {
this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= 450); this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST);
this.setSpecialtyTypes(...specialtyTypes); this.setSpecialtyTypes(...specialtyTypes);
} else { } else {
this.setSpeciesFilter(p => p.baseTotal >= 450); this.setSpeciesFilter(p => p.baseTotal >= ELITE_FOUR_MINIMUM_BST);
} }
// Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
@ -913,8 +918,7 @@ export class TrainerConfig {
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// Set species filter to only include species with a base total of 470 or higher. this.setSpeciesFilter(p => p.baseTotal >= CHAMPION_MINIMUM_BST);
this.setSpeciesFilter(p => p.baseTotal >= 470);
// Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");

View File

@ -1,4 +1,3 @@
export enum ArenaTagType { export enum ArenaTagType {
NONE = "NONE", NONE = "NONE",
MUD_SPORT = "MUD_SPORT", MUD_SPORT = "MUD_SPORT",
@ -25,4 +24,8 @@ export enum ArenaTagType {
SAFEGUARD = "SAFEGUARD", SAFEGUARD = "SAFEGUARD",
NO_CRIT = "NO_CRIT", NO_CRIT = "NO_CRIT",
IMPRISON = "IMPRISON", IMPRISON = "IMPRISON",
ION_DELUGE = "ION_DELUGE",
FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE",
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
} }

View File

@ -1,4 +1,3 @@
export enum BattlerTagType { export enum BattlerTagType {
NONE = "NONE", NONE = "NONE",
RECHARGING = "RECHARGING", RECHARGING = "RECHARGING",
@ -85,4 +84,6 @@ export enum BattlerTagType {
TORMENT = "TORMENT", TORMENT = "TORMENT",
TAUNT = "TAUNT", TAUNT = "TAUNT",
IMPRISON = "IMPRISON", IMPRISON = "IMPRISON",
SYRUP_BOMB = "SYRUP_BOMB",
ELECTRIFIED = "ELECTRIFIED",
} }

View File

@ -1,4 +1,3 @@
export enum BerryType { export enum BerryType {
SITRUS, SITRUS,
LUM, LUM,

View File

@ -1,4 +1,3 @@
export enum Biome { export enum Biome {
TOWN, TOWN,
PLAINS, PLAINS,

View File

@ -1,6 +1,6 @@
export enum EggTier { export enum EggTier {
COMMON, COMMON,
GREAT, RARE,
ULTRA, EPIC,
MASTER LEGENDARY
} }

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