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_TUTORIAL=0
VITE_SERVER_URL=https://api.beta.pokerogue.net
VITE_SERVER_URL=https://apibeta.pokerogue.net
VITE_DISCORD_CLIENT_ID=1248062921129459756
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_DISCORD_CLIENT_ID=1234567890
VITE_GOOGLE_CLIENT_ID=1234567890
VITE_I18N_DEBUG=1
VITE_I18N_DEBUG=0
VITE_PORT=8000

38
.github/CODEOWNERS vendored
View File

@ -5,41 +5,3 @@
# github actions/templates etc. - 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
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- uses: actions/setup-node@v4
with:
node-version: "20"

View File

@ -1,8 +1,12 @@
name: Deploy
name: Deploy Main
on:
push: {}
pull_request: {}
push:
branches:
- main
pull_request:
branches:
- main
jobs:
deploy:
@ -10,6 +14,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- uses: actions/setup-node@v4
with:
node-version: "20"
@ -20,7 +26,7 @@ jobs:
env:
NODE_ENV: production
- 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: |
mkdir ~/.ssh
echo "${{ secrets.SSH_PUBLIC_KEY }}" > ~/.ssh/id_ed25519.pub
@ -28,12 +34,12 @@ jobs:
chmod 600 ~/.ssh/*
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- 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: |
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"
- 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
uses: NathanVaughn/actions-cloudflare-purge@v3.1.0
with:

View File

@ -22,6 +22,8 @@ jobs:
steps:
- name: Check out Git repository # Step to check out the repository
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
uses: actions/setup-node@v4 # Use the setup-node action version 4

View File

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

View File

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

View File

@ -22,6 +22,7 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v4
with:
submodules: 'recursive'
path: tests-action
- name: Set up Node.js
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`
#### 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
**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
**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
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.
* @param {string} type - The type of test to create. Either "move", "ability",
* or "item".
* @param {string} fileName - The name of the file to create.
* @example npm run create-test move tackle
* This script creates a test boilerplate file in the appropriate
* directory based on the type selected.
* @example npm run create-test
*/
import fs from "fs";
@ -34,7 +32,7 @@ async function promptTestType() {
console.log("Exiting...");
return process.exit();
} 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();
}
@ -51,7 +49,7 @@ async function promptFileName(selectedType) {
{
type: "input",
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}`;
break;
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);
}
@ -112,7 +110,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("${description}", () => {
let phaserGame: Phaser.Game;
@ -131,15 +129,22 @@ describe("${description}", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.SPLASH])
.moveset([ Moves.SPLASH ])
.ability(Abilities.BALL_FETCH)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH);
});
it("test case", async () => {
// await game.classicMode.startBattle([Species.MAGIKARP]);
// game.move.select(Moves.SPLASH);
it("should do X", async () => {
await game.classicMode.startBattle([ Species.FEEBAS ]);
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
"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
"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

@ -13,4 +13,9 @@ pre-push:
commands:
eslint:
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",
"version": "1.0.4",
"hasInstallScript": true,
"dependencies": {
"@material/material-color-utilities": "^0.2.7",
"crypto-js": "^4.2.0",
"i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.6.1",
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0",
@ -32,11 +34,12 @@
"inquirer": "^11.0.2",
"jsdom": "^24.0.0",
"lefthook": "^1.6.12",
"msw": "^2.4.9",
"phaser3spectorjs": "^0.0.8",
"typedoc": "^0.26.4",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.54",
"vite": "^5.3.5",
"vite": "^5.4.8",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.4",
"vitest-canvas-mock": "^0.3.3"
@ -486,6 +489,37 @@
"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": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -1461,6 +1495,24 @@
"integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==",
"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": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1499,6 +1551,31 @@
"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": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -1777,6 +1854,13 @@
"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": {
"version": "8.56.11",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz",
@ -1842,6 +1926,13 @@
"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": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
@ -2622,6 +2713,16 @@
"dev": true,
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@ -3551,7 +3652,6 @@
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -3740,6 +3840,16 @@
"dev": true,
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -3797,6 +3907,13 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
@ -3888,9 +4005,10 @@
}
},
"node_modules/i18next-http-backend": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz",
"integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.6.1.tgz",
"integrity": "sha512-rCilMAnlEQNeKOZY1+x8wLM5IpYOj10guGvEpeC59tNjj6MMreLIjIW8D1RclhD3ifLwn6d/Y9HEM1RUE6DSog==",
"license": "MIT",
"dependencies": {
"cross-fetch": "4.0.0"
}
@ -4072,6 +4190,13 @@
"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": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -4805,6 +4930,90 @@
"dev": true,
"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": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
@ -4835,7 +5044,6 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@ -4983,6 +5191,13 @@
"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": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -5105,6 +5320,13 @@
"dev": true,
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@ -5220,9 +5442,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.39",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
"version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"dev": true,
"funding": [
{
@ -5238,16 +5460,21 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.1",
"source-map-js": "^1.2.0"
"picocolors": "^1.1.0",
"source-map-js": "^1.2.1"
},
"engines": {
"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": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -5644,11 +5871,10 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
@ -5667,6 +5893,16 @@
"dev": true,
"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": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
@ -5674,6 +5910,13 @@
"dev": true,
"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": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@ -6231,15 +6474,14 @@
}
},
"node_modules/vite": {
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
"integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
"version": "5.4.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.39",
"rollup": "^4.13.0"
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
@ -6258,6 +6500,7 @@
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
@ -6275,6 +6518,9 @@
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},

View File

@ -19,7 +19,8 @@
"docs": "typedoc",
"depcruise": "depcruise src",
"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": {
"@eslint/js": "^9.3.0",
@ -36,11 +37,12 @@
"inquirer": "^11.0.2",
"jsdom": "^24.0.0",
"lefthook": "^1.6.12",
"msw": "^2.4.9",
"phaser3spectorjs": "^0.0.8",
"typedoc": "^0.26.4",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.54",
"vite": "^5.3.5",
"vite": "^5.4.8",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.4",
"vitest-canvas-mock": "^0.3.3"
@ -50,6 +52,7 @@
"crypto-js": "^4.2.0",
"i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.6.1",
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0",

View File

@ -399,13 +399,13 @@
"x": 0,
"y": 6,
"w": 36,
"h": 55
"h": 54
},
"frame": {
"x": 72,
"y": 55,
"w": 36,
"h": 55
"h": 54
}
},
{
@ -420,13 +420,13 @@
"x": 0,
"y": 6,
"w": 36,
"h": 55
"h": 54
},
"frame": {
"x": 72,
"y": 55,
"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,
"y": 6,
"w": 36,
"h": 55
"h": 54
},
"frame": {
"x": 72,
"y": 55,
"w": 36,
"h": 55
"h": 54
}
},
{
@ -420,13 +420,13 @@
"x": 0,
"y": 6,
"w": 36,
"h": 55
"h": 54
},
"frame": {
"x": 72,
"y": 55,
"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 },
"sourceSize": { "w": 40, "h": 30 }
},
"981_2.png": {
"981_2": {
"frame": { "x": 108, "y": 87, "w": 23, "h": 30 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 0, "w": 23, "h": 30 },
"sourceSize": { "w": 40, "h": 30 }
},
"981_3.png": {
"981_3": {
"frame": { "x": 246, "y": 86, "w": 23, "h": 30 },
"rotated": false,
"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";
//TODO: this needs to be type properly in the future
// Module declared to make referencing keys in the localization files type-safe.
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "menu", // needed here as well for typedoc
resources: typeof enConfig
}
interface TFunction {
(
key: string | string[],
options?: TOptions & Record<string, unknown>
): string;
}
interface TFunction {
(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

@ -20,7 +20,7 @@ export function initLoggedInUser(): void {
export function updateUserInfo(): Promise<[boolean, integer]> {
return new Promise<[boolean, integer]>(resolve => {
if (bypassLogin) {
loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: "", hasAdminRole: false};
loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: "", hasAdminRole: false };
let lastSessionSlot = -1;
for (let s = 0; s < 5; s++) {
if (localStorage.getItem(`sessionData${s ? s : ""}_${loggedInUser.username}`)) {

View File

@ -1,57 +1,57 @@
import Phaser from "phaser";
import UI from "./ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
import UI from "#app/ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
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 { PokeballType } from "./data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
import { Phase } from "./phase";
import { initGameSpeed } from "./system/game-speed";
import { Arena, ArenaBase } from "./field/arena";
import { GameData } from "./system/game-data";
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
import { allMoves } from "./data/move";
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "./modifier/modifier-type";
import AbilityBar from "./ui/ability-bar";
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, ChangeMovePriorityAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "./data/ability";
import Battle, { BattleType, FixedBattleConfig } from "./battle";
import { GameMode, GameModes, getGameMode } from "./game-mode";
import FieldSpritePipeline from "./pipelines/field-sprite";
import SpritePipeline from "./pipelines/sprite";
import PartyExpBar from "./ui/party-exp-bar";
import { trainerConfigs, TrainerSlot } from "./data/trainer-config";
import Trainer, { TrainerVariant } from "./field/trainer";
import TrainerData from "./system/trainer-data";
import { PokeballType } from "#app/data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import { Phase } from "#app/phase";
import { initGameSpeed } from "#app/system/game-speed";
import { Arena, ArenaBase } from "#app/field/arena";
import { GameData } from "#app/system/game-data";
import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
import { allMoves } from "#app/data/move";
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import AbilityBar from "#app/ui/ability-bar";
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "#app/data/ability";
import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
import FieldSpritePipeline from "#app/pipelines/field-sprite";
import SpritePipeline from "#app/pipelines/sprite";
import PartyExpBar from "#app/ui/party-exp-bar";
import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
import Trainer, { TrainerVariant } from "#app/field/trainer";
import TrainerData from "#app/system/trainer-data";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
import PokeballTray from "./ui/pokeball-tray";
import InvertPostFX from "./pipelines/invert";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv";
import { Voucher, vouchers } from "./system/voucher";
import { Gender } from "./data/gender";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokeballTray from "#app/ui/pokeball-tray";
import InvertPostFX from "#app/pipelines/invert";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
import { Voucher, vouchers } from "#app/system/voucher";
import { Gender } from "#app/data/gender";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import { addUiThemeOverrides } from "./ui/ui-theme";
import PokemonData from "./system/pokemon-data";
import { Nature } from "./data/nature";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms";
import { FormChangePhase } from "./phases/form-change-phase";
import { getTypeRgb } from "./data/type";
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
import CharSprite from "./ui/char-sprite";
import DamageNumberHandler from "./field/damage-number-handler";
import PokemonInfoContainer from "./ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "./data/biomes";
import { SceneBase } from "./scene-base";
import CandyBar from "./ui/candy-bar";
import { Variant, variantData } from "./data/variant";
import { addUiThemeOverrides } from "#app/ui/ui-theme";
import PokemonData from "#app/system/pokemon-data";
import { Nature } from "#app/data/nature";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
import { FormChangePhase } from "#app/phases/form-change-phase";
import { getTypeRgb } from "#app/data/type";
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
import CharSprite from "#app/ui/char-sprite";
import DamageNumberHandler from "#app/field/damage-number-handler";
import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "#app/scene-base";
import CandyBar from "#app/ui/candy-bar";
import { Variant, variantData } from "#app/data/variant";
import { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides";
import { InputsController } from "./inputs-controller";
import { UiInputs } from "./ui-inputs";
import { NewArenaEvent } from "./events/battle-scene";
import { ArenaFlyout } from "./ui/arena-flyout";
import { InputsController } from "#app/inputs-controller";
import { UiInputs } from "#app/ui-inputs";
import { NewArenaEvent } from "#app/events/battle-scene";
import { ArenaFlyout } from "#app/ui/arena-flyout";
import { EaseType } from "#enums/ease-type";
import { BattleSpec } from "#enums/battle-spec";
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 i18next from "i18next";
import { TrainerType } from "#enums/trainer-type";
import { battleSpecDialogue } from "./data/dialogue";
import { LoadingScene } from "./loading-scene";
import { LevelCapPhase } from "./phases/level-cap-phase";
import { LoginPhase } from "./phases/login-phase";
import { MessagePhase } from "./phases/message-phase";
import { MovePhase } from "./phases/move-phase";
import { NewBiomeEncounterPhase } from "./phases/new-biome-encounter-phase";
import { NextEncounterPhase } from "./phases/next-encounter-phase";
import { PokemonAnimPhase } from "./phases/pokemon-anim-phase";
import { QuietFormChangePhase } from "./phases/quiet-form-change-phase";
import { ReturnPhase } from "./phases/return-phase";
import { SelectBiomePhase } from "./phases/select-biome-phase";
import { ShowTrainerPhase } from "./phases/show-trainer-phase";
import { SummonPhase } from "./phases/summon-phase";
import { SwitchPhase } from "./phases/switch-phase";
import { TitlePhase } from "./phases/title-phase";
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase";
import { TurnInitPhase } from "./phases/turn-init-phase";
import { ShopCursorTarget } from "./enums/shop-cursor-target";
import MysteryEncounter from "./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 { battleSpecDialogue } from "#app/data/dialogue";
import { LoadingScene } from "#app/loading-scene";
import { LevelCapPhase } from "#app/phases/level-cap-phase";
import { LoginPhase } from "#app/phases/login-phase";
import { MessagePhase } from "#app/phases/message-phase";
import { MovePhase } from "#app/phases/move-phase";
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
import { ReturnPhase } from "#app/phases/return-phase";
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
import { SummonPhase } from "#app/phases/summon-phase";
import { SwitchPhase } from "#app/phases/switch-phase";
import { TitlePhase } from "#app/phases/title-phase";
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
import { TurnInitPhase } from "#app/phases/turn-init-phase";
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
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 "#app/data/mystery-encounters/mystery-encounters";
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
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 { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
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";
@ -889,6 +889,9 @@ export default class BattleScene extends SceneBase {
}
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource);
if (Overrides.OPP_FUSION_OVERRIDE) {
pokemon.generateFusionSpecies();
}
overrideModifiers(this, false);
overrideHeldItems(this, pokemon, false);
@ -1040,10 +1043,6 @@ export default class BattleScene extends SceneBase {
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.score = 0;
@ -1078,6 +1077,12 @@ export default class BattleScene extends SceneBase {
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
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.setVisible(false);
@ -1201,7 +1206,7 @@ export default class BattleScene extends SceneBase {
// Check for mystery encounter
// 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;
// Reset base spawn weight
this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
@ -1259,7 +1264,7 @@ export default class BattleScene extends SceneBase {
const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
const resetArenaState = isNewBiome || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
const resetArenaState = isNewBiome || [ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.currentBattle.battleType) || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
this.trySpreadPokerus();
if (!isNewBiome && (newWaveIndex % 10) === 5) {
@ -1756,14 +1761,14 @@ export default class BattleScene extends SceneBase {
if (fromArenaPool) {
return this.arena.randomSpecies(waveIndex, level, undefined, getPartyLuckValue(this.party));
}
const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => {
const filteredSpecies = speciesFilter ? [ ...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => {
if (!filterAllEvolutions) {
while (pokemonPrevolutions.hasOwnProperty(s.speciesId)) {
s = getPokemonSpecies(pokemonPrevolutions[s.speciesId]);
}
}
return s;
}))] : allSpecies.filter(s => s.isCatchable());
})) ] : allSpecies.filter(s => s.isCatchable());
return filteredSpecies[Utils.randSeedInt(filteredSpecies.length)];
}
@ -1883,14 +1888,14 @@ export default class BattleScene extends SceneBase {
case "battle_anims":
case "cry":
if (soundDetails[1].startsWith("PRSFX- ")) {
sound.setVolume(this.masterVolume*this.fieldVolume*0.5);
sound.setVolume(this.masterVolume * this.fieldVolume * 0.5);
} else {
sound.setVolume(this.masterVolume*this.fieldVolume);
sound.setVolume(this.masterVolume * this.fieldVolume);
}
break;
case "se":
case "ui":
sound.setVolume(this.masterVolume*this.seVolume);
sound.setVolume(this.masterVolume * this.seVolume);
}
}
}
@ -2219,7 +2224,7 @@ export default class BattleScene extends SceneBase {
*
*/
pushConditionalPhase(phase: Phase, condition: () => boolean): void {
this.conditionalQueue.push([condition, phase]);
this.conditionalQueue.push([ condition, phase ]);
}
/**
@ -2354,17 +2359,6 @@ export default class BattleScene extends SceneBase {
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()
* @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 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)) {
this.playSound(soundName);
@ -2470,7 +2467,7 @@ export default class BattleScene extends SceneBase {
for (const p in this.party) {
const pokemon = this.party[p];
const args: any[] = [ pokemon ];
const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
@ -2483,8 +2480,8 @@ export default class BattleScene extends SceneBase {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
}
if (modifier.shouldApply(args)) {
const result = modifier.apply(args);
if (modifier.shouldApply(pokemon, ...args)) {
const result = modifier.apply(pokemon, ...args);
if (result instanceof Promise) {
modifierPromises.push(result.then(s => success ||= s));
} else {
@ -2493,11 +2490,11 @@ 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 {
const args = [ this ];
if (modifier.shouldApply(args)) {
const result = modifier.apply(args);
if (modifier.shouldApply(...args)) {
const result = modifier.apply(...args);
if (result instanceof Promise) {
return result.then(success => resolve(success));
} else {
@ -2519,7 +2516,10 @@ export default class BattleScene extends SceneBase {
}
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) {
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) {
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 {
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 {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier);
clearEnemyHeldItemModifiers(pokemon?: Pokemon): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon));
for (const m of modifiersToRemove) {
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
}
@ -2753,7 +2754,10 @@ export default class BattleScene extends SceneBase {
if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1);
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;
}
@ -2771,38 +2775,66 @@ export default class BattleScene extends SceneBase {
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 {
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(() => {
const shuffleModifiers = mods => {
if (mods.length < 1) {
return mods;
}
const rand = Utils.randSeedInt(mods.length);
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ];
};
modifiers = shuffleModifiers(modifiers);
}, scene.currentBattle.turn << 4, scene.waveSeed);
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);
}
applyModifiersInternal(modifiers: PersistentModifier[], player: boolean, args: any[]): PersistentModifier[] {
const appliedModifiers: PersistentModifier[] = [];
/** Helper function to apply all passed modifiers */
applyModifiersInternal<T extends PersistentModifier>(modifiers: T[], player: boolean, args: Parameters<T["apply"]>): T[] {
const appliedModifiers: T[] = [];
for (const modifier of modifiers) {
if (modifier.apply(args)) {
if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
appliedModifiers.push(modifier);
}
@ -2811,10 +2843,17 @@ export default class BattleScene extends SceneBase {
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) {
if (modifier.apply(args)) {
if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
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)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv);
@ -2893,7 +2932,7 @@ export default class BattleScene extends SceneBase {
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)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher);
@ -2932,7 +2971,7 @@ export default class BattleScene extends SceneBase {
keys.push(p.getBattleSpriteKey(true, true));
keys.push("cry/" + p.species.getCryKey(p.formIndex));
if (p.fusionSpecies) {
keys.push("cry/"+p.fusionSpecies.getCryKey(p.fusionFormIndex));
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
}
});
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
@ -2941,7 +2980,7 @@ export default class BattleScene extends SceneBase {
keys.push(p.getSpriteKey(true));
keys.push("cry/" + p.species.getCryKey(p.formIndex));
if (p.fusionSpecies) {
keys.push("cry/"+p.fusionSpecies.getCryKey(p.fusionFormIndex));
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
}
});
return keys;
@ -3002,7 +3041,7 @@ export default class BattleScene extends SceneBase {
if (participantIds.size > 0) {
if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
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);
}
for (const partyMember of nonFaintedPartyMembers) {
@ -3089,22 +3128,18 @@ export default class BattleScene extends SceneBase {
* @param sessionDataEncounterType
*/
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 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
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
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)
// 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 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!;
@ -3127,13 +3162,17 @@ export default class BattleScene extends SceneBase {
/**
* Loads or generates a mystery encounter
* @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
*/
getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter {
getMysteryEncounter(encounterType?: MysteryEncounterType, canBypass?: boolean): MysteryEncounter {
// Loading override or session encounter
let encounter: MysteryEncounter | null;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
} else if (canBypass) {
encounter = allMysteryEncounters[encounterType ?? -1];
return encounter;
} else {
encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null;
}
@ -3159,7 +3198,7 @@ export default class BattleScene extends SceneBase {
}
// See Enum values for base tier weights
const tierWeights = [MysteryEncounterTier.COMMON, MysteryEncounterTier.GREAT, MysteryEncounterTier.ULTRA, MysteryEncounterTier.ROGUE];
const tierWeights = [ MysteryEncounterTier.COMMON, MysteryEncounterTier.GREAT, MysteryEncounterTier.ULTRA, MysteryEncounterTier.ROGUE ];
// Adjust tier weights by previously encountered events to lower odds of only Common/Great in run
this.mysteryEncounterSaveData.encounteredEvents.forEach(seenEncounterData => {

View File

@ -213,7 +213,7 @@ export default class Battle {
getBgmOverride(scene: BattleScene): string | null {
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()
// Should not use any BGM overrides before swapping from DEFAULT mode
return null;
@ -409,6 +409,13 @@ export default class Battle {
scene.rngSeedOverride = tempSeedOverride;
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 {
@ -490,7 +497,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
}
/* 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]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
@ -520,34 +527,34 @@ export const classicFixedBattles: FixedBattleConfigs = {
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ 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 ], true)),
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ 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 ], true)),
[64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ 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 ], true)),
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true)),
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true)),
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ 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 ], true)),
[114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true, 1)),
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true, 1)),
[ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])),
[184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182)
@ -560,5 +567,5 @@ export const classicFixedBattles: FixedBattleConfigs = {
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])),
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false })
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false })
};

View File

@ -1,5 +1,5 @@
import {Button} from "#enums/buttons";
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
import { Button } from "#enums/buttons";
import { SettingKeyboard } from "#app/system/settings/settings-keyboard";
const cfg_keyboard_qwerty = {
padID: "default",

View File

@ -1,4 +1,4 @@
import {Device} from "#enums/devices";
import { Device } from "#enums/devices";
/**
* Retrieves the key associated with the specified keycode from the mapping.

View File

@ -1,5 +1,5 @@
import {SettingGamepad} from "../../system/settings/settings-gamepad";
import {Button} from "#enums/buttons";
import { SettingGamepad } from "../../system/settings/settings-gamepad";
import { Button } from "#enums/buttons";
/**
* Dualshock mapping

View File

@ -1,5 +1,5 @@
import {SettingGamepad} from "../../system/settings/settings-gamepad";
import {Button} from "#enums/buttons";
import { SettingGamepad } from "../../system/settings/settings-gamepad";
import { Button } from "#enums/buttons";
/**
* Generic pad mapping

View File

@ -1,5 +1,5 @@
import {SettingGamepad} from "#app/system/settings/settings-gamepad";
import {Button} from "#enums/buttons";
import { SettingGamepad } from "#app/system/settings/settings-gamepad";
import { Button } from "#enums/buttons";
/**
* Nintendo Pro Controller mapping

View File

@ -1,5 +1,5 @@
import {SettingGamepad} from "../../system/settings/settings-gamepad";
import {Button} from "#enums/buttons";
import { SettingGamepad } from "../../system/settings/settings-gamepad";
import { Button } from "#enums/buttons";
/**
* 081f-e401 - UnlicensedSNES

View File

@ -1,5 +1,5 @@
import {SettingGamepad} from "../../system/settings/settings-gamepad";
import {Button} from "#enums/buttons";
import { SettingGamepad } from "../../system/settings/settings-gamepad";
import { Button } from "#enums/buttons";
/**
* Generic pad mapping

View File

@ -4,7 +4,7 @@ import { Constructor } from "#app/utils";
import * as Utils from "../utils";
import { getPokemonNameWithAffix } from "../messages";
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 { 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";
@ -123,6 +123,7 @@ export class Ability implements Localizable {
type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
type AbAttrCondition = (pokemon: Pokemon) => boolean;
// TODO: Can this be improved?
type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
@ -161,7 +162,7 @@ export class BlockRecoilDamageAttr extends AbAttr {
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) {
return i18next.t("abilityTriggers:blockRecoilDamage", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName});
return i18next.t("abilityTriggers:blockRecoilDamage", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName });
}
}
@ -518,6 +519,7 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
if (pokemon.isFullHp() && typeMultiplier.value > 0.5) {
typeMultiplier.value = 0.5;
pokemon.turnData.moveEffectiveness = 0.5;
return true;
}
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 {
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const attackPriority = new Utils.IntegerHolder(move.priority);
@ -679,15 +634,15 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr {
* Examples include: Absorb, Draining Kiss, Bitter Blade, etc.
* Also displays a message to show this ability was activated.
* @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 move {@linkcode PokemonMove} that is being used
* @param hitResult N/A
* @args N/A
* @param _hitResult N/A
* @param _args N/A
* @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 {
if (move.hasAttr(HitHealAttr)) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) {
pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) }));
}
@ -714,8 +669,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr {
this.allOthers = allOthers;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) {
return true;
}
@ -752,13 +707,13 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr {
this.selfTarget = selfTarget;
}
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);
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate);
const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1];
const damageReceived = lastAttackReceived?.damage || 0;
if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) {
if (!simulated ) {
if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages));
}
return true;
@ -779,8 +734,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag;
if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) {
if (!simulated) {
@ -803,8 +758,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (!pokemon.getTag(this.tagType) && !simulated) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
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 {
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) {
return true;
}
@ -832,7 +787,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
return false;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendTypeChange", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
@ -850,8 +805,8 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
this.terrainType = terrainType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) {
return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined);
} else {
@ -874,8 +829,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
this.effects = effects;
}
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)) {
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) && !move.hitsSubstitute(attacker, pokemon)) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
if (simulated) {
return attacker.canSetStatus(effect, true, false, pokemon);
@ -914,8 +870,8 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
this.turnCount = turnCount;
}
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) {
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 && !move.hitsSubstitute(attacker, pokemon)) {
if (simulated) {
return attacker.canAddTag(this.tagType);
} else {
@ -938,7 +894,11 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr {
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) {
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;
}
getCondition(): AbAttrCondition {
override getCondition(): AbAttrCondition {
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;
}
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)) {
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) && !move.hitsSubstitute(attacker, pokemon)) {
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio));
return true;
@ -970,7 +931,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
return false;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendContactDamage", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName
@ -993,8 +954,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
this.turns = turns;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
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) && !move.hitsSubstitute(attacker, pokemon)) {
if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) {
return false;
} else {
@ -1008,24 +969,24 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
return false;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return i18next.t("abilityTriggers:perishBody", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName});
override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName });
}
}
export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
private weatherType: WeatherType;
protected condition: PokemonDefendCondition | null;
protected condition?: PokemonDefendCondition;
constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) {
super();
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 {
if (this.condition !== null && !this.condition(pokemon, attacker, move)) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
if (this.condition && !this.condition(pokemon, attacker, move) || move.hitsSubstitute(attacker, pokemon)) {
return false;
}
if (!pokemon.scene.arena.weather?.isImmutable()) {
@ -1044,8 +1005,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
super();
}
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)) {
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) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) {
const tempAbilityId = attacker.getAbility().id;
attacker.summonData.ability = pokemon.getAbility().id;
@ -1057,7 +1019,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
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) });
}
}
@ -1070,8 +1032,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
this.ability = ability;
}
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)) {
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) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) {
attacker.summonData.ability = this.ability;
}
@ -1082,7 +1045,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
return false;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:postDefendAbilityGive", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName
@ -1101,8 +1064,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
this.chance = chance;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (attacker.getTag(BattlerTagType.DISABLED) === null) {
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean {
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 (simulated) {
return true;
@ -1313,7 +1276,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
if (pokemon.getTypes().some((t) => t !== moveType)) {
if (!simulated) {
this.moveType = moveType;
pokemon.summonData.types = [moveType];
pokemon.summonData.types = [ moveType ];
pokemon.updateInfo();
}
@ -1769,17 +1732,17 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
}
export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition | null;
private condition?: PokemonDefendCondition;
constructor(condition?: PokemonDefendCondition) {
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 => {
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);
if (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.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();
@ -2859,7 +2822,7 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) {
super();
this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [immuneTagTypes];
this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [ immuneTagTypes ];
}
applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean {
@ -3144,17 +3107,17 @@ function getAnticipationCondition(): AbAttrCondition {
// edge case for hidden power, type is computed
if (move.getMove().id === Moves.HIDDEN_POWER) {
const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1)
+(opponent.ivs[Stat.ATK] & 1) * 2
+(opponent.ivs[Stat.DEF] & 1) * 4
+(opponent.ivs[Stat.SPD] & 1) * 8
+(opponent.ivs[Stat.SPATK] & 1) * 16
+(opponent.ivs[Stat.SPDEF] & 1) * 32) * 15/63);
+ (opponent.ivs[Stat.ATK] & 1) * 2
+ (opponent.ivs[Stat.DEF] & 1) * 4
+ (opponent.ivs[Stat.SPD] & 1) * 8
+ (opponent.ivs[Stat.SPATK] & 1) * 16
+ (opponent.ivs[Stat.SPDEF] & 1) * 32) * 15 / 63);
const type = [
Type.FIGHTING, Type.FLYING, Type.POISON, Type.GROUND,
Type.ROCK, Type.BUG, Type.GHOST, Type.STEEL,
Type.FIRE, Type.WATER, Type.GRASS, Type.ELECTRIC,
Type.PSYCHIC, Type.ICE, Type.DRAGON, Type.DARK][iv_val];
Type.PSYCHIC, Type.ICE, Type.DRAGON, Type.DARK ][iv_val];
if (pokemon.getAttackTypeEffectiveness(type, opponent) >= 2) {
return true;
@ -3199,12 +3162,12 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
} else if (move?.getMove().power === -1) {
movePower = 80;
} else {
movePower = move!.getMove().power; // TODO: is this bang correct?
movePower = move?.getMove().power ?? 0;
}
if (movePower > maxPowerSeen) {
maxPowerSeen = movePower;
maxMove = move!.getName(); // TODO: is this bang correct?
maxMove = move?.getName() ?? "";
}
}
}
@ -3689,7 +3652,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
if (!simulated) {
opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)}));
pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }));
}
hadEffect = true;
}
@ -3800,8 +3763,8 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
*/
applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean | Promise<boolean> {
// List of tags that prevent the Dancer from replicating the move
const forbiddenTags = [BattlerTagType.FLYING, BattlerTagType.UNDERWATER,
BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN];
const forbiddenTags = [ BattlerTagType.FLYING, BattlerTagType.UNDERWATER,
BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN ];
// The move to replicate cannot come from the Dancer
if (source.getBattlerIndex() !== dancer.getBattlerIndex()
&& !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))) {
@ -3812,7 +3775,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true, true));
} else if (move.getMove() instanceof SelfStatusMove) {
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true, true));
dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [ dancer.getBattlerIndex() ], move, true, true));
}
}
return true;
@ -3829,9 +3792,9 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
*/
getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]) : BattlerIndex[] {
if (dancer.isPlayer()) {
return source.isPlayer() ? targets : [source.getBattlerIndex()];
return source.isPlayer() ? targets : [ source.getBattlerIndex() ];
}
return source.isPlayer() ? [source.getBattlerIndex()] : targets;
return source.isPlayer() ? [ source.getBattlerIndex() ] : targets;
}
}
@ -4521,7 +4484,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC
export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
private multiplier: number;
private tagType: BattlerTagType;
private recoilDamageFunc: ((pokemon: Pokemon) => number) | undefined;
private recoilDamageFunc?: ((pokemon: Pokemon) => number);
private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string;
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.
* 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 {boolean} passive n/a
* @param {Pokemon} attacker The attacking Pokémon.
* @param {PokemonMove} move The move being used.
* @param {Utils.BooleanHolder} cancelled n/a
* @param {any[]} args Additional arguments.
* @returns {boolean} Whether the immunity was applied.
* @param pokemon The Pokémon with the ability.
* @param _passive n/a
* @param attacker The attacking Pokémon.
* @param move The move being used.
* @param _cancelled n/a
* @param args Additional arguments.
* @returns `true` if the immunity was applied.
*/
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) {
if (!simulated) {
(args[0] as Utils.NumberHolder).value = this.multiplier;
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.
* @param {Pokemon} pokemon The Pokémon with the ability.
* @param {string} abilityName The name of the ability.
* @param {...any} args n/a
* @returns {string} The trigger message.
* @param pokemon The Pokémon with the ability.
* @param abilityName The name of the ability.
* @param _args n/a
* @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);
}
}
@ -4606,7 +4569,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
const turnCommand =
pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
const isCommandFight = turnCommand?.command === Command.FIGHT;
const move = turnCommand?.move?.move ?allMoves[turnCommand.move.move] : null;
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
if (isCommandFight && isDamageMove) {
@ -4619,7 +4582,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return i18next.t("abilityTriggers:quickDraw", {pokemonName: getPokemonNameWithAffix(pokemon)});
return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) });
}
}
@ -4667,8 +4630,8 @@ async function applyAbAttrsInternal<TAttr extends AbAttr>(
simulated: boolean = false,
messages: string[] = [],
) {
for (const passive of [false, true]) {
if (!pokemon?.canApplyAbility(passive)) {
for (const passive of [ false, true ]) {
if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
continue;
}
@ -4917,7 +4880,7 @@ export function initAbilities() {
.attr(TypeImmunityHealAbAttr, Type.WATER)
.ignorable(),
new Ability(Abilities.OBLIVIOUS, 3)
.attr(BattlerTagImmunityAbAttr, [BattlerTagType.INFATUATED, BattlerTagType.TAUNT])
.attr(BattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT ])
.attr(IntimidateImmunityAbAttr)
.ignorable(),
new Ability(Abilities.CLOUD_NINE, 3)
@ -5062,10 +5025,10 @@ export function initAbilities() {
new Ability(Abilities.CUTE_CHARM, 3)
.attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED),
new Ability(Abilities.PLUS, 3)
.conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
.ignorable(),
new Ability(Abilities.MINUS, 3)
.conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
.ignorable(),
new Ability(Abilities.FORECAST, 3)
.attr(UncopiableAbilityAbAttr)
@ -5183,7 +5146,7 @@ export function initAbilities() {
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5),
new Ability(Abilities.NORMALIZE, 4)
.attr(MoveTypeChangeAbAttr, Type.NORMAL, 1.2, (user, target, move) => {
return ![Moves.HIDDEN_POWER, Moves.WEATHER_BALL, Moves.NATURAL_GIFT, Moves.JUDGMENT, Moves.TECHNO_BLAST].includes(move.id);
return ![ Moves.HIDDEN_POWER, Moves.WEATHER_BALL, Moves.NATURAL_GIFT, Moves.JUDGMENT, Moves.TECHNO_BLAST ].includes(move.id);
}),
new Ability(Abilities.SNIPER, 4)
.attr(MultCritAbAttr, 1.5),
@ -5210,8 +5173,7 @@ export function initAbilities() {
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(MoveAbilityBypassAbAttr),
new Ability(Abilities.SUPER_LUCK, 4)
.attr(BonusCritAbAttr)
.partial(),
.attr(BonusCritAbAttr),
new Ability(Abilities.AFTERMATH, 4)
.attr(PostFaintContactDamageAbAttr, 4)
.bypassFaint(),
@ -5223,15 +5185,14 @@ export function initAbilities() {
.attr(IgnoreOpponentStatStagesAbAttr)
.ignorable(),
new Ability(Abilities.TINTED_LENS, 4)
//@ts-ignore
.attr(DamageBoostAbAttr, 2, (user, target, move) => target?.getMoveEffectiveness(user, move) <= 0.5), // TODO: fix TS issues
.attr(DamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5),
new Ability(Abilities.FILTER, 4)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75)
.ignorable(),
new Ability(Abilities.SLOW_START, 4)
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5),
new Ability(Abilities.SCRAPPY, 4)
.attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [Type.NORMAL, Type.FIGHTING])
.attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [ Type.NORMAL, Type.FIGHTING ])
.attr(IntimidateImmunityAbAttr),
new Ability(Abilities.STORM_DRAIN, 4)
.attr(RedirectTypeMoveAbAttr, Type.WATER)
@ -5272,7 +5233,7 @@ export function initAbilities() {
.attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT))
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.SHEER_FORCE, 5)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461/4096)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096)
.attr(MoveEffectChanceMultiplierAbAttr, 0)
.partial(),
new Ability(Abilities.CONTRARY, 5)
@ -5281,7 +5242,7 @@ export function initAbilities() {
new Ability(Abilities.UNNERVE, 5)
.attr(PreventBerryUseAbAttr),
new Ability(Abilities.DEFIANT, 5)
.attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [Stat.ATK], 2),
.attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [ Stat.ATK ], 2),
new Ability(Abilities.DEFEATIST, 5)
.attr(StatMultiplierAbAttr, Stat.ATK, 0.5)
.attr(StatMultiplierAbAttr, Stat.SPATK, 0.5)
@ -5342,8 +5303,9 @@ export function initAbilities() {
.attr(WonderSkinAbAttr)
.ignorable(),
new Ability(Abilities.ANALYTIC, 5)
//@ts-ignore
.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
.attr(MovePowerBoostAbAttr, (user, target, move) =>
!!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)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
@ -5366,7 +5328,7 @@ export function initAbilities() {
return move.category !== MoveCategory.STATUS
&& (moveType === Type.DARK || moveType === Type.BUG || moveType === Type.GHOST);
}, Stat.SPD, 1)
.attr(PostIntimidateStatStageChangeAbAttr, [Stat.SPD], 1),
.attr(PostIntimidateStatStageChangeAbAttr, [ Stat.SPD ], 1),
new Ability(Abilities.MAGIC_BOUNCE, 5)
.ignorable()
.unimplemented(),
@ -5403,12 +5365,12 @@ export function initAbilities() {
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTeravolt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(MoveAbilityBypassAbAttr),
new Ability(Abilities.AROMA_VEIL, 6)
.attr(UserFieldBattlerTagImmunityAbAttr, [BattlerTagType.INFATUATED, BattlerTagType.TAUNT, BattlerTagType.DISABLED, BattlerTagType.TORMENT, BattlerTagType.HEAL_BLOCK]),
.attr(UserFieldBattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT, BattlerTagType.DISABLED, BattlerTagType.TORMENT, BattlerTagType.HEAL_BLOCK ]),
new Ability(Abilities.FLOWER_VEIL, 6)
.ignorable()
.unimplemented(),
new Ability(Abilities.CHEEK_POUCH, 6)
.attr(HealFromBerryUseAbAttr, 1/3),
.attr(HealFromBerryUseAbAttr, 1 / 3),
new Ability(Abilities.PROTEAN, 6)
.attr(PokemonTypeChangeAbAttr),
//.condition((p) => !p.summonData?.abilitiesApplied.includes(Abilities.PROTEAN)), //Gen 9 Implementation
@ -5421,7 +5383,7 @@ export function initAbilities() {
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.BALLBOMB_MOVE))
.ignorable(),
new Ability(Abilities.COMPETITIVE, 6)
.attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [Stat.SPATK], 2),
.attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [ Stat.SPATK ], 2),
new Ability(Abilities.STRONG_JAW, 6)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
new Ability(Abilities.REFRIGERATE, 6)
@ -5508,8 +5470,7 @@ export function initAbilities() {
.bypassFaint()
.partial(),
new Ability(Abilities.STAKEOUT, 7)
//@ts-ignore
.attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), // TODO: fix TS issues
.attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2),
new Ability(Abilities.WATER_BUBBLE, 7)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(MoveTypePowerBoostAbAttr, Type.WATER, 2)
@ -5518,7 +5479,7 @@ export function initAbilities() {
new Ability(Abilities.STEELWORKER, 7)
.attr(MoveTypePowerBoostAbAttr, Type.STEEL),
new Ability(Abilities.BERSERK, 7)
.attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [Stat.SPATK], 1)
.attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.SPATK ], 1)
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.SLUSH_RUSH, 7)
.attr(StatMultiplierAbAttr, Stat.SPD, 2)
@ -5550,7 +5511,8 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr)
// Add BattlerTagType.DISGUISE if the pokemon is in its disguised form
.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) => Utils.toDmgValue(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
@ -5575,7 +5537,7 @@ export function initAbilities() {
.bypassFaint()
.partial(),
new Ability(Abilities.CORROSION, 7) // TODO: Test Corrosion against Magic Bounce once it is implemented
.attr(IgnoreTypeStatusEffectImmunityAbAttr, [StatusEffect.POISON, StatusEffect.TOXIC], [Type.STEEL, Type.POISON])
.attr(IgnoreTypeStatusEffectImmunityAbAttr, [ StatusEffect.POISON, StatusEffect.TOXIC ], [ Type.STEEL, Type.POISON ])
.partial(),
new Ability(Abilities.COMATOSE, 7)
.attr(UncopiableAbilityAbAttr)
@ -5592,7 +5554,7 @@ export function initAbilities() {
new Ability(Abilities.DANCER, 7)
.attr(PostDancingMoveAbAttr),
new Ability(Abilities.BATTERY, 7)
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL], 1.3),
.attr(AllyMoveCategoryPowerBoostAbAttr, [ MoveCategory.SPECIAL ], 1.3),
new Ability(Abilities.FLUFFY, 7)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE, 2)
@ -5647,8 +5609,7 @@ export function initAbilities() {
new Ability(Abilities.PRISM_ARMOR, 7)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75),
new Ability(Abilities.NEUROFORCE, 7)
//@ts-ignore
.attr(MovePowerBoostAbAttr, (user, target, move) => target?.getMoveEffectiveness(user, move) >= 2, 1.25), // TODO: fix TS issues
.attr(MovePowerBoostAbAttr, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) >= 2, 1.25),
new Ability(Abilities.INTREPID_SWORD, 8)
.attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)),
@ -5669,13 +5630,19 @@ export function initAbilities() {
new Ability(Abilities.MIRROR_ARMOR, 8)
.ignorable()
.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)
.attr(UnsuppressableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(PostDefendGulpMissileAbAttr),
.bypassFaint(),
new Ability(Abilities.STALWART, 8)
.attr(BlockRedirectAbAttr),
new Ability(Abilities.STEAM_ENGINE, 8)
@ -5707,17 +5674,18 @@ export function initAbilities() {
.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
.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 }))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint()
.ignorable(),
new Ability(Abilities.POWER_SPOT, 8)
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3),
.attr(AllyMoveCategoryPowerBoostAbAttr, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ], 1.3),
new Ability(Abilities.MIMICRY, 8)
.unimplemented(),
new Ability(Abilities.SCREEN_CLEANER, 8)
.attr(PostSummonRemoveArenaTagAbAttr, [ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.REFLECT]),
.attr(PostSummonRemoveArenaTagAbAttr, [ ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.REFLECT ]),
new Ability(Abilities.STEELY_SPIRIT, 8)
.attr(UserFieldMoveTypePowerBoostAbAttr, Type.STEEL),
new Ability(Abilities.PERISH_BODY, 8)
@ -5740,10 +5708,8 @@ export function initAbilities() {
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.ignorable(),
new Ability(Abilities.HUNGER_SWITCH, 8)
//@ts-ignore
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) // TODO: fix ts-ignore
//@ts-ignore
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) // TODO: fix ts-ignore
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 1 : 0)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
@ -5802,7 +5768,7 @@ export function initAbilities() {
.attr(PostSummonStatStageChangeOnArenaAbAttr, ArenaTagType.TAILWIND)
.ignorable(),
new Ability(Abilities.GUARD_DOG, 9)
.attr(PostIntimidateStatStageChangeAbAttr, [Stat.ATK], 1, true)
.attr(PostIntimidateStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.attr(ForceSwitchOutImmunityAbAttr)
.ignorable(),
new Ability(Abilities.ROCKY_PAYLOAD, 9)
@ -5891,7 +5857,7 @@ export function initAbilities() {
.attr(PreventBypassSpeedChanceAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS)
.attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS),
new Ability(Abilities.MINDS_EYE, 9)
.attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [Type.NORMAL, Type.FIGHTING])
.attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [ Type.NORMAL, Type.FIGHTING ])
.attr(ProtectStatAbAttr, Stat.ACC)
.attr(IgnoreOpponentStatStagesAbAttr, [ Stat.EVA ])
.ignorable(),

View File

@ -19,6 +19,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
export enum ArenaTagSide {
BOTH,
@ -135,7 +136,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
*/
apply(arena: Arena, args: any[]): boolean {
if (this.weakenedCategories.includes((args[0] as MoveCategory))) {
(args[2] as Utils.NumberHolder).value = (args[1] as boolean) ? 2732/4096 : 0.5;
(args[2] as Utils.NumberHolder).value = (args[1] as boolean) ? 2732 / 4096 : 0.5;
return true;
}
return false;
@ -148,7 +149,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
*/
class ReflectTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [MoveCategory.PHYSICAL]);
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [ MoveCategory.PHYSICAL ]);
}
onAdd(arena: Arena, quiet: boolean = false): void {
@ -164,7 +165,7 @@ class ReflectTag extends WeakenMoveScreenTag {
*/
class LightScreenTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [MoveCategory.SPECIAL]);
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [ MoveCategory.SPECIAL ]);
}
onAdd(arena: Arena, quiet: boolean = false): void {
@ -180,7 +181,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
*/
class AuroraVeilTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL]);
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ]);
}
onAdd(arena: Arena, quiet: boolean = false): void {
@ -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.
*/
@ -955,7 +990,7 @@ class ImprisonTag extends ArenaTrapTag {
party?.forEach((p: PlayerPokemon | EnemyPokemon ) => {
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
});
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", {pokemonNameWithAffix: getPokemonNameWithAffix(this.source)}));
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(this.source) }));
}
}
@ -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 {
switch (tagType) {
case ArenaTagType.MIST:
@ -1010,6 +1120,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT:
return new WaterSportTag(turnCount, sourceId);
case ArenaTagType.ION_DELUGE:
return new IonDelugeTag(sourceMove);
case ArenaTagType.SPIKES:
return new SpikesTag(sourceId, side);
case ArenaTagType.TOXIC_SPIKES:
@ -1041,6 +1153,12 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new SafeguardTag(turnCount, sourceId, side);
case ArenaTagType.IMPRISON:
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:
return null;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
import { allMoves } from "./move";
import * as Utils from "../utils";
import { allMoves } from "#app/data/move";
import * as Utils from "#app/utils";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
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.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.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.SWORDS_DANCE, Moves.ATTACK_ORDER, Moves.NOXIOUS_TORQUE ],
[Species.PIDGEY]: [ Moves.WILDBOLT_STORM, Moves.SANDSEAR_STORM, Moves.CALM_MIND, Moves.BOOMBURST ],
[Species.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.NOXIOUS_TORQUE, Moves.ATTACK_ORDER, Moves.VICTORY_DANCE ],
[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.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 ],
@ -21,46 +21,46 @@ export const speciesEggMoves = {
[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.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.MEOWTH]: [ Moves.COVET, Moves.SWORDS_DANCE, Moves.DOUBLE_KICK, Moves.TAIL_SLAP ],
[Species.PSYDUCK]: [ Moves.SPLISHY_SPLASH, Moves.AQUA_STEP, Moves.AURA_SPHERE, Moves.MYSTICAL_POWER ],
[Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.PLAY_ROUGH, Moves.METEOR_MASH, Moves.NO_RETREAT ],
[Species.MEOWTH]: [ Moves.HEART_STAMP, Moves.SWORDS_DANCE, Moves.SIZZLY_SLIDE, Moves.TAIL_SLAP ],
[Species.PSYDUCK]: [ Moves.FROST_BREATH, Moves.AQUA_STEP, Moves.MYSTICAL_POWER, Moves.BOUNCY_BUBBLE ],
[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.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.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.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.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.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.ROOST, Moves.VICTORY_DANCE ],
[Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.FLAMETHROWER, Moves.ICE_BEAM, Moves.THUNDERCLAP ],
[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.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.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.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.KRABBY]: [ Moves.FIRE_LASH, Moves.PLAY_ROUGH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ],
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
[Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.JET_PUNCH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ],
[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.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.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.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.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.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.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.BLAZING_TORQUE, Moves.LIQUIDATION, Moves.COMBAT_TORQUE ],
[Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ],
[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.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.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ],
[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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.DUNSPARCE]: [ Moves.BODY_SLAM, Moves.MAGICAL_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ],
[Species.PINECO]: [ Moves.METAL_BURST, Moves.SHORE_UP, Moves.BODY_PRESS, Moves.DIAMOND_STORM ],
[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.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.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.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.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.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.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.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.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.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.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 ],
@ -125,10 +125,10 @@ export const speciesEggMoves = {
[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.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.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.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 ],
@ -154,7 +154,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -168,7 +168,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -177,7 +177,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -189,13 +189,13 @@ export const speciesEggMoves = {
[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.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.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.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.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.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 ],
@ -206,7 +206,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -223,7 +223,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -232,7 +232,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -243,7 +243,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -274,18 +274,18 @@ export const speciesEggMoves = {
[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.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.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.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.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.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.YAMASK]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ],
[Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ],
[Species.YAMASK]: [ Moves.STRENGTH_SAP, Moves.GLARE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ],
[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.TRUBBISH]: [ Moves.COIL, Moves.RECOVER, Moves.DIRE_CLAW, Moves.GIGATON_HAMMER ],
[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.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.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.TAILWIND, Moves.ZIPPY_ZAP ],
[Species.KARRABLAST]: [ Moves.LEECH_LIFE, Moves.HEAL_ORDER, Moves.HIGH_HORSEPOWER, Moves.DOUBLE_IRON_BASH ],
[Species.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.AURA_WHEEL ],
[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.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
[Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.TOXIC, Moves.GLITZY_GLOW ],
[Species.JOLTIK]: [ Moves.THUNDER, Moves.PARABOLIC_CHARGE, Moves.EARTH_POWER, Moves.QUIVER_DANCE ],
[Species.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.BUZZY_BUZZ, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
[Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ],
[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.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.ELGYEM]: [ Moves.LUSTER_PURGE, Moves.TRICK_ROOM, Moves.AURA_SPHERE, Moves.TAIL_GLOW ],
[Species.TYNAMO]: [ Moves.SCALD, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.AURA_WHEEL ],
[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.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.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 ],
@ -315,24 +315,24 @@ export const speciesEggMoves = {
[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.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.VULLABY]: [ Moves.FOUL_PLAY, Moves.BODY_PRESS, Moves.ROOST, Moves.RUINATION ],
[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.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.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.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.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.ZEKROM]: [ Moves.DRAGON_DANCE, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ],
[Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ],
[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.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.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.CHESPIN]: [ Moves.BODY_PRESS, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ],
[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.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.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COVET, Moves.MULTI_ATTACK ],
[Species.ESPURR]: [ Moves.GLARE, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PSYSTRIKE ],
[Species.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COMBAT_TORQUE, Moves.MULTI_ATTACK ],
[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.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.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.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 ],
@ -360,15 +360,15 @@ export const speciesEggMoves = {
[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.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.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.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.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.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 ],
@ -379,7 +379,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -394,31 +394,31 @@ export const speciesEggMoves = {
[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.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.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.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.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.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.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.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_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.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.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.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.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.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 ],
@ -428,11 +428,11 @@ export const speciesEggMoves = {
[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.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.SKWOVET]: [ Moves.KNOCK_OFF, Moves.SLACK_OFF, Moves.BODY_PRESS, Moves.POPULATION_BOMB ],
[Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.ENERGY_BALL, Moves.NASTY_PLOT ],
[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.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.EXPANDING_FORCE, Moves.SPORE, Moves.TAIL_GLOW ],
[Species.NICKIT]: [ Moves.BADDY_BAD, Moves.BURNING_JEALOUSY, Moves.SPARKLY_SWIRL, Moves.FIERY_WRATH ],
[Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.LUSTER_PURGE, Moves.SLEEP_POWDER, Moves.TAIL_GLOW ],
[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.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 ],
@ -446,25 +446,25 @@ export const speciesEggMoves = {
[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.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.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.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.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.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.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.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.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.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.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 ],
@ -482,7 +482,7 @@ export const speciesEggMoves = {
[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.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.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ],
[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.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.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.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 ],
@ -509,7 +509,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -521,7 +521,7 @@ export const speciesEggMoves = {
[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_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.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 ],
@ -540,7 +540,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -566,7 +566,7 @@ export const speciesEggMoves = {
[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_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_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 ],

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 { PokeballType } from "./pokeball";
import Pokemon from "../field/pokemon";
import { Gender } from "#app/data/gender";
import { PokeballType } from "#app/data/pokeball";
import Pokemon from "#app/field/pokemon";
import { Stat } from "#enums/stat";
import { Type } from "./type";
import * as Utils from "../utils";
import { SpeciesFormKey } from "./pokemon-species";
import { WeatherType } from "./weather";
import { Nature } from "./nature";
import { Type } from "#app/data/type";
import * as Utils from "#app/utils";
import { WeatherType } from "#app/data/weather";
import { Nature } from "#app/data/nature";
import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
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 {
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)
],
[Species.GIMMIGHOUL]: [
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 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),
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -556,7 +556,7 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
* @param encounterAnim one or more animations to fetch
*/
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ];
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
for (const anim of anims) {
@ -774,9 +774,9 @@ export abstract class BattleAnim {
private getGraphicFrameData(scene: BattleScene, frames: AnimFrame[], onSubstitute?: boolean): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
[ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
]);
const isOppAnim = this.isOppAnim();
@ -1093,9 +1093,9 @@ export abstract class BattleAnim {
private getGraphicFrameDataWithoutTarget(frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
[ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
]);
let g = 0;

View File

@ -363,7 +363,7 @@ export class RechargingTag extends BattlerTag {
super.onAdd(pokemon);
// Queue a placeholder move for the Pokemon to "use" next turn
pokemon.getMoveQueue().push({ move: Moves.NONE, targets: [] });
pokemon.getMoveQueue().push({ move: Moves.NONE, targets: []});
}
/** Cancels the source's move this turn and queues a "__ must recharge!" message */
@ -569,7 +569,7 @@ export class InterruptedTag extends BattlerTag {
super.onAdd(pokemon);
pokemon.getMoveQueue().shift();
pokemon.pushMoveHistory({move: Moves.NONE, result: MoveResult.OTHER});
pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.OTHER });
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -1376,7 +1376,7 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag {
const effectPhase = pokemon.scene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) {
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));
}
}
@ -1935,10 +1935,10 @@ export class RoostedTag extends BattlerTag {
modifiedTypes = currentTypes.filter(type => type !== Type.NORMAL);
modifiedTypes.push(Type.FLYING);
} else {
modifiedTypes = [Type.FLYING];
modifiedTypes = [ Type.FLYING ];
}
} else {
modifiedTypes = [...currentTypes];
modifiedTypes = [ ...currentTypes ];
modifiedTypes.push(Type.FLYING);
}
pokemon.summonData.types = modifiedTypes;
@ -1958,10 +1958,10 @@ export class RoostedTag extends BattlerTag {
if (this.isBaseFlying) {
let modifiedTypes: Type[];
if (this.isBasePureFlying && !isCurrentlyDualType) {
modifiedTypes = [Type.NORMAL];
modifiedTypes = [ Type.NORMAL ];
} else {
if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
modifiedTypes = [Type.UNKNOWN];
modifiedTypes = [ Type.UNKNOWN ];
} else {
modifiedTypes = currentTypes.filter(type => type !== Type.FLYING);
}
@ -2067,8 +2067,8 @@ export class StockpilingTag extends BattlerTag {
super.loadTag(source);
this.stockpiledCount = source.stockpiledCount || 0;
this.statChangeCounts = {
[ Stat.DEF ]: source.statChangeCounts?.[ Stat.DEF ] ?? 0,
[ Stat.SPDEF ]: source.statChangeCounts?.[ Stat.SPDEF ] ?? 0,
[Stat.DEF]: source.statChangeCounts?.[Stat.DEF] ?? 0,
[Stat.SPDEF]: source.statChangeCounts?.[Stat.SPDEF] ?? 0,
};
}
@ -2090,7 +2090,7 @@ export class StockpilingTag extends BattlerTag {
// Attempt to increase DEF and SPDEF by one stage, keeping track of successful changes.
pokemon.scene.unshiftPhase(new StatStageChangePhase(
pokemon.scene, pokemon.getBattlerIndex(), true,
[Stat.SPDEF, Stat.DEF], 1, true, false, true, this.onStatStagesChanged
[ Stat.SPDEF, Stat.DEF ], 1, true, false, true, this.onStatStagesChanged
));
}
}
@ -2123,7 +2123,40 @@ export class StockpilingTag extends BattlerTag {
*/
export class GulpMissileTag extends BattlerTag {
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
*/
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
*/
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 {
@ -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
* Each count of Autotomization reduces the weight by 100kg
@ -2325,7 +2373,7 @@ export class SubstituteTag extends BattlerTag {
public sourceInFocus: boolean;
constructor(sourceMove: Moves, sourceId: integer) {
super(BattlerTagType.SUBSTITUTE, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE, BattlerTagLapseType.HIT], 0, sourceMove, sourceId, true);
super(BattlerTagType.SUBSTITUTE, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE, BattlerTagLapseType.HIT ], 0, sourceMove, sourceId, true);
}
/** Sets the Substitute's HP and queues an on-add battle animation that initializes the Substitute's sprite. */
@ -2349,7 +2397,7 @@ export class SubstituteTag extends BattlerTag {
onRemove(pokemon: Pokemon): void {
// Only play the animation if the cause of removal isn't from the source's own move
if (!this.sourceInFocus) {
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_REMOVE, [this.sprite]);
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_REMOVE, [ this.sprite ]);
} else {
this.sprite.destroy();
}
@ -2373,13 +2421,13 @@ export class SubstituteTag extends BattlerTag {
/** Triggers an animation that brings the Pokemon into focus before it uses a move */
onPreMove(pokemon: Pokemon): void {
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_PRE_MOVE, [this.sprite]);
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_PRE_MOVE, [ this.sprite ]);
this.sourceInFocus = true;
}
/** Triggers an animation that brings the Pokemon out of focus after it uses a move */
onAfterMove(pokemon: Pokemon): void {
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_POST_MOVE, [this.sprite]);
pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_POST_MOVE, [ this.sprite ]);
this.sourceInFocus = false;
}
@ -2501,8 +2549,8 @@ export class TormentTag extends MoveRestrictionBattlerTag {
return false;
}
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
return i18next.t("battle:moveDisabledTorment", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) });
}
}
@ -2513,7 +2561,7 @@ export class TormentTag extends MoveRestrictionBattlerTag {
*/
export class TauntTag extends MoveRestrictionBattlerTag {
constructor() {
super(BattlerTagType.TAUNT, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE], 4, Moves.TAUNT);
super(BattlerTagType.TAUNT, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 4, Moves.TAUNT);
}
override onAdd(pokemon: Pokemon) {
@ -2530,12 +2578,12 @@ export class TauntTag extends MoveRestrictionBattlerTag {
return allMoves[move].category === MoveCategory.STATUS;
}
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveDisabledTaunt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
}
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 });
}
}
@ -2548,7 +2596,7 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
private source: Pokemon | null;
constructor(sourceId: number) {
super(BattlerTagType.IMPRISON, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE], 1, Moves.IMPRISON, sourceId);
super(BattlerTagType.IMPRISON, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 1, Moves.IMPRISON, sourceId);
}
override onAdd(pokemon: Pokemon) {
@ -2580,24 +2628,58 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
return false;
}
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveDisabledImprison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
}
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.
*
* @param {BattlerTagType} tagType the type of the {@linkcode BattlerTagType}.
* @param turnCount the turn count.
* @param {Moves} sourceMove the source {@linkcode Moves}.
* @param sourceId the source ID.
* @returns {BattlerTag} the corresponding {@linkcode BattlerTag} object.
* @param sourceId - The ID of the pokemon adding the tag
* @returns The corresponding {@linkcode BattlerTag} object.
*/
export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag {
switch (tagType) {
@ -2737,14 +2819,16 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
case BattlerTagType.DISABLED:
return new DisabledTag(sourceId);
case BattlerTagType.IGNORE_GHOST:
return new ExposedTag(tagType, sourceMove, Type.GHOST, [Type.NORMAL, Type.FIGHTING]);
return new ExposedTag(tagType, sourceMove, Type.GHOST, [ Type.NORMAL, Type.FIGHTING ]);
case BattlerTagType.IGNORE_DARK:
return new ExposedTag(tagType, sourceMove, Type.DARK, [Type.PSYCHIC]);
return new ExposedTag(tagType, sourceMove, Type.DARK, [ Type.PSYCHIC ]);
case BattlerTagType.GULP_MISSILE_ARROKUDA:
case BattlerTagType.GULP_MISSILE_PIKACHU:
return new GulpMissileTag(tagType, sourceMove);
case BattlerTagType.TAR_SHOT:
return new TarShotTag();
case BattlerTagType.ELECTRIFIED:
return new ElectrifiedTag();
case BattlerTagType.THROAT_CHOPPED:
return new ThroatChoppedTag();
case BattlerTagType.GORILLA_TACTICS:
@ -2763,6 +2847,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new TauntTag();
case BattlerTagType.IMPRISON:
return new ImprisonTag(sourceId);
case BattlerTagType.SYRUP_BOMB:
return new SyrupBombTag(sourceId);
case BattlerTagType.NONE:
default:
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 { 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 { BattleType, FixedBattleConfig } from "#app/battle";
import Trainer, { TrainerVariant } from "#app/field/trainer";
import { GameMode } from "#app/game-mode";
import { Type } from "./type";
import { Type } from "#app/data/type";
import { Challenges } from "#enums/challenges";
import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type";
import { Nature } from "./nature";
import { Moves } from "#app/enums/moves";
import { TypeColor, TypeShadow } from "#app/enums/color";
import { pokemonEvolutions } from "./pokemon-evolutions";
import { pokemonFormChanges } from "./pokemon-forms";
import { Nature } from "#app/data/nature";
import { Moves } from "#enums/moves";
import { TypeColor, TypeShadow } from "#enums/color";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { pokemonFormChanges } from "#app/data/pokemon-forms";
/** A constant for the default max cost of the starting party before a run */
const DEFAULT_PARTY_MAX_COST = 10;
@ -184,7 +185,7 @@ export abstract class Challenge {
*/
getDescription(overrideValue?: number): string {
const value = overrideValue ?? this.value;
return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${value}`, `challenges:${this.geti18nKey()}.desc`])}`;
return `${i18next.t([ `challenges:${this.geti18nKey()}.desc.${value}`, `challenges:${this.geti18nKey()}.desc` ])}`;
}
/**
@ -413,9 +414,9 @@ export class SingleGenerationChallenge extends Challenge {
}
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean {
const generations = [pokemon.generation];
const generations = [ pokemon.generation ];
if (soft) {
const speciesToCheck = [pokemon.speciesId];
const speciesToCheck = [ pokemon.speciesId ];
while (speciesToCheck.length) {
const checking = speciesToCheck.pop();
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
@ -454,7 +455,7 @@ export class SingleGenerationChallenge extends Challenge {
trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ];
break;
case 186:
trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE]), TrainerType.LARRY_ELITE ];
trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([ TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE ]), TrainerType.LARRY_ELITE ];
break;
case 188:
trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ];
@ -527,19 +528,20 @@ interface monotypeOverride {
*/
export class SingleTypeChallenge extends Challenge {
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() {
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 types = [speciesForm.type1, speciesForm.type2];
if (soft) {
const speciesToCheck = [pokemon.speciesId];
const types = [ speciesForm.type1, speciesForm.type2 ];
if (soft && !SingleTypeChallenge.SPECIES_OVERRIDES.includes(pokemon.speciesId)) {
const speciesToCheck = [ pokemon.speciesId ];
while (speciesToCheck.length) {
const checking = speciesToCheck.pop();
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
@ -604,9 +606,9 @@ export class SingleTypeChallenge extends Challenge {
overrideValue = this.value;
}
const type = i18next.t(`pokemonInfo:Type.${Type[this.value - 1]}`);
const typeColor = `[color=${TypeColor[Type[this.value-1]]}][shadow=${TypeShadow[Type[this.value-1]]}]${type}[/shadow][/color]`;
const typeColor = `[color=${TypeColor[Type[this.value - 1]]}][shadow=${TypeShadow[Type[this.value - 1]]}]${type}[/shadow][/color]`;
const defaultDesc = i18next.t("challenges:singleType.desc_default");
const typeDesc = i18next.t("challenges:singleType.desc", {type: typeColor});
const typeDesc = i18next.t("challenges:singleType.desc", { type: typeColor });
return this.value === 0 ? defaultDesc : typeDesc;
}
@ -636,7 +638,7 @@ export class FreshStartChallenge extends Challenge {
applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean {
if (defaultStarterSpecies.includes(species)) {
cost.value = speciesStarters[species];
cost.value = speciesStarterCosts[species];
return true;
}
return false;
@ -651,7 +653,7 @@ export class FreshStartChallenge extends Challenge {
pokemon.shiny = false; // Not shiny
pokemon.variant = 0; // Not shiny
pokemon.formIndex = 0; // Froakie should be base form
pokemon.ivs = [10, 10, 10, 10, 10, 10]; // Default IVs of 10 for all stats
pokemon.ivs = [ 10, 10, 10, 10, 10, 10 ]; // Default IVs of 10 for all stats
return true;
}
@ -718,7 +720,7 @@ export class LowerStarterMaxCostChallenge extends Challenge {
}
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;
return true;
}

View File

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

View File

@ -3123,44 +3123,44 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
export const doubleBattleDialogue = {
"blue_red_double": {
encounter: ["doubleBattleDialogue:blue_red_double.encounter.1"],
victory: ["doubleBattleDialogue:blue_red_double.victory.1"]
encounter: [ "doubleBattleDialogue:blue_red_double.encounter.1" ],
victory: [ "doubleBattleDialogue:blue_red_double.victory.1" ]
},
"red_blue_double": {
encounter: ["doubleBattleDialogue:red_blue_double.encounter.1"],
victory: ["doubleBattleDialogue:red_blue_double.victory.1"]
encounter: [ "doubleBattleDialogue:red_blue_double.encounter.1" ],
victory: [ "doubleBattleDialogue:red_blue_double.victory.1" ]
},
"tate_liza_double": {
encounter: ["doubleBattleDialogue:tate_liza_double.encounter.1"],
victory: ["doubleBattleDialogue:tate_liza_double.victory.1"]
encounter: [ "doubleBattleDialogue:tate_liza_double.encounter.1" ],
victory: [ "doubleBattleDialogue:tate_liza_double.victory.1" ]
},
"liza_tate_double": {
encounter: ["doubleBattleDialogue:liza_tate_double.encounter.1"],
victory: [ "doubleBattleDialogue:liza_tate_double.victory.1"]
encounter: [ "doubleBattleDialogue:liza_tate_double.encounter.1" ],
victory: [ "doubleBattleDialogue:liza_tate_double.victory.1" ]
},
"wallace_steven_double": {
encounter: [ "doubleBattleDialogue:wallace_steven_double.encounter.1"],
victory: [ "doubleBattleDialogue:wallace_steven_double.victory.1"]
encounter: [ "doubleBattleDialogue:wallace_steven_double.encounter.1" ],
victory: [ "doubleBattleDialogue:wallace_steven_double.victory.1" ]
},
"steven_wallace_double": {
encounter: [ "doubleBattleDialogue:steven_wallace_double.encounter.1"],
victory: [ "doubleBattleDialogue:steven_wallace_double.victory.1"]
encounter: [ "doubleBattleDialogue:steven_wallace_double.encounter.1" ],
victory: [ "doubleBattleDialogue:steven_wallace_double.victory.1" ]
},
"alder_iris_double": {
encounter: [ "doubleBattleDialogue:alder_iris_double.encounter.1"],
victory: [ "doubleBattleDialogue:alder_iris_double.victory.1"]
encounter: [ "doubleBattleDialogue:alder_iris_double.encounter.1" ],
victory: [ "doubleBattleDialogue:alder_iris_double.victory.1" ]
},
"iris_alder_double": {
encounter: [ "doubleBattleDialogue:iris_alder_double.encounter.1"],
victory: [ "doubleBattleDialogue:iris_alder_double.victory.1"]
encounter: [ "doubleBattleDialogue:iris_alder_double.encounter.1" ],
victory: [ "doubleBattleDialogue:iris_alder_double.victory.1" ]
},
"marnie_piers_double": {
encounter: [ "doubleBattleDialogue:marnie_piers_double.encounter.1"],
victory: [ "doubleBattleDialogue:marnie_piers_double.victory.1"]
encounter: [ "doubleBattleDialogue:marnie_piers_double.encounter.1" ],
victory: [ "doubleBattleDialogue:marnie_piers_double.victory.1" ]
},
"piers_marnie_double": {
encounter: [ "doubleBattleDialogue:piers_marnie_double.encounter.1"],
victory: [ "doubleBattleDialogue:piers_marnie_double.victory.1"]
encounter: [ "doubleBattleDialogue:piers_marnie_double.encounter.1" ],
victory: [ "doubleBattleDialogue:piers_marnie_double.victory.1" ]
},
@ -3193,7 +3193,7 @@ export function initTrainerTypeDialogue(): void {
const trainerTypes = Object.keys(trainerTypeDialogue).map(t => parseInt(t) as TrainerType);
for (const trainerType of trainerTypes) {
const messages = trainerTypeDialogue[trainerType];
const messageTypes = ["encounter", "victory", "defeat"];
const messageTypes = [ "encounter", "victory", "defeat" ];
for (const messageType of messageTypes) {
if (Array.isArray(messages)) {
if (messages[0][messageType]) {

View File

@ -48,7 +48,7 @@ export class EggHatchData {
seenCount: currDexEntry.seenCount,
caughtCount: currDexEntry.caughtCount,
hatchedCount: currDexEntry.hatchedCount,
ivs: [...currDexEntry.ivs]
ivs: [ ...currDexEntry.ivs ]
};
this.starterDataEntryBeforeUpdate = {
moveset: currStarterDataEntry.moveset,

View File

@ -1,30 +1,20 @@
import BattleScene from "../battle-scene";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
import { VariantTier } from "../enums/variant-tier";
import * as Utils from "../utils";
import BattleScene from "#app/battle-scene";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { VariantTier } from "#enums/variant-tier";
import * as Utils from "#app/utils";
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 i18next from "i18next";
import { EggTier } from "#enums/egg-type";
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;
// 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 */
export interface IEggOptions {
/** 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
if (eggOptions?.species) {
this._tier = this.getEggTierFromSpeciesStarterValue();
this._tier = this.getEggTier();
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves();
}
// If species has no variant, set variantTier to common. This needs to
@ -272,11 +262,11 @@ export class Egg {
return "Manaphy";
}
switch (this.tier) {
case EggTier.GREAT:
case EggTier.RARE:
return i18next.t("egg:greatTier");
case EggTier.ULTRA:
case EggTier.EPIC:
return i18next.t("egg:ultraTier");
case EggTier.MASTER:
case EggTier.LEGENDARY:
return i18next.t("egg:masterTier");
default:
return i18next.t("egg:defaultTier");
@ -299,7 +289,7 @@ export class Egg {
public getEggTypeDescriptor(scene: BattleScene): string {
switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG:
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() });
case EggSourceType.GACHA_LEGENDARY:
return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
case EggSourceType.GACHA_SHINY:
@ -323,7 +313,7 @@ export class Egg {
////
private rollEggMoveIndex() {
let baseChance = DEFAULT_RARE_EGGMOVE_RATE;
let baseChance = GACHA_DEFAULT_RARE_EGGMOVE_RATE;
switch (this._sourceType) {
case EggSourceType.SAME_SPECIES_EGG:
baseChance = SAME_SPECIES_EGG_RARE_EGGMOVE_RATE;
@ -341,24 +331,24 @@ export class Egg {
private getEggTierDefaultHatchWaves(eggTier?: EggTier): number {
if (this._species === Species.PHIONE || this._species === Species.MANAPHY) {
return 50;
return HATCH_WAVES_MANAPHY_EGG;
}
switch (eggTier ?? this._tier) {
case EggTier.COMMON:
return 10;
case EggTier.GREAT:
return 25;
case EggTier.ULTRA:
return 50;
return HATCH_WAVES_COMMON_EGG;
case EggTier.RARE:
return HATCH_WAVES_RARE_EGG;
case EggTier.EPIC:
return HATCH_WAVES_EPIC_EGG;
}
return 100;
return HATCH_WAVES_LEGENDARY_EGG;
}
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);
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 {
@ -378,7 +368,7 @@ export class Egg {
*/
const rand = (Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1);
return rand ? Species.PHIONE : Species.MANAPHY;
} else if (this.tier === EggTier.MASTER
} else if (this.tier === EggTier.LEGENDARY
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!Utils.randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp);
@ -389,15 +379,15 @@ export class Egg {
let maxStarterValue: integer;
switch (this.tier) {
case EggTier.GREAT:
case EggTier.RARE:
minStarterValue = 4;
maxStarterValue = 5;
break;
case EggTier.ULTRA:
case EggTier.EPIC:
minStarterValue = 6;
maxStarterValue = 7;
break;
case EggTier.MASTER:
case EggTier.LEGENDARY:
minStarterValue = 8;
maxStarterValue = 9;
break;
@ -407,10 +397,10 @@ export class Egg {
break;
}
const ignoredSpecies = [Species.PHIONE, Species.MANAPHY, Species.ETERNATUS];
const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ];
let speciesPool = Object.keys(speciesStarters)
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
let speciesPool = Object.keys(speciesEggTiers)
.filter(s => speciesEggTiers[s] === this.tier)
.map(s => parseInt(s) as Species)
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
@ -441,7 +431,9 @@ export class Egg {
let totalWeight = 0;
const speciesWeights : number[] = [];
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);
if (species.isRegional()) {
weight = Math.floor(weight / 2);
@ -475,7 +467,7 @@ export class Egg {
* @returns True if the egg is shiny
**/
private rollShiny(): boolean {
let shinyChance = DEFAULT_SHINY_RATE;
let shinyChance = GACHA_DEFAULT_SHINY_RATE;
switch (this._sourceType) {
case EggSourceType.GACHA_SHINY:
shinyChance = GACHA_SHINY_UP_SHINY_RATE;
@ -498,9 +490,9 @@ export class Egg {
}
const rand = Utils.randSeedInt(10);
if (rand >= 4) {
if (rand >= SHINY_VARIANT_CHANCE) {
return VariantTier.STANDARD; // 6/10
} else if (rand >= 1) {
} else if (rand >= SHINY_EPIC_CHANCE) {
return VariantTier.RARE; // 3/10
} else {
return VariantTier.EPIC; // 1/10
@ -508,17 +500,17 @@ export class Egg {
}
private checkForPityTierOverrides(scene: BattleScene): void {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? 1 : 0;
scene.gameData.eggPity[EggTier.GREAT] += 1;
scene.gameData.eggPity[EggTier.ULTRA] += 1;
scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset;
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
scene.gameData.eggPity[EggTier.RARE] += 1;
scene.gameData.eggPity[EggTier.EPIC] += 1;
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.
if (scene.gameData.eggPity[EggTier.MASTER] >= 412 && this._tier === EggTier.COMMON) {
this._tier = EggTier.MASTER;
} else if (scene.gameData.eggPity[EggTier.ULTRA] >= 59 && this._tier === EggTier.COMMON) {
this._tier = EggTier.ULTRA;
} else if (scene.gameData.eggPity[EggTier.GREAT] >= 9 && this._tier === EggTier.COMMON) {
this._tier = EggTier.GREAT;
if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.LEGENDARY;
} else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.EPIC;
} else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.RARE;
}
scene.gameData.eggPity[this._tier] = 0;
}
@ -527,38 +519,24 @@ export class Egg {
scene.gameData.gameStats.eggsPulled++;
if (this.isManaphyEgg()) {
scene.gameData.gameStats.manaphyEggsPulled++;
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.ULTRA);
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC);
return;
}
switch (this.tier) {
case EggTier.GREAT:
case EggTier.RARE:
scene.gameData.gameStats.rareEggsPulled++;
break;
case EggTier.ULTRA:
case EggTier.EPIC:
scene.gameData.gameStats.epicEggsPulled++;
break;
case EggTier.MASTER:
case EggTier.LEGENDARY:
scene.gameData.gameStats.legendaryEggsPulled++;
break;
}
}
private getEggTierFromSpeciesStarterValue(): EggTier {
const speciesStartValue = speciesStarters[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;
private getEggTier(): EggTier {
return speciesEggTiers[this.species];
}
////
@ -567,8 +545,8 @@ export class Egg {
}
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species {
const legendarySpecies = Object.entries(speciesStarters)
.filter(s => s[1] >= 8 && s[1] <= 9)
const legendarySpecies = Object.entries(speciesEggTiers)
.filter(s => s[1] === EggTier.LEGENDARY)
.map(s => parseInt(s[0]))
.filter(s => getPokemonSpecies(s).isObtainable());
@ -590,17 +568,9 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
/**
* 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
*/
export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier {
const speciesBaseValue = speciesStarters[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;
return speciesEggTiers[pokemonSpecies.getRootSpeciesId()];
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -24,9 +24,10 @@ import { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:absoluteAvarice";
const namespace = "mysteryEncounters/absoluteAvarice";
/**
* Absolute Avarice encounter.
@ -38,6 +39,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
// This sprite has the shadow
@ -161,12 +163,13 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -193,7 +196,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Can't define stack count on a ModifierType, have to just create separate instances for each stack
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway
for (let i = 0; i < berryMod.stackCount; i++) {
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [berryMod.berryType]) as PokemonHeldItemModifierType;
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [ berryMod.berryType ]) as PokemonHeldItemModifierType;
bossModifierConfigs.push({ modifier: modifierType });
}
});
@ -202,8 +205,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// SpDef buff 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 ?
[Stat.SPDEF] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
[ Stat.SPDEF ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
// Calculate boss mon
const config: EnemyPartyConfig = {
@ -213,18 +216,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
species: getPokemonSpecies(Species.GREEDENT),
isBoss: true,
bossSegments: 3,
moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH],
moveSet: [ Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH ],
modifierConfigs: bossModifierConfigs,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
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));
}
}
],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
encounter.setDialogueToken("greedentName", getPokemonSpecies(Species.GREEDENT).getName());
return true;
@ -248,11 +251,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
})
@ -262,25 +265,23 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Provides 1x Reviver Seed to each party member at end of battle
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
const givePartyPokemonReviverSeeds = () => {
const party = scene.getParty();
party.forEach(p => {
const heldItems = p.getHeldItems();
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);
}
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);
encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
targets: [ BattlerIndex.ENEMY ],
move: new PokemonMove(Moves.STUFF_CHEEKS),
ignorePp: true
});
@ -294,11 +295,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
})
@ -320,7 +321,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
Phaser.Math.RND.shuffle(berryTypesAsArray);
const randBerryType = berryTypesAsArray.pop();
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [randBerryType]) as BerryModifierType;
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType;
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType);
}
}
@ -336,11 +337,11 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
],
})
@ -355,7 +356,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Greedent joins the team, level equal to 2 below highest party member
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false);
greedent.moveset = [new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF)];
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
greedent.passive = true;
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
@ -472,7 +473,7 @@ function doGreedentEatBerries(scene: BattleScene) {
*/
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
const berryAddDelay = 150;
let animationOrder = ["starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa"];
let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ];
if (isEat) {
animationOrder = animationOrder.reverse();
}
@ -496,7 +497,7 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
// Animate Petaya berry falling off the pile
if (berry === "petaya" && sprite && tintSprite && !isEat) {
scene.time.delayedCall(200, () => {
doBerryBounce(scene, [sprite, tintSprite], 30, 500);
doBerryBounce(scene, [ sprite, tintSprite ], 30, 500);
});
}
});

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 { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** 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
@ -56,22 +57,23 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true);
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true);
const starterValue: number = speciesStarters[baseSpecies] ?? 1;
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId();
const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1;
const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER);
const price = scene.getWaveMoneyAmount(multiplier);
@ -104,12 +106,12 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
speaker: `${namespace}.speaker`,
text: `${namespace}:option.1.selected`,
speaker: `${namespace}:speaker`,
},
],
})
@ -135,13 +137,13 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
new AbilityRequirement(EXTORTION_ABILITIES))
)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.tooltip_disabled`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected`,
},
],
})
@ -159,12 +161,12 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.3.selected`,
speaker: `${namespace}:speaker`,
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:berriesAbound";
const namespace = "mysteryEncounters/berriesAbound";
/**
* Berries Abound encounter.
@ -46,10 +46,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -68,7 +69,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
isBoss: true
}],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
// Calculate the number of extra berries that player receives
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
@ -109,16 +110,17 @@ export const BerriesAboundEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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 doBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`);
const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
@ -156,8 +158,8 @@ export const BerriesAboundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`
})
.withOptionPhase(async (scene: BattleScene) => {
// Pick race for berries
@ -179,7 +181,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
if (speedDiff < 1) {
// Caught and attacked by boss, gets +1 to all stats at start of fight
const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`);
const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
@ -192,25 +194,25 @@ export const BerriesAboundEncounter: MysteryEncounter =
// 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 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
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) => {
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));
};
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);
return;
} else {
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
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));
const doFasterBerryRewards = () => {
const berryText = i18next.t(`${namespace}.berries`);
const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare");
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);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected`);
await showEncounterText(scene, `${namespace}:option.2.selected`);
leaveEncounterWithoutBattle(scene);
}
})
@ -231,11 +233,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
],
},
@ -249,7 +251,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [ berryType ]) as BerryModifierType;
const party = scene.getParty();

View File

@ -49,9 +49,10 @@ import MoveInfoOverlay from "#app/ui/move-info-overlay";
import { allMoves } from "#app/data/move";
import { ModifierTier } from "#app/modifier/modifier-tier";
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 */
const namespace = "mysteryEncounter:bugTypeSuperfan";
const namespace = "mysteryEncounters/bugTypeSuperfan";
const POOL_1_POKEMON = [
Species.PARASECT,
@ -177,9 +178,14 @@ const MISC_TUTOR_MOVES = [
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.
* @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}
*/
export const BugTypeSuperfanEncounter: MysteryEncounter =
@ -187,7 +193,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.GREAT)
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
// Must have at least 1 Bug type on team, OR have a bug item somewhere on the team
new HeldItemRequirement(["BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier"], 1),
new HeldItemRequirement([ "BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier" ], 1),
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1),
new TypeRequirement(Type.BUG, false, 1)
))
@ -197,11 +203,11 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withOnInit((scene: BattleScene) => {
@ -216,18 +222,53 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
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 = [
{
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,
fileRoot: "trainer",
hasShadow: true,
x: 4,
y: 7,
yShadow: 7
},
];
const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.BUG]),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]),
];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
@ -235,17 +276,18 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.1.selected`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.1.selected`,
},
],
},
@ -276,9 +318,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new TypeRequirement(Type.BUG, false, 1)) // Must have 1 Bug type on team
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Player shows off their bug types
@ -286,36 +328,36 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
// 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 numBugTypesText = i18next.t(`${namespace}.numBugTypes`, { count: numBugTypes });
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
encounter.setDialogueToken("numBugTypes", numBugTypesText);
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 = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_0_to_1`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected_0_to_1`,
},
];
} 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 = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_2_to_3`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected_2_to_3`,
},
];
} 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 = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_4_to_5`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected_4_to_5`,
},
];
} else {
// If player has any evolution/form change items that are valid for their party, will spawn one of those items in addition to a Master Ball
const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)!, generateModifierTypeOption(scene, modifierTypes.MAX_LURE)!];
const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)!, generateModifierTypeOption(scene, modifierTypes.MAX_LURE)! ];
const specialOptions: ModifierTypeOption[] = [];
const nonRareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.EVOLUTION_ITEM);
@ -330,6 +372,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
if (formChangeModifier) {
specialOptions.push(formChangeModifier);
}
const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeModifier) {
specialOptions.push(rareFormChangeModifier);
}
if (specialOptions.length > 0) {
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
}
@ -337,8 +383,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_6`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected_6`,
},
];
}
@ -352,23 +398,23 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
// Meets one or both of the below reqs
new HeldItemRequirement(["BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier"], 1),
new HeldItemRequirement([ "BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier" ], 1),
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1)
))
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.3.selected_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.3.selected_dialogue`,
},
],
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
secondOptionPrompt: `${namespace}:option.3.select_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -407,7 +453,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
});
if (!hasValidItem) {
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
}
return null;
@ -429,13 +475,13 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
const bugNet = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!;
bugNet.type.tier = ModifierTier.ROGUE;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [bugNet], guaranteedModifierTypeFuncs: [modifierTypes.REVIVER_SEED], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false });
leaveEncounterWithoutBattle(scene, true);
})
.build())
.withOutroDialogue([
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
])
.build();
@ -445,29 +491,29 @@ function getTrainerConfigForWave(waveIndex: number) {
const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone();
config.name = i18next.t("trainerNames:bug_type_superfan");
const pool3Copy = POOL_3_POKEMON.slice(0);
randSeedShuffle(pool3Copy);
let pool3Copy = POOL_3_POKEMON.slice(0);
pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon = pool3Copy.pop()!;
if (waveIndex < 30) {
if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) {
// Use default template (2 AVG)
config
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true));
} else if (waveIndex < 50) {
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[1]) {
config
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 70) {
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[2]) {
config
.setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_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
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.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(3, 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
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -490,15 +536,15 @@ function getTrainerConfigForWave(waveIndex: number) {
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ pool3Mon.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}));
} else if (waveIndex < 140) {
randSeedShuffle(pool3Copy);
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[5]) {
pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon2 = pool3Copy.pop()!;
config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
@ -513,21 +559,21 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ pool3Mon.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon2.species], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ pool3Mon2.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon2.formIndex)) {
p.formIndex = pool3Mon2.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}));
} else if (waveIndex < 160) {
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[6]) {
config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -541,7 +587,7 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ pool3Mon.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
@ -550,6 +596,8 @@ function getTrainerConfigForWave(waveIndex: number) {
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true));
} else {
pool3Copy = randSeedShuffle(pool3Copy);
const pool3Mon2 = pool3Copy.pop()!;
config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
@ -564,16 +612,16 @@ function getTrainerConfigForWave(waveIndex: number) {
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ pool3Mon.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex;
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ pool3Mon2.species ], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon2.formIndex)) {
p.formIndex = pool3Mon2.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
@ -587,7 +635,7 @@ function getTrainerConfigForWave(waveIndex: number) {
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
return new Promise<void>(async resolve => {
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 moveInfoOverlay = new MoveInfoOverlay(scene, {
@ -622,34 +670,14 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
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;
if (!result) {
moveInfoOverlay.active = false;
moveInfoOverlay.setVisible(false);
}
// 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);
// }
// }
// }
// TODO: add menu to confirm player doesn't want to teach a move?
// Option select complete, handle if they are learning a move
if (result && result.selectedOptionIndex < moveOptions.length) {

View File

@ -34,7 +34,7 @@ import { EncounterAnim } from "#enums/encounter-anims";
import { Challenges } from "#enums/challenges";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:clowningAround";
const namespace = "mysteryEncounters/clowningAround";
const RANDOM_ABILITY_POOL = [
Abilities.STURDY,
@ -98,11 +98,11 @@ export const ClowningAroundEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`
},
])
.withOnInit((scene: BattleScene) => {
@ -129,38 +129,39 @@ export const ClowningAroundEncounter: MysteryEncounter =
{
species: getPokemonSpecies(Species.MR_MIME),
isBoss: true,
moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC]
moveSet: [ Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC ]
},
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(Species.BLACEPHALON),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ ability: ability, types: [randSeedInt(18), randSeedInt(18)] }),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ ability: ability, types: [ randSeedInt(18), randSeedInt(18) ]}),
isBoss: true,
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
moveSet: [ Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN ]
},
],
doubleBattle: true
});
// Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
loadCustomMovesForEncounter(scene, [ Moves.ROLE_PLAY, Moves.TAUNT ]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.1.selected`,
speaker: `${namespace}:speaker`
},
],
})
@ -175,19 +176,19 @@ export const ClowningAroundEncounter: MysteryEncounter =
encounter.startOfBattleEffects.push(
{ // Mr. Mime copies the Blacephalon's random ability
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY_2],
targets: [ BattlerIndex.ENEMY_2 ],
move: new PokemonMove(Moves.ROLE_PLAY),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
targets: [ BattlerIndex.PLAYER_2 ],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true
});
@ -199,7 +200,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
// After the battle, offer the player the opportunity to permanently swap ability
const abilityWasSwapped = await handleSwapAbility(scene);
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
@ -222,19 +223,19 @@ export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.2.selected`,
speaker: `${namespace}:speaker`
},
{
text: `${namespace}.option.2.selected_2`,
text: `${namespace}:option.2.selected_2`,
},
{
text: `${namespace}.option.2.selected_3`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.2.selected_3`,
speaker: `${namespace}:speaker`
},
],
})
@ -308,19 +309,19 @@ export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.3.selected`,
speaker: `${namespace}:speaker`
},
{
text: `${namespace}.option.3.selected_2`,
text: `${namespace}:option.3.selected_2`,
},
{
text: `${namespace}.option.3.selected_3`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.3.selected_3`,
speaker: `${namespace}:speaker`
},
],
})
@ -336,11 +337,11 @@ export const ClowningAroundEncounter: MysteryEncounter =
.filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
.map(move => move!.getMove().type);
if (priorityTypes?.length > 0) {
priorityTypes = [...new Set(priorityTypes)];
randSeedShuffle(priorityTypes);
priorityTypes = [ ...new Set(priorityTypes) ].sort();
priorityTypes = randSeedShuffle(priorityTypes);
}
const newTypes = [originalTypes[0]];
const newTypes = [ originalTypes[0] ];
let secondType: Type | null = null;
while (secondType === null || secondType === newTypes[0] || originalTypes.includes(secondType)) {
if (priorityTypes.length > 0) {
@ -377,15 +378,15 @@ export const ClowningAroundEncounter: MysteryEncounter =
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
])
.build();
async function handleSwapAbility(scene: BattleScene) {
return new Promise<boolean>(async resolve => {
await showEncounterDialogue(scene, `${namespace}.option.1.apply_ability_dialogue`, `${namespace}.speaker`);
await showEncounterText(scene, `${namespace}.option.1.apply_ability_message`);
await showEncounterDialogue(scene, `${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
await showEncounterText(scene, `${namespace}:option.1.apply_ability_message`);
scene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve);
@ -394,7 +395,7 @@ async function handleSwapAbility(scene: BattleScene) {
}
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 = [
{
label: i18next.t("menu:yes"),
@ -453,37 +454,37 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
// This is to prevent "over-generating" a random item of a certain type during item swaps
const ultraPool = [
[modifierTypes.REVIVER_SEED, 1],
[modifierTypes.GOLDEN_PUNCH, 5],
[modifierTypes.ATTACK_TYPE_BOOSTER, 99],
[modifierTypes.QUICK_CLAW, 3],
[modifierTypes.WIDE_LENS, 3]
[ modifierTypes.REVIVER_SEED, 1 ],
[ modifierTypes.GOLDEN_PUNCH, 5 ],
[ modifierTypes.ATTACK_TYPE_BOOSTER, 99 ],
[ modifierTypes.QUICK_CLAW, 3 ],
[ modifierTypes.WIDE_LENS, 3 ]
];
const roguePool = [
[modifierTypes.LEFTOVERS, 4],
[modifierTypes.SHELL_BELL, 4],
[modifierTypes.SOUL_DEW, 10],
[modifierTypes.SOOTHE_BELL, 3],
[modifierTypes.SCOPE_LENS, 1],
[modifierTypes.BATON, 1],
[modifierTypes.FOCUS_BAND, 5],
[modifierTypes.KINGS_ROCK, 3],
[modifierTypes.GRIP_CLAW, 5]
[ modifierTypes.LEFTOVERS, 4 ],
[ modifierTypes.SHELL_BELL, 4 ],
[ modifierTypes.SOUL_DEW, 10 ],
[ modifierTypes.SOOTHE_BELL, 3 ],
[ modifierTypes.SCOPE_LENS, 1 ],
[ modifierTypes.BATON, 1 ],
[ modifierTypes.FOCUS_BAND, 5 ],
[ modifierTypes.KINGS_ROCK, 3 ],
[ modifierTypes.GRIP_CLAW, 5 ]
];
const berryPool = [
[BerryType.APICOT, 3],
[BerryType.ENIGMA, 2],
[BerryType.GANLON, 3],
[BerryType.LANSAT, 3],
[BerryType.LEPPA, 2],
[BerryType.LIECHI, 3],
[BerryType.LUM, 2],
[BerryType.PETAYA, 3],
[BerryType.SALAC, 2],
[BerryType.SITRUS, 2],
[BerryType.STARF, 3]
[ BerryType.APICOT, 3 ],
[ BerryType.ENIGMA, 2 ],
[ BerryType.GANLON, 3 ],
[ BerryType.LANSAT, 3 ],
[ BerryType.LEPPA, 2 ],
[ BerryType.LIECHI, 3 ],
[ BerryType.LUM, 2 ],
[ BerryType.PETAYA, 3 ],
[ BerryType.SALAC, 2 ],
[ BerryType.SITRUS, 2 ],
[ BerryType.STARF, 3 ]
];
let pool: any[];
@ -494,11 +495,15 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
}
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 newItemType = pool[randIndex];
let newMod;
let newMod: PokemonHeldItemModifierType;
if (tier === "Berries") {
newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType;
newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
} else {
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType;
}

View File

@ -30,7 +30,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:dancingLessons";
const namespace = "mysteryEncounters/dancingLessons";
// Fire form
const BAILE_STYLE_BIOMES = [
@ -90,6 +90,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withFleeAllowed(false)
.withOnVisualsStart((scene: BattleScene) => {
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]);
danceAnim.play(scene);
@ -98,12 +99,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
})
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -140,7 +142,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
scene.getEnemyParty().forEach(enemyPokemon => {
scene.field.remove(enemyPokemon, true);
});
scene.currentBattle.enemyParty = [oricorio];
scene.currentBattle.enemyParty = [ oricorio ];
scene.field.add(oricorio);
// Spawns on offscreen field
oricorio.x -= 300;
@ -152,14 +154,14 @@ export const DancingLessonsEncounter: MysteryEncounter =
dataSource: oricorioData,
isBoss: true,
// 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) => {
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));
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));
}
}],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
encounter.misc = {
oricorioData
};
@ -172,11 +174,11 @@ export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
})
@ -186,13 +188,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.REVELATION_DANCE),
ignorePp: true
});
await hideOricorioPokemon(scene);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
})
.build()
@ -201,11 +203,11 @@ export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
})
@ -236,13 +238,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(DANCING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
secondOptionPrompt: `${namespace}:option.3.select_prompt`,
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);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? 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";
/** 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 */
const excludedBosses = [
@ -107,32 +107,33 @@ export const DarkDealEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party
.withCatchAllowed(true)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.1.selected_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.1.selected_dialogue`,
},
{
text: `${namespace}.option.1.selected_message`,
text: `${namespace}:option.1.selected_message`,
},
],
})
@ -164,7 +165,7 @@ export const DarkDealEncounter: MysteryEncounter =
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
const roll = randSeedInt(100);
const starterTier: number | [number, number] =
roll >= 65 ? 6 : roll >= 15 ? 7 : roll >= 5 ? 8 : [9, 10];
roll >= 65 ? 6 : roll >= 15 ? 7 : roll >= 5 ? 8 : [ 9, 10 ];
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
const pokemonConfig: EnemyPokemonConfig = {
species: bossSpecies,
@ -179,7 +180,7 @@ export const DarkDealEncounter: MysteryEncounter =
pokemonConfig.formIndex = 0;
}
const config: EnemyPartyConfig = {
pokemonConfigs: [pokemonConfig],
pokemonConfigs: [ pokemonConfig ],
};
return initBattleWithEnemyConfig(scene, config);
})
@ -187,12 +188,12 @@ export const DarkDealEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -204,7 +205,7 @@ export const DarkDealEncounter: MysteryEncounter =
)
.withOutroDialogue([
{
text: `${namespace}.outro`
text: `${namespace}:outro`
}
])
.build();

View File

@ -19,10 +19,10 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy";
const namespace = "mysteryEncounters/delibirdy";
/** Berries only */
const OPTION_2_ALLOWED_MODIFIERS = ["BerryModifier", "PokemonInstantReviveModifier"];
const OPTION_2_ALLOWED_MODIFIERS = [ "BerryModifier", "PokemonInstantReviveModifier" ];
/** Disallowed items are berries, Reviver Seeds, and Vitamins (form change items and fusion items are not PokemonHeldItemModifiers) */
const OPTION_3_DISALLOWED_MODIFIERS = [
@ -81,15 +81,16 @@ export const DelibirdyEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOutroDialogue([
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
}
])
.withOnInit((scene: BattleScene) => {
@ -108,11 +109,11 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER) // Must have money to spawn
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
})
@ -145,12 +146,12 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS))
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
secondOptionPrompt: `${namespace}.option.2.select_prompt`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}:option.2.select_prompt`,
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
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
}
return null;
@ -193,7 +194,7 @@ export const DelibirdyEncounter: MysteryEncounter =
})
.withOptionPhase(async (scene: BattleScene) => {
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
if (modifier instanceof BerryModifier) {
@ -239,12 +240,12 @@ export const DelibirdyEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true))
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:option.3.select_prompt`,
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
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? 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";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:departmentStoreSale";
const namespace = "mysteryEncounters/departmentStoreSale";
/**
* Department Store Sale encounter.
@ -43,21 +43,22 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`,
},
])
.withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
},
async (scene: BattleScene) => {
// Choose TMs
@ -82,8 +83,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
},
async (scene: BattleScene) => {
// Choose Vitamins
@ -106,8 +107,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
},
async (scene: BattleScene) => {
// Choose X Items
@ -130,8 +131,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`,
buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}:option.4.tooltip`,
},
async (scene: BattleScene) => {
// Choose Pokeballs
@ -158,7 +159,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
}
])
.build();

View File

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

View File

@ -24,7 +24,7 @@ import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieryFallout";
const namespace = "mysteryEncounters/fieryFallout";
/**
* Damage percentage taken when suffering the heat.
@ -46,9 +46,10 @@ export const FieryFalloutEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // Set in onInit()
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
.withAutoHideIntroVisuals(false)
.withFleeAllowed(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -72,7 +73,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
doubleBattle: true,
disableSwitch: true
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
// Load hidden Volcarona sprites
encounter.spriteConfigs = [
@ -98,7 +99,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
];
// Load animations/sfx for Volcarona moves
loadCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]);
loadCustomMovesForEncounter(scene, [ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]);
scene.arena.trySetWeather(WeatherType.SUNNY, true);
@ -121,16 +122,17 @@ export const FieryFalloutEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
},
@ -142,25 +144,25 @@ export const FieryFalloutEncounter: MysteryEncounter =
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
targets: [ BattlerIndex.PLAYER_2 ],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
targets: [ BattlerIndex.ENEMY ],
move: new PokemonMove(Moves.QUIVER_DANCE),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.ENEMY_2],
targets: [ BattlerIndex.ENEMY_2 ],
move: new PokemonMove(Moves.QUIVER_DANCE),
ignorePp: true
});
@ -169,11 +171,11 @@ export const FieryFalloutEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -196,7 +198,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
// Burn applied
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
.withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
],
})
@ -236,7 +238,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
const primary = encounter.options[2].primaryPokemon!;
const secondary = encounter.options[2].secondaryPokemon![0];
setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
setEncounterExp(scene, [ primary.id, secondary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
leaveEncounterWithoutBattle(scene);
})
.build()
@ -247,9 +249,9 @@ function giveLeadPokemonCharcoal(scene: BattleScene) {
// Give first party pokemon Charcoal for free at end of battle
const leadPokemon = scene.getParty()?.[0];
if (leadPokemon) {
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);
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fightOrFlight";
const namespace = "mysteryEncounters/fightOrFlight";
/**
* Fight or Flight encounter.
@ -44,10 +44,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -64,16 +65,16 @@ export const FightOrFlightEncounter: MysteryEncounter =
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
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
// 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));
}
}],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
// Calculate item
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
@ -89,7 +90,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
let item: ModifierTypeOption | null = null;
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
}
encounter.setDialogueToken("itemName", item.type.name);
encounter.misc = item;
@ -119,16 +120,17 @@ export const FightOrFlightEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
},
@ -136,7 +138,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
// Pick battle
// Pokemon will randomly boost 1 stat by 2 stages
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
}
)
@ -145,12 +147,12 @@ export const FightOrFlightEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`
text: `${namespace}:option.2.selected`
}
]
})
@ -158,7 +160,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
// Pick steal
const encounter = scene.currentBattle.mysteryEncounter!;
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
// Use primaryPokemon to execute the thievery
const primaryPokemon = encounter.options[1].primaryPokemon!;
@ -169,11 +171,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:funAndGames";
const namespace = "mysteryEncounters/funAndGames";
/**
* Fun and Games! encounter.
@ -44,6 +44,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withSkipEnemyBattleTurns(true)
// Will skip COMMAND selection menu and go straight to FIGHT (move select) menu
.withSkipToFightInput(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: "fun_and_games_game",
@ -71,13 +72,14 @@ export const FunAndGamesEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
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)
.withSceneRequirement(new MoneyRequirement(0, 1.5)) // Cost equal to 1 Max Potion
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -140,11 +142,11 @@ export const FunAndGamesEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -196,7 +198,7 @@ async function summonPlayerPokemon(scene: BattleScene) {
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
scene.currentBattle.enemyParty = [];
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false);
wobbuffet.ivs = [0, 0, 0, 0, 0, 0];
wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ];
wobbuffet.setNature(Nature.MILD);
wobbuffet.setAlpha(0);
wobbuffet.setVisible(false);
@ -230,7 +232,7 @@ function handleLoseMinigame(scene: BattleScene) {
scene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, true);
await showEncounterText(scene, `${namespace}.ko`);
await showEncounterText(scene, `${namespace}:ko`);
const reviveCost = scene.getWaveMoneyAmount(1.5);
updatePlayerMoney(scene, -reviveCost, true, false);
}
@ -255,20 +257,20 @@ function handleNextTurn(scene: BattleScene) {
let isHealPhase = false;
if (healthRatio < 0.03) {
// Grand prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.MULTI_LENS], fillRemaining: false });
resultMessageKey = `${namespace}.best_result`;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:best_result`;
} else if (healthRatio < 0.15) {
// 2nd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SCOPE_LENS], fillRemaining: false });
resultMessageKey = `${namespace}.great_result`;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:great_result`;
} else if (healthRatio < 0.33) {
// 3rd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.WIDE_LENS], fillRemaining: false });
resultMessageKey = `${namespace}.good_result`;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:good_result`;
} else {
// No prize
isHealPhase = true;
resultMessageKey = `${namespace}.bad_result`;
resultMessageKey = `${namespace}:bad_result`;
}
// End the battle
@ -278,7 +280,7 @@ function handleNextTurn(scene: BattleScene) {
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, isHealPhase);
// 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);
// Skip remainder of TurnInitPhase
@ -286,9 +288,9 @@ function handleNextTurn(scene: BattleScene) {
} else {
if (encounter.misc.turnsRemaining < 3) {
// 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--;
}
@ -410,7 +412,7 @@ function hideShowmanIntroSprite(scene: BattleScene) {
// Slide the Wobbuffet and Game over slightly
scene.tweens.add({
targets: [wobbuffet, carnivalGame],
targets: [ wobbuffet, carnivalGame ],
x: "+=16",
ease: "Sine.easeInOut",
duration: 750

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";
/** 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. */
const WONDER_TRADE_SHINY_CHANCE = 512;
@ -35,15 +35,15 @@ const WONDER_TRADE_SHINY_CHANCE = 512;
const MAX_WONDER_TRADE_SHINY_CHANCE = 4096;
const LEGENDARY_TRADE_POOLS = {
1: [Species.RATTATA, Species.PIDGEY, Species.WEEDLE],
2: [Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA],
3: [Species.POOCHYENA, Species.ZIGZAGOON, Species.TAILLOW],
4: [Species.BIDOOF, Species.STARLY, Species.KRICKETOT],
5: [Species.PATRAT, Species.PURRLOIN, Species.PIDOVE],
6: [Species.BUNNELBY, Species.LITLEO, Species.SCATTERBUG],
7: [Species.PIKIPEK, Species.YUNGOOS, Species.ROCKRUFF],
8: [Species.SKWOVET, Species.WOOLOO, Species.ROOKIDEE],
9: [Species.LECHONK, Species.FIDOUGH, Species.TAROUNTULA]
1: [ Species.RATTATA, Species.PIDGEY, Species.WEEDLE ],
2: [ Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA ],
3: [ Species.POOCHYENA, Species.ZIGZAGOON, Species.TAILLOW ],
4: [ Species.BIDOOF, Species.STARLY, Species.KRICKETOT ],
5: [ Species.PATRAT, Species.PURRLOIN, Species.PIDOVE ],
6: [ Species.BUNNELBY, Species.LITLEO, Species.SCATTERBUG ],
7: [ Species.PIKIPEK, Species.YUNGOOS, Species.ROCKRUFF ],
8: [ Species.SKWOVET, Species.WOOLOO, Species.ROOKIDEE ],
9: [ Species.LECHONK, Species.FIDOUGH, Species.TAROUNTULA ]
};
/** Exclude Paradox mons as they aren't considered legendary/mythical */
@ -93,12 +93,13 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -133,9 +134,9 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
secondOptionPrompt: `${namespace}.option.1.trade_options_prompt`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -159,7 +160,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return true;
},
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 line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
@ -201,7 +202,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Show the trade animation
await showTradeBackground(scene);
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);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
await hideTradeBackground(scene);
@ -216,8 +217,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -309,7 +310,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Show the trade animation
await showTradeBackground(scene);
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);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
await hideTradeBackground(scene);
@ -323,9 +324,9 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.trade_options_prompt`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -355,7 +356,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return it.isTransferable;
}).length > 0;
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
}
return null;
@ -387,11 +388,11 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
let item: ModifierTypeOption | null = null;
// TMs excluded from possible rewards
while (!item || item.type.id.includes("TM_")) {
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
}
encounter.setDialogueToken("itemName", item.type.name);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
// Remove the chosen modifier if its stacks go to 0
modifier.stackCount -= 1;
@ -403,18 +404,18 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
// Generate a trainer name
const traderName = generateRandomTraderName();
encounter.setDialogueToken("tradeTrainerName", traderName.trim());
await showEncounterText(scene, `${namespace}.item_trade_selected`);
await showEncounterText(scene, `${namespace}:item_trade_selected`);
leaveEncounterWithoutBattle(scene);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`,
buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}:option.4.tooltip`,
selected: [
{
text: `${namespace}.option.4.selected`,
text: `${namespace}:option.4.selected`,
},
],
},
@ -628,10 +629,10 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
duration: 500,
onComplete: async () => {
scene.fadeOutBgm(1000, false);
await showEncounterText(scene, `${namespace}.pokemon_trade_selected`);
await showEncounterText(scene, `${namespace}:pokemon_trade_selected`);
tradedPokemon.cry();
scene.playBgm("evolution");
await showEncounterText(scene, `${namespace}.pokemon_trade_goodbye`);
await showEncounterText(scene, `${namespace}:pokemon_trade_goodbye`);
tradedPokeball.setAlpha(0);
tradedPokeball.setVisible(true);
@ -650,7 +651,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
// addPokeballOpenParticles(scene, tradedPokemon.x, tradedPokemon.y, tradedPokemon.pokeball);
scene.tweens.add({
targets: [tradedPokemonTintSprite, tradedPokemonSprite],
targets: [ tradedPokemonTintSprite, tradedPokemonSprite ],
duration: 500,
ease: "Sine.easeIn",
scale: 0.25,
@ -726,7 +727,7 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
duration: FADE_DELAY,
onComplete: () => {
scene.tweens.add({
targets: [receivedPokemonSprite, tradedPokemonSprite],
targets: [ receivedPokemonSprite, tradedPokemonSprite ],
y: tradeBaseBg.displayWidth / 2 - 100,
ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 3,

View File

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

View File

@ -20,7 +20,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:mysteriousChallengers";
const namespace = "mysteryEncounters/mysteriousChallengers";
/**
* Mysterious Challengers encounter.
@ -34,7 +34,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -125,16 +125,17 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
},
@ -143,7 +144,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Spawn standard trainer battle with memory mushroom reward
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
@ -155,11 +156,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
},
@ -168,7 +169,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Spawn hard fight
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
@ -180,11 +181,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
},
@ -196,7 +197,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// To avoid player level snowballing from picking this option
encounter.expMultiplier = 0.9;
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
@ -208,7 +209,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
])
.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";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:mysteriousChest";
const namespace = "mysteryEncounters/mysteriousChest";
const RAND_LENGTH = 100;
const TRAP_PERCENT = 35;
@ -58,12 +58,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -76,12 +77,12 @@ export const MysteriousChestEncounter: MysteryEncounter =
species: getPokemonSpecies(Species.GIMMIGHOUL),
formIndex: 0,
isBoss: true,
moveSet: [Moves.NASTY_PLOT, Moves.SHADOW_BALL, Moves.POWER_GEM, Moves.THIEF]
moveSet: [ Moves.NASTY_PLOT, Moves.SHADOW_BALL, Moves.POWER_GEM, Moves.THIEF ]
}
],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(Species.GIMMIGHOUL).getName());
encounter.setDialogueToken("trapPercent", TRAP_PERCENT.toString());
@ -96,11 +97,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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
queueEncounterMessage(scene, `${namespace}.option.1.normal`);
queueEncounterMessage(scene, `${namespace}:option.1.normal`);
leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%)
@ -153,19 +154,19 @@ export const MysteriousChestEncounter: MysteryEncounter =
],
});
// Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.good`);
queueEncounterMessage(scene, `${namespace}:option.1.good`);
leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// 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
queueEncounterMessage(scene, `${namespace}.option.1.great`);
queueEncounterMessage(scene, `${namespace}:option.1.great`);
leaveEncounterWithoutBattle(scene);
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
// Choose 1 MASTER tier item (5%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER] });
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]});
// Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.amazing`);
queueEncounterMessage(scene, `${namespace}:option.1.amazing`);
leaveEncounterWithoutBattle(scene);
} else {
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
@ -173,7 +174,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
koPlayerPokemon(scene, highestLevelPokemon);
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
await showEncounterText(scene, `${namespace}.option.1.bad`);
await showEncounterText(scene, `${namespace}:option.1.bad`);
// Handle game over edge case
const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle());
@ -193,11 +194,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:partTimer";
const namespace = "mysteryEncounters/partTimer";
/**
* Part Timer encounter.
@ -45,11 +45,11 @@ export const PartTimerEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withOnInit((scene: BattleScene) => {
@ -69,17 +69,18 @@ export const PartTimerEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`
text: `${namespace}:option.1.selected`
}
]
})
@ -94,7 +95,7 @@ export const PartTimerEncounter: MysteryEncounter =
// Calculation from Pokemon.calculateStats
const baselineValue = Math.floor(((2 * 90 + 16) * pokemon.level) * 0.01) + 5;
const percentDiff = (pokemon.getStat(Stat.SPD) - baselineValue) / baselineValue;
const moneyMultiplier = Math.min(Math.max(2.5 * (1+ percentDiff), 1), 4);
const moneyMultiplier = Math.min(Math.max(2.5 * (1 + percentDiff), 1), 4);
encounter.misc = {
moneyMultiplier
@ -118,7 +119,7 @@ export const PartTimerEncounter: MysteryEncounter =
// Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -132,14 +133,14 @@ export const PartTimerEncounter: MysteryEncounter =
// Give money and do dialogue
if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`);
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
} else {
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`);
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
}
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false);
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 });
leaveEncounterWithoutBattle(scene);
@ -149,11 +150,11 @@ export const PartTimerEncounter: MysteryEncounter =
.withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -209,14 +210,14 @@ export const PartTimerEncounter: MysteryEncounter =
// Give money and do dialogue
if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`);
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
} else {
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`);
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
}
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false);
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 });
leaveEncounterWithoutBattle(scene);
@ -228,12 +229,12 @@ export const PartTimerEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
],
})
@ -264,11 +265,11 @@ export const PartTimerEncounter: MysteryEncounter =
await transitionMysteryEncounterIntroVisuals(scene, false, false);
// 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);
updatePlayerMoney(scene, moneyChange, true, false);
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 });
leaveEncounterWithoutBattle(scene);
@ -277,8 +278,8 @@ export const PartTimerEncounter: MysteryEncounter =
)
.withOutroDialogue([
{
speaker: `${namespace}.speaker`,
text: `${namespace}.outro`,
speaker: `${namespace}:speaker`,
text: `${namespace}:outro`,
}
])
.build();

View File

@ -21,9 +21,9 @@ import { SummonPhase } from "#app/phases/summon-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** 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 ];
const SAFARI_MONEY_MULTIPLIER = 2;
@ -51,12 +51,13 @@ export const SafariZoneEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
return true;
@ -65,11 +66,11 @@ export const SafariZoneEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneRequirement(new MoneyRequirement(0, SAFARI_MONEY_MULTIPLIER)) // Cost equal to 1 Max Revive
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
})
@ -98,11 +99,11 @@ export const SafariZoneEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -133,11 +134,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.safari.1.label`,
buttonTooltip: `${namespace}.safari.1.tooltip`,
buttonLabel: `${namespace}:safari.1.label`,
buttonTooltip: `${namespace}:safari.1.tooltip`,
selected: [
{
text: `${namespace}.safari.1.selected`,
text: `${namespace}:safari.1.selected`,
}
],
})
@ -168,11 +169,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.safari.2.label`,
buttonTooltip: `${namespace}.safari.2.tooltip`,
buttonLabel: `${namespace}:safari.2.label`,
buttonTooltip: `${namespace}:safari.2.tooltip`,
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
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
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 {
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);
@ -198,11 +199,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.safari.3.label`,
buttonTooltip: `${namespace}.safari.3.tooltip`,
buttonLabel: `${namespace}:safari.3.label`,
buttonTooltip: `${namespace}:safari.3.tooltip`,
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
const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
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 {
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);
@ -227,8 +228,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.safari.4.label`,
buttonTooltip: `${namespace}.safari.4.tooltip`,
buttonLabel: `${namespace}:safari.4.label`,
buttonTooltip: `${namespace}:safari.4.tooltip`,
})
.withOptionPhase(async (scene: BattleScene) => {
// Flee option
@ -253,14 +254,14 @@ async function summonSafariPokemon(scene: BattleScene) {
const encounter = scene.currentBattle.mysteryEncounter!;
// Message pokemon remaining
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
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
let enemySpecies;
let pokemon;
scene.executeWithSeedOffset(() => {
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
const level = scene.currentBattle.getLevelForWave();
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode));
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
@ -518,7 +519,7 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number) {
leaveEncounterWithoutBattle(scene, true);
}
} 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 });
}
}

View File

@ -18,7 +18,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next";
/** 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_EXPENSIVE_PRICE_MULTIPLIER = 3.5;
@ -33,7 +33,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)) // Must have the money for at least the cheap deal
.withPrimaryPokemonHealthRatioRequirement([0.51, 1]) // At least 1 Pokemon must have above half HP
.withPrimaryPokemonHealthRatioRequirement([ 0.51, 1 ]) // At least 1 Pokemon must have above half HP
.withIntroSpriteConfigs([
{
spriteKey: Species.KROOKODILE.toString(),
@ -55,26 +55,27 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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;
}
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
}
return null;
@ -139,8 +140,8 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
chosenPokemon.nature = newNature;
encounter.setDialogueToken("newNature", getNatureName(newNature));
queueEncounterMessage(scene, `${namespace}.cheap_side_effects`);
setEncounterExp(scene, [chosenPokemon.id], 100);
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo();
})
.build()
@ -150,11 +151,11 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -200,8 +201,8 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon;
queueEncounterMessage(scene, `${namespace}.no_bad_effects`);
setEncounterExp(scene, [chosenPokemon.id], 100);
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo();
})
@ -209,12 +210,12 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
speaker: `${namespace}.speaker`
text: `${namespace}:option.3.selected`,
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";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounter:slumberingSnorlax";
const namespace = "mysteryEncounters/slumberingSnorlax";
/**
* Sleeping Snorlax encounter.
@ -34,6 +34,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: Species.SNORLAX.toString(),
@ -47,7 +48,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -59,15 +60,15 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
const pokemonConfig: EnemyPokemonConfig = {
species: bossSpecies,
isBoss: true,
status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves
moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT],
status: [ StatusEffect.SLEEP, 5 ], // Extra turns on timer for Snorlax's start of fight moves
moveSet: [ Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
stackCount: 2
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
stackCount: 2
},
],
@ -76,44 +77,45 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
};
const config: EnemyPartyConfig = {
levelAdditiveModifier: 0.5,
pokemonConfigs: [pokemonConfig],
pokemonConfigs: [ pokemonConfig ],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
// Load animations/sfx for Snorlax fight start moves
loadCustomMovesForEncounter(scene, [Moves.SNORE]);
loadCustomMovesForEncounter(scene, [ Moves.SNORE ]);
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: true });
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.SNORE),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.SNORE),
ignorePp: true
});
@ -122,11 +124,11 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -134,7 +136,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
// Fall asleep waiting for Snorlax
// Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true));
queueEncounterMessage(scene, `${namespace}.option.2.rest_result`);
queueEncounterMessage(scene, `${namespace}:option.2.rest_result`);
leaveEncounterWithoutBattle(scene);
}
)
@ -143,19 +145,19 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES))
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`
text: `${namespace}:option.3.selected`
}
]
})
.withOptionPhase(async (scene: BattleScene) => {
// Steal the Snorlax's Leftovers
const instance = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: false });
// Snorlax exp to Pokemon that did the stealing
setEncounterExp(scene, instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp);
leaveEncounterWithoutBattle(scene);

View File

@ -23,11 +23,11 @@ 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";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:teleportingHijinks";
const namespace = "mysteryEncounters/teleportingHijinks";
const MONEY_COST_MULTIPLIER = 1.75;
const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO];
const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL];
const BIOME_CANDIDATES = [ Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO ];
const MACHINE_INTERFACING_TYPES = [ Type.ELECTRIC, Type.STEEL ];
/**
* Teleporting Hijinks encounter.
@ -38,10 +38,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TELEPORTING_HIJINKS)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new WaveModulusRequirement([1, 2, 3], 10)) // Must be in first 3 waves after boss wave
.withSceneRequirement(new WaveModulusRequirement([ 1, 2, 3 ], 10)) // Must be in first 3 waves after boss wave
.withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: "teleporting_hijinks_teleporter",
@ -54,12 +55,13 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
@ -75,11 +77,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
}
],
})
@ -99,12 +101,12 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPokemonTypeRequirement(MACHINE_INTERFACING_TYPES, true, 1) // Must have Steel or Electric type
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
}
],
})
@ -118,11 +120,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
text: `${namespace}:option.3.selected`,
},
],
},
@ -144,9 +146,9 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
}],
};
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.STEEL])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.ELECTRIC])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [magnet, metalCoat], fillRemaining: true });
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
transitionMysteryEncounterIntroVisuals(scene, true, true);
await initBattleWithEnemyConfig(scene, config);
}
@ -161,10 +163,10 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
// Show dialogue and transition biome
await showEncounterText(scene, `${namespace}.transport`);
await Promise.all([animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene)]);
await showEncounterText(scene, `${namespace}:transport`);
await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]);
scene.playBgm();
await showEncounterText(scene, `${namespace}.attacked`);
await showEncounterText(scene, `${namespace}:attacked`);
// Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
@ -174,8 +176,8 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
// 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 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
const config: EnemyPartyConfig = {
pokemonConfigs: [{
@ -183,9 +185,9 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
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));
}
}],
@ -197,7 +199,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) {
return new Promise<void>(resolve => {
scene.tweens.add({
targets: [scene.arenaEnemy, scene.lastEnemyTrainer],
targets: [ scene.arenaEnemy, scene.lastEnemyTrainer ],
x: "+=300",
duration: 2000,
onComplete: () => {
@ -213,7 +215,7 @@ async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) {
scene.arenaPlayerTransition.setVisible(true);
scene.tweens.add({
targets: [scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition],
targets: [ scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition ],
duration: 1000,
ease: "Sine.easeInOut",
alpha: (target: any) => target === scene.arenaPlayer ? 0 : 1,

View File

@ -10,7 +10,8 @@ import { Biome } from "#enums/biome";
import { TrainerType } from "#enums/trainer-type";
import i18next from "i18next";
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 { Moves } from "#enums/moves";
import { PlayerPokemon } from "#app/field/pokemon";
@ -26,7 +27,7 @@ import { Type } from "#app/data/type";
import { getPokeballTintColor } from "#app/data/pokeball";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:expertPokemonBreeder";
const namespace = "mysteryEncounters/theExpertPokemonBreeder";
const trainerNameKey = "trainerNames:expert_pokemon_breeder";
@ -47,29 +48,29 @@ class BreederSpeciesEvolution {
}
const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
[Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.HAPPINY, new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.MAGBY, new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.ELEKID, new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.BUDEW, new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.MIME_JR, new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE)]
[ Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.HAPPINY, new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.MAGBY, new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.ELEKID, new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.BUDEW, new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.MIME_JR, new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE) ]
];
const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.JYNX],
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.IGGLYBUFF, new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.AZURILL, new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE)],
[Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)],
[Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)]
[ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.JYNX ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.IGGLYBUFF, new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.AZURILL, new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE) ],
[ Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE) ],
[ Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE) ]
];
/**
@ -85,11 +86,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
.withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: trainerNameKey,
text: `${namespace}.intro_dialogue`,
text: `${namespace}:intro_dialogue`,
},
])
.withOnInit((scene: BattleScene) => {
@ -137,46 +138,46 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.setDialogueToken("pokemon3Name", pokemon3.getNameToRender());
// Dialogue and egg calcs for Pokemon 1
const [pokemon1CommonEggs, pokemon1RareEggs] = calculateEggRewardsForPokemon(pokemon1);
let pokemon1Tooltip = getEncounterText(scene, `${namespace}.option.1.tooltip_base`)!;
const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1);
let pokemon1Tooltip = getEncounterText(scene, `${namespace}:option.1.tooltip_base`)!;
if (pokemon1RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1RareEggs", eggsText);
}
if (pokemon1CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
}
encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip;
// Dialogue and egg calcs for Pokemon 2
const [pokemon2CommonEggs, pokemon2RareEggs] = calculateEggRewardsForPokemon(pokemon2);
let pokemon2Tooltip = getEncounterText(scene, `${namespace}.option.2.tooltip_base`)!;
const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2);
let pokemon2Tooltip = getEncounterText(scene, `${namespace}:option.2.tooltip_base`)!;
if (pokemon2RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon2RareEggs", eggsText);
}
if (pokemon2CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
}
encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip;
// Dialogue and egg calcs for Pokemon 3
const [pokemon3CommonEggs, pokemon3RareEggs] = calculateEggRewardsForPokemon(pokemon3);
let pokemon3Tooltip = getEncounterText(scene, `${namespace}.option.3.tooltip_base`)!;
const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3);
let pokemon3Tooltip = getEncounterText(scene, `${namespace}:option.3.tooltip_base`)!;
if (pokemon3RareEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon3RareEggs", eggsText);
}
if (pokemon3CommonEggs > 0) {
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") });
pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
encounter.setDialogueToken("pokemon3CommonEggs", eggsText);
}
encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip;
@ -195,18 +196,19 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonLabel: `${namespace}:option.1.label`,
selected: [
{
speaker: trainerNameKey,
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
})
@ -228,17 +230,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [
{
speaker: trainerNameKey,
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
];
if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) {
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")) {
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
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonLabel: `${namespace}:option.2.label`,
selected: [
{
speaker: trainerNameKey,
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
})
@ -280,17 +282,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [
{
speaker: trainerNameKey,
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
];
if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) {
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")) {
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
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonLabel: `${namespace}:option.3.label`,
selected: [
{
speaker: trainerNameKey,
text: `${namespace}.option.selected`,
text: `${namespace}:option.selected`,
},
],
})
@ -332,17 +334,17 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
encounter.dialogue.outro = [
{
speaker: trainerNameKey,
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
];
if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) {
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")) {
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([
{
speaker: trainerNameKey,
text: `${namespace}.outro`,
text: `${namespace}:outro`,
},
])
.build();
@ -374,17 +376,17 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
trainerType: TrainerType.EXPERT_POKEMON_BREEDER,
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),
isBoss: false,
abilityIndex: 1, // Magic Guard
shiny: false,
nature: Nature.ADAMANT,
moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH],
ivs: [31, 31, 31, 31, 31, 31],
moveSet: [ Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH ],
ivs: [ 31, 31, 31, 31, 31, 31 ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [Type.STEEL]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType,
}
]
}
@ -394,26 +396,26 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
if (scene.arena.biomeType === Biome.SPACE) {
// All 3 members always Cleffa line, but different configs
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),
isBoss: false,
abilityIndex: 1, // Magic Guard
shiny: true,
variant: 1,
nature: Nature.MODEST,
moveSet: [Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT],
ivs: [31, 31, 31, 31, 31, 31]
moveSet: [ Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT ],
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),
isBoss: false,
abilityIndex: 2, // Friend Guard / Unaware
shiny: true,
variant: 2,
nature: Nature.BOLD,
moveSet: [Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT],
ivs: [31, 31, 31, 31, 31, 31]
moveSet: [ Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT ],
ivs: [ 31, 31, 31, 31, 31, 31 ]
});
} else {
// Second member from pool 1
@ -424,12 +426,12 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
baseConfig.pokemonConfigs!.push({
species: getPokemonSpecies(pool1Species),
isBoss: false,
ivs: [31, 31, 31, 31, 31, 31]
ivs: [ 31, 31, 31, 31, 31, 31 ]
},
{
species: getPokemonSpecies(pool2Species),
isBoss: false,
ivs: [31, 31, 31, 31, 31, 31]
ivs: [ 31, 31, 31, 31, 31, 31 ]
});
}
@ -437,8 +439,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
}
function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species {
const poolCopy = speciesPool.slice(0);
randSeedShuffle(poolCopy);
const poolCopy = randSeedShuffle(speciesPool.slice(0));
const speciesEvolutions = poolCopy.pop()!.slice(0);
let speciesObject = speciesEvolutions.pop()!;
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)
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;
// 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0)
if (speciesStarters.hasOwnProperty(rootSpecies)) {
const starterTier = speciesStarters[rootSpecies];
if (speciesStarterCosts.hasOwnProperty(rootSpecies)) {
const starterTier = speciesStarterCosts[rootSpecies];
pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12);
}
@ -468,11 +469,11 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
// 1 Common egg for every point leftover
const numCommons = totalPoints % 6;
return [numCommons, numRares];
return [ numCommons, numRares ];
}
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[] = [];
if (commonEggs > 0) {
@ -493,7 +494,7 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
pulled: false,
sourceType: EggSourceType.EVENT,
eggDescriptor: eggDescription,
tier: EggTier.GREAT
tier: EggTier.RARE
});
}
}
@ -541,7 +542,7 @@ function onGameOver(scene: BattleScene) {
encounter.dialogue.outro = [
{
speaker: trainerNameKey,
text: `${namespace}.outro_failed`,
text: `${namespace}:outro_failed`,
},
];
@ -559,6 +560,10 @@ function onGameOver(scene: BattleScene) {
// Revert BGM
scene.playBgm(scene.arena.bgm);
// Clear any leftover battle phases
scene.clearPhaseQueue();
scene.clearPhaseQueueSplice();
// Return enemy Pokemon
const pokemon = scene.getEnemyPokemon();
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 { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
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 { PokeballType } from "#app/data/pokeball";
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";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:pokemonSalesman";
const namespace = "mysteryEncounters/thePokemonSalesman";
const MAX_POKEMON_PRICE_MULTIPLIER = 4;
@ -45,25 +46,26 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
let tries = 0;
// Reroll any species that don't have HAs
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
species = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
tries++;
}
@ -88,15 +90,15 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
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
let priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER * (Math.max(starterTier, 2.5) / 5);
if (pokemon.shiny) {
// Always max price for shiny (flip HA back to normal), and add special messaging
priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER;
pokemon.abilityIndex = 0;
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}.description_shiny`;
encounter.options[0].dialogue!.buttonTooltip = `${namespace}.option.1.tooltip_shiny`;
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}:description_shiny`;
encounter.options[0].dialogue!.buttonTooltip = `${namespace}:option.1.tooltip_shiny`;
}
const price = scene.getWaveMoneyAmount(priceMultiplier);
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
@ -116,11 +118,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
.withHasDexProgress(true)
.withSceneMoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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);
// 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);
// "Catch" purchased pokemon
@ -147,11 +149,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theStrongStuff";
const namespace = "mysteryEncounters/theStrongStuff";
// Halved for HP stat
const HIGH_BST_REDUCTION_VALUE = 15;
@ -39,6 +39,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
.withMaxAllowedEncounters(1)
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: "berry_juice",
@ -63,7 +64,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -80,52 +81,53 @@ export const TheStrongStuffEncounter: MysteryEncounter =
bossSegments: 5,
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
nature: Nature.BOLD,
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2
}
],
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 2));
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2));
}
}
],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
loadCustomMovesForEncounter(scene, [ Moves.GASTRO_ACID, Moves.STEALTH_ROCK ]);
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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("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 = [
{
text: `${namespace}.outro`,
text: `${namespace}:outro`,
}
];
setEncounterRewards(scene, { fillRemaining: true });
@ -172,28 +174,28 @@ export const TheStrongStuffEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SOUL_DEW ], fillRemaining: true });
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.GASTRO_ACID),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.STEALTH_ROCK),
ignorePp: true
});

View File

@ -25,7 +25,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theWinstrateChallenge";
const namespace = "mysteryEncounters/theWinstrateChallenge";
/**
* The Winstrate Challenge encounter.
@ -74,11 +74,11 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withAutoHideIntroVisuals(false)
@ -94,17 +94,18 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.1.selected`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.1.selected`,
},
],
},
@ -119,19 +120,19 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected`,
speaker: `${namespace}:speaker`,
text: `${namespace}:option.2.selected`,
},
],
},
async (scene: BattleScene) => {
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
scene.unshiftPhase(new PartyHealPhase(scene, true));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.RARER_CANDY ], fillRemaining: false });
leaveEncounterWithoutBattle(scene);
}
)
@ -142,7 +143,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
const nextConfig = encounter.enemyPartyConfigs.pop();
if (!nextConfig) {
await transitionMysteryEncounterIntroVisuals(scene, false, false);
await showEncounterDialogue(scene, `${namespace}.victory`, `${namespace}.speaker`);
await showEncounterDialogue(scene, `${namespace}:victory`, `${namespace}:speaker`);
// Give 10x Voucher
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
@ -150,11 +151,11 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
scene.playSound("item_fanfare");
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
const machoBrace = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
machoBrace.type.tier = ModifierTier.MASTER;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [machoBrace], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ machoBrace ], fillRemaining: false });
encounter.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, false, MysteryEncounterMode.NO_BATTLE);
} else {
@ -232,7 +233,7 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 0, // Guts
nature: Nature.ADAMANT,
moveSet: [Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK],
moveSet: [ Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
@ -250,7 +251,7 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 1, // Guts
nature: Nature.ADAMANT,
moveSet: [Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH],
moveSet: [ Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
@ -276,7 +277,7 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 0, // Natural Cure
nature: Nature.CALM,
moveSet: [Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER],
moveSet: [ Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
@ -294,15 +295,15 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
formIndex: 1,
nature: Nature.TIMID,
moveSet: [Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP],
moveSet: [ Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.PSYCHIC]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.PSYCHIC ]) as PokemonHeldItemModifierType,
stackCount: 1,
isTransferable: false
},
{
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FAIRY]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FAIRY ]) as PokemonHeldItemModifierType,
stackCount: 1,
isTransferable: false
}
@ -321,15 +322,15 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 3, // Lightning Rod
nature: Nature.ADAMANT,
moveSet: [Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST],
moveSet: [ Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2,
isTransferable: false
},
{
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
stackCount: 4,
isTransferable: false
}
@ -340,10 +341,10 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 1, // Poison Heal
nature: Nature.JOLLY,
moveSet: [Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH],
moveSet: [ Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
stackCount: 4,
isTransferable: false
},
@ -358,7 +359,7 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
formIndex: 1,
nature: Nature.CALM,
moveSet: [Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT],
moveSet: [ Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
@ -380,7 +381,7 @@ function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
formIndex: 1,
nature: Nature.IMPISH,
moveSet: [Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH],
moveSet: [ Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
@ -401,10 +402,10 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 0, // Soundproof
nature: Nature.MODEST,
moveSet: [Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE],
moveSet: [ Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.SPD ]) as PokemonHeldItemModifierType,
stackCount: 2,
isTransferable: false
}
@ -415,50 +416,50 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 2, // Gluttony
nature: Nature.QUIET,
moveSet: [Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE],
moveSet: [ Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.STARF]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.STARF ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SALAC]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SALAC ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LANSAT]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LANSAT ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LIECHI]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LIECHI ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.PETAYA]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.PETAYA ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
stackCount: 2,
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LEPPA]) as PokemonHeldItemModifierType,
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LEPPA ]) as PokemonHeldItemModifierType,
stackCount: 2,
}
]
@ -468,7 +469,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 2, // Tangled Feet
nature: Nature.JOLLY,
moveSet: [Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF],
moveSet: [ Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
@ -482,7 +483,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
formIndex: 1,
nature: Nature.BOLD,
moveSet: [Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT],
moveSet: [ Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
@ -496,7 +497,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
isBoss: false,
abilityIndex: 0, // Sheer Force
nature: Nature.IMPISH,
moveSet: [Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE],
moveSet: [ Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE ],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,

View File

@ -1,7 +1,7 @@
import { Ability, allAbilities } from "#app/data/ability";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
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 { PokemonHeldItemModifier } from "#app/modifier/modifier";
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";
/** The i18n namespace for the encounter */
const namespace = "mysteryEncounter:trainingSession";
const namespace = "mysteryEncounters/trainingSession";
/**
* Training Session encounter.
@ -49,22 +49,23 @@ export const TrainingSessionEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -101,7 +102,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
encounter.setDialogueToken("stat1", "-");
encounter.setDialogueToken("stat2", "-");
// Add the pokemon back to party with IV boost
const ivIndexes: any[] = [];
let ivIndexes: any[] = [];
playerPokemon.ivs.forEach((iv, index) => {
if (iv < 31) {
ivIndexes.push({ iv: iv, index: index });
@ -117,7 +118,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
// 25-27 starting IV caps in 2 encounters
let improvedCount = 0;
while (ivIndexes.length > 0 && improvedCount < 2) {
randSeedShuffle(ivIndexes);
ivIndexes = randSeedShuffle(ivIndexes);
const ivToChange = ivIndexes.pop();
let newVal = ivToChange.iv;
if (improvedCount === 0) {
@ -145,10 +146,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
if (improvedCount > 0) {
playerPokemon.calculateStats();
scene.gameData.updateSpeciesDexIvs(
playerPokemon.species.getRootSpeciesId(true),
playerPokemon.ivs
);
scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
scene.gameData.setPokemonCaught(playerPokemon, false);
}
@ -159,7 +157,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.addModifier(mod, true, false, false, true);
}
scene.updateModifiers(true);
queueEncounterMessage(scene, `${namespace}.option.1.finished`);
queueEncounterMessage(scene, `${namespace}:option.1.finished`);
};
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
@ -173,12 +171,12 @@ export const TrainingSessionEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
secondOptionPrompt: `${namespace}.option.2.select_prompt`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}:option.2.select_prompt`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -224,7 +222,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}.option.2.finished`);
queueEncounterMessage(scene, `${namespace}:option.2.finished`);
// Add the pokemon back to party with Nature change
playerPokemon.setNature(encounter.misc.chosenNature);
scene.gameData.setPokemonCaught(playerPokemon, false);
@ -249,12 +247,12 @@ export const TrainingSessionEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:option.3.select_prompt`,
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
const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`);
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
@ -319,30 +317,26 @@ export const TrainingSessionEncounter: MysteryEncounter =
scene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}.option.3.finished`);
queueEncounterMessage(scene, `${namespace}:option.3.finished`);
// Add the pokemon back to party with ability change
const abilityIndex = encounter.misc.abilityIndex;
if (!!playerPokemon.getFusionSpeciesForm()) {
playerPokemon.fusionAbilityIndex = abilityIndex;
if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) {
scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId]
.abilityAttr |=
abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2
? Math.pow(2, playerPokemon.fusionAbilityIndex)
: AbilityAttr.ABILITY_HIDDEN;
// Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals)
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
if (!isNullOrUndefined(rootFusionSpecies)
&& 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;
}
} else {
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();
scene.gameData.setPokemonCaught(playerPokemon, false);
@ -363,11 +357,11 @@ export const TrainingSessionEncounter: MysteryEncounter =
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`,
buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}:option.4.tooltip`,
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";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:trashToTreasure";
const namespace = "mysteryEncounters/trashToTreasure";
const SOUND_EFFECT_WAIT_TIME = 700;
@ -36,6 +36,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withMaxAllowedEncounters(1)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: Species.GARBODOR.toString() + "-gigantamax",
@ -50,12 +51,13 @@ export const TrashToTreasureEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
@ -66,17 +68,17 @@ export const TrashToTreasureEncounter: MysteryEncounter =
isBoss: true,
formIndex: 1, // Gmax
bossSegmentModifier: 1, // +1 Segment from normal
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH]
moveSet: [ Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH ]
};
const config: EnemyPartyConfig = {
levelAdditiveModifier: 1,
pokemonConfigs: [pokemonConfig],
pokemonConfigs: [ pokemonConfig ],
disableSwitch: true
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
// Load animations/sfx for Garbodor fight start moves
loadCustomMovesForEncounter(scene, [Moves.TOXIC, Moves.AMNESIA]);
loadCustomMovesForEncounter(scene, [ Moves.TOXIC, Moves.AMNESIA ]);
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
@ -89,11 +91,11 @@ export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
})
@ -106,7 +108,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
transitionMysteryEncounterIntroVisuals(scene);
await tryApplyDigRewardItems(scene);
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [SHOP_ITEM_COST_MULTIPLIER]);
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
const modifier = blackSludge?.newModifier();
if (modifier) {
await scene.addModifier(modifier, false, false, false, true);
@ -122,33 +124,33 @@ export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
// Investigate garbage, battle Gmax Garbodor
scene.setFieldScale(0.75);
await showEncounterText(scene, `${namespace}.option.2.selected_2`);
await showEncounterText(scene, `${namespace}:option.2.selected_2`);
transitionMysteryEncounterIntroVisuals(scene);
const encounter = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
targets: [ BattlerIndex.PLAYER ],
move: new PokemonMove(Moves.TOXIC),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
targets: [ BattlerIndex.ENEMY ],
move: new PokemonMove(Moves.AMNESIA),
ignorePp: true
});

View File

@ -25,7 +25,7 @@ import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:uncommonBreed";
const namespace = "mysteryEncounters/uncommonBreed";
/**
* Uncommon Breed encounter.
@ -38,10 +38,11 @@ export const UncommonBreedEncounter: MysteryEncounter =
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
@ -59,21 +60,22 @@ export const UncommonBreedEncounter: MysteryEncounter =
const eggMoveIndex = randSeedInt(4);
const randomEggMove: Moves = eggMoves[eggMoveIndex];
encounter.misc = {
eggMove: randomEggMove
eggMove: randomEggMove,
pokemon: pokemon
};
if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
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
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
const config: EnemyPartyConfig = {
pokemonConfigs: [{
@ -81,14 +83,14 @@ export const UncommonBreedEncounter: MysteryEncounter =
species: species,
dataSource: new PokemonData(pokemon),
isBoss: false,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
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));
}
}],
};
encounter.enemyPartyConfigs = [config];
encounter.enemyPartyConfigs = [ config ];
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
encounter.spriteConfigs = [
@ -123,16 +125,17 @@ export const UncommonBreedEncounter: MysteryEncounter =
scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2"));
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
},
],
},
@ -150,7 +153,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [target],
targets: [ target ],
move: pokemonMove,
ignorePp: true
});
@ -165,12 +168,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`
text: `${namespace}:option.2.selected`
}
]
})
@ -179,7 +182,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
// Remove 4 random berries from player's party
// Get all player berry items, remove from party, and store reference
const berryItems: BerryModifier[]= scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const berryItems: BerryModifier[] = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
for (let i = 0; i < 4; i++) {
const index = randSeedInt(berryItems.length);
const randBerry = berryItems[index];
@ -209,12 +212,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
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";
/** 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 */
const EXCLUDED_TRANSFORMATION_SPECIES = [
@ -89,12 +89,12 @@ const PERCENT_LEVEL_LOSS_ON_REFUSE = 12.5;
* Value ranges of the resulting species BST transformations after adding values to original species
* 2 Pokemon in the party use this range
*/
const HIGH_BST_TRANSFORM_BASE_VALUES: [number, number] = [90, 110];
const HIGH_BST_TRANSFORM_BASE_VALUES: [number, number] = [ 90, 110 ];
/**
* Value ranges of the resulting species BST transformations after adding values to original species
* All remaining Pokemon in the party use this range
*/
const STANDARD_BST_TRANSFORM_BASE_VALUES: [number, number] = [40, 50];
const STANDARD_BST_TRANSFORM_BASE_VALUES: [number, number] = [ 40, 50 ];
/**
* Weird Dream encounter.
@ -118,16 +118,17 @@ export const WeirdDreamEncounter: MysteryEncounter =
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
text: `${namespace}:intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}:speaker`,
text: `${namespace}:intro_dialogue`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
return true;
@ -141,11 +142,11 @@ export const WeirdDreamEncounter: MysteryEncounter =
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withHasDexProgress(true)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
text: `${namespace}:option.1.selected`,
}
],
})
@ -165,7 +166,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
})
.withOptionPhase(async (scene: BattleScene) => {
// 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
// Wait for all new Pokemon assets to be loaded before showing transformation animations
@ -189,21 +190,21 @@ export const WeirdDreamEncounter: MysteryEncounter =
await cutsceneDialoguePromise;
doHideDreamBackground(scene);
await showEncounterText(scene, `${namespace}.option.1.dream_complete`);
await showEncounterText(scene, `${namespace}:option.1.dream_complete`);
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 ]});
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
text: `${namespace}:option.2.selected`,
},
],
},
@ -368,11 +369,11 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
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)
addFavoredMoveToNewPokemonMoveset(scene, newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
// Randomize the second type of the pokemon
// If the pokemon does not normally have a second type, it will gain 1
const newTypes = [newPokemon.getTypes()[0]];
const newTypes = [ newPokemon.getTypes()[0] ];
let newType = randSeedInt(18) as Type;
while (newType === newTypes[0]) {
newType = randSeedInt(18) as Type;
@ -390,14 +391,14 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
// Any pokemon that is at or below 450 BST gets +20 permanent BST to 3 stats: HP (halved, +10), lowest of Atk/SpAtk, and lowest of Def/SpDef
if (newPokemon.getSpeciesForm().getBaseStatTotal() <= GAIN_OLD_GATEAU_ITEM_BST_THRESHOLD) {
const stats: Stat[] = [Stat.HP];
const stats: Stat[] = [ Stat.HP ];
const baseStats = newPokemon.getSpeciesForm().baseStats.slice(0);
// Attack or SpAtk
stats.push(baseStats[Stat.ATK] < baseStats[Stat.SPATK] ? Stat.ATK : Stat.SPATK);
// Def or SpDef
stats.push(baseStats[Stat.DEF] < baseStats[Stat.SPDEF] ? Stat.DEF : Stat.SPDEF);
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
.generateType(scene.getParty(), [20, stats])
.generateType(scene.getParty(), [ 20, stats ])
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
const modifier = modType?.newModifier(newPokemon);
if (modifier) {
@ -553,8 +554,7 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla
let eggMoveIndex: null | number = null;
const eggMoves = newPokemon.getEggMoves()?.slice(0);
if (eggMoves) {
const eggMoveIndices = [0, 1, 2, 3];
randSeedShuffle(eggMoveIndices);
const eggMoveIndices = randSeedShuffle([ 0, 1, 2, 3 ]);
let randomEggMoveIndex = eggMoveIndices.pop();
let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
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`)
* @param scene
* @param newPokemon
* @param newPokemonGeneratedMoveset
* @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;
for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced
@ -614,11 +613,15 @@ function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Playe
}
// Finally, assign favored move to random index that isn't the new egg move index
if (favoredMove) {
let favoredMoveIndex = randSeedInt(4);
while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) {
favoredMoveIndex = randSeedInt(4);
}
if (newPokemon.moveset.length < 4) {
newPokemon.moveset.push(favoredMove);
} else {
let favoredMoveIndex = randSeedInt(4);
while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) {
favoredMoveIndex = randSeedInt(4);
}
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 { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day";
import { Nature } from "../nature";
import { EvolutionItem, pokemonEvolutions } from "../pokemon-evolutions";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "../pokemon-forms";
import { SpeciesFormKey } from "../pokemon-species";
import { StatusEffect } from "../status-effect";
import { Type } from "../type";
import { WeatherType } from "../weather";
import { Nature } from "#app/data/nature";
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
import { StatusEffect } from "#app/data/status-effect";
import { Type } from "#app/data/type";
import { WeatherType } from "#app/data/weather";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { SpeciesFormKey } from "#enums/species-form-key";
export interface EncounterRequirement {
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met
@ -147,7 +147,7 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["previousEncounter", scene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? ""];
return [ "previousEncounter", scene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? "" ];
}
}
@ -175,7 +175,7 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["waveIndex", scene.currentBattle.waveIndex.toString()];
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ];
}
}
@ -204,7 +204,7 @@ export class WaveModulusRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["waveIndex", scene.currentBattle.waveIndex.toString()];
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ];
}
}
@ -213,7 +213,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
super();
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [ timeOfDay ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -226,7 +226,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["timeOfDay", TimeOfDay[scene.arena.getTimeOfDay()].toLocaleLowerCase()];
return [ "timeOfDay", TimeOfDay[scene.arena.getTimeOfDay()].toLocaleLowerCase() ];
}
}
@ -235,7 +235,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
constructor(weather: WeatherType | WeatherType[]) {
super();
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
this.requiredWeather = Array.isArray(weather) ? weather : [ weather ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -253,7 +253,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
if (!isNullOrUndefined(currentWeather)) {
token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase();
}
return ["weather", token];
return [ "weather", token ];
}
}
@ -285,7 +285,7 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["partySize", scene.getParty().length.toString()];
return [ "partySize", scene.getParty().length.toString() ];
}
}
@ -296,7 +296,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
constructor(heldItem: string | string[], minNumberOfItems: number = 1) {
super();
this.minNumberOfItems = minNumberOfItems;
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [ heldItem ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -318,7 +318,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["requiredItem", this.requiredHeldItemModifiers[0]];
return [ "requiredItem", this.requiredHeldItemModifiers[0] ];
}
}
@ -346,7 +346,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const value = this.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
return ["money", value];
return [ "money", value ];
}
}
@ -359,7 +359,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredSpecies = Array.isArray(species) ? species : [species];
this.requiredSpecies = Array.isArray(species) ? species : [ species ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -381,9 +381,9 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
return ["species", Species[pokemon.species.speciesId]];
return [ "species", Species[pokemon.species.speciesId] ];
}
return ["species", ""];
return [ "species", "" ];
}
}
@ -397,7 +397,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredNature = Array.isArray(nature) ? nature : [nature];
this.requiredNature = Array.isArray(nature) ? nature : [ nature ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -419,9 +419,9 @@ export class NatureRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) {
return ["nature", Nature[pokemon.nature]];
return [ "nature", Nature[pokemon.nature] ];
}
return ["nature", ""];
return [ "nature", "" ];
}
}
@ -436,7 +436,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
this.excludeFainted = excludeFainted;
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredType = Array.isArray(type) ? type : [type];
this.requiredType = Array.isArray(type) ? type : [ type ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -465,9 +465,9 @@ export class TypeRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty));
if (includedTypes.length > 0) {
return ["type", Type[includedTypes[0]]];
return [ "type", Type[includedTypes[0]] ];
}
return ["type", ""];
return [ "type", "" ];
}
}
@ -481,7 +481,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
this.requiredMoves = Array.isArray(moves) ? moves : [ moves ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -504,9 +504,9 @@ export class MoveRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId));
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
return ["move", includedMoves[0].getName()];
return [ "move", includedMoves[0].getName() ];
}
return ["move", ""];
return [ "move", "" ];
}
}
@ -525,7 +525,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [ learnableMove ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -548,9 +548,9 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove));
if (includedCompatMoves.length > 0) {
return ["compatibleMove", Moves[includedCompatMoves[0]]];
return [ "compatibleMove", Moves[includedCompatMoves[0]] ];
}
return ["compatibleMove", ""];
return [ "compatibleMove", "" ];
}
}
@ -564,7 +564,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
this.requiredAbilities = Array.isArray(abilities) ? abilities : [ abilities ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -586,9 +586,9 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (pokemon?.getAbility().id && this.requiredAbilities.some(a => pokemon.getAbility().id === a)) {
return ["ability", pokemon.getAbility().name];
return [ "ability", pokemon.getAbility().name ];
}
return ["ability", ""];
return [ "ability", "" ];
}
}
@ -601,7 +601,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [ statusEffect ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -649,9 +649,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
return pokemon!.status?.effect === a;
});
if (reqStatus.length > 0) {
return ["status", StatusEffect[reqStatus[0]]];
return [ "status", StatusEffect[reqStatus[0]] ];
}
return ["status", ""];
return [ "status", "" ];
}
}
@ -670,7 +670,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [ formChangeItem ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -706,9 +706,9 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem));
if (requiredItems.length > 0) {
return ["formChangeItem", FormChangeItem[requiredItems[0]]];
return [ "formChangeItem", FormChangeItem[requiredItems[0]] ];
}
return ["formChangeItem", ""];
return [ "formChangeItem", "" ];
}
}
@ -722,7 +722,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [ evolutionItems ];
}
override meetsRequirement(scene: BattleScene): boolean {
@ -756,9 +756,9 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredEvolutionItem.filter((evoItem) => this.filterByEvo(pokemon, evoItem));
if (requiredItems.length > 0) {
return ["evolutionItem", EvolutionItem[requiredItems[0]]];
return [ "evolutionItem", EvolutionItem[requiredItems[0]] ];
}
return ["evolutionItem", ""];
return [ "evolutionItem", "" ];
}
}
@ -772,7 +772,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [ heldItem ];
this.requireTransferable = requireTransferable;
}
@ -807,9 +807,9 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
&& (!this.requireTransferable || it.isTransferable);
});
if (requiredItems && requiredItems.length > 0) {
return ["heldItem", requiredItems[0].type.name];
return [ "heldItem", requiredItems[0].type.name ];
}
return ["heldItem", ""];
return [ "heldItem", "" ];
}
}
@ -823,7 +823,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
super();
this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery;
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [ heldItemTypes ];
this.requireTransferable = requireTransferable;
}
@ -864,9 +864,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
&& (!this.requireTransferable || it.isTransferable);
});
if (requiredItems && requiredItems.length > 0) {
return ["heldItem", requiredItems[0].type.name];
return [ "heldItem", requiredItems[0].type.name ];
}
return ["heldItem", ""];
return [ "heldItem", "" ];
}
}
@ -904,7 +904,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["level", pokemon?.level.toString() ?? ""];
return [ "level", pokemon?.level.toString() ?? "" ];
}
}
@ -942,7 +942,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["friendship", pokemon?.friendship.toString() ?? ""];
return [ "friendship", pokemon?.friendship.toString() ?? "" ];
}
}
@ -989,9 +989,9 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const hpRatio = pokemon?.getHpRatio();
if (!isNullOrUndefined(hpRatio)) {
return ["healthRatio", Math.floor(hpRatio * 100).toString() + "%"];
return [ "healthRatio", Math.floor(hpRatio * 100).toString() + "%" ];
}
return ["healthRatio", ""];
return [ "healthRatio", "" ];
}
}
@ -1029,8 +1029,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["weight", pokemon?.getWeight().toString() ?? ""];
return [ "weight", pokemon?.getWeight().toString() ?? "" ];
}
}

View File

@ -190,7 +190,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
secondaryPokemon?: PlayerPokemon[];
// #region Post-construct / Auto-populated params
localizationKey: string;
/**
* Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter
*/
@ -264,8 +264,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
Object.assign(this, encounter);
}
this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON;
this.localizationKey = this.localizationKey ?? "";
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
this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? DEFAULT_MAX_ALLOWED_ROGUE_ENCOUNTERS : DEFAULT_MAX_ALLOWED_ENCOUNTERS;
this.encounterMode = MysteryEncounterMode.DEFAULT;
@ -528,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]];
enemyPartyConfigs: EnemyPartyConfig[] = [];
localizationKey: string = "";
dialogue: MysteryEncounterDialogue = {};
requirements: EncounterSceneRequirement[] = [];
primaryPokemonRequirements: EncounterPokemonRequirement[] = [];
@ -573,7 +575,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
*/
withOption(option: MysteryEncounterOption): this & Pick<IMysteryEncounter, "options"> {
if (!this.options) {
const options = [option];
const options = [ option ];
return Object.assign(this, { options });
} else {
this.options.push(option);
@ -624,14 +626,24 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
}
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this {
this.dialogue = {...this.dialogue, intro: dialogue };
this.dialogue = { ...this.dialogue, intro: dialogue };
return this;
}
withIntro({spriteConfigs, dialogue} : {spriteConfigs: MysteryEncounterSpriteConfig[], dialogue?: MysteryEncounterDialogue["intro"]}) {
withIntro({ spriteConfigs, dialogue } : {spriteConfigs: MysteryEncounterSpriteConfig[], dialogue?: MysteryEncounterDialogue["intro"]}) {
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
*/
@ -660,7 +672,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns
*/
withAnimations(...encounterAnimations: EncounterAnim[]): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations];
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [ encounterAnimations ];
return Object.assign(this, { encounterAnimations: animations });
}
@ -670,7 +682,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param disallowedGameModes
*/
withDisallowedGameModes(...disallowedGameModes: GameModes[]): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [disallowedGameModes];
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [ disallowedGameModes ];
return Object.assign(this, { disallowedGameModes: gameModes });
}
@ -680,7 +692,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param disallowedChallenges
*/
withDisallowedChallenges(...disallowedChallenges: Challenges[]): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [disallowedChallenges];
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [ disallowedChallenges ];
return Object.assign(this, { disallowedChallenges: challenges });
}
@ -755,7 +767,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns
*/
withSceneWaveRangeRequirement(min: number, max?: number): this & Required<Pick<IMysteryEncounter, "requirements">> {
return this.withSceneRequirement(new WaveRangeRequirement([min, max ?? min]));
return this.withSceneRequirement(new WaveRangeRequirement([ min, max ?? min ]));
}
/**
@ -767,7 +779,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns
*/
withScenePartySizeRequirement(min: number, max?: number, excludeDisallowedPokemon: boolean = false): this & Required<Pick<IMysteryEncounter, "requirements">> {
return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min], excludeDisallowedPokemon));
return this.withSceneRequirement(new PartySizeRequirement([ min, max ?? min ], excludeDisallowedPokemon));
}
/**
@ -982,7 +994,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns
*/
withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []): this {
this.dialogue = {...this.dialogue, outro: dialogue };
this.dialogue = { ...this.dialogue, outro: dialogue };
return this;
}

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