diff --git a/.dependency-cruiser.cjs b/.dependency-cruiser.cjs index 84d01599727..08daf98fca4 100644 --- a/.dependency-cruiser.cjs +++ b/.dependency-cruiser.cjs @@ -1,6 +1,19 @@ /** @type {import('dependency-cruiser').IConfiguration} */ module.exports = { forbidden: [ + { + name: "no-non-type-@type-exports", + severity: "error", + comment: + "Files in @types should not export anything but types and interfaces. " + + "The folder is intended to house imports that are removed at runtime, " + + "and thus should not contain anything with a bearing on runtime code.", + from: {}, + to: { + path: "(^|/)src/@types", + dependencyTypesNot: ["type-only"], + }, + }, { name: "only-type-imports", severity: "error", @@ -310,7 +323,7 @@ module.exports = { conditionNames: ["import", "require", "node", "default", "types"], /* The extensions, by default are the same as the ones dependency-cruiser - can access (run `npx depcruise --info` to see which ones that are in + can access (run `pnpm exec depcruise --info` to see which ones that are in _your_ environment). If that list is larger than you need you can pass the extensions you actually use (e.g. [".js", ".jsx"]). This can speed up module resolution, which is the most expensive step. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c7d8b1e4d9c..c358986281b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -68,8 +68,8 @@ Do the reviewers need to do something special in order to test your changes? - [ ] The PR is self-contained and cannot be split into smaller PRs? - [ ] Have I provided a clear explanation of the changes? - [ ] Have I tested the changes manually? -- [ ] Are all unit tests still passing? (`npm run test:silent`) - - [ ] Have I created new automated tests (`npm run test:create`) or updated existing tests related to the PR's changes? +- [ ] Are all unit tests still passing? (`pnpm test:silent`) + - [ ] Have I created new automated tests (`pnpm test:create`) or updated existing tests related to the PR's changes? - [ ] Have I provided screenshots/videos of the changes (if applicable)? - [ ] Have I made sure that any UI change works for both UI themes (default and legacy)? diff --git a/.github/workflows/deploy-beta.yml b/.github/workflows/deploy-beta.yml index 90b3008c8e9..0894032c8ad 100644 --- a/.github/workflows/deploy-beta.yml +++ b/.github/workflows/deploy-beta.yml @@ -11,22 +11,31 @@ on: jobs: deploy: - if: github.repository == 'pagefaultgames/pokerogue' && github.ref_name == ${{ vars.BETA_DEPLOY_BRANCH || 'beta' }} + if: github.repository == 'pagefaultgames/pokerogue' && github.ref_name == (vars.BETA_DEPLOY_BRANCH || 'beta') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: "recursive" ref: ${{ vars.BETA_DEPLOY_BRANCH || 'beta'}} + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" + - name: Install dependencies - run: npm ci + run: pnpm i + - name: Build - run: npm run build:beta + run: pnpm build:beta env: NODE_ENV: production + - name: Set up SSH run: | mkdir ~/.ssh @@ -34,6 +43,7 @@ jobs: echo "${{ secrets.BETA_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/* ssh-keyscan -H ${{ secrets.BETA_SSH_HOST }} >> ~/.ssh/known_hosts + - name: Deploy build on server run: | rsync --del --no-times --checksum -vrm dist/* ${{ secrets.BETA_SSH_USER }}@${{ secrets.BETA_SSH_HOST }}:${{ secrets.BETA_DESTINATION_DIR }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a233a2fccab..0e7102a41dd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,15 +16,24 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' + - name: Install dependencies - run: npm ci + run: pnpm i + - name: Build - run: npm run build + run: pnpm build env: NODE_ENV: production + - name: Set up SSH if: github.event_name == 'push' && github.ref_name == 'main' run: | @@ -33,11 +42,13 @@ jobs: echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 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 == '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 == 'main' id: purge-cache diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index ce7c17e2db9..fff90047df2 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -34,6 +34,11 @@ jobs: sudo apt update sudo apt install -y git openssh-client + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup Node 22.14.1 uses: actions/setup-node@v4 with: @@ -50,13 +55,13 @@ jobs: working-directory: ${{env.api-dir}} run: | cd pokerogue_docs - npm ci + pnpm i - name: Generate Typedoc docs working-directory: ${{env.api-dir}} run: | cd pokerogue_docs - npm run docs -- --out /tmp/docs --githubPages false --entryPoints ./src/ + pnpm exec typedoc --out /tmp/docs --githubPages false --entryPoints ./src/ - name: Commit & Push docs if: github.event_name == 'push' diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 0fdbc8b1952..0b2e4c1a5da 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -23,20 +23,22 @@ jobs: with: submodules: 'recursive' + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' - cache: 'npm' + cache: 'pnpm' - name: Install Node.js dependencies - run: npm ci - - - name: Run ESLint - run: npm run eslint-ci + run: pnpm i - name: Lint with Biome - run: npm run biome-ci + run: pnpm biome-ci - name: Check dependencies with depcruise - run: npm run depcruise \ No newline at end of file + run: pnpm depcruise \ No newline at end of file diff --git a/.github/workflows/test-shard-template.yml b/.github/workflows/test-shard-template.yml index 98836bd335a..b154166f81b 100644 --- a/.github/workflows/test-shard-template.yml +++ b/.github/workflows/test-shard-template.yml @@ -28,12 +28,20 @@ jobs: uses: actions/checkout@v4.2.2 with: submodules: "recursive" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - cache: "npm" + cache: "pnpm" + - name: Install Node.js dependencies - run: npm ci + run: pnpm i + - name: Run tests - run: npx vitest --project ${{ inputs.project }} --no-isolate --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }} + run: pnpm exec vitest --project ${{ inputs.project }} --no-isolate --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c3b9666caa9..d3dd23eb379 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,16 +1,14 @@ name: Tests on: - # Trigger the workflow on push or pull request, - # but only for the main branch push: branches: - - main # Trigger on push events to the main branch - - beta # Trigger on push events to the beta branch + - main + - beta pull_request: branches: - - main # Trigger on pull request events targeting the main branch - - beta # Trigger on pull request events targeting the beta branch + - main + - beta merge_group: types: [checks_requested] @@ -24,6 +22,7 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 id: filter with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..7092b40b1b6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,110 @@ +# Contributing to PokéRogue + +Thank you for taking the time to contribute, every little bit helps. This project is entirely open-source and unmonetized - community contributions are what keep it alive! + +Please make sure you understand everything relevant to your changes from the [Table of Contents](#-table-of-contents), and absolutely *feel free to reach out in the **#dev-corner** channel on [Discord](https://discord.gg/pokerogue)*. +We are here to help and the better you understand what you're working on, the easier it will be for it to find its way into the game. + +## 📄 Table of Contents + +- [Development Basics](#️-development-basics) +- [Environment Setup](#-environment-setup) +- [Getting Started](#-getting-started) +- [Documentation](#-documentation) +- [Testing Your Changes](#-testing-your-changes) +- [Development Save File (Unlock Everything)](#-development-save-file) + +## 🛠️ Development Basics + +PokéRogue is built with [Typescript](https://www.typescriptlang.org/docs/handbook/intro.html), using the [Phaser](https://github.com/phaserjs/phaser) game framework. + +If you have the motivation and experience with Typescript/Javascript (or are willing to learn) you can contribute by forking the repository and making pull requests with contributions. + +## 💻 Environment Setup + +### Prerequisites + +- node: >=22.14.0 - [manage with pnpm](https://pnpm.io/cli/env) | [manage with fnm](https://github.com/Schniz/fnm) | [manage with nvm](https://github.com/nvm-sh/nvm) +- pnpm: 10.x - [how to install](https://pnpm.io/installation) (not recommended to install via `npm` on Windows native) | [alternate method - volta.sh](https://volta.sh/) +- The repository [forked](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and [cloned](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) locally on your device + +### Running Locally + +1. Run `pnpm install` from the repository root + - *if you run into any errors, reach out in the **#dev-corner** channel on Discord* +2. Run `pnpm start:dev` to locally run the project at `localhost:8000` + +## 🚀 Getting Started + +A great way to develop an understanding of how the project works is to look at test cases (located in [the `test` folder](./test/)). +Tests show you both how things are supposed to work and the expected "flow" to get from point A to point B in battles. + +*This is a big project and you will be confused at times - never be afraid to reach out and ask questions in **#dev-corner***! + +### Where to Look + +Once you have your feet under you, check out the [Issues](https://github.com/pagefaultgames/pokerogue/issues) page to see how you can help us! +Most issues are bugs and are labeled with their area, such as `Move`, `Ability`, `UI/UX`, etc. There are also priority labels: +- `P0`: Completely gamebreaking (very rare) +- `P1`: Major - Game crash +- `P2`: Minor - Incorrect (but non-crashing) move/ability/interaction +- `P3`: No gameplay impact - typo, minor graphical error, etc. + +Also under issues, you can take a look at the [List of Partial / Unimplemented Moves and Abilities](https://github.com/pagefaultgames/pokerogue/issues/3503) and the [Bug Board](https://github.com/orgs/pagefaultgames/projects/3) (the latter is essentially the same as the issues page but easier to work with). + +You are free to comment on any issue so that you may be assigned to it and we can avoid multiple people working on the same thing. + +## 📚 Documentation + +You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html). + +Additionally, the [docs folder](./docs) contains a variety of in-depth documents and guides useful for aspiring contributors. +Notable topics include: +- [Commenting your code](./docs/comments.md) +- [Linting & Formatting](./docs/linting.md) +- [Localization](./docs/localization.md) +- [Enemy AI move selection](./docs/enemy-ai.md) + +Again, if you have unanswered questions please feel free to ask! + +## 🧪 Testing Your Changes + +You've just made a change - how can you check if it works? You have two areas to hit: + +### 1 - Manual Testing + +> This will likely be your first stop. After making a change, you'll want to spin the game up and make sure everything is as you expect. To do this, you will need a way to manipulate the game to produce the situation you're looking to test. + +[src/overrides.ts](../src/overrides.ts) contains overrides for most values you'll need to change for testing, controlled through the `overrides` object. +For example, here is how you could test a scenario where the player Pokemon has the ability Drought and the enemy Pokemon has the move Water Gun: + +```typescript +const overrides = { + ABILITY_OVERRIDE: AbilityId.DROUGHT, + OPP_MOVESET_OVERRIDE: MoveId.WATER_GUN, +} satisfies Partial>; +``` + +Read through `src/overrides.ts` file to find the override that fits your needs - there are a lot of them! +If the situation you're trying to test can't be created using existing overrides (or with the [Dev Save](#-development-save-file)), reach out in **#dev-corner**. +You can get help testing your specific changes, and you might have found a new override that needs to be created! + +### 2 - Automatic Testing + +> PokéRogue uses [Vitest](https://vitest.dev/) for automatic testing. Checking out the existing tests in the [test](./test/) folder is a great way to understand how this works, and to get familiar with the project as a whole. + +To make sure your changes didn't break any existing test cases, run `pnpm test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `pnpm test:silent dancer`. + - __Note that passing all test cases does *not* guarantee that everything is working properly__. The project does not have complete regression testing. + +Most non-trivial changes (*especially bug fixes*) should come along with new test cases. + - To make a new test file, run `pnpm test:create` and follow the prompts. If the move/ability/etc. you're modifying already has tests, simply add new cases to the end of the file. As mentioned before, the easiest way to get familiar with the system and understand how to write your own tests is simply to read the existing tests, particularly ones similar to the tests you intend to write. + - Ensure that new tests: + - Are deterministic. In other words, the test should never pass or fail when it shouldn't due to randomness. This involves primarily ensuring that abilities and moves are never randomly selected. + - As much as possible, are unit tests. If you have made two distinct changes, they should be tested in two separate cases. + - Test edge cases. A good strategy is to think of edge cases beforehand and create tests for them using `it.todo`. Once the edge case has been handled, you can remove the `todo` marker. + +## 😈 Development Save File +> Some issues may require you to have unlocks on your save file which go beyond normal overrides. For this reason, the repository contains a [save file](../test/testUtils/saves/everything.psrv) with _everything_ unlocked (even ones not legitimately obtainable, like unimplemented variant shinies). + +1. Start the game up locally and navigate to `Menu -> Manage Data -> Import Data` +2. Select [everything.prsv](test/testUtils/saves/everything.prsv) (`test/testUtils/saves/everything.prsv`) and confirm. diff --git a/CREDITS.md b/CREDITS.md index 6d884c8fd60..bca89c816ca 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -40,6 +40,7 @@ ## Backgrounds - Squip (Paid Commissions) - Contributions by Someonealive-QN +- Contributions by redactedinlight ## UI - GAMEFREAK diff --git a/README.md b/README.md index 56392808b3c..1bb8c7772f3 100644 --- a/README.md +++ b/README.md @@ -4,47 +4,7 @@ PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite # Contributing -## 🛠️ Development - -If you have the motivation and experience with Typescript/Javascript (or are willing to learn) please feel free to fork the repository and make pull requests with contributions. If you don't know what to work on but want to help, reference the below **To-Do** section or the **#feature-vote** channel in the discord. - -### 💻 Environment Setup - -#### Prerequisites - -- node: 22.14.0 -- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - -#### Running Locally - -1. Clone the repo and in the root directory run `npm install` - - *if you run into any errors, reach out in the **#dev-corner** channel in discord* -2. Run `npm run start:dev` to locally run the project in `localhost:8000` - -#### Linting - -We're using Biome 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 biome` script. To view the complete rules, check out the [biome.jsonc](./biome.jsonc) 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! +See [CONTRIBUTING.md](./CONTRIBUTING.md), this includes instructions on how to set up the game locally. # 📝 Credits > diff --git a/biome.jsonc b/biome.jsonc index d183334ad58..d4cb67d33a6 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json", "vcs": { "enabled": false, "clientKind": "git", @@ -10,35 +10,46 @@ "enabled": true, "useEditorconfig": true, "indentStyle": "space", - "ignore": ["src/enums/*", "src/data/balance/*"], + "includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"], "lineWidth": 120 }, "files": { "ignoreUnknown": true, // Adding folders to the ignore list is GREAT for performance because it prevents biome from descending into them // and having to verify whether each individual file is ignored - "ignore": [ - "**/*.d.ts", - "dist/*", - "build/*", - "coverage/*", - "public/*", - ".github/*", - "node_modules/*", - ".vscode/*", - "*.css", // TODO? - "*.html", // TODO? - // TODO: these files are too big and complex, ignore them until their respective refactors - "src/data/moves/move.ts", - - // this file is just too big: - "src/data/balance/tms.ts" + "includes": [ + "**", + "!**/*.d.ts", + "!**/dist/**/*", + "!**/build/**/*", + "!**/coverage/**/*", + "!**/public/**/*", + "!**/.github/**/*", + "!**/node_modules/**/*", + "!**/.vscode/**/*", + "!**/typedoc/**/*", + // TODO: lint css and html? + "!**/*.css", + "!**/*.html", + // TODO: enable linting this file + "!**/src/data/moves/move.ts", + // this file is too big + "!**/src/data/balance/tms.ts" ] }, - // While it'd be nice to enable consistent sorting, enabling this causes issues due to circular import resolution order - // TODO: Remove if we ever get down to 0 circular imports - "organizeImports": { "enabled": false }, + "assist": { + "actions": { + "source": { + "organizeImports": { + "level": "on", + "options": { + "groups": [":ALIAS:", ":NODE:", ":PACKAGE_WITH_PROTOCOL:", ":PACKAGE:", ":PATH:"] + } + } + } + } + }, "linter": { "enabled": true, "rules": { @@ -48,22 +59,50 @@ "noUnusedVariables": "error", "noSwitchDeclarations": "error", "noVoidTypeReturn": "error", - "noUnusedImports": "error" + "noUnusedImports": { + "level": "error", + "fix": "safe" + }, + "noUnusedFunctionParameters": "error", + "noUnusedLabels": "error", + "noPrivateImports": "error" }, "style": { - "noVar": "error", "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome - "useBlockStatements": "error", + "useBlockStatements": { + "level": "error", + "fix": "safe" + }, "useConst": "error", "useImportType": "error", "noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files "noParameterAssign": "off", "useExponentiationOperator": "off", // Too typo-prone and easy to mixup with standard multiplication (* vs **) - "useDefaultParameterLast": "off", // TODO: Fix spots in the codebase where this flag would be triggered, and then enable + "useDefaultParameterLast": { + // TODO: Fix spots in the codebase where this flag would be triggered + // and then set to "error" and re-enable the fixer + "level": "warn", + "fix": "none" + }, "useSingleVarDeclarator": "off", "useNodejsImportProtocol": "off", "useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation - "noNamespaceImport": "error" + "useAsConstAssertion": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error", + "noRestrictedTypes": { + "level": "error", + "options": { + "types": { + "integer": { + "message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", + "use": "number" + } + } + } + } }, "suspicious": { "noDoubleEquals": "error", @@ -77,45 +116,62 @@ "noImplicitAnyLet": "warn", // TODO: Refactor and make this an error "noRedeclare": "info", // TODO: Refactor and make this an error "noGlobalIsNan": "off", - "noAsyncPromiseExecutor": "warn" // TODO: Refactor and make this an error + "noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error + "noVar": "error", + "noDocumentCookie": "off" // Firefox has minimal support for the "Cookie Store API" }, "complexity": { - "noExcessiveCognitiveComplexity": "warn", // TODO: Refactor and make this an error + "noExcessiveCognitiveComplexity": "info", // TODO: Refactor and make this an error "useLiteralKeys": "off", "noForEach": "off", // Foreach vs for of is not that simple. "noUselessSwitchCase": "off", // Explicit > Implicit "noUselessConstructor": "error", - "noBannedTypes": "warn" // TODO: Refactor and make this an error + "noBannedTypes": "warn", // TODO: Refactor and make this an error + "noThisInStatic": "error", + "noUselessThisAlias": "error", + "noUselessTernary": "error" + }, + "performance": { + "noNamespaceImport": "error", + "noDelete": "error" }, "nursery": { - "noRestrictedTypes": { - "level": "error", - "options": { - "types": { - "integer": { - "message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", - "use": "number" - } - } - } - } + "useAdjacentGetterSetter": "error", + "noConstantBinaryExpression": "error", + "noTsIgnore": "error", + "noAwaitInLoop": "off", + "useJsonImportAttribute": "off", // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)" + "useIndexOf": "error", + "useObjectSpread": "error", + "useNumericSeparators": "off", // TODO: enable? + "useIterableCallbackReturn": "warn", // TODO: refactor and make "error" + "noShadow": "warn" // TODO: refactor and make "error" } } }, "javascript": { - "formatter": { "quoteStyle": "double", "arrowParentheses": "asNeeded" } + "formatter": { + "quoteStyle": "double", + "arrowParentheses": "asNeeded" + }, + "parser": { + "jsxEverywhere": false + } }, "overrides": [ { - "include": ["test/**/*.test.ts"], - "javascript": { "globals": [] }, + "includes": ["**/test/**/*.test.ts"], "linter": { "rules": { "performance": { - "noDelete": "off" // TODO: evaluate if this is necessary for the test(s) to function + "noDelete": "off", // TODO: evaluate if this is necessary for the test(s) to function + "noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests }, "style": { - "noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests + "noNonNullAssertion": "off" + }, + "nursery": { + "noFloatingPromises": "error" } } } @@ -123,7 +179,7 @@ // Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes) { - "include": ["src/overrides.ts", "src/enums/*"], + "includes": ["**/src/overrides.ts", "**/src/enums/**/*"], "linter": { "rules": { "correctness": { @@ -133,7 +189,7 @@ } }, { - "include": ["src/overrides.ts"], + "includes": ["**/src/overrides.ts"], "linter": { "rules": { "style": { diff --git a/dependency-graph.js b/dependency-graph.js deleted file mode 100644 index dff960d8781..00000000000 --- a/dependency-graph.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Graphviz } from "@hpcc-js/wasm/graphviz"; - -const graphviz = await Graphviz.load(); - -const inputFile = []; -for await (const chunk of process.stdin) { - inputFile.push(chunk); -} - -const file = Buffer.concat(inputFile).toString("utf-8"); - -const svg = graphviz.dot(file, "svg"); -process.stdout.write(svg); diff --git a/docs/comments.md b/docs/comments.md index ba6c9929625..ac9474ce5b2 100644 --- a/docs/comments.md +++ b/docs/comments.md @@ -23,7 +23,7 @@ When formatted correctly, these comments are shown within VS Code or similar IDE - Functions also show the comment for each parameter as you type them, making keeping track of arguments inside lengthy functions much more clear. They can also be used to generate a commentated overview of the codebase. There is a GitHub action that automatically updates [this docs site](https://pagefaultgames.github.io/pokerogue/main/index.html) -and you can generate it locally as well via `npm run docs` which will generate into the `typedoc/` directory. +and you can generate it locally as well via `pnpm run docs` which will generate into the `typedoc/` directory. ## Syntax For an example of how TSDoc comments work, here are some TSDoc comments taken from `src/data/moves/move.ts`: diff --git a/docs/linting.md b/docs/linting.md index d3b4e47675f..d925b2f29af 100644 --- a/docs/linting.md +++ b/docs/linting.md @@ -1,14 +1,10 @@ # Linting & Formatting -> "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." -> -> — Martin Fowler - Writing clean, readable code is important, and linters and formatters are an integral part of ensuring code quality and readability. It is for this reason we are using [Biome](https://biomejs.dev), an opinionated linter/formatter (akin to Prettier) with a heavy focus on speed and performance. ### Installation -You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `npm install` after cloning the repo (assuming you followed proper instructions, that is). If you haven't done that yet, go do it. +You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `pnpm install` after cloning the repo. If you haven't done that yet, go do it. # Using Biome @@ -24,17 +20,11 @@ You will **not** be able to push code with `error`-level linting problems - fix We also have a [Github Action](../.github/workflows/quality.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. -### Why am I getting errors for code I didn't write? - -To save time and minimize friction with existing code, both the pre-commit hook and workflow run will only check files **directly changed** by a given PR or commit. -As a result, changes to files not updated since Biome's introduction can cause any _prior_ linting errors in them to resurface and get flagged. -This should occur less and less often as time passes and more files are updated to the new standard. - ## Running Biome via CLI If you want Biome to check your files manually, you can run it from the command line like so: ```sh -npx biome check --[flags] +pnpm exec biome check --[flags] ``` A full list of flags and options can be found on [their website](https://biomejs.dev/reference/cli/), but here's a few useful ones to keep in mind: @@ -56,10 +46,3 @@ Some things to consider: Any questions about linting rules should be brought up in the `#dev-corner` channel in the discord. [^1]: A complete list of rules can be found in the `biome.jsonc` file in the project root. - -## What about ESLint? - - -Our project migrated away from ESLint around March 2025 due to it simply not scaling well enough with the codebase's ever-growing size. The [existing eslint rules](../eslint.config.js) are considered _deprecated_, only kept due to Biome lacking the corresponding rules in its current ruleset. - -No additional ESLint rules should be added under any circumstances - even the few currently in circulation take longer to run than the entire Biome formatting/linting suite combined. \ No newline at end of file diff --git a/docs/localization.md b/docs/localization.md new file mode 100644 index 00000000000..0fe950a361d --- /dev/null +++ b/docs/localization.md @@ -0,0 +1,142 @@ +# Localization 101 + +PokéRogue's localization team puts immense effort into making the game accessible around the world, supporting over 12 different languages at the time of writing this document. +As a developer, it's important to help maintain global accessibility by effectively coordinating with the Translation Team on any new features or enhancements. + +This document aims to cover everything you need to know to help keep the integration process for localization smooth and simple. + +# Prerequisites +Before you continue, this document assumes: +- You have already forked the repository and set up a development environment according to [CONTRIBUTING.md](../CONTRIBUTING.md). +- You have a basic level of familiarity with Git commands and GitHub repositories. +- You have joined the [community Discord](https://discord.gg/pokerogue) and have access to `#dev-corner` and related channels via **[#select-roles](https://discord.com/channels/1125469663833370665/1194825607738052621)**. +This is the easiest way to keep in touch with both the Translation Team and other like-minded contributors! + +# About the `pokerogue-locales` submodule + +PokéRogue's translations are managed under a separate dedicated repository, [`pokerogue-locales`](https://github.com/pagefaultgames/pokerogue-locales/). +This repository is integrated into the main one as a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) within the `public/locales` folder. + +## What Is a Submodule? + +In essence, a submodule is a way for one repository (i.e. `pokerogue`) to use another repository (i.e. `pokerogue-locales`) internally. +The parent repo (the "superproject") houses a cloned version of the 2nd repository (the "submodule") inside it, making locales effectively a "repository within a repository", so to speak. + +>[!TIP] +> Many popular IDEs have integrated `git` support with special handling around submodules: +> ![Image showing Visual Studio Code's `git` integration in the File Explorer. A blue "S" in the top right hand corner indicates the `public/locales` folder is a submodule.](https://github.com/user-attachments/assets/bd42d354-c65b-4cbe-8873-23d760dc1714 "What the `public/locales` submodule looks like in VS Code's File Explorer") +> +> ![Image showing Visual Studio Code's Source Control tab. A separate dropdown can be seen for each individual submodule.](https://github.com/user-attachments/assets/8b4d3f64-aec1-4474-91df-03dc1252a2fa "Making commits on submodules without even changing directories!") + +## Fetching Changes from Submodules + +The following command will initialize your branch's locales repository and update its HEAD: +```bash +git submodule update --init --recursive +``` + +> [!TIP] +> This command is run _automatically_ after cloning, merging or changing branches, so you should rarely have to run it manually. + +> [!IMPORTANT] +> If you run into issues with the `locales` submodule, try deleting the `.git/modules/public` and `public/locales` folders before re-running the command. + +## How Are Translations Integrated? + +This project uses the [i18next library](https://www.i18next.com/) to integrate translations from `public/locales` into the source code. +The basic process for fetching translated text goes roughly as follows: +1. The source code fetches text by a given key. + ```ts + globalScene.phaseManager.queueMessage( + i18next.t("fileName:keyName", { arg1: "Hello", arg2: "an example", ... }) + ); + ``` +2. The game looks up the key in the corresponding JSON file for the user's language. + ```jsonc + // from "en/file-name.json"... + { + "keyName": "{{arg1}}! This is {{arg2}} of translated text!" + } + ``` + If the key doesn't exist for the given language, the game will default to an appropriate fallback (usually the corresponding English key). +3. The game shows the translated text to the user. + ```ts + "Hello! This is an example of translated text!" + ``` + +# Submitting Locales Changes +If you have a feature or enhancement that requires additions or changes to in-game text, you will need to make a fork of the `pokerogue-locales` repo and submit your text changes as a pull request _in addition_ to your pull request to the main project. +Since these two PRs aren't _technically_ linked, it's important to coordinate with the Translation Team to ensure that both PRs are integrated safely into the project. + +> [!CAUTION] +> **DO NOT HARDCODE PLAYER-FACING TEXT INTO THE CODE!** + +## Making Changes + +One perk of submodules is you don't actually _need_ to clone the locales repository to start contributing - initializing the submodule already does that for you. + +Given `pokerogue-locales` is a full-fledged `git` repository _inside_ `pokerogue`, making changes is roughly the same as normal, merely using `public/locales` as your root directory. + +> [!WARNING] +> Make sure to checkout or rebase onto `upstream/HEAD` **BEFORE** creating a PR! +> The checked-out commit is based on the superproject's SHA-1 by default, so hastily making changes may see you basing your commits on last week's `HEAD`. + +## Requirements for Adding Translated Text +When your new feature or enhancement requires adding a new locales key **without changing text in existing keys**, we require the following workflow with regards to localization: +1. You (the developer) make a pull request to the main repository for your new feature. +If this feature requires new text, the text should be integrated into the code with a new `i18next` key pointing to where you plan to add it into the locales repository. +2. You then make another pull request — this time to the `pokerogue-locales` repository — adding a new entry with text for each key you added to your main PR. + - You must add the corresponding **English keys** while making the PR; the Translation Team can take care of the rest[^2]. + - For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text. + [Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice. + - You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response. +3. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes). +4. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`. +5. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment. + +[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates). +If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle. + +### Requirements for Modifying Translated Text + +PRs that modify existing text have different risks with respect to coordination between development and translation, so their requirements are slightly different: +- As above, you set up 2 PRs: one for the feature itself in the main repo, and another for the associated locales changes in the locale repo. +- Now, however, you need to have your main PR be approved by the Dev Team **before** your corresponding locale changes are merged in. +- After your main PR is approved, the Translation Team will merge your locale PR, and you may update the submodule and post video evidence of integration into the **locales PR**. +- A Lead or Senior Translator from the Translation Team will then approve your main PR (if all is well), clearing your feature for merging into `beta`. + +## Documenting Locales Changes + +After making a PR involving any outwards-facing behavior (but _especially_ locales-related ones), it's generally considered good practice to attach proof of those changes working in-game. + +The basic procedure is roughly as follows: +1. Update your locales submodule to point to **the branch you used to make the locales PR**. \ + Many IDEs with `git` integration support doing this from the GUI, \ + or you can simply do it via command-line: + ```bash + cd public/locales + git checkout your-branch-name-here + ``` +2. Set some of the [in-game overrides](../CONTRIBUTING.md#1---manual-testing) inside `overrides.ts` to values corresponding to the interactions being tested. +3. Start a local dev server (`pnpm start:dev`) and open localhost in your browser. +4. Take screenshots or record a video of the locales changes being displayed in-game using the software of your choice[^2]. + +[^2]: For those lacking a screen capture device, [OBS Studio](https://obsproject.com) is a popular open-source option. + +> [!NOTE] +> For those aiming to film their changes, bear in mind that GitHub has a hard **10mB limit** on uploaded media content. +> If your video is too large, consider making it shorter or downscaling the quality. + +## Notifying Translation +Put simply, stating that a PR exists makes it much easier to review and merge. + +The easiest way to do this is by **pinging the current Head of Translation** in the [community Discord](https://discord.gg/pokerogue) (ideally in `#dev-corner` or similar). + + +> [!IMPORTANT] +> The current Head of Translation is: \ +> ** @lugiadrien ** + +# Closing Remarks +If you have any questions about the developer process for localization, don't hesitate to ask! +Feel free to contact us on Discord - the Dev Team and Translation Team will be happy to answer any questions. diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index aebcab7feae..00000000000 --- a/eslint.config.js +++ /dev/null @@ -1,43 +0,0 @@ -/** @ts-check */ -import tseslint from "typescript-eslint"; -import stylisticTs from "@stylistic/eslint-plugin-ts"; -import parser from "@typescript-eslint/parser"; -import importX from "eslint-plugin-import-x"; - -export default tseslint.config( - { - name: "eslint-config", - files: ["src/**/*.{ts,tsx,js,jsx}", "test/**/*.{ts,tsx,js,jsx}"], - ignores: ["dist/*", "build/*", "coverage/*", "public/*", ".github/*", "node_modules/*", ".vscode/*"], - languageOptions: { - parser: parser, - }, - plugins: { - "import-x": importX, - "@stylistic/ts": stylisticTs, - "@typescript-eslint": tseslint.plugin, - }, - rules: { - "no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this) - "no-extra-semi": "error", // Disallows unnecessary semicolons for TypeScript-specific syntax - "import-x/extensions": ["error", "never", { json: "always" }], // Enforces no extension for imports unless json - }, - }, - { - name: "eslint-tests", - files: ["test/**/**.test.ts"], - languageOptions: { - parser: parser, - parserOptions: { - project: ["./tsconfig.json"], - }, - }, - plugins: { - "@typescript-eslint": tseslint.plugin, - }, - rules: { - "@typescript-eslint/no-floating-promises": "error", // Require Promise-like statements to be handled appropriately. - https://typescript-eslint.io/rules/no-floating-promises/ - "@typescript-eslint/no-misused-promises": "error", // Disallow Promises in places not designed to handle them. - https://typescript-eslint.io/rules/no-misused-promises/ - }, - }, -); diff --git a/lefthook.yml b/lefthook.yml index 0f91f658171..40875073fdd 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,8 +1,7 @@ pre-commit: - parallel: true commands: biome-lint: - run: npx biome check --write --reporter=summary --staged --no-errors-on-unmatched + run: pnpm exec biome check --write --reporter=summary --staged --no-errors-on-unmatched stage_fixed: true skip: - merge @@ -16,4 +15,6 @@ post-merge: post-checkout: commands: update-submodules: - run: git submodule update --init --recursive \ No newline at end of file + # cf https://git-scm.com/docs/githooks#_post_checkout: + # The 3rd argument is 1 for branch checkouts and 0 for file checkouts. + run: if test {3} -eq "1"; then git submodule update --init --recursive; fi \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 5ee523f965b..00000000000 --- a/package-lock.json +++ /dev/null @@ -1,8029 +0,0 @@ -{ - "name": "pokemon-rogue-battle", - "version": "1.9.6", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "pokemon-rogue-battle", - "version": "1.9.6", - "hasInstallScript": true, - "dependencies": { - "@material/material-color-utilities": "^0.2.7", - "compare-versions": "^6.1.1", - "crypto-js": "^4.2.0", - "i18next": "^24.2.2", - "i18next-browser-languagedetector": "^8.0.4", - "i18next-http-backend": "^3.0.2", - "i18next-korean-postposition-processor": "^1.0.0", - "json-stable-stringify": "^1.2.0", - "jszip": "^3.10.1", - "phaser": "^3.88.2", - "phaser3-rex-plugins": "^1.80.15" - }, - "devDependencies": { - "@biomejs/biome": "1.9.4", - "@eslint/js": "^9.23.0", - "@hpcc-js/wasm": "^2.22.4", - "@stylistic/eslint-plugin-ts": "^4.1.0", - "@types/jsdom": "^21.1.7", - "@types/node": "^22.13.14", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", - "@vitest/coverage-istanbul": "^3.0.9", - "dependency-cruiser": "^16.3.10", - "eslint": "^9.23.0", - "eslint-plugin-import-x": "^4.9.4", - "inquirer": "^12.4.2", - "jsdom": "^26.0.0", - "lefthook": "^1.11.5", - "msw": "^2.7.3", - "phaser3spectorjs": "^0.0.8", - "rollup": "^4.40.1", - "typedoc": "^0.28.1", - "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", - "vite": "^6.3.4", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.0.9", - "vitest-canvas-mock": "^0.3.3" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", - "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", - "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", - "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", - "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", - "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cookie": "^0.7.2" - } - }, - "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/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", - "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", - "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz", - "integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", - "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", - "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.2.2.tgz", - "integrity": "sha512-vaZNGhGLKMY14HbF53xxHNgFO9Wz+t5lTlGNpl2N9xFiKQ0I5oIe0vKjU9dh7Nb3Dw6lZ7wqUE0ri+zcdpnK+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-oniguruma": "^3.2.1", - "@shikijs/langs": "^3.2.1", - "@shikijs/themes": "^3.2.1", - "@shikijs/types": "^3.2.1", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@hpcc-js/wasm": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.22.4.tgz", - "integrity": "sha512-58JkRkxZffiBAbZhc7z+9iaaAOmn1cyxLL3rRwsUvco/I0Wwb7uVAlHM9HiU6XASe2k11jrIjCFff1t9QKjlqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "yargs": "17.7.2" - }, - "bin": { - "dot-wasm": "node ./node_modules/@hpcc-js/wasm-graphviz-cli/bin/index.js" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.4.tgz", - "integrity": "sha512-d30576EZdApjAMceijXA5jDzRQHT/MygbC+J8I7EqA6f/FRpYxlRtRJbHF8gHeWYeSdOuTEJqonn7QLB1ELezA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.8.tgz", - "integrity": "sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz", - "integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/editor": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.9.tgz", - "integrity": "sha512-8HjOppAxO7O4wV1ETUlJFg6NDjp/W2NP5FB9ZPAcinAlNT4ZIWOLe2pUVwmmPRSV0NMdI5r/+lflN55AwZOKSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "external-editor": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.11.tgz", - "integrity": "sha512-OZSUW4hFMW2TYvX/Sv+NnOZgO8CHT2TU1roUCUIF2T+wfw60XFRRp9MRUPCT06cRnKL+aemt2YmTWwt7rOrNEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", - "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.8.tgz", - "integrity": "sha512-WXJI16oOZ3/LiENCAxe8joniNp8MQxF6Wi5V+EBbVA0ZIOpFcL4I9e7f7cXse0HJeIPCWO8Lcgnk98juItCi7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.11.tgz", - "integrity": "sha512-pQK68CsKOgwvU2eA53AG/4npRTH2pvs/pZ2bFvzpBhrznh8Mcwt19c+nMO7LHRr3Vreu1KPhNBF3vQAKrjIulw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.11.tgz", - "integrity": "sha512-dH6zLdv+HEv1nBs96Case6eppkRggMe8LoOTl30+Gq5Wf27AO/vHFgStTVz4aoevLdNXqwE23++IXGw4eiOXTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.0.tgz", - "integrity": "sha512-EZiJidQOT4O5PYtqnu1JbF0clv36oW2CviR66c7ma4LsupmmQlUwmdReGKRp456OWPWMz3PdrPiYg3aCk3op2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.1.4", - "@inquirer/confirm": "^5.1.8", - "@inquirer/editor": "^4.2.9", - "@inquirer/expand": "^4.0.11", - "@inquirer/input": "^4.1.8", - "@inquirer/number": "^3.0.11", - "@inquirer/password": "^4.0.11", - "@inquirer/rawlist": "^4.0.11", - "@inquirer/search": "^3.0.11", - "@inquirer/select": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.11.tgz", - "integrity": "sha512-uAYtTx0IF/PqUAvsRrF3xvnxJV516wmR6YVONOmCWJbbt87HcDHLfL9wmBQFbNJRv5kCjdYKrZcavDkH3sVJPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.11.tgz", - "integrity": "sha512-9CWQT0ikYcg6Ls3TOa7jljsD7PgjcsYEM0bYE+Gkz+uoW9u8eaJCRHJKkucpRE5+xKtaaDbrND+nPDoxzjYyew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.0.tgz", - "integrity": "sha512-z0a2fmgTSRN+YBuiK1ROfJ2Nvrpij5lVN3gPDkQGhavdvIVGHGW29LwYZfM/j42Ai2hUghTI/uoBuTbrJk42bA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", - "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@material/material-color-utilities": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz", - "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==", - "license": "Apache-2.0" - }, - "node_modules/@mswjs/interceptors": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.6.tgz", - "integrity": "sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==", - "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/@napi-rs/wasm-runtime": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz", - "integrity": "sha512-5yximcFK5FNompXfJFoWanu5l8v1hNGqNHh9du1xETp9HWk/B/PzvchX55WYOPaIeNglG8++68AAiauBAtbnzw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.3.1", - "@emnapi/runtime": "^1.3.1", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "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", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.0.tgz", - "integrity": "sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", - "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", - "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", - "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", - "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", - "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", - "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", - "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", - "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", - "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", - "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", - "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", - "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", - "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", - "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", - "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", - "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", - "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", - "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", - "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", - "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.2.1.tgz", - "integrity": "sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.2.1.tgz", - "integrity": "sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.2.1.tgz", - "integrity": "sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1" - } - }, - "node_modules/@shikijs/types": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.2.1.tgz", - "integrity": "sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@stylistic/eslint-plugin-ts": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-4.2.0.tgz", - "integrity": "sha512-j2o2GvOx9v66x8hmp/HJ+0T+nOppiO5ycGsCkifh7JPGgjxEhpkGmIGx3RWsoxpWbad3VCX8e8/T8n3+7ze1Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^8.23.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=9.0.0" - } - }, - "node_modules/@stylistic/eslint-plugin-ts/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.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/doctrine": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", - "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/jsdom": { - "version": "21.1.7", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", - "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.13.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", - "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "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", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz", - "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/type-utils": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", - "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", - "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", - "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", - "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", - "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz", - "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", - "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.3.3.tgz", - "integrity": "sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.3.3.tgz", - "integrity": "sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.3.3.tgz", - "integrity": "sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.3.3.tgz", - "integrity": "sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.3.3.tgz", - "integrity": "sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.3.3.tgz", - "integrity": "sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.3.3.tgz", - "integrity": "sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.3.3.tgz", - "integrity": "sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.3.3.tgz", - "integrity": "sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.3.3.tgz", - "integrity": "sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.3.3.tgz", - "integrity": "sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.7" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.3.3.tgz", - "integrity": "sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.3.3.tgz", - "integrity": "sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.3.3.tgz", - "integrity": "sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@vitest/coverage-istanbul": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-3.0.9.tgz", - "integrity": "sha512-/TXh2qmOhclmVPjOnPTpIO4Xr6l2P5EwyXQygenwq4/ZQ/vPsrz+GCRZF9kBeQi6xrGcHv368Si9PGImWQawVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@istanbuljs/schema": "^0.1.3", - "debug": "^4.4.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-instrument": "^6.0.3", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magicast": "^0.3.5", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "3.0.9" - } - }, - "node_modules/@vitest/expect": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", - "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.0.9", - "@vitest/utils": "3.0.9", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", - "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.0.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", - "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", - "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.0.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", - "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.0.9", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", - "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", - "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.0.9", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-jsx-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/acorn-jsx-walk/-/acorn-jsx-walk-2.0.0.tgz", - "integrity": "sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn-loose": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.4.0.tgz", - "integrity": "sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/compare-versions": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/cssfontparser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", - "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", - "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.1.1", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/dagre": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", - "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", - "license": "MIT", - "dependencies": { - "graphlib": "^2.1.8", - "lodash": "^4.17.15" - } - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dependency-cruiser": { - "version": "16.3.10", - "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.3.10.tgz", - "integrity": "sha512-WkCnibHBfvaiaQ+S46LZ6h4AR6oj42Vsf5/0Vgtrwdwn7ZekMJdZ/ALoTwNp/RaGlKW+MbV/fhSZOvmhAWVWzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "8.12.1", - "acorn-jsx": "5.3.2", - "acorn-jsx-walk": "2.0.0", - "acorn-loose": "8.4.0", - "acorn-walk": "8.3.3", - "ajv": "8.17.1", - "commander": "12.1.0", - "enhanced-resolve": "5.17.1", - "ignore": "5.3.1", - "interpret": "^3.1.1", - "is-installed-globally": "1.0.0", - "json5": "2.2.3", - "memoize": "10.0.0", - "picocolors": "1.0.1", - "picomatch": "4.0.2", - "prompts": "2.4.2", - "rechoir": "^0.8.0", - "safe-regex": "2.1.1", - "semver": "^7.6.3", - "teamcity-service-messages": "0.1.14", - "tsconfig-paths-webpack-plugin": "4.1.0", - "watskeburt": "4.1.0" - }, - "bin": { - "depcruise": "bin/dependency-cruise.mjs", - "depcruise-baseline": "bin/depcruise-baseline.mjs", - "depcruise-fmt": "bin/depcruise-fmt.mjs", - "depcruise-wrap-stream-in-html": "bin/wrap-stream-in-html.mjs", - "dependency-cruise": "bin/dependency-cruise.mjs", - "dependency-cruiser": "bin/dependency-cruise.mjs" - }, - "engines": { - "node": "^18.17||>=20" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.830", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.830.tgz", - "integrity": "sha512-TrPKKH20HeN0J1LHzsYLs2qwXrp8TF4nHdu4sq61ozGbzMpWhI7iIOPYPPkxeq1azMT9PZ8enPFcftbs/Npcjg==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import-x": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.10.0.tgz", - "integrity": "sha512-5ej+0WILhX3D6wkcdsyYmPp10SUIK6fmuZ6KS8nf9MD8CJ6/S/3Dl7m21g+MLeaTMsvcEXo3JunNAbgHwXxs/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.0", - "@types/doctrine": "^0.0.9", - "@typescript-eslint/utils": "^8.28.0", - "debug": "^4.4.0", - "doctrine": "^3.0.0", - "eslint-import-resolver-node": "^0.3.9", - "get-tsconfig": "^4.10.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.3 || ^10.0.1", - "semver": "^7.7.1", - "stable-hash": "^0.0.5", - "tslib": "^2.8.1", - "unrs-resolver": "^1.3.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/graphology": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", - "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", - "license": "MIT", - "dependencies": { - "events": "^3.3.0", - "obliterator": "^2.0.2" - }, - "peerDependencies": { - "graphology-types": ">=0.24.0" - } - }, - "node_modules/graphology-types": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", - "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", - "license": "MIT", - "peer": true - }, - "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", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "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", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/i18next": { - "version": "24.2.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", - "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.10" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/i18next-browser-languagedetector": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.4.tgz", - "integrity": "sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/i18next-http-backend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", - "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", - "license": "MIT", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, - "node_modules/i18next-korean-postposition-processor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/i18next-korean-postposition-processor/-/i18next-korean-postposition-processor-1.0.0.tgz", - "integrity": "sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==", - "license": "MIT", - "peerDependencies": { - "i18next": ">=8.4.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/inquirer": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.5.0.tgz", - "integrity": "sha512-aiBBq5aKF1k87MTxXDylLfwpRwToShiHrSv4EmB07EYyLgmnjEz5B3rn0aGw1X3JA/64Ngf2T54oGwc+BCsPIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/prompts": "^7.4.0", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "mute-stream": "^2.0.0", - "run-async": "^3.0.0", - "rxjs": "^7.8.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", - "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-directory": "^4.0.1", - "is-path-inside": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-installed-globally/node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "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", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-canvas-mock": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", - "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssfontparser": "^1.2.1", - "moo-color": "^1.0.2" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.1", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/json-stable-stringify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz", - "integrity": "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "license": "Public Domain", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lefthook": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.11.6.tgz", - "integrity": "sha512-j0VmMM50WlPDassmgvapRum9po29Tv1BXzBNFpzGkk9E91CEG9jKik/OHyH/r/na+q8qNIUUyPL6QQuTN/UhQQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "lefthook": "bin/index.js" - }, - "optionalDependencies": { - "lefthook-darwin-arm64": "1.11.6", - "lefthook-darwin-x64": "1.11.6", - "lefthook-freebsd-arm64": "1.11.6", - "lefthook-freebsd-x64": "1.11.6", - "lefthook-linux-arm64": "1.11.6", - "lefthook-linux-x64": "1.11.6", - "lefthook-openbsd-arm64": "1.11.6", - "lefthook-openbsd-x64": "1.11.6", - "lefthook-windows-arm64": "1.11.6", - "lefthook-windows-x64": "1.11.6" - } - }, - "node_modules/lefthook-darwin-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.6.tgz", - "integrity": "sha512-gWgdWrKgZgX+bKc6Vs/x7JkO+58lLOpRzpteLx//82D0MKVPlNZwjd4zz4AbIBXtM4Hcj+6gSsOzQ7QDXxjVvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-darwin-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.6.tgz", - "integrity": "sha512-Ia0TjTKuYcSaDTuoCnbWtpPZ2VEoKzgn33OB90VjNaSVs4ooE0PIdpO+w00x1elqIaf1pbrpq6HgeB26Du8KbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-freebsd-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.6.tgz", - "integrity": "sha512-PxIwj+hmjLahyzEmcIfalIBDhgklAQCavwM4sGCgbzDi4/+VQX+4aEs4pQqtd7v3aohmjtO/4n2emzTI8donww==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-freebsd-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.6.tgz", - "integrity": "sha512-3o1lMKxz1VtWaP/o117wgUn3ZOpefMoSf+8LuiTzI3/PDprIuzgyw2nXKlBZAMDpNPHMNnJeQNts9XLMRmkldg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-linux-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.6.tgz", - "integrity": "sha512-nKPFZ5cA9f5tVn0ybDVqcXXlpTHZqo05N4KQRhWTj5Nem+JoD2YzJIlvZhdJhUrldERqj6deDMXChH5T3z4Rrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-linux-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.11.6.tgz", - "integrity": "sha512-naN8dllLCOEeP+wznLnq+oXrs1dvt/iMLkcl+pOPWLqFccPfDiHzr8V8GslaTa+rSFsAnvjR7SJIOi5C29xedA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-openbsd-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.6.tgz", - "integrity": "sha512-dPxhJfYQ667T+U3pz1+O3mTRNHzXH/BvPlXSH+oy8uiSry4AtVNRXkVvXPUcpLlrAy6HuFYodsrpCIlWFeYwiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-openbsd-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.6.tgz", - "integrity": "sha512-9D26kcSsjiW4D0AuVDdi+0ZqrsOzRWOpMS/kcUbLfrU99yCvma0rMTqKbbDMkVur/znS7qL53oGahXCXDNA+IQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-windows-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.6.tgz", - "integrity": "sha512-xdCenr4+BFnfBEhiXj6GJp02EPmcwTAGa7NYm6hVTfDwGXw24tuLv7lpnGjgK3kovN6EukgLH1FYkeyDOBEMnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/lefthook-windows-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.11.6.tgz", - "integrity": "sha512-Fg2GzLhzeDV/GX8+ydrI0wBOytQWpPkNdngx+a8B/feCDbwjAiFklDG5oV4ytuWrtg1JPEEWLJd6nHefj4wtHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true - }, - "node_modules/memoize": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz", - "integrity": "sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/memoize?sponsor=1" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/moo-color": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz", - "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.1.4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/msw": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.3.tgz", - "integrity": "sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.1", - "@bundled-es-modules/statuses": "^1.0.1", - "@bundled-es-modules/tough-cookie": "^0.1.6", - "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.37.0", - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "picocolors": "^1.1.1", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.26.1", - "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/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "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", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", - "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", - "dev": true - }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT" - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "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", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", - "license": "MIT" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "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/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/phaser": { - "version": "3.88.2", - "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.88.2.tgz", - "integrity": "sha512-UBgd2sAFuRJbF2xKaQ5jpMWB8oETncChLnymLGHcrnT53vaqiGrQWbUKUDBawKLm24sghjKo4Bf+/xfv8espZQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1" - } - }, - "node_modules/phaser3-rex-plugins": { - "version": "1.80.15", - "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.15.tgz", - "integrity": "sha512-Ur973N1W5st6XEYBcJko8eTcEbdDHMM+m7VqvT3j/EJeJwYyJ3bVb33JJDsFgefk3A2iAz2itP/UY7CzxJOJVA==", - "license": "MIT", - "dependencies": { - "dagre": "^0.8.5", - "eventemitter3": "^3.1.2", - "graphology": "^0.25.4", - "i18next": "^22.5.1", - "i18next-http-backend": "^2.5.2", - "js-yaml": "^4.1.0", - "mustache": "^4.2.0", - "papaparse": "^5.4.1", - "webfontloader": "^1.6.28" - } - }, - "node_modules/phaser3-rex-plugins/node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "license": "MIT" - }, - "node_modules/phaser3-rex-plugins/node_modules/i18next": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz", - "integrity": "sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.6" - } - }, - "node_modules/phaser3-rex-plugins/node_modules/i18next-http-backend": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.7.3.tgz", - "integrity": "sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==", - "license": "MIT", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, - "node_modules/phaser3spectorjs": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/phaser3spectorjs/-/phaser3spectorjs-0.0.8.tgz", - "integrity": "sha512-0dSO7/aMjEUPrp5EcjRvRRsEf+jXDbmzalPeJ6VtTB2Pn1PeaKc+qlL/DmO3l1Dvc5lkzc+Sil1Ta+Hkyi5cbA==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", - "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.1", - "@rollup/rollup-android-arm64": "4.40.1", - "@rollup/rollup-darwin-arm64": "4.40.1", - "@rollup/rollup-darwin-x64": "4.40.1", - "@rollup/rollup-freebsd-arm64": "4.40.1", - "@rollup/rollup-freebsd-x64": "4.40.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", - "@rollup/rollup-linux-arm-musleabihf": "4.40.1", - "@rollup/rollup-linux-arm64-gnu": "4.40.1", - "@rollup/rollup-linux-arm64-musl": "4.40.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-musl": "4.40.1", - "@rollup/rollup-linux-s390x-gnu": "4.40.1", - "@rollup/rollup-linux-x64-gnu": "4.40.1", - "@rollup/rollup-linux-x64-musl": "4.40.1", - "@rollup/rollup-win32-arm64-msvc": "4.40.1", - "@rollup/rollup-win32-ia32-msvc": "4.40.1", - "@rollup/rollup-win32-x64-msvc": "4.40.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "regexp-tree": "~0.1.1" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/source-map-js": { - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "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.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", - "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_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/teamcity-service-messages": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/teamcity-service-messages/-/teamcity-service-messages-0.1.14.tgz", - "integrity": "sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "6.1.85", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", - "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.85" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.85", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz", - "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tsconfck": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz", - "integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedoc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.1.tgz", - "integrity": "sha512-Mn2VPNMaxoe/hlBiLriG4U55oyAa3Xo+8HbtEwV7F5WEOPXqtxzGuMZhJYHaqFJpajeQ6ZDUC2c990NAtTbdgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@gerrit0/mini-shiki": "^3.2.1", - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "yaml": "^2.7.0 " - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18", - "pnpm": ">= 10" - }, - "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" - } - }, - "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz", - "integrity": "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.0", - "@typescript-eslint/parser": "8.29.0", - "@typescript-eslint/utils": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unrs-resolver": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz", - "integrity": "sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/JounQin" - }, - "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.3.3", - "@unrs/resolver-binding-darwin-x64": "1.3.3", - "@unrs/resolver-binding-freebsd-x64": "1.3.3", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-arm64-musl": "1.3.3", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-s390x-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-musl": "1.3.3", - "@unrs/resolver-binding-wasm32-wasi": "1.3.3", - "@unrs/resolver-binding-win32-arm64-msvc": "1.3.3", - "@unrs/resolver-binding-win32-ia32-msvc": "1.3.3", - "@unrs/resolver-binding-win32-x64-msvc": "1.3.3" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/vite": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", - "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", - "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", - "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "3.0.9", - "@vitest/mocker": "3.0.9", - "@vitest/pretty-format": "^3.0.9", - "@vitest/runner": "3.0.9", - "@vitest/snapshot": "3.0.9", - "@vitest/spy": "3.0.9", - "@vitest/utils": "3.0.9", - "chai": "^5.2.0", - "debug": "^4.4.0", - "expect-type": "^1.1.0", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.9", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.9", - "@vitest/ui": "3.0.9", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest-canvas-mock": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/vitest-canvas-mock/-/vitest-canvas-mock-0.3.3.tgz", - "integrity": "sha512-3P968tYBpqYyzzOaVtqnmYjqbe13576/fkjbDEJSfQAkHtC5/UjuRHOhFEN/ZV5HVZIkaROBUWgazDKJ+Ibw+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-canvas-mock": "~2.5.2" - }, - "peerDependencies": { - "vitest": "*" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/watskeburt": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.1.0.tgz", - "integrity": "sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==", - "dev": true, - "bin": { - "watskeburt": "dist/run-cli.js" - }, - "engines": { - "node": "^18||>=20" - } - }, - "node_modules/webfontloader": { - "version": "1.6.28", - "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz", - "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==", - "license": "Apache-2.0" - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index e2b35026d0c..64f2f9786db 100644 --- a/package.json +++ b/package.json @@ -21,50 +21,42 @@ "biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched", "docs": "typedoc", "depcruise": "depcruise src test", - "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", - "postinstall": "npx lefthook install && npx lefthook run post-merge", - "update-version:patch": "npm version patch --force --no-git-tag-version", - "update-version:minor": "npm version minor --force --no-git-tag-version", + "postinstall": "lefthook install; git submodule update --init --recursive", + "update-version:patch": "pnpm version patch --force --no-git-tag-version", + "update-version:minor": "pnpm version minor --force --no-git-tag-version", "update-locales:remote": "git submodule update --progress --init --recursive --force --remote" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@eslint/js": "^9.23.0", - "@hpcc-js/wasm": "^2.22.4", - "@stylistic/eslint-plugin-ts": "^4.1.0", + "@biomejs/biome": "2.0.0", "@types/jsdom": "^21.1.7", - "@types/node": "^22.13.14", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", - "@vitest/coverage-istanbul": "^3.0.9", - "dependency-cruiser": "^16.3.10", - "eslint": "^9.23.0", - "eslint-plugin-import-x": "^4.9.4", - "inquirer": "^12.4.2", - "jsdom": "^26.0.0", - "lefthook": "^1.11.5", - "msw": "^2.7.3", + "@types/node": "^22.16.3", + "@vitest/coverage-istanbul": "^3.2.4", + "chalk": "^5.4.1", + "dependency-cruiser": "^16.10.4", + "inquirer": "^12.7.0", + "jsdom": "^26.1.0", + "lefthook": "^1.12.2", + "msw": "^2.10.4", "phaser3spectorjs": "^0.0.8", - "typedoc": "^0.28.1", - "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", - "vite": "^6.3.4", + "typedoc": "^0.28.7", + "typescript": "^5.8.3", + "vite": "^6.3.5", "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.0.9", + "vitest": "^3.2.4", "vitest-canvas-mock": "^0.3.3" }, "dependencies": { "@material/material-color-utilities": "^0.2.7", "compare-versions": "^6.1.1", "crypto-js": "^4.2.0", - "i18next": "^24.2.2", - "i18next-browser-languagedetector": "^8.0.4", + "i18next": "^24.2.3", + "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", "i18next-korean-postposition-processor": "^1.0.0", - "json-stable-stringify": "^1.2.0", + "json-stable-stringify": "^1.3.0", "jszip": "^3.10.1", - "phaser": "^3.88.2", - "phaser3-rex-plugins": "^1.80.15" + "phaser": "^3.90.0", + "phaser3-rex-plugins": "^1.80.16" }, "engines": { "node": ">=22.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000000..9f465c56778 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3906 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@material/material-color-utilities': + specifier: ^0.2.7 + version: 0.2.7 + compare-versions: + specifier: ^6.1.1 + version: 6.1.1 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + i18next: + specifier: ^24.2.3 + version: 24.2.3(typescript@5.8.3) + i18next-browser-languagedetector: + specifier: ^8.2.0 + version: 8.2.0 + i18next-http-backend: + specifier: ^3.0.2 + version: 3.0.2 + i18next-korean-postposition-processor: + specifier: ^1.0.0 + version: 1.0.0(i18next@24.2.3(typescript@5.8.3)) + json-stable-stringify: + specifier: ^1.3.0 + version: 1.3.0 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + phaser: + specifier: ^3.90.0 + version: 3.90.0 + phaser3-rex-plugins: + specifier: ^1.80.16 + version: 1.80.16(graphology-types@0.24.8) + devDependencies: + '@biomejs/biome': + specifier: 2.0.0 + version: 2.0.0 + '@types/jsdom': + specifier: ^21.1.7 + version: 21.1.7 + '@types/node': + specifier: ^22.16.3 + version: 22.16.3 + '@vitest/coverage-istanbul': + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0)) + chalk: + specifier: ^5.4.1 + version: 5.4.1 + dependency-cruiser: + specifier: ^16.10.4 + version: 16.10.4 + inquirer: + specifier: ^12.7.0 + version: 12.7.0(@types/node@22.16.3) + jsdom: + specifier: ^26.1.0 + version: 26.1.0 + lefthook: + specifier: ^1.12.2 + version: 1.12.2 + msw: + specifier: ^2.10.4 + version: 2.10.4(@types/node@22.16.3)(typescript@5.8.3) + phaser3spectorjs: + specifier: ^0.0.8 + version: 0.0.8 + typedoc: + specifier: ^0.28.7 + version: 0.28.7(typescript@5.8.3) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.16.3)(yaml@2.8.0) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.3)(yaml@2.8.0)) + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0) + vitest-canvas-mock: + specifier: ^0.3.3 + version: 0.3.3(vitest@3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0)) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.0.0': + resolution: {integrity: sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.0.0': + resolution: {integrity: sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.0.0': + resolution: {integrity: sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.0.0': + resolution: {integrity: sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.0.0': + resolution: {integrity: sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.0.0': + resolution: {integrity: sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.0.0': + resolution: {integrity: sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.0.0': + resolution: {integrity: sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.0.0': + resolution: {integrity: sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + + '@csstools/color-helpers@5.0.2': + resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.0.10': + resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.25.6': + resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.6': + resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.6': + resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.6': + resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.6': + resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.6': + resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.6': + resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.6': + resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.6': + resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.6': + resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.6': + resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.6': + resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.6': + resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.6': + resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.6': + resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.6': + resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.6': + resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.6': + resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.6': + resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.6': + resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.6': + resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.6': + resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.6': + resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.6': + resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.6': + resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.6': + resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@gerrit0/mini-shiki@3.7.0': + resolution: {integrity: sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg==} + + '@inquirer/checkbox@4.1.9': + resolution: {integrity: sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.13': + resolution: {integrity: sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.14': + resolution: {integrity: sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.14': + resolution: {integrity: sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.16': + resolution: {integrity: sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.12': + resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} + engines: {node: '>=18'} + + '@inquirer/input@4.2.0': + resolution: {integrity: sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.16': + resolution: {integrity: sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.16': + resolution: {integrity: sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.6.0': + resolution: {integrity: sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.4': + resolution: {integrity: sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.0.16': + resolution: {integrity: sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.2.4': + resolution: {integrity: sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.7': + resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@material/material-color-utilities@0.2.7': + resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==} + + '@mswjs/interceptors@0.39.2': + resolution: {integrity: sha512-RuzCup9Ct91Y7V79xwCb146RaBRHZ7NBbrIUySumd1rpKqHL5OonaqrGIbug5hNwP/fRyxFMA6ISgw4FTtYFYg==} + engines: {node: '>=18'} + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@oxlint/darwin-arm64@1.6.0': + resolution: {integrity: sha512-m3wyqBh1TOHjpr/dXeIZY7OoX+MQazb+bMHQdDtwUvefrafUx+5YHRvulYh1sZSQ449nQ3nk3qj5qj535vZRjg==} + cpu: [arm64] + os: [darwin] + + '@oxlint/darwin-x64@1.6.0': + resolution: {integrity: sha512-75fJfF/9xNypr7cnOYoZBhfmG1yP7ex3pUOeYGakmtZRffO9z1i1quLYhjZsmaDXsAIZ3drMhenYHMmFKS3SRg==} + cpu: [x64] + os: [darwin] + + '@oxlint/linux-arm64-gnu@1.6.0': + resolution: {integrity: sha512-YhXGf0FXa72bEt4F7eTVKx5X3zWpbAOPnaA/dZ6/g8tGhw1m9IFjrabVHFjzcx3dQny4MgA59EhyElkDvpUe8A==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-arm64-musl@1.6.0': + resolution: {integrity: sha512-T3JDhx8mjGjvh5INsPZJrlKHmZsecgDYvtvussKRdkc1Nnn7WC+jH9sh5qlmYvwzvmetlPVNezAoNvmGO9vtMg==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-x64-gnu@1.6.0': + resolution: {integrity: sha512-Dx7ghtAl8aXBdqofJpi338At6lkeCtTfoinTYQXd9/TEJx+f+zCGNlQO6nJz3ydJBX48FDuOFKkNC+lUlWrd8w==} + cpu: [x64] + os: [linux] + + '@oxlint/linux-x64-musl@1.6.0': + resolution: {integrity: sha512-7KvMGdWmAZtAtg6IjoEJHKxTXdAcrHnUnqfgs0JpXst7trquV2mxBeRZusQXwxpu4HCSomKMvJfsp1qKaqSFDg==} + cpu: [x64] + os: [linux] + + '@oxlint/win32-arm64@1.6.0': + resolution: {integrity: sha512-iSGC9RwX+dl7o5KFr5aH7Gq3nFbkq/3Gda6mxNPMvNkWrgXdIyiINxpyD8hJu566M+QSv1wEAu934BZotFDyoQ==} + cpu: [arm64] + os: [win32] + + '@oxlint/win32-x64@1.6.0': + resolution: {integrity: sha512-jOj3L/gfLc0IwgOTkZMiZ5c673i/hbAmidlaylT0gE6H18hln9HxPgp5GCf4E4y6mwEJlW8QC5hQi221+9otdA==} + cpu: [x64] + os: [win32] + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/rollup-android-arm-eabi@4.45.0': + resolution: {integrity: sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.45.0': + resolution: {integrity: sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.45.0': + resolution: {integrity: sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.0': + resolution: {integrity: sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.45.0': + resolution: {integrity: sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.0': + resolution: {integrity: sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + resolution: {integrity: sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + resolution: {integrity: sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + resolution: {integrity: sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.45.0': + resolution: {integrity: sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + resolution: {integrity: sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + resolution: {integrity: sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + resolution: {integrity: sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + resolution: {integrity: sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + resolution: {integrity: sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.45.0': + resolution: {integrity: sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.45.0': + resolution: {integrity: sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + resolution: {integrity: sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + resolution: {integrity: sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.0': + resolution: {integrity: sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==} + cpu: [x64] + os: [win32] + + '@shikijs/engine-oniguruma@3.7.0': + resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} + + '@shikijs/langs@3.7.0': + resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} + + '@shikijs/themes@3.7.0': + resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} + + '@shikijs/types@3.7.0': + resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/node@22.16.3': + resolution: {integrity: sha512-sr4Xz74KOUeYadexo1r8imhRtlVXcs+j3XK3TcoiYk7B1t3YRVJgtaD3cwX73NYb71pmVuMLNRhJ9XKdoDB74g==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@vitest/coverage-istanbul@3.2.4': + resolution: {integrity: sha512-IDlpuFJiWU9rhcKLkpzj8mFu/lpe64gVgnV15ZOrYx1iFzxxrxCzbExiUEKtwwXRvEiEMUS6iZeYgnMxgbqbxQ==} + peerDependencies: + vitest: 3.2.4 + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + acorn-jsx-walk@2.0.0: + resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-loose@8.5.2: + resolution: {integrity: sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001727: + resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} + + chai@5.2.1: + resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + cssfontparser@1.2.1: + resolution: {integrity: sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + dagre@0.8.5: + resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + dependency-cruiser@16.10.4: + resolution: {integrity: sha512-hrxVOjIm8idZ9ZVDGSyyG3SHiNcEUPhL6RTEmO/3wfQWLepH5pA3nuDMMrcJ1DkZztFA7xg3tk8OVO+MmwwH9w==} + engines: {node: ^18.17||>=20} + hasBin: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.182: + resolution: {integrity: sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.6: + resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@3.1.2: + resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphlib@2.1.8: + resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} + + graphology-types@0.24.8: + resolution: {integrity: sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==} + + graphology@0.25.4: + resolution: {integrity: sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==} + peerDependencies: + graphology-types: '>=0.24.0' + + graphql@16.11.0: + resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + i18next-browser-languagedetector@8.2.0: + resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==} + + i18next-http-backend@2.7.3: + resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} + + i18next-http-backend@3.0.2: + resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==} + + i18next-korean-postposition-processor@1.0.0: + resolution: {integrity: sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==} + peerDependencies: + i18next: '>=8.4.0' + + i18next@22.5.1: + resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} + + i18next@24.2.3: + resolution: {integrity: sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@12.7.0: + resolution: {integrity: sha512-KKFRc++IONSyE2UYw9CJ1V0IWx5yQKomwB+pp3cWomWs+v2+ZsG11G2OVfAjFS6WWCppKw+RfKmpqGfSzD5QBQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jest-canvas-mock@2.5.2: + resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify@1.3.0: + resolution: {integrity: sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==} + engines: {node: '>= 0.4'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonify@0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lefthook-darwin-arm64@1.12.2: + resolution: {integrity: sha512-fTxeI9tEskrHjc3QyEO+AG7impBXY2Ed8V5aiRc3fw9POfYtVh9b5jRx90fjk2+ld5hf+Z1DsyyLq/vOHDFskQ==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@1.12.2: + resolution: {integrity: sha512-T1dCDKAAfdHgYZ8qtrS02SJSHoR52RFcrGArFNll9Mu4ZSV19Sp8BO+kTwDUOcLYdcPGNaqOp9PkRBQGZWQC7g==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@1.12.2: + resolution: {integrity: sha512-2n9z7Q4BKeMBoB9cuEdv0UBQH82Z4GgBQpCrfjCtyzpDnYQwrH8Tkrlnlko4qPh9MM6nLLGIYMKsA5nltzo8Cg==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@1.12.2: + resolution: {integrity: sha512-1hNY/irY+/3kjRzKoJYxG+m3BYI8QxopJUK1PQnknGo1Wy5u302SdX+tR7pnpz6JM5chrNw4ozSbKKOvdZ5VEw==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@1.12.2: + resolution: {integrity: sha512-1W4swYIVRkxq/LFTuuK4oVpd6NtTKY4E3VY2Uq2JDkIOJV46+8qGBF+C/QA9K3O9chLffgN7c+i+NhIuGiZ/Vw==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@1.12.2: + resolution: {integrity: sha512-J6VGuMfhq5iCsg1Pv7xULbuXC63gP5LaikT0PhkyBNMi3HQneZFDJ8k/sp0Ue9HkQv6QfWIo3/FgB9gz38MCFw==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@1.12.2: + resolution: {integrity: sha512-wncDRW3ml24DaOyH22KINumjvCohswbQqbxyH2GORRCykSnE859cTjOrRIchTKBIARF7PSeGPUtS7EK0+oDbaw==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@1.12.2: + resolution: {integrity: sha512-2jDOkCHNnc/oK/vR62hAf3vZb1EQ6Md2GjIlgZ/V7A3ztOsM8QZ5IxwYN3D1UOIR5ZnwMBy7PtmTJC/HJrig5w==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@1.12.2: + resolution: {integrity: sha512-ZMH/q6UNSidhHEG/1QoqIl1n4yPTBWuVmKx5bONtKHicoz4QCQ+QEiNjKsG5OO4C62nfyHGThmweCzZVUQECJw==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@1.12.2: + resolution: {integrity: sha512-TqT2jIPcTQ9uwaw+v+DTmvnUHM/p7bbsSrPoPX+fRXSGLzFjyiY+12C9dObSwfCQq6rT70xqQJ9AmftJQsa5/Q==} + cpu: [x64] + os: [win32] + + lefthook@1.12.2: + resolution: {integrity: sha512-2CeTu5NcmoT9YnqsHTq/TF36MlqlzHzhivGx3DrXHwcff4TdvrkIwUTA56huM3Nlo5ODAF/0hlPzaKLmNHCBnQ==} + hasBin: true + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + memoize@10.1.0: + resolution: {integrity: sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==} + engines: {node: '>=18'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + moo-color@1.0.3: + resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msw@2.10.4: + resolution: {integrity: sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nwsapi@2.2.20: + resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + obliterator@2.0.5: + resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + + oxlint@1.6.0: + resolution: {integrity: sha512-jtaD65PqzIa1udvSxxscTKBxYKuZoFXyKGLiU1Qjo1ulq3uv/fQDtoV1yey1FrQZrQjACGPi1Widsy1TucC7Jg==} + engines: {node: '>=8.*'} + hasBin: true + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + papaparse@5.5.3: + resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + phaser3-rex-plugins@1.80.16: + resolution: {integrity: sha512-c34SfEjdK7rz8EbM7bDmJwB/xCb/YR77IaAaowzqkCLb7pLeI3E8PfLivm882iirN11XOHjYT2uYlvtdbNAtMA==} + + phaser3spectorjs@0.0.8: + resolution: {integrity: sha512-0dSO7/aMjEUPrp5EcjRvRRsEf+jXDbmzalPeJ6VtTB2Pn1PeaKc+qlL/DmO3l1Dvc5lkzc+Sil1Ta+Hkyi5cbA==} + + phaser@3.90.0: + resolution: {integrity: sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.45.0: + resolution: {integrity: sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-async@4.0.4: + resolution: {integrity: sha512-2cgeRHnV11lSXBEhq7sN7a5UVjTKm9JTb9x8ApIT//16D7QL96AgnNeWSGoB4gIHc0iYw/Ha0Z+waBaCYZVNhg==} + engines: {node: '>=0.12.0'} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-regex@2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + teamcity-service-messages@0.1.14: + resolution: {integrity: sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==} + + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typedoc@0.28.7: + resolution: {integrity: sha512-lpz0Oxl6aidFkmS90VQDQjk/Qf2iw0IUvFqirdONBdj7jPSN9mGXhy66BcGNDxx5ZMyKKiBVAREvPEzT6Uxipw==} + engines: {node: '>= 18', pnpm: '>= 10'} + hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest-canvas-mock@0.3.3: + resolution: {integrity: sha512-3P968tYBpqYyzzOaVtqnmYjqbe13576/fkjbDEJSfQAkHtC5/UjuRHOhFEN/ZV5HVZIkaROBUWgazDKJ+Ibw+Q==} + peerDependencies: + vitest: '*' + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + watskeburt@4.2.3: + resolution: {integrity: sha512-uG9qtQYoHqAsnT711nG5iZc/8M5inSmkGCOp7pFaytKG2aTfIca7p//CjiVzAE4P7hzaYuCozMjNNaLgmhbK5g==} + engines: {node: ^18||>=20} + hasBin: true + + webfontloader@1.6.28: + resolution: {integrity: sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.1 + + '@babel/runtime@7.27.6': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@biomejs/biome@2.0.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.0.0 + '@biomejs/cli-darwin-x64': 2.0.0 + '@biomejs/cli-linux-arm64': 2.0.0 + '@biomejs/cli-linux-arm64-musl': 2.0.0 + '@biomejs/cli-linux-x64': 2.0.0 + '@biomejs/cli-linux-x64-musl': 2.0.0 + '@biomejs/cli-win32-arm64': 2.0.0 + '@biomejs/cli-win32-x64': 2.0.0 + + '@biomejs/cli-darwin-arm64@2.0.0': + optional: true + + '@biomejs/cli-darwin-x64@2.0.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.0.0': + optional: true + + '@biomejs/cli-linux-arm64@2.0.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.0.0': + optional: true + + '@biomejs/cli-linux-x64@2.0.0': + optional: true + + '@biomejs/cli-win32-arm64@2.0.0': + optional: true + + '@biomejs/cli-win32-x64@2.0.0': + optional: true + + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.2 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + + '@csstools/color-helpers@5.0.2': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.0.2 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@esbuild/aix-ppc64@0.25.6': + optional: true + + '@esbuild/android-arm64@0.25.6': + optional: true + + '@esbuild/android-arm@0.25.6': + optional: true + + '@esbuild/android-x64@0.25.6': + optional: true + + '@esbuild/darwin-arm64@0.25.6': + optional: true + + '@esbuild/darwin-x64@0.25.6': + optional: true + + '@esbuild/freebsd-arm64@0.25.6': + optional: true + + '@esbuild/freebsd-x64@0.25.6': + optional: true + + '@esbuild/linux-arm64@0.25.6': + optional: true + + '@esbuild/linux-arm@0.25.6': + optional: true + + '@esbuild/linux-ia32@0.25.6': + optional: true + + '@esbuild/linux-loong64@0.25.6': + optional: true + + '@esbuild/linux-mips64el@0.25.6': + optional: true + + '@esbuild/linux-ppc64@0.25.6': + optional: true + + '@esbuild/linux-riscv64@0.25.6': + optional: true + + '@esbuild/linux-s390x@0.25.6': + optional: true + + '@esbuild/linux-x64@0.25.6': + optional: true + + '@esbuild/netbsd-arm64@0.25.6': + optional: true + + '@esbuild/netbsd-x64@0.25.6': + optional: true + + '@esbuild/openbsd-arm64@0.25.6': + optional: true + + '@esbuild/openbsd-x64@0.25.6': + optional: true + + '@esbuild/openharmony-arm64@0.25.6': + optional: true + + '@esbuild/sunos-x64@0.25.6': + optional: true + + '@esbuild/win32-arm64@0.25.6': + optional: true + + '@esbuild/win32-ia32@0.25.6': + optional: true + + '@esbuild/win32-x64@0.25.6': + optional: true + + '@gerrit0/mini-shiki@3.7.0': + dependencies: + '@shikijs/engine-oniguruma': 3.7.0 + '@shikijs/langs': 3.7.0 + '@shikijs/themes': 3.7.0 + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@inquirer/checkbox@4.1.9(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@22.16.3) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/confirm@5.1.13(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/core@10.1.14(@types/node@22.16.3)': + dependencies: + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@22.16.3) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/editor@4.2.14(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + external-editor: 3.1.0 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/expand@4.0.16(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/figures@1.0.12': {} + + '@inquirer/input@4.2.0(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/number@3.0.16(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/password@4.0.16(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/prompts@7.6.0(@types/node@22.16.3)': + dependencies: + '@inquirer/checkbox': 4.1.9(@types/node@22.16.3) + '@inquirer/confirm': 5.1.13(@types/node@22.16.3) + '@inquirer/editor': 4.2.14(@types/node@22.16.3) + '@inquirer/expand': 4.0.16(@types/node@22.16.3) + '@inquirer/input': 4.2.0(@types/node@22.16.3) + '@inquirer/number': 3.0.16(@types/node@22.16.3) + '@inquirer/password': 4.0.16(@types/node@22.16.3) + '@inquirer/rawlist': 4.1.4(@types/node@22.16.3) + '@inquirer/search': 3.0.16(@types/node@22.16.3) + '@inquirer/select': 4.2.4(@types/node@22.16.3) + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/rawlist@4.1.4(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/search@3.0.16(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@22.16.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/select@4.2.4(@types/node@22.16.3)': + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@22.16.3) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.16.3 + + '@inquirer/type@3.0.7(@types/node@22.16.3)': + optionalDependencies: + '@types/node': 22.16.3 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@material/material-color-utilities@0.2.7': {} + + '@mswjs/interceptors@0.39.2': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@oxlint/darwin-arm64@1.6.0': + optional: true + + '@oxlint/darwin-x64@1.6.0': + optional: true + + '@oxlint/linux-arm64-gnu@1.6.0': + optional: true + + '@oxlint/linux-arm64-musl@1.6.0': + optional: true + + '@oxlint/linux-x64-gnu@1.6.0': + optional: true + + '@oxlint/linux-x64-musl@1.6.0': + optional: true + + '@oxlint/win32-arm64@1.6.0': + optional: true + + '@oxlint/win32-x64@1.6.0': + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.45.0': + optional: true + + '@rollup/rollup-android-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-x64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.0': + optional: true + + '@shikijs/engine-oniguruma@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/themes@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/types@3.7.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/cookie@0.6.0': {} + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 22.16.3 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/node@22.16.3': + dependencies: + undici-types: 6.21.0 + + '@types/statuses@2.0.6': {} + + '@types/tough-cookie@4.0.5': {} + + '@types/unist@3.0.3': {} + + '@vitest/coverage-istanbul@3.2.4(vitest@3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0))': + dependencies: + '@istanbuljs/schema': 0.1.3 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magicast: 0.3.5 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.3)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + msw: 2.10.4(@types/node@22.16.3)(typescript@5.8.3) + vite: 6.3.5(@types/node@22.16.3)(yaml@2.8.0) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.3 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.1.4 + tinyrainbow: 2.0.0 + + acorn-jsx-walk@2.0.0: {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-loose@8.5.2: + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001727 + electron-to-chromium: 1.5.182 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001727: {} + + chai@5.2.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.4 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chardet@0.7.0: {} + + check-error@2.1.1: {} + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@13.1.0: {} + + compare-versions@6.1.1: {} + + convert-source-map@2.0.0: {} + + cookie@0.7.2: {} + + core-util-is@1.0.3: {} + + cross-fetch@4.0.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-js@4.2.0: {} + + cssfontparser@1.2.1: {} + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + dagre@0.8.5: + dependencies: + graphlib: 2.1.8 + lodash: 4.17.21 + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + dependency-cruiser@16.10.4: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + acorn-jsx-walk: 2.0.0 + acorn-loose: 8.5.2 + acorn-walk: 8.3.4 + ajv: 8.17.1 + commander: 13.1.0 + enhanced-resolve: 5.18.2 + ignore: 7.0.5 + interpret: 3.1.1 + is-installed-globally: 1.0.0 + json5: 2.2.3 + memoize: 10.1.0 + picocolors: 1.1.1 + picomatch: 4.0.2 + prompts: 2.4.2 + rechoir: 0.8.0 + safe-regex: 2.1.1 + semver: 7.7.2 + teamcity-service-messages: 0.1.14 + tsconfig-paths-webpack-plugin: 4.2.0 + watskeburt: 4.2.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.182: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + entities@4.5.0: {} + + entities@6.0.1: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.25.6: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.6 + '@esbuild/android-arm': 0.25.6 + '@esbuild/android-arm64': 0.25.6 + '@esbuild/android-x64': 0.25.6 + '@esbuild/darwin-arm64': 0.25.6 + '@esbuild/darwin-x64': 0.25.6 + '@esbuild/freebsd-arm64': 0.25.6 + '@esbuild/freebsd-x64': 0.25.6 + '@esbuild/linux-arm': 0.25.6 + '@esbuild/linux-arm64': 0.25.6 + '@esbuild/linux-ia32': 0.25.6 + '@esbuild/linux-loong64': 0.25.6 + '@esbuild/linux-mips64el': 0.25.6 + '@esbuild/linux-ppc64': 0.25.6 + '@esbuild/linux-riscv64': 0.25.6 + '@esbuild/linux-s390x': 0.25.6 + '@esbuild/linux-x64': 0.25.6 + '@esbuild/netbsd-arm64': 0.25.6 + '@esbuild/netbsd-x64': 0.25.6 + '@esbuild/openbsd-arm64': 0.25.6 + '@esbuild/openbsd-x64': 0.25.6 + '@esbuild/openharmony-arm64': 0.25.6 + '@esbuild/sunos-x64': 0.25.6 + '@esbuild/win32-arm64': 0.25.6 + '@esbuild/win32-ia32': 0.25.6 + '@esbuild/win32-x64': 0.25.6 + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + eventemitter3@3.1.2: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + expect-type@1.2.2: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-uri@3.0.6: {} + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphlib@2.1.8: + dependencies: + lodash: 4.17.21 + + graphology-types@0.24.8: {} + + graphology@0.25.4(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-types: 0.24.8 + obliterator: 2.0.5 + + graphql@16.11.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + headers-polyfill@4.0.3: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-escaper@2.0.2: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + i18next-browser-languagedetector@8.2.0: + dependencies: + '@babel/runtime': 7.27.6 + + i18next-http-backend@2.7.3: + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + + i18next-http-backend@3.0.2: + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + + i18next-korean-postposition-processor@1.0.0(i18next@24.2.3(typescript@5.8.3)): + dependencies: + i18next: 24.2.3(typescript@5.8.3) + + i18next@22.5.1: + dependencies: + '@babel/runtime': 7.27.6 + + i18next@24.2.3(typescript@5.8.3): + dependencies: + '@babel/runtime': 7.27.6 + optionalDependencies: + typescript: 5.8.3 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + inherits@2.0.4: {} + + ini@4.1.1: {} + + inquirer@12.7.0(@types/node@22.16.3): + dependencies: + '@inquirer/core': 10.1.14(@types/node@22.16.3) + '@inquirer/prompts': 7.6.0(@types/node@22.16.3) + '@inquirer/type': 3.0.7(@types/node@22.16.3) + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 4.0.4 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 22.16.3 + + interpret@3.1.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-fullwidth-code-point@3.0.0: {} + + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + + is-node-process@1.2.0: {} + + is-path-inside@4.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jest-canvas-mock@2.5.2: + dependencies: + cssfontparser: 1.2.1 + moo-color: 1.0.3 + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.20 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify@1.3.0: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + isarray: 2.0.5 + jsonify: 0.0.1 + object-keys: 1.1.1 + + json5@2.2.3: {} + + jsonify@0.0.1: {} + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + kleur@3.0.3: {} + + lefthook-darwin-arm64@1.12.2: + optional: true + + lefthook-darwin-x64@1.12.2: + optional: true + + lefthook-freebsd-arm64@1.12.2: + optional: true + + lefthook-freebsd-x64@1.12.2: + optional: true + + lefthook-linux-arm64@1.12.2: + optional: true + + lefthook-linux-x64@1.12.2: + optional: true + + lefthook-openbsd-arm64@1.12.2: + optional: true + + lefthook-openbsd-x64@1.12.2: + optional: true + + lefthook-windows-arm64@1.12.2: + optional: true + + lefthook-windows-x64@1.12.2: + optional: true + + lefthook@1.12.2: + optionalDependencies: + lefthook-darwin-arm64: 1.12.2 + lefthook-darwin-x64: 1.12.2 + lefthook-freebsd-arm64: 1.12.2 + lefthook-freebsd-x64: 1.12.2 + lefthook-linux-arm64: 1.12.2 + lefthook-linux-x64: 1.12.2 + lefthook-openbsd-arm64: 1.12.2 + lefthook-openbsd-x64: 1.12.2 + lefthook-windows-arm64: 1.12.2 + lefthook-windows-x64: 1.12.2 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + lodash@4.17.21: {} + + loupe@3.1.4: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lunr@2.3.9: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + math-intrinsics@1.1.0: {} + + mdurl@2.0.0: {} + + memoize@10.1.0: + dependencies: + mimic-function: 5.0.1 + + mimic-function@5.0.1: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + moo-color@1.0.3: + dependencies: + color-name: 1.1.4 + + ms@2.1.3: {} + + msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.13(@types/node@22.16.3) + '@mswjs/interceptors': 0.39.2 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.6 + graphql: 16.11.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.41.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - '@types/node' + + mustache@4.2.0: {} + + mute-stream@2.0.0: {} + + nanoid@3.3.11: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.19: {} + + nwsapi@2.2.20: {} + + object-keys@1.1.1: {} + + obliterator@2.0.5: {} + + os-tmpdir@1.0.2: {} + + outvariant@1.4.3: {} + + oxlint@1.6.0: + optionalDependencies: + '@oxlint/darwin-arm64': 1.6.0 + '@oxlint/darwin-x64': 1.6.0 + '@oxlint/linux-arm64-gnu': 1.6.0 + '@oxlint/linux-arm64-musl': 1.6.0 + '@oxlint/linux-x64-gnu': 1.6.0 + '@oxlint/linux-x64-musl': 1.6.0 + '@oxlint/win32-arm64': 1.6.0 + '@oxlint/win32-x64': 1.6.0 + + package-json-from-dist@1.0.1: {} + + pako@1.0.11: {} + + papaparse@5.5.3: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + phaser3-rex-plugins@1.80.16(graphology-types@0.24.8): + dependencies: + dagre: 0.8.5 + eventemitter3: 3.1.2 + graphology: 0.25.4(graphology-types@0.24.8) + i18next: 22.5.1 + i18next-http-backend: 2.7.3 + js-yaml: 4.1.0 + mustache: 4.2.0 + papaparse: 5.5.3 + webfontloader: 1.6.28 + transitivePeerDependencies: + - encoding + - graphology-types + + phaser3spectorjs@0.0.8: {} + + phaser@3.90.0: + dependencies: + eventemitter3: 5.0.1 + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.6.2: {} + + process-nextick-args@2.0.1: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + querystringify@2.2.0: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + rechoir@0.8.0: + dependencies: + resolve: 1.22.10 + + regexp-tree@0.1.27: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.45.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.0 + '@rollup/rollup-android-arm64': 4.45.0 + '@rollup/rollup-darwin-arm64': 4.45.0 + '@rollup/rollup-darwin-x64': 4.45.0 + '@rollup/rollup-freebsd-arm64': 4.45.0 + '@rollup/rollup-freebsd-x64': 4.45.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.0 + '@rollup/rollup-linux-arm-musleabihf': 4.45.0 + '@rollup/rollup-linux-arm64-gnu': 4.45.0 + '@rollup/rollup-linux-arm64-musl': 4.45.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-musl': 4.45.0 + '@rollup/rollup-linux-s390x-gnu': 4.45.0 + '@rollup/rollup-linux-x64-gnu': 4.45.0 + '@rollup/rollup-linux-x64-musl': 4.45.0 + '@rollup/rollup-win32-arm64-msvc': 4.45.0 + '@rollup/rollup-win32-ia32-msvc': 4.45.0 + '@rollup/rollup-win32-x64-msvc': 4.45.0 + fsevents: 2.3.3 + + rrweb-cssom@0.8.0: {} + + run-async@4.0.4: + dependencies: + oxlint: 1.6.0 + prettier: 3.6.2 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.1.2: {} + + safe-regex@2.1.1: + dependencies: + regexp-tree: 0.1.27 + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + semver@6.3.1: {} + + semver@7.7.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.9.0: {} + + strict-event-emitter@0.5.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-tree@3.2.4: {} + + tapable@2.2.2: {} + + teamcity-service-messages@0.1.14: {} + + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.3: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@0.0.3: {} + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + tsconfck@3.1.6(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.18.2 + tapable: 2.2.2 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + typedoc@0.28.7(typescript@5.8.3): + dependencies: + '@gerrit0/mini-shiki': 3.7.0 + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.8.3 + yaml: 2.8.0 + + typescript@5.8.3: {} + + uc.micro@2.1.0: {} + + undici-types@6.21.0: {} + + universalify@0.2.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + vite-node@3.2.4(@types/node@22.16.3)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@22.16.3)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.3)(yaml@2.8.0)): + dependencies: + debug: 4.4.1 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.8.3) + optionalDependencies: + vite: 6.3.5(@types/node@22.16.3)(yaml@2.8.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@6.3.5(@types/node@22.16.3)(yaml@2.8.0): + dependencies: + esbuild: 0.25.6 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.45.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.16.3 + fsevents: 2.3.3 + yaml: 2.8.0 + + vitest-canvas-mock@0.3.3(vitest@3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0)): + dependencies: + jest-canvas-mock: 2.5.2 + vitest: 3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0) + + vitest@3.2.4(@types/node@22.16.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.16.3)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.3)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + debug: 4.4.1 + expect-type: 1.2.2 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@22.16.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@22.16.3)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.16.3 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + watskeburt@4.2.3: {} + + webfontloader@1.6.28: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + ws@8.18.3: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@2.8.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yoctocolors-cjs@2.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000000..ec73d96e0db --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +onlyBuiltDependencies: + - esbuild + - msw + - lefthook + +shellEmulator: true diff --git a/public/audio/bgm/abyss.mp3 b/public/audio/bgm/abyss.mp3 index 08882ac7e09..778b2cd77e8 100644 Binary files a/public/audio/bgm/abyss.mp3 and b/public/audio/bgm/abyss.mp3 differ diff --git a/public/audio/bgm/battle_legendary_eternatus_p1.mp3 b/public/audio/bgm/battle_legendary_eternatus_p1.mp3 new file mode 100644 index 00000000000..742a3a198c7 Binary files /dev/null and b/public/audio/bgm/battle_legendary_eternatus_p1.mp3 differ diff --git a/public/audio/bgm/battle_legendary_eternatus_p2.mp3 b/public/audio/bgm/battle_legendary_eternatus_p2.mp3 new file mode 100644 index 00000000000..2015e735840 Binary files /dev/null and b/public/audio/bgm/battle_legendary_eternatus_p2.mp3 differ diff --git a/public/fonts/Galmuri11.subset.woff2 b/public/fonts/Galmuri11.subset.woff2 deleted file mode 100644 index 0b626506255..00000000000 Binary files a/public/fonts/Galmuri11.subset.woff2 and /dev/null differ diff --git a/public/fonts/Galmuri9.subset.woff2 b/public/fonts/Galmuri9.subset.woff2 deleted file mode 100644 index 5475af0cdad..00000000000 Binary files a/public/fonts/Galmuri9.subset.woff2 and /dev/null differ diff --git a/public/fonts/item-count.png b/public/fonts/item-count.png index 85cfe88e902..ea6ad8d8ee5 100644 Binary files a/public/fonts/item-count.png and b/public/fonts/item-count.png differ diff --git a/public/fonts/pokemon-bw.ttf b/public/fonts/pokemon-bw.ttf new file mode 100644 index 00000000000..b246be443e2 Binary files /dev/null and b/public/fonts/pokemon-bw.ttf differ diff --git a/public/images/arenas/abyss_a.png b/public/images/arenas/abyss_a.png index 1b3b9d287d3..9bd8f1a9032 100644 Binary files a/public/images/arenas/abyss_a.png and b/public/images/arenas/abyss_a.png differ diff --git a/public/images/arenas/abyss_b.png b/public/images/arenas/abyss_b.png index 2bcf8b6f243..98811c108f2 100644 Binary files a/public/images/arenas/abyss_b.png and b/public/images/arenas/abyss_b.png differ diff --git a/public/images/arenas/abyss_b_1.png b/public/images/arenas/abyss_b_1.png index 24f1340de48..91ab529e898 100644 Binary files a/public/images/arenas/abyss_b_1.png and b/public/images/arenas/abyss_b_1.png differ diff --git a/public/images/arenas/abyss_b_2.png b/public/images/arenas/abyss_b_2.png index 1ba1aa60e59..1a9bd1dc9f4 100644 Binary files a/public/images/arenas/abyss_b_2.png and b/public/images/arenas/abyss_b_2.png differ diff --git a/public/images/arenas/abyss_b_3.png b/public/images/arenas/abyss_b_3.png index 4dddce63274..823c36d6c8b 100644 Binary files a/public/images/arenas/abyss_b_3.png and b/public/images/arenas/abyss_b_3.png differ diff --git a/public/images/arenas/abyss_bg.png b/public/images/arenas/abyss_bg.png index 1abc378806a..224071c0dac 100644 Binary files a/public/images/arenas/abyss_bg.png and b/public/images/arenas/abyss_bg.png differ diff --git a/public/images/arenas/badlands_a.png b/public/images/arenas/badlands_a.png index 2e404d30253..f1118d46310 100644 Binary files a/public/images/arenas/badlands_a.png and b/public/images/arenas/badlands_a.png differ diff --git a/public/images/arenas/badlands_b.png b/public/images/arenas/badlands_b.png index 6232eddfede..355e82883f0 100644 Binary files a/public/images/arenas/badlands_b.png and b/public/images/arenas/badlands_b.png differ diff --git a/public/images/arenas/badlands_b_1.png b/public/images/arenas/badlands_b_1.png index a4cbe062061..f8568ff4163 100644 Binary files a/public/images/arenas/badlands_b_1.png and b/public/images/arenas/badlands_b_1.png differ diff --git a/public/images/arenas/badlands_b_2.png b/public/images/arenas/badlands_b_2.png index 64396591282..21da949a4ce 100644 Binary files a/public/images/arenas/badlands_b_2.png and b/public/images/arenas/badlands_b_2.png differ diff --git a/public/images/arenas/badlands_b_3.png b/public/images/arenas/badlands_b_3.png index ba8553182fd..4ab8d7d5520 100644 Binary files a/public/images/arenas/badlands_b_3.png and b/public/images/arenas/badlands_b_3.png differ diff --git a/public/images/arenas/badlands_bg.png b/public/images/arenas/badlands_bg.png index 71efce424fa..92a8562c21a 100644 Binary files a/public/images/arenas/badlands_bg.png and b/public/images/arenas/badlands_bg.png differ diff --git a/public/images/arenas/beach_a.png b/public/images/arenas/beach_a.png index 90e9189a4be..ea66c2d19d6 100644 Binary files a/public/images/arenas/beach_a.png and b/public/images/arenas/beach_a.png differ diff --git a/public/images/arenas/beach_b.png b/public/images/arenas/beach_b.png index e3b7e31ea96..233fb53358e 100644 Binary files a/public/images/arenas/beach_b.png and b/public/images/arenas/beach_b.png differ diff --git a/public/images/arenas/beach_b_1.png b/public/images/arenas/beach_b_1.png index 1441c75abde..73ad6db675b 100644 Binary files a/public/images/arenas/beach_b_1.png and b/public/images/arenas/beach_b_1.png differ diff --git a/public/images/arenas/beach_b_2.png b/public/images/arenas/beach_b_2.png index 8f79f5b59f2..50c9be97b0d 100644 Binary files a/public/images/arenas/beach_b_2.png and b/public/images/arenas/beach_b_2.png differ diff --git a/public/images/arenas/beach_b_3.png b/public/images/arenas/beach_b_3.png index 45904c68986..c31a4f6b7f6 100644 Binary files a/public/images/arenas/beach_b_3.png and b/public/images/arenas/beach_b_3.png differ diff --git a/public/images/arenas/beach_bg.png b/public/images/arenas/beach_bg.png index b1ee271c7b7..48c81a61336 100644 Binary files a/public/images/arenas/beach_bg.png and b/public/images/arenas/beach_bg.png differ diff --git a/public/images/arenas/cave_a.png b/public/images/arenas/cave_a.png index 23e79a31b43..071546b4411 100644 Binary files a/public/images/arenas/cave_a.png and b/public/images/arenas/cave_a.png differ diff --git a/public/images/arenas/cave_b.png b/public/images/arenas/cave_b.png index 884a2232b66..7b3f53c6465 100644 Binary files a/public/images/arenas/cave_b.png and b/public/images/arenas/cave_b.png differ diff --git a/public/images/arenas/cave_b_1.png b/public/images/arenas/cave_b_1.png index 4e6a3219081..fe9f07f6ebd 100644 Binary files a/public/images/arenas/cave_b_1.png and b/public/images/arenas/cave_b_1.png differ diff --git a/public/images/arenas/cave_b_2.png b/public/images/arenas/cave_b_2.png index cc2bdd98f27..f76560e1f03 100644 Binary files a/public/images/arenas/cave_b_2.png and b/public/images/arenas/cave_b_2.png differ diff --git a/public/images/arenas/cave_b_3.png b/public/images/arenas/cave_b_3.png index d05144c6d1e..346fd5f08e6 100644 Binary files a/public/images/arenas/cave_b_3.png and b/public/images/arenas/cave_b_3.png differ diff --git a/public/images/arenas/cave_bg.png b/public/images/arenas/cave_bg.png index 37f4b9c41ff..24c0462a231 100644 Binary files a/public/images/arenas/cave_bg.png and b/public/images/arenas/cave_bg.png differ diff --git a/public/images/arenas/construction_site_a.png b/public/images/arenas/construction_site_a.png index bbd63269244..8c8ea7d49fb 100644 Binary files a/public/images/arenas/construction_site_a.png and b/public/images/arenas/construction_site_a.png differ diff --git a/public/images/arenas/construction_site_b.png b/public/images/arenas/construction_site_b.png index 51884ff0453..4c1333c12d0 100644 Binary files a/public/images/arenas/construction_site_b.png and b/public/images/arenas/construction_site_b.png differ diff --git a/public/images/arenas/construction_site_b_1.png b/public/images/arenas/construction_site_b_1.png index cc01a5a86d5..4e3e22f1fae 100644 Binary files a/public/images/arenas/construction_site_b_1.png and b/public/images/arenas/construction_site_b_1.png differ diff --git a/public/images/arenas/construction_site_b_2.png b/public/images/arenas/construction_site_b_2.png index b4ad8d154d8..4dc1c383952 100644 Binary files a/public/images/arenas/construction_site_b_2.png and b/public/images/arenas/construction_site_b_2.png differ diff --git a/public/images/arenas/construction_site_b_3.png b/public/images/arenas/construction_site_b_3.png index fd00a05fb9f..484dd7197f4 100644 Binary files a/public/images/arenas/construction_site_b_3.png and b/public/images/arenas/construction_site_b_3.png differ diff --git a/public/images/arenas/construction_site_bg.png b/public/images/arenas/construction_site_bg.png index 79aaaef7cab..ff324a620b1 100644 Binary files a/public/images/arenas/construction_site_bg.png and b/public/images/arenas/construction_site_bg.png differ diff --git a/public/images/arenas/default_bg.png b/public/images/arenas/default_bg.png index 3c1ae88d032..f49ff0de8fa 100644 Binary files a/public/images/arenas/default_bg.png and b/public/images/arenas/default_bg.png differ diff --git a/public/images/arenas/desert_a.png b/public/images/arenas/desert_a.png index 0d8ead8251e..83e5a9d5122 100644 Binary files a/public/images/arenas/desert_a.png and b/public/images/arenas/desert_a.png differ diff --git a/public/images/arenas/desert_b.png b/public/images/arenas/desert_b.png index 85403341266..c03b5fa4d4b 100644 Binary files a/public/images/arenas/desert_b.png and b/public/images/arenas/desert_b.png differ diff --git a/public/images/arenas/desert_b_1.png b/public/images/arenas/desert_b_1.png index 1e8d33d48b7..f1f04caef99 100644 Binary files a/public/images/arenas/desert_b_1.png and b/public/images/arenas/desert_b_1.png differ diff --git a/public/images/arenas/desert_b_2.png b/public/images/arenas/desert_b_2.png index a907372731e..3c0561040c2 100644 Binary files a/public/images/arenas/desert_b_2.png and b/public/images/arenas/desert_b_2.png differ diff --git a/public/images/arenas/desert_b_3.png b/public/images/arenas/desert_b_3.png index 8016fe6c933..101b2bbada5 100644 Binary files a/public/images/arenas/desert_b_3.png and b/public/images/arenas/desert_b_3.png differ diff --git a/public/images/arenas/desert_bg.png b/public/images/arenas/desert_bg.png index 57c00d7bbc5..f4d44e69afc 100644 Binary files a/public/images/arenas/desert_bg.png and b/public/images/arenas/desert_bg.png differ diff --git a/public/images/arenas/dojo_a.png b/public/images/arenas/dojo_a.png index a68acc61a5a..969bd0c09cf 100644 Binary files a/public/images/arenas/dojo_a.png and b/public/images/arenas/dojo_a.png differ diff --git a/public/images/arenas/dojo_b.png b/public/images/arenas/dojo_b.png index 1797a371cf4..4a55429c4ec 100644 Binary files a/public/images/arenas/dojo_b.png and b/public/images/arenas/dojo_b.png differ diff --git a/public/images/arenas/dojo_bg.png b/public/images/arenas/dojo_bg.png index 2a23c835211..c1c9e883627 100644 Binary files a/public/images/arenas/dojo_bg.png and b/public/images/arenas/dojo_bg.png differ diff --git a/public/images/arenas/end_a.png b/public/images/arenas/end_a.png index eeaef65eb7a..06a82254e0c 100644 Binary files a/public/images/arenas/end_a.png and b/public/images/arenas/end_a.png differ diff --git a/public/images/arenas/end_b.png b/public/images/arenas/end_b.png index 37a59201d23..6093d09dd61 100644 Binary files a/public/images/arenas/end_b.png and b/public/images/arenas/end_b.png differ diff --git a/public/images/arenas/end_b_1.png b/public/images/arenas/end_b_1.png index a79e8d0e0ea..2cb508226f0 100644 Binary files a/public/images/arenas/end_b_1.png and b/public/images/arenas/end_b_1.png differ diff --git a/public/images/arenas/end_b_2.png b/public/images/arenas/end_b_2.png index 8753543b8b0..9c66867b5bc 100644 Binary files a/public/images/arenas/end_b_2.png and b/public/images/arenas/end_b_2.png differ diff --git a/public/images/arenas/end_b_3.png b/public/images/arenas/end_b_3.png index 2ce6e372487..3310c9b9c18 100644 Binary files a/public/images/arenas/end_b_3.png and b/public/images/arenas/end_b_3.png differ diff --git a/public/images/arenas/end_bg.png b/public/images/arenas/end_bg.png index 0f821d9e357..0cfae7f7b40 100644 Binary files a/public/images/arenas/end_bg.png and b/public/images/arenas/end_bg.png differ diff --git a/public/images/arenas/factory_a.png b/public/images/arenas/factory_a.png index 7752f08e938..aa1b1024619 100644 Binary files a/public/images/arenas/factory_a.png and b/public/images/arenas/factory_a.png differ diff --git a/public/images/arenas/factory_b.png b/public/images/arenas/factory_b.png index eea4397eac6..31242986bc0 100644 Binary files a/public/images/arenas/factory_b.png and b/public/images/arenas/factory_b.png differ diff --git a/public/images/arenas/factory_b_1.png b/public/images/arenas/factory_b_1.png index 73530a9acbe..c38ffe00b0d 100644 Binary files a/public/images/arenas/factory_b_1.png and b/public/images/arenas/factory_b_1.png differ diff --git a/public/images/arenas/factory_b_2.png b/public/images/arenas/factory_b_2.png index 4b60c243717..0b2347b36c4 100644 Binary files a/public/images/arenas/factory_b_2.png and b/public/images/arenas/factory_b_2.png differ diff --git a/public/images/arenas/factory_b_3.png b/public/images/arenas/factory_b_3.png index 83c0c29f80c..f2bb93128cf 100644 Binary files a/public/images/arenas/factory_b_3.png and b/public/images/arenas/factory_b_3.png differ diff --git a/public/images/arenas/factory_bg.png b/public/images/arenas/factory_bg.png index 1d3b7e7cfd6..5bc8939f3f7 100644 Binary files a/public/images/arenas/factory_bg.png and b/public/images/arenas/factory_bg.png differ diff --git a/public/images/arenas/fairy_cave_a.png b/public/images/arenas/fairy_cave_a.png index f2636d6f601..ffbe4397a9a 100644 Binary files a/public/images/arenas/fairy_cave_a.png and b/public/images/arenas/fairy_cave_a.png differ diff --git a/public/images/arenas/fairy_cave_b.png b/public/images/arenas/fairy_cave_b.png index 5d9f4e8cad0..0afd5cf86a9 100644 Binary files a/public/images/arenas/fairy_cave_b.png and b/public/images/arenas/fairy_cave_b.png differ diff --git a/public/images/arenas/fairy_cave_b_1.png b/public/images/arenas/fairy_cave_b_1.png index e1c86ddfc0e..c30fd89bdf3 100644 Binary files a/public/images/arenas/fairy_cave_b_1.png and b/public/images/arenas/fairy_cave_b_1.png differ diff --git a/public/images/arenas/fairy_cave_b_2.png b/public/images/arenas/fairy_cave_b_2.png index 2b73e8111b7..6738be06508 100644 Binary files a/public/images/arenas/fairy_cave_b_2.png and b/public/images/arenas/fairy_cave_b_2.png differ diff --git a/public/images/arenas/fairy_cave_b_3.png b/public/images/arenas/fairy_cave_b_3.png index 8e0459f94d0..44fe937fe25 100644 Binary files a/public/images/arenas/fairy_cave_b_3.png and b/public/images/arenas/fairy_cave_b_3.png differ diff --git a/public/images/arenas/fairy_cave_bg.png b/public/images/arenas/fairy_cave_bg.png index 0eee1df0796..3282bfc6736 100644 Binary files a/public/images/arenas/fairy_cave_bg.png and b/public/images/arenas/fairy_cave_bg.png differ diff --git a/public/images/arenas/forest_a.png b/public/images/arenas/forest_a.png index a19c6568caa..31cb7947951 100644 Binary files a/public/images/arenas/forest_a.png and b/public/images/arenas/forest_a.png differ diff --git a/public/images/arenas/forest_b.png b/public/images/arenas/forest_b.png index 0fc971d11b3..dcd1981a5e9 100644 Binary files a/public/images/arenas/forest_b.png and b/public/images/arenas/forest_b.png differ diff --git a/public/images/arenas/forest_bg.png b/public/images/arenas/forest_bg.png index 92219224dff..6d9eb8dbee5 100644 Binary files a/public/images/arenas/forest_bg.png and b/public/images/arenas/forest_bg.png differ diff --git a/public/images/arenas/grass_a.png b/public/images/arenas/grass_a.png index 90685819201..f3428ee9f30 100644 Binary files a/public/images/arenas/grass_a.png and b/public/images/arenas/grass_a.png differ diff --git a/public/images/arenas/grass_b.png b/public/images/arenas/grass_b.png index 88a31b5512a..e2056c9cac8 100644 Binary files a/public/images/arenas/grass_b.png and b/public/images/arenas/grass_b.png differ diff --git a/public/images/arenas/grass_bg.png b/public/images/arenas/grass_bg.png index 8ebfa5ae695..490198179d8 100644 Binary files a/public/images/arenas/grass_bg.png and b/public/images/arenas/grass_bg.png differ diff --git a/public/images/arenas/graveyard_a.png b/public/images/arenas/graveyard_a.png index c7ee580dddc..a77cda3f519 100644 Binary files a/public/images/arenas/graveyard_a.png and b/public/images/arenas/graveyard_a.png differ diff --git a/public/images/arenas/graveyard_b.png b/public/images/arenas/graveyard_b.png index e8980bd859c..77a7acd2dd7 100644 Binary files a/public/images/arenas/graveyard_b.png and b/public/images/arenas/graveyard_b.png differ diff --git a/public/images/arenas/graveyard_b_1.png b/public/images/arenas/graveyard_b_1.png index 1d693865691..2e4f524d5bc 100644 Binary files a/public/images/arenas/graveyard_b_1.png and b/public/images/arenas/graveyard_b_1.png differ diff --git a/public/images/arenas/graveyard_b_2.png b/public/images/arenas/graveyard_b_2.png index 7e99aa4a36d..04eec264773 100644 Binary files a/public/images/arenas/graveyard_b_2.png and b/public/images/arenas/graveyard_b_2.png differ diff --git a/public/images/arenas/graveyard_b_3.png b/public/images/arenas/graveyard_b_3.png index b7879e9df36..7e7e174552b 100644 Binary files a/public/images/arenas/graveyard_b_3.png and b/public/images/arenas/graveyard_b_3.png differ diff --git a/public/images/arenas/graveyard_bg.png b/public/images/arenas/graveyard_bg.png index 9beffdc817c..c49c05e0b6f 100644 Binary files a/public/images/arenas/graveyard_bg.png and b/public/images/arenas/graveyard_bg.png differ diff --git a/public/images/arenas/ice_cave_a.png b/public/images/arenas/ice_cave_a.png index 52f2e468e1b..30b1ccc675e 100644 Binary files a/public/images/arenas/ice_cave_a.png and b/public/images/arenas/ice_cave_a.png differ diff --git a/public/images/arenas/ice_cave_b.png b/public/images/arenas/ice_cave_b.png index 684c008b467..63878dbb4e9 100644 Binary files a/public/images/arenas/ice_cave_b.png and b/public/images/arenas/ice_cave_b.png differ diff --git a/public/images/arenas/ice_cave_b_1.png b/public/images/arenas/ice_cave_b_1.png index 8d287a2c526..a28dedb50a8 100644 Binary files a/public/images/arenas/ice_cave_b_1.png and b/public/images/arenas/ice_cave_b_1.png differ diff --git a/public/images/arenas/ice_cave_b_2.png b/public/images/arenas/ice_cave_b_2.png index 675d0e52b8d..d7e6fb1784e 100644 Binary files a/public/images/arenas/ice_cave_b_2.png and b/public/images/arenas/ice_cave_b_2.png differ diff --git a/public/images/arenas/ice_cave_b_3.png b/public/images/arenas/ice_cave_b_3.png index 44e4e25a8e6..f674ab8f796 100644 Binary files a/public/images/arenas/ice_cave_b_3.png and b/public/images/arenas/ice_cave_b_3.png differ diff --git a/public/images/arenas/ice_cave_bg.png b/public/images/arenas/ice_cave_bg.png index 3f3e809e5ed..a9675202932 100644 Binary files a/public/images/arenas/ice_cave_bg.png and b/public/images/arenas/ice_cave_bg.png differ diff --git a/public/images/arenas/island_a.png b/public/images/arenas/island_a.png index e48675aa59b..e57c28dfa82 100644 Binary files a/public/images/arenas/island_a.png and b/public/images/arenas/island_a.png differ diff --git a/public/images/arenas/island_b.png b/public/images/arenas/island_b.png index 53bd4f1fe4c..d2dbe8bf0b1 100644 Binary files a/public/images/arenas/island_b.png and b/public/images/arenas/island_b.png differ diff --git a/public/images/arenas/island_b_1.png b/public/images/arenas/island_b_1.png index 8619e406a3f..bf7f3cb32e1 100644 Binary files a/public/images/arenas/island_b_1.png and b/public/images/arenas/island_b_1.png differ diff --git a/public/images/arenas/island_b_2.png b/public/images/arenas/island_b_2.png index b6dfa077346..a414da194ee 100644 Binary files a/public/images/arenas/island_b_2.png and b/public/images/arenas/island_b_2.png differ diff --git a/public/images/arenas/island_b_3.png b/public/images/arenas/island_b_3.png index 192c06950ae..07595401d2a 100644 Binary files a/public/images/arenas/island_b_3.png and b/public/images/arenas/island_b_3.png differ diff --git a/public/images/arenas/island_bg.png b/public/images/arenas/island_bg.png index 5ecb6d8100d..e05c3afa775 100644 Binary files a/public/images/arenas/island_bg.png and b/public/images/arenas/island_bg.png differ diff --git a/public/images/arenas/jungle_a.png b/public/images/arenas/jungle_a.png index 5501593d9e1..48a801fb65f 100644 Binary files a/public/images/arenas/jungle_a.png and b/public/images/arenas/jungle_a.png differ diff --git a/public/images/arenas/jungle_b.png b/public/images/arenas/jungle_b.png index 29ec524298c..e6f239fda0a 100644 Binary files a/public/images/arenas/jungle_b.png and b/public/images/arenas/jungle_b.png differ diff --git a/public/images/arenas/jungle_b_1.png b/public/images/arenas/jungle_b_1.png index 462bd0df264..e1f45c38e85 100644 Binary files a/public/images/arenas/jungle_b_1.png and b/public/images/arenas/jungle_b_1.png differ diff --git a/public/images/arenas/jungle_b_2.png b/public/images/arenas/jungle_b_2.png index 1a395af55ae..dfcfa355960 100644 Binary files a/public/images/arenas/jungle_b_2.png and b/public/images/arenas/jungle_b_2.png differ diff --git a/public/images/arenas/jungle_b_3.png b/public/images/arenas/jungle_b_3.png index e579f4013ce..5bb08ec72e4 100644 Binary files a/public/images/arenas/jungle_b_3.png and b/public/images/arenas/jungle_b_3.png differ diff --git a/public/images/arenas/jungle_bg.png b/public/images/arenas/jungle_bg.png index ff2cda1c9da..e03de2a3223 100644 Binary files a/public/images/arenas/jungle_bg.png and b/public/images/arenas/jungle_bg.png differ diff --git a/public/images/arenas/laboratory_a.png b/public/images/arenas/laboratory_a.png index 628a6919ebb..56f080f49fb 100644 Binary files a/public/images/arenas/laboratory_a.png and b/public/images/arenas/laboratory_a.png differ diff --git a/public/images/arenas/laboratory_b.png b/public/images/arenas/laboratory_b.png index eabd398bada..7948b89f072 100644 Binary files a/public/images/arenas/laboratory_b.png and b/public/images/arenas/laboratory_b.png differ diff --git a/public/images/arenas/laboratory_b_1.png b/public/images/arenas/laboratory_b_1.png index b3ee7875943..57786e6b224 100644 Binary files a/public/images/arenas/laboratory_b_1.png and b/public/images/arenas/laboratory_b_1.png differ diff --git a/public/images/arenas/laboratory_b_2.png b/public/images/arenas/laboratory_b_2.png index 66ce17d947b..fcd945b8b86 100644 Binary files a/public/images/arenas/laboratory_b_2.png and b/public/images/arenas/laboratory_b_2.png differ diff --git a/public/images/arenas/laboratory_b_3.png b/public/images/arenas/laboratory_b_3.png index 99b23e95a1f..e2538fbef77 100644 Binary files a/public/images/arenas/laboratory_b_3.png and b/public/images/arenas/laboratory_b_3.png differ diff --git a/public/images/arenas/laboratory_bg.png b/public/images/arenas/laboratory_bg.png index 331e15f8db7..0e64877c0b6 100644 Binary files a/public/images/arenas/laboratory_bg.png and b/public/images/arenas/laboratory_bg.png differ diff --git a/public/images/arenas/lake_a.png b/public/images/arenas/lake_a.png index 6ca45fb4101..cce0a61a5f6 100644 Binary files a/public/images/arenas/lake_a.png and b/public/images/arenas/lake_a.png differ diff --git a/public/images/arenas/lake_b.png b/public/images/arenas/lake_b.png index c1c81f2b29c..c24893a1392 100644 Binary files a/public/images/arenas/lake_b.png and b/public/images/arenas/lake_b.png differ diff --git a/public/images/arenas/lake_b_1.png b/public/images/arenas/lake_b_1.png index be6db3451a8..5426dd1e8d9 100644 Binary files a/public/images/arenas/lake_b_1.png and b/public/images/arenas/lake_b_1.png differ diff --git a/public/images/arenas/lake_b_2.png b/public/images/arenas/lake_b_2.png index ee8af5049c3..b08ccd7b8e4 100644 Binary files a/public/images/arenas/lake_b_2.png and b/public/images/arenas/lake_b_2.png differ diff --git a/public/images/arenas/lake_b_3.png b/public/images/arenas/lake_b_3.png index f0cf92f5389..aff06718ecb 100644 Binary files a/public/images/arenas/lake_b_3.png and b/public/images/arenas/lake_b_3.png differ diff --git a/public/images/arenas/lake_bg.png b/public/images/arenas/lake_bg.png index 547d30782c2..15d8348e20c 100644 Binary files a/public/images/arenas/lake_bg.png and b/public/images/arenas/lake_bg.png differ diff --git a/public/images/arenas/loading_bg.png b/public/images/arenas/loading_bg.png index 79c249f6879..2ed3ca2333b 100644 Binary files a/public/images/arenas/loading_bg.png and b/public/images/arenas/loading_bg.png differ diff --git a/public/images/arenas/meadow_a.png b/public/images/arenas/meadow_a.png index 1069d0fab93..549ddadc549 100644 Binary files a/public/images/arenas/meadow_a.png and b/public/images/arenas/meadow_a.png differ diff --git a/public/images/arenas/meadow_b.png b/public/images/arenas/meadow_b.png index 50db0026271..42bbe50606f 100644 Binary files a/public/images/arenas/meadow_b.png and b/public/images/arenas/meadow_b.png differ diff --git a/public/images/arenas/meadow_b_1.png b/public/images/arenas/meadow_b_1.png index 82dbbdcda86..fcd599c9477 100644 Binary files a/public/images/arenas/meadow_b_1.png and b/public/images/arenas/meadow_b_1.png differ diff --git a/public/images/arenas/meadow_b_2.png b/public/images/arenas/meadow_b_2.png index 547e0a2b8a7..23a075e9215 100644 Binary files a/public/images/arenas/meadow_b_2.png and b/public/images/arenas/meadow_b_2.png differ diff --git a/public/images/arenas/meadow_b_3.png b/public/images/arenas/meadow_b_3.png index 5a763e19b0e..493ea6d0b78 100644 Binary files a/public/images/arenas/meadow_b_3.png and b/public/images/arenas/meadow_b_3.png differ diff --git a/public/images/arenas/meadow_bg.png b/public/images/arenas/meadow_bg.png index 8f4bbc5655c..7463051755e 100644 Binary files a/public/images/arenas/meadow_bg.png and b/public/images/arenas/meadow_bg.png differ diff --git a/public/images/arenas/metropolis_a.png b/public/images/arenas/metropolis_a.png index 5a51c500702..cbb1a17c7e9 100644 Binary files a/public/images/arenas/metropolis_a.png and b/public/images/arenas/metropolis_a.png differ diff --git a/public/images/arenas/metropolis_b.png b/public/images/arenas/metropolis_b.png index 638899f3e53..6ae764af398 100644 Binary files a/public/images/arenas/metropolis_b.png and b/public/images/arenas/metropolis_b.png differ diff --git a/public/images/arenas/metropolis_b_1.png b/public/images/arenas/metropolis_b_1.png index 6e543b4c55e..52a0df3a7f9 100644 Binary files a/public/images/arenas/metropolis_b_1.png and b/public/images/arenas/metropolis_b_1.png differ diff --git a/public/images/arenas/metropolis_b_2.png b/public/images/arenas/metropolis_b_2.png index bc609ddb300..8402a90a42d 100644 Binary files a/public/images/arenas/metropolis_b_2.png and b/public/images/arenas/metropolis_b_2.png differ diff --git a/public/images/arenas/metropolis_b_3.png b/public/images/arenas/metropolis_b_3.png index c29a3b7bb7c..7e8f9e7b825 100644 Binary files a/public/images/arenas/metropolis_b_3.png and b/public/images/arenas/metropolis_b_3.png differ diff --git a/public/images/arenas/metropolis_bg.png b/public/images/arenas/metropolis_bg.png index b84c5aa494d..374744f245e 100644 Binary files a/public/images/arenas/metropolis_bg.png and b/public/images/arenas/metropolis_bg.png differ diff --git a/public/images/arenas/mountain_a.png b/public/images/arenas/mountain_a.png index f3896fb409f..7f539ba0a7a 100644 Binary files a/public/images/arenas/mountain_a.png and b/public/images/arenas/mountain_a.png differ diff --git a/public/images/arenas/mountain_b.png b/public/images/arenas/mountain_b.png index 99b4399b1f3..9c970e74180 100644 Binary files a/public/images/arenas/mountain_b.png and b/public/images/arenas/mountain_b.png differ diff --git a/public/images/arenas/mountain_b_1.png b/public/images/arenas/mountain_b_1.png index aa7c1209f5a..1f895b64aec 100644 Binary files a/public/images/arenas/mountain_b_1.png and b/public/images/arenas/mountain_b_1.png differ diff --git a/public/images/arenas/mountain_b_2.png b/public/images/arenas/mountain_b_2.png index 377c3b16b23..9f60c9e15d7 100644 Binary files a/public/images/arenas/mountain_b_2.png and b/public/images/arenas/mountain_b_2.png differ diff --git a/public/images/arenas/mountain_b_3.png b/public/images/arenas/mountain_b_3.png index ba44117634a..55e892377cd 100644 Binary files a/public/images/arenas/mountain_b_3.png and b/public/images/arenas/mountain_b_3.png differ diff --git a/public/images/arenas/mountain_bg.png b/public/images/arenas/mountain_bg.png index 972a08a8695..102f331bcf9 100644 Binary files a/public/images/arenas/mountain_bg.png and b/public/images/arenas/mountain_bg.png differ diff --git a/public/images/arenas/plains_a.png b/public/images/arenas/plains_a.png index ada3c63ccd8..49a36b80844 100644 Binary files a/public/images/arenas/plains_a.png and b/public/images/arenas/plains_a.png differ diff --git a/public/images/arenas/plains_b.png b/public/images/arenas/plains_b.png index b75b9825b21..85e3439fb12 100644 Binary files a/public/images/arenas/plains_b.png and b/public/images/arenas/plains_b.png differ diff --git a/public/images/arenas/plains_b_1.png b/public/images/arenas/plains_b_1.png new file mode 100644 index 00000000000..4e4a0a46164 Binary files /dev/null and b/public/images/arenas/plains_b_1.png differ diff --git a/public/images/arenas/plains_b_2.png b/public/images/arenas/plains_b_2.png new file mode 100644 index 00000000000..a35dbec9917 Binary files /dev/null and b/public/images/arenas/plains_b_2.png differ diff --git a/public/images/arenas/plains_b_3.png b/public/images/arenas/plains_b_3.png new file mode 100644 index 00000000000..f61b9206a46 Binary files /dev/null and b/public/images/arenas/plains_b_3.png differ diff --git a/public/images/arenas/plains_bg.png b/public/images/arenas/plains_bg.png index 7815fd8843f..f2cbec9197c 100644 Binary files a/public/images/arenas/plains_bg.png and b/public/images/arenas/plains_bg.png differ diff --git a/public/images/arenas/power_plant_a.png b/public/images/arenas/power_plant_a.png index 15f7970d726..f5e99d62b3b 100644 Binary files a/public/images/arenas/power_plant_a.png and b/public/images/arenas/power_plant_a.png differ diff --git a/public/images/arenas/power_plant_b.png b/public/images/arenas/power_plant_b.png index 67fa01426c9..9f6647adf8d 100644 Binary files a/public/images/arenas/power_plant_b.png and b/public/images/arenas/power_plant_b.png differ diff --git a/public/images/arenas/power_plant_b_1.png b/public/images/arenas/power_plant_b_1.png index 43c4979214f..c97baf92450 100644 Binary files a/public/images/arenas/power_plant_b_1.png and b/public/images/arenas/power_plant_b_1.png differ diff --git a/public/images/arenas/power_plant_b_2.png b/public/images/arenas/power_plant_b_2.png index 1cecfa0e56a..cb8cdddc9cc 100644 Binary files a/public/images/arenas/power_plant_b_2.png and b/public/images/arenas/power_plant_b_2.png differ diff --git a/public/images/arenas/power_plant_b_3.png b/public/images/arenas/power_plant_b_3.png index 25d75ff53ed..228060e3a8d 100644 Binary files a/public/images/arenas/power_plant_b_3.png and b/public/images/arenas/power_plant_b_3.png differ diff --git a/public/images/arenas/power_plant_bg.png b/public/images/arenas/power_plant_bg.png index 578f2a141d4..f0a91431a04 100644 Binary files a/public/images/arenas/power_plant_bg.png and b/public/images/arenas/power_plant_bg.png differ diff --git a/public/images/arenas/ruins_a.png b/public/images/arenas/ruins_a.png index b264c82b452..e24bc129809 100644 Binary files a/public/images/arenas/ruins_a.png and b/public/images/arenas/ruins_a.png differ diff --git a/public/images/arenas/ruins_b.png b/public/images/arenas/ruins_b.png index 88a0c3f87d9..c019f4d0317 100644 Binary files a/public/images/arenas/ruins_b.png and b/public/images/arenas/ruins_b.png differ diff --git a/public/images/arenas/ruins_b_1.png b/public/images/arenas/ruins_b_1.png index 548b9266866..dd6643f5f36 100644 Binary files a/public/images/arenas/ruins_b_1.png and b/public/images/arenas/ruins_b_1.png differ diff --git a/public/images/arenas/ruins_b_2.png b/public/images/arenas/ruins_b_2.png index 4b2393cf886..f14eac400be 100644 Binary files a/public/images/arenas/ruins_b_2.png and b/public/images/arenas/ruins_b_2.png differ diff --git a/public/images/arenas/ruins_b_3.png b/public/images/arenas/ruins_b_3.png index b15755d0d81..bc34b12fe10 100644 Binary files a/public/images/arenas/ruins_b_3.png and b/public/images/arenas/ruins_b_3.png differ diff --git a/public/images/arenas/ruins_bg.png b/public/images/arenas/ruins_bg.png index 7e6448e14ef..616c586c8c6 100644 Binary files a/public/images/arenas/ruins_bg.png and b/public/images/arenas/ruins_bg.png differ diff --git a/public/images/arenas/sea_a.png b/public/images/arenas/sea_a.png index ef93c3159d5..05cc9c2880d 100644 Binary files a/public/images/arenas/sea_a.png and b/public/images/arenas/sea_a.png differ diff --git a/public/images/arenas/sea_b.png b/public/images/arenas/sea_b.png index 17081770fb6..31984c870ab 100644 Binary files a/public/images/arenas/sea_b.png and b/public/images/arenas/sea_b.png differ diff --git a/public/images/arenas/sea_bg.png b/public/images/arenas/sea_bg.png index 65c48634fc7..9ab7445681f 100644 Binary files a/public/images/arenas/sea_bg.png and b/public/images/arenas/sea_bg.png differ diff --git a/public/images/arenas/seabed_a.png b/public/images/arenas/seabed_a.png index b6a854e7807..f4f5408e218 100644 Binary files a/public/images/arenas/seabed_a.png and b/public/images/arenas/seabed_a.png differ diff --git a/public/images/arenas/seabed_b.png b/public/images/arenas/seabed_b.png index a9ded4f54a2..6743c67ea87 100644 Binary files a/public/images/arenas/seabed_b.png and b/public/images/arenas/seabed_b.png differ diff --git a/public/images/arenas/seabed_b_1.png b/public/images/arenas/seabed_b_1.png index e71de87d194..c913d219bc4 100644 Binary files a/public/images/arenas/seabed_b_1.png and b/public/images/arenas/seabed_b_1.png differ diff --git a/public/images/arenas/seabed_b_2.png b/public/images/arenas/seabed_b_2.png index e0a87d6a4b7..33eb117a70c 100644 Binary files a/public/images/arenas/seabed_b_2.png and b/public/images/arenas/seabed_b_2.png differ diff --git a/public/images/arenas/seabed_b_3.png b/public/images/arenas/seabed_b_3.png index 868c9951f2b..5389076edd9 100644 Binary files a/public/images/arenas/seabed_b_3.png and b/public/images/arenas/seabed_b_3.png differ diff --git a/public/images/arenas/seabed_bg.png b/public/images/arenas/seabed_bg.png index 9d3ea46a681..38b28453362 100644 Binary files a/public/images/arenas/seabed_bg.png and b/public/images/arenas/seabed_bg.png differ diff --git a/public/images/arenas/slum_a.png b/public/images/arenas/slum_a.png index 4756d7acfed..4230b89abd8 100644 Binary files a/public/images/arenas/slum_a.png and b/public/images/arenas/slum_a.png differ diff --git a/public/images/arenas/slum_b.png b/public/images/arenas/slum_b.png index 38bdadae526..82a36473ae4 100644 Binary files a/public/images/arenas/slum_b.png and b/public/images/arenas/slum_b.png differ diff --git a/public/images/arenas/slum_bg.png b/public/images/arenas/slum_bg.png index f3c39504204..ad2cf0e2951 100644 Binary files a/public/images/arenas/slum_bg.png and b/public/images/arenas/slum_bg.png differ diff --git a/public/images/arenas/snowy_forest_a.png b/public/images/arenas/snowy_forest_a.png index c44abbbce51..b1a049543ae 100644 Binary files a/public/images/arenas/snowy_forest_a.png and b/public/images/arenas/snowy_forest_a.png differ diff --git a/public/images/arenas/snowy_forest_b.png b/public/images/arenas/snowy_forest_b.png index e4ae9f4b180..e1675300279 100644 Binary files a/public/images/arenas/snowy_forest_b.png and b/public/images/arenas/snowy_forest_b.png differ diff --git a/public/images/arenas/snowy_forest_b_1.png b/public/images/arenas/snowy_forest_b_1.png index f47f4f91508..1631722b6e1 100644 Binary files a/public/images/arenas/snowy_forest_b_1.png and b/public/images/arenas/snowy_forest_b_1.png differ diff --git a/public/images/arenas/snowy_forest_b_2.png b/public/images/arenas/snowy_forest_b_2.png index 5bc27fc5161..aea120be719 100644 Binary files a/public/images/arenas/snowy_forest_b_2.png and b/public/images/arenas/snowy_forest_b_2.png differ diff --git a/public/images/arenas/snowy_forest_b_3.png b/public/images/arenas/snowy_forest_b_3.png index 521870232de..14c8f4c64aa 100644 Binary files a/public/images/arenas/snowy_forest_b_3.png and b/public/images/arenas/snowy_forest_b_3.png differ diff --git a/public/images/arenas/snowy_forest_bg.png b/public/images/arenas/snowy_forest_bg.png index c449ddb32ee..b0dcc3ddfc5 100644 Binary files a/public/images/arenas/snowy_forest_bg.png and b/public/images/arenas/snowy_forest_bg.png differ diff --git a/public/images/arenas/space_a.png b/public/images/arenas/space_a.png index 8176fd9217c..dbf5c18ecc3 100644 Binary files a/public/images/arenas/space_a.png and b/public/images/arenas/space_a.png differ diff --git a/public/images/arenas/space_b.png b/public/images/arenas/space_b.png index 1be1c0c5309..42cc73943d8 100644 Binary files a/public/images/arenas/space_b.png and b/public/images/arenas/space_b.png differ diff --git a/public/images/arenas/space_bg.png b/public/images/arenas/space_bg.png index a83db0fc6ff..4fdd53348c6 100644 Binary files a/public/images/arenas/space_bg.png and b/public/images/arenas/space_bg.png differ diff --git a/public/images/arenas/swamp_a.png b/public/images/arenas/swamp_a.png index ea037b85b9c..731092a2fe3 100644 Binary files a/public/images/arenas/swamp_a.png and b/public/images/arenas/swamp_a.png differ diff --git a/public/images/arenas/swamp_b.png b/public/images/arenas/swamp_b.png index 3e8f5f308e5..3a571027e3c 100644 Binary files a/public/images/arenas/swamp_b.png and b/public/images/arenas/swamp_b.png differ diff --git a/public/images/arenas/swamp_bg.png b/public/images/arenas/swamp_bg.png index 52cf79213e9..b48b72bed9f 100644 Binary files a/public/images/arenas/swamp_bg.png and b/public/images/arenas/swamp_bg.png differ diff --git a/public/images/arenas/tall_grass_a.png b/public/images/arenas/tall_grass_a.png index 5fa1421903d..8078351eaaa 100644 Binary files a/public/images/arenas/tall_grass_a.png and b/public/images/arenas/tall_grass_a.png differ diff --git a/public/images/arenas/tall_grass_b.png b/public/images/arenas/tall_grass_b.png index 39d23a2247f..a1b405fbf0c 100644 Binary files a/public/images/arenas/tall_grass_b.png and b/public/images/arenas/tall_grass_b.png differ diff --git a/public/images/arenas/tall_grass_bg.png b/public/images/arenas/tall_grass_bg.png index ea8e31d0e75..d76a0b4a4a6 100644 Binary files a/public/images/arenas/tall_grass_bg.png and b/public/images/arenas/tall_grass_bg.png differ diff --git a/public/images/arenas/temple_a.png b/public/images/arenas/temple_a.png index aa6b3b2eb55..245131d9ae1 100644 Binary files a/public/images/arenas/temple_a.png and b/public/images/arenas/temple_a.png differ diff --git a/public/images/arenas/temple_b.png b/public/images/arenas/temple_b.png index 07d4c2b86bb..cb910289119 100644 Binary files a/public/images/arenas/temple_b.png and b/public/images/arenas/temple_b.png differ diff --git a/public/images/arenas/temple_b_1.png b/public/images/arenas/temple_b_1.png index 971a890c8a3..ed7f6c9d3d6 100644 Binary files a/public/images/arenas/temple_b_1.png and b/public/images/arenas/temple_b_1.png differ diff --git a/public/images/arenas/temple_b_2.png b/public/images/arenas/temple_b_2.png index 30ab5653742..7e864271eaa 100644 Binary files a/public/images/arenas/temple_b_2.png and b/public/images/arenas/temple_b_2.png differ diff --git a/public/images/arenas/temple_b_3.png b/public/images/arenas/temple_b_3.png index 3ea8793526b..3d3c93b5d56 100644 Binary files a/public/images/arenas/temple_b_3.png and b/public/images/arenas/temple_b_3.png differ diff --git a/public/images/arenas/temple_bg.png b/public/images/arenas/temple_bg.png index 9296648d836..0510ff86bb0 100644 Binary files a/public/images/arenas/temple_bg.png and b/public/images/arenas/temple_bg.png differ diff --git a/public/images/arenas/town_a.png b/public/images/arenas/town_a.png index faeea0c4aee..0837806c678 100644 Binary files a/public/images/arenas/town_a.png and b/public/images/arenas/town_a.png differ diff --git a/public/images/arenas/town_b.png b/public/images/arenas/town_b.png index 10db2998a36..f1de28f625a 100644 Binary files a/public/images/arenas/town_b.png and b/public/images/arenas/town_b.png differ diff --git a/public/images/arenas/town_bg.png b/public/images/arenas/town_bg.png index e1dc5a9363e..b2f035135cc 100644 Binary files a/public/images/arenas/town_bg.png and b/public/images/arenas/town_bg.png differ diff --git a/public/images/arenas/volcano_a.png b/public/images/arenas/volcano_a.png index ab98acabda2..ecf086689cc 100644 Binary files a/public/images/arenas/volcano_a.png and b/public/images/arenas/volcano_a.png differ diff --git a/public/images/arenas/volcano_b.png b/public/images/arenas/volcano_b.png index 5a5b27d623c..f995e5b8c9b 100644 Binary files a/public/images/arenas/volcano_b.png and b/public/images/arenas/volcano_b.png differ diff --git a/public/images/arenas/volcano_b_1.png b/public/images/arenas/volcano_b_1.png index 0d6c291620d..68558800f9f 100644 Binary files a/public/images/arenas/volcano_b_1.png and b/public/images/arenas/volcano_b_1.png differ diff --git a/public/images/arenas/volcano_b_2.png b/public/images/arenas/volcano_b_2.png index 30677ca16af..3bef9ff5ae4 100644 Binary files a/public/images/arenas/volcano_b_2.png and b/public/images/arenas/volcano_b_2.png differ diff --git a/public/images/arenas/volcano_b_3.png b/public/images/arenas/volcano_b_3.png index 5b0401ebfde..ba4e493cd7e 100644 Binary files a/public/images/arenas/volcano_b_3.png and b/public/images/arenas/volcano_b_3.png differ diff --git a/public/images/arenas/volcano_bg.png b/public/images/arenas/volcano_bg.png index bb72170f70e..7c4d9d9e962 100644 Binary files a/public/images/arenas/volcano_bg.png and b/public/images/arenas/volcano_bg.png differ diff --git a/public/images/arenas/wasteland_a.png b/public/images/arenas/wasteland_a.png index 7a88f33feef..61e33b93c60 100644 Binary files a/public/images/arenas/wasteland_a.png and b/public/images/arenas/wasteland_a.png differ diff --git a/public/images/arenas/wasteland_b.png b/public/images/arenas/wasteland_b.png index 38234d4140a..6ac4a317b17 100644 Binary files a/public/images/arenas/wasteland_b.png and b/public/images/arenas/wasteland_b.png differ diff --git a/public/images/arenas/wasteland_b_1.png b/public/images/arenas/wasteland_b_1.png index 6cf27c532cd..d10a7f27699 100644 Binary files a/public/images/arenas/wasteland_b_1.png and b/public/images/arenas/wasteland_b_1.png differ diff --git a/public/images/arenas/wasteland_b_2.png b/public/images/arenas/wasteland_b_2.png index 1f5f23a86cd..e4102a19ce6 100644 Binary files a/public/images/arenas/wasteland_b_2.png and b/public/images/arenas/wasteland_b_2.png differ diff --git a/public/images/arenas/wasteland_b_3.png b/public/images/arenas/wasteland_b_3.png index b5329ada69b..cdc5a2bdd50 100644 Binary files a/public/images/arenas/wasteland_b_3.png and b/public/images/arenas/wasteland_b_3.png differ diff --git a/public/images/arenas/wasteland_bg.png b/public/images/arenas/wasteland_bg.png index 3c321a8568e..f1a94e1e22d 100644 Binary files a/public/images/arenas/wasteland_bg.png and b/public/images/arenas/wasteland_bg.png differ diff --git a/public/images/battle_anims/003-Attack01.png b/public/images/battle_anims/003-Attack01.png index 870fb592852..2b249874d6a 100644 Binary files a/public/images/battle_anims/003-Attack01.png and b/public/images/battle_anims/003-Attack01.png differ diff --git a/public/images/battle_anims/004-Attack02.png b/public/images/battle_anims/004-Attack02.png index e60f3cfc46f..061aa9dd706 100644 Binary files a/public/images/battle_anims/004-Attack02.png and b/public/images/battle_anims/004-Attack02.png differ diff --git a/public/images/battle_anims/007-Weapon02.png b/public/images/battle_anims/007-Weapon02.png index aab407081f7..e76da15bf4c 100644 Binary files a/public/images/battle_anims/007-Weapon02.png and b/public/images/battle_anims/007-Weapon02.png differ diff --git a/public/images/battle_anims/029-Emotion01.png b/public/images/battle_anims/029-Emotion01.png index 3fe970fb4ea..40d67887d00 100644 Binary files a/public/images/battle_anims/029-Emotion01.png and b/public/images/battle_anims/029-Emotion01.png differ diff --git a/public/images/battle_anims/Anima (2).png b/public/images/battle_anims/Anima (2).png index f4651241e03..c51c95c483e 100644 Binary files a/public/images/battle_anims/Anima (2).png and b/public/images/battle_anims/Anima (2).png differ diff --git a/public/images/battle_anims/GEN8- Aura Wheel Ally.png b/public/images/battle_anims/GEN8- Aura Wheel Ally.png index 97243d3f121..3c506b190b8 100644 Binary files a/public/images/battle_anims/GEN8- Aura Wheel Ally.png and b/public/images/battle_anims/GEN8- Aura Wheel Ally.png differ diff --git a/public/images/battle_anims/GEN8- Aura Wheel Opp.png b/public/images/battle_anims/GEN8- Aura Wheel Opp.png index 516b015f384..9849b68422e 100644 Binary files a/public/images/battle_anims/GEN8- Aura Wheel Opp.png and b/public/images/battle_anims/GEN8- Aura Wheel Opp.png differ diff --git a/public/images/battle_anims/GEN8- Behemoth Bash Sword.png b/public/images/battle_anims/GEN8- Behemoth Bash Sword.png index bfa2a645b5d..91677023868 100644 Binary files a/public/images/battle_anims/GEN8- Behemoth Bash Sword.png and b/public/images/battle_anims/GEN8- Behemoth Bash Sword.png differ diff --git a/public/images/battle_anims/GEN8- Fiery.png b/public/images/battle_anims/GEN8- Fiery.png index 329a0663c0e..51abe55cf88 100644 Binary files a/public/images/battle_anims/GEN8- Fiery.png and b/public/images/battle_anims/GEN8- Fiery.png differ diff --git a/public/images/battle_anims/GEN8- No Retreat.png b/public/images/battle_anims/GEN8- No Retreat.png index fa4f45c9bd8..42bf8eb6eda 100644 Binary files a/public/images/battle_anims/GEN8- No Retreat.png and b/public/images/battle_anims/GEN8- No Retreat.png differ diff --git a/public/images/battle_anims/GEN8- PrimalGroudon2.png b/public/images/battle_anims/GEN8- PrimalGroudon2.png index 781e38e77fc..dcb704c3f9a 100644 Binary files a/public/images/battle_anims/GEN8- PrimalGroudon2.png and b/public/images/battle_anims/GEN8- PrimalGroudon2.png differ diff --git a/public/images/battle_anims/GEN8- PrimalKyogre2.png b/public/images/battle_anims/GEN8- PrimalKyogre2.png index 120f9ecf67f..b4581bdeadb 100644 Binary files a/public/images/battle_anims/GEN8- PrimalKyogre2.png and b/public/images/battle_anims/GEN8- PrimalKyogre2.png differ diff --git a/public/images/battle_anims/GEN8- UltraBurst2.png b/public/images/battle_anims/GEN8- UltraBurst2.png index d0f33ea0d22..37a48d24a6c 100644 Binary files a/public/images/battle_anims/GEN8- UltraBurst2.png and b/public/images/battle_anims/GEN8- UltraBurst2.png differ diff --git a/public/images/battle_anims/Gen9- Bitter Blade.png b/public/images/battle_anims/Gen9- Bitter Blade.png index 763d4fcc063..7733e521b39 100644 Binary files a/public/images/battle_anims/Gen9- Bitter Blade.png and b/public/images/battle_anims/Gen9- Bitter Blade.png differ diff --git a/public/images/battle_anims/Light1.png b/public/images/battle_anims/Light1.png index 78e278fdcef..36af8bb5060 100644 Binary files a/public/images/battle_anims/Light1.png and b/public/images/battle_anims/Light1.png differ diff --git a/public/images/battle_anims/Majesty - Shed Tail.png b/public/images/battle_anims/Majesty - Shed Tail.png index 47969335b35..6cf1a8fd219 100644 Binary files a/public/images/battle_anims/Majesty - Shed Tail.png and b/public/images/battle_anims/Majesty - Shed Tail.png differ diff --git a/public/images/battle_anims/PRAS- All Out Pummeling.png b/public/images/battle_anims/PRAS- All Out Pummeling.png index f0528fe59ba..c54dfeb25aa 100644 Binary files a/public/images/battle_anims/PRAS- All Out Pummeling.png and b/public/images/battle_anims/PRAS- All Out Pummeling.png differ diff --git a/public/images/battle_anims/PRAS- Aqua Jet.png b/public/images/battle_anims/PRAS- Aqua Jet.png index 6079d67d384..284743c12e2 100644 Binary files a/public/images/battle_anims/PRAS- Aqua Jet.png and b/public/images/battle_anims/PRAS- Aqua Jet.png differ diff --git a/public/images/battle_anims/PRAS- Aurora Veil FG.png b/public/images/battle_anims/PRAS- Aurora Veil FG.png index 87061305e38..1ac8f7341c0 100644 Binary files a/public/images/battle_anims/PRAS- Aurora Veil FG.png and b/public/images/battle_anims/PRAS- Aurora Veil FG.png differ diff --git a/public/images/battle_anims/PRAS- Aurora Veil Opp FG.png b/public/images/battle_anims/PRAS- Aurora Veil Opp FG.png index 0a283cd59cb..186fa027eb2 100644 Binary files a/public/images/battle_anims/PRAS- Aurora Veil Opp FG.png and b/public/images/battle_anims/PRAS- Aurora Veil Opp FG.png differ diff --git a/public/images/battle_anims/PRAS- Barrier.png b/public/images/battle_anims/PRAS- Barrier.png index 2d0af4db9ba..afac47f8dbd 100644 Binary files a/public/images/battle_anims/PRAS- Barrier.png and b/public/images/battle_anims/PRAS- Barrier.png differ diff --git a/public/images/battle_anims/PRAS- Black Hole Eclipse.png b/public/images/battle_anims/PRAS- Black Hole Eclipse.png index 6963b4b2f3f..8f4c75237b3 100644 Binary files a/public/images/battle_anims/PRAS- Black Hole Eclipse.png and b/public/images/battle_anims/PRAS- Black Hole Eclipse.png differ diff --git a/public/images/battle_anims/PRAS- Blizzard BG.png b/public/images/battle_anims/PRAS- Blizzard BG.png index f83fde2e4f2..31c45ffa3a4 100644 Binary files a/public/images/battle_anims/PRAS- Blizzard BG.png and b/public/images/battle_anims/PRAS- Blizzard BG.png differ diff --git a/public/images/battle_anims/PRAS- Blizzard FG.png b/public/images/battle_anims/PRAS- Blizzard FG.png index d61f1e40f8b..b52ee422d66 100644 Binary files a/public/images/battle_anims/PRAS- Blizzard FG.png and b/public/images/battle_anims/PRAS- Blizzard FG.png differ diff --git a/public/images/battle_anims/PRAS- BoneMerang.png b/public/images/battle_anims/PRAS- BoneMerang.png index 8a548433ceb..40730f1c992 100644 Binary files a/public/images/battle_anims/PRAS- BoneMerang.png and b/public/images/battle_anims/PRAS- BoneMerang.png differ diff --git a/public/images/battle_anims/PRAS- Catastropika.png b/public/images/battle_anims/PRAS- Catastropika.png index b3b38200a23..5d69114429c 100644 Binary files a/public/images/battle_anims/PRAS- Catastropika.png and b/public/images/battle_anims/PRAS- Catastropika.png differ diff --git a/public/images/battle_anims/PRAS- Draining Kiss.png b/public/images/battle_anims/PRAS- Draining Kiss.png index 33f11acf9ae..8d5fe34a817 100644 Binary files a/public/images/battle_anims/PRAS- Draining Kiss.png and b/public/images/battle_anims/PRAS- Draining Kiss.png differ diff --git a/public/images/battle_anims/PRAS- Earthquake BG.png b/public/images/battle_anims/PRAS- Earthquake BG.png index 94895b238ff..c78f189188f 100644 Binary files a/public/images/battle_anims/PRAS- Earthquake BG.png and b/public/images/battle_anims/PRAS- Earthquake BG.png differ diff --git a/public/images/battle_anims/PRAS- Electric Terrain BG.png b/public/images/battle_anims/PRAS- Electric Terrain BG.png index 3ff1abf6318..934ed1067b1 100644 Binary files a/public/images/battle_anims/PRAS- Electric Terrain BG.png and b/public/images/battle_anims/PRAS- Electric Terrain BG.png differ diff --git a/public/images/battle_anims/PRAS- Elemental Fangs.png b/public/images/battle_anims/PRAS- Elemental Fangs.png index 5c631982488..da0900f6915 100644 Binary files a/public/images/battle_anims/PRAS- Elemental Fangs.png and b/public/images/battle_anims/PRAS- Elemental Fangs.png differ diff --git a/public/images/battle_anims/PRAS- False Swipe.png b/public/images/battle_anims/PRAS- False Swipe.png index 5e17c520d43..eca24570f26 100644 Binary files a/public/images/battle_anims/PRAS- False Swipe.png and b/public/images/battle_anims/PRAS- False Swipe.png differ diff --git a/public/images/battle_anims/PRAS- Frenzy Plant.png b/public/images/battle_anims/PRAS- Frenzy Plant.png index 756290186ff..65dd328e984 100644 Binary files a/public/images/battle_anims/PRAS- Frenzy Plant.png and b/public/images/battle_anims/PRAS- Frenzy Plant.png differ diff --git a/public/images/battle_anims/PRAS- Giga Impact Opp BG.png b/public/images/battle_anims/PRAS- Giga Impact Opp BG.png index 031483c4d4f..0b571c7bc18 100644 Binary files a/public/images/battle_anims/PRAS- Giga Impact Opp BG.png and b/public/images/battle_anims/PRAS- Giga Impact Opp BG.png differ diff --git a/public/images/battle_anims/PRAS- Glitch BG.png b/public/images/battle_anims/PRAS- Glitch BG.png index e8938f6d92b..f08383c48a6 100644 Binary files a/public/images/battle_anims/PRAS- Glitch BG.png and b/public/images/battle_anims/PRAS- Glitch BG.png differ diff --git a/public/images/battle_anims/PRAS- Gravity BG.png b/public/images/battle_anims/PRAS- Gravity BG.png index f5e7cb66a5a..f5ceb490677 100644 Binary files a/public/images/battle_anims/PRAS- Gravity BG.png and b/public/images/battle_anims/PRAS- Gravity BG.png differ diff --git a/public/images/battle_anims/PRAS- Heat Wave.png b/public/images/battle_anims/PRAS- Heat Wave.png index 6f1cba45fd0..8a88b7063aa 100644 Binary files a/public/images/battle_anims/PRAS- Heat Wave.png and b/public/images/battle_anims/PRAS- Heat Wave.png differ diff --git a/public/images/battle_anims/PRAS- Hyperspace Hole.png b/public/images/battle_anims/PRAS- Hyperspace Hole.png index 0f4d9016eee..ecf066c3207 100644 Binary files a/public/images/battle_anims/PRAS- Hyperspace Hole.png and b/public/images/battle_anims/PRAS- Hyperspace Hole.png differ diff --git a/public/images/battle_anims/PRAS- Leaf Tornado.png b/public/images/battle_anims/PRAS- Leaf Tornado.png index b3919b2dc9c..f1ade2553db 100644 Binary files a/public/images/battle_anims/PRAS- Leaf Tornado.png and b/public/images/battle_anims/PRAS- Leaf Tornado.png differ diff --git a/public/images/battle_anims/PRAS- Leech Life.png b/public/images/battle_anims/PRAS- Leech Life.png index cacb94faa93..dd3c64e86da 100644 Binary files a/public/images/battle_anims/PRAS- Leech Life.png and b/public/images/battle_anims/PRAS- Leech Life.png differ diff --git a/public/images/battle_anims/PRAS- Light Screen.png b/public/images/battle_anims/PRAS- Light Screen.png index 58a43861c80..830327f1739 100644 Binary files a/public/images/battle_anims/PRAS- Light Screen.png and b/public/images/battle_anims/PRAS- Light Screen.png differ diff --git a/public/images/battle_anims/PRAS- Luster Purge.png b/public/images/battle_anims/PRAS- Luster Purge.png index 1b1e7fea7b1..d165bc97ec3 100644 Binary files a/public/images/battle_anims/PRAS- Luster Purge.png and b/public/images/battle_anims/PRAS- Luster Purge.png differ diff --git a/public/images/battle_anims/PRAS- Magic Coat.png b/public/images/battle_anims/PRAS- Magic Coat.png index 769984806ad..fcaec4f6b07 100644 Binary files a/public/images/battle_anims/PRAS- Magic Coat.png and b/public/images/battle_anims/PRAS- Magic Coat.png differ diff --git a/public/images/battle_anims/PRAS- Magnet bomb.png b/public/images/battle_anims/PRAS- Magnet bomb.png index 17c58b03f97..2d9d7417f4a 100644 Binary files a/public/images/battle_anims/PRAS- Magnet bomb.png and b/public/images/battle_anims/PRAS- Magnet bomb.png differ diff --git a/public/images/battle_anims/PRAS- Mist FG.png b/public/images/battle_anims/PRAS- Mist FG.png index cee92715ba3..f75af146109 100644 Binary files a/public/images/battle_anims/PRAS- Mist FG.png and b/public/images/battle_anims/PRAS- Mist FG.png differ diff --git a/public/images/battle_anims/PRAS- Mist.png b/public/images/battle_anims/PRAS- Mist.png index 11a92964717..c9161afdf9a 100644 Binary files a/public/images/battle_anims/PRAS- Mist.png and b/public/images/battle_anims/PRAS- Mist.png differ diff --git a/public/images/battle_anims/PRAS- Odor Sleuth.png b/public/images/battle_anims/PRAS- Odor Sleuth.png index a0e76eef728..a3b832c24c6 100644 Binary files a/public/images/battle_anims/PRAS- Odor Sleuth.png and b/public/images/battle_anims/PRAS- Odor Sleuth.png differ diff --git a/public/images/battle_anims/PRAS- Ominous Wind BG.png b/public/images/battle_anims/PRAS- Ominous Wind BG.png index da7f1a85bef..a7cd026bef7 100644 Binary files a/public/images/battle_anims/PRAS- Ominous Wind BG.png and b/public/images/battle_anims/PRAS- Ominous Wind BG.png differ diff --git a/public/images/battle_anims/PRAS- Petal Blizzard FG.png b/public/images/battle_anims/PRAS- Petal Blizzard FG.png index 50730ac068a..d62cd4623ae 100644 Binary files a/public/images/battle_anims/PRAS- Petal Blizzard FG.png and b/public/images/battle_anims/PRAS- Petal Blizzard FG.png differ diff --git a/public/images/battle_anims/PRAS- Powder.png b/public/images/battle_anims/PRAS- Powder.png index 9656c31e6c9..e4283e53e73 100644 Binary files a/public/images/battle_anims/PRAS- Powder.png and b/public/images/battle_anims/PRAS- Powder.png differ diff --git a/public/images/battle_anims/PRAS- Psychic BG.png b/public/images/battle_anims/PRAS- Psychic BG.png index 67e4ea88af4..17ebbf41049 100644 Binary files a/public/images/battle_anims/PRAS- Psychic BG.png and b/public/images/battle_anims/PRAS- Psychic BG.png differ diff --git a/public/images/battle_anims/PRAS- Psychic Terrain BG.png b/public/images/battle_anims/PRAS- Psychic Terrain BG.png index eab6c8c5bf6..44cb39fc3d6 100644 Binary files a/public/images/battle_anims/PRAS- Psychic Terrain BG.png and b/public/images/battle_anims/PRAS- Psychic Terrain BG.png differ diff --git a/public/images/battle_anims/PRAS- Pulverizing Pancake BG.png b/public/images/battle_anims/PRAS- Pulverizing Pancake BG.png index a74d9c307a3..e235c7c2e68 100644 Binary files a/public/images/battle_anims/PRAS- Pulverizing Pancake BG.png and b/public/images/battle_anims/PRAS- Pulverizing Pancake BG.png differ diff --git a/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png b/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png index a2a0f9afed2..44b50a6d385 100644 Binary files a/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png and b/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png differ diff --git a/public/images/battle_anims/PRAS- Pulverizing Pancake.png b/public/images/battle_anims/PRAS- Pulverizing Pancake.png index 787015e0731..519a52c63c6 100644 Binary files a/public/images/battle_anims/PRAS- Pulverizing Pancake.png and b/public/images/battle_anims/PRAS- Pulverizing Pancake.png differ diff --git a/public/images/battle_anims/PRAS- Reflect.png b/public/images/battle_anims/PRAS- Reflect.png index d7db4a3561f..f82d2ce50d4 100644 Binary files a/public/images/battle_anims/PRAS- Reflect.png and b/public/images/battle_anims/PRAS- Reflect.png differ diff --git a/public/images/battle_anims/PRAS- Retaliate BG.png b/public/images/battle_anims/PRAS- Retaliate BG.png index 58ea9540ea8..82688e4bf88 100644 Binary files a/public/images/battle_anims/PRAS- Retaliate BG.png and b/public/images/battle_anims/PRAS- Retaliate BG.png differ diff --git a/public/images/battle_anims/PRAS- Roost.png b/public/images/battle_anims/PRAS- Roost.png index 5c9167ff393..6b9e70e0539 100644 Binary files a/public/images/battle_anims/PRAS- Roost.png and b/public/images/battle_anims/PRAS- Roost.png differ diff --git a/public/images/battle_anims/PRAS- Sky Uppercut BG.png b/public/images/battle_anims/PRAS- Sky Uppercut BG.png index e9780d3bb5a..5c6ae5e4484 100644 Binary files a/public/images/battle_anims/PRAS- Sky Uppercut BG.png and b/public/images/battle_anims/PRAS- Sky Uppercut BG.png differ diff --git a/public/images/battle_anims/PRAS- Space BG.png b/public/images/battle_anims/PRAS- Space BG.png index 6a72789df99..99aeded6f15 100644 Binary files a/public/images/battle_anims/PRAS- Space BG.png and b/public/images/battle_anims/PRAS- Space BG.png differ diff --git a/public/images/battle_anims/PRAS- Spectral thief BG.png b/public/images/battle_anims/PRAS- Spectral thief BG.png index 03d15aeee4a..35fc434fdca 100644 Binary files a/public/images/battle_anims/PRAS- Spectral thief BG.png and b/public/images/battle_anims/PRAS- Spectral thief BG.png differ diff --git a/public/images/battle_anims/PRAS- Speed BG.png b/public/images/battle_anims/PRAS- Speed BG.png index abb97f035ba..388065a2d49 100644 Binary files a/public/images/battle_anims/PRAS- Speed BG.png and b/public/images/battle_anims/PRAS- Speed BG.png differ diff --git a/public/images/battle_anims/PRAS- Spike Cannon.png b/public/images/battle_anims/PRAS- Spike Cannon.png index e2ee3ab8c3e..6d121870b3e 100644 Binary files a/public/images/battle_anims/PRAS- Spike Cannon.png and b/public/images/battle_anims/PRAS- Spike Cannon.png differ diff --git a/public/images/battle_anims/PRAS- Steam Eruption FG.png b/public/images/battle_anims/PRAS- Steam Eruption FG.png index 710d4d768f7..b3910fac9ff 100644 Binary files a/public/images/battle_anims/PRAS- Steam Eruption FG.png and b/public/images/battle_anims/PRAS- Steam Eruption FG.png differ diff --git a/public/images/battle_anims/PRAS- Underwater BG.png b/public/images/battle_anims/PRAS- Underwater BG.png index 9b8d1b255b2..8d5d8c83744 100644 Binary files a/public/images/battle_anims/PRAS- Underwater BG.png and b/public/images/battle_anims/PRAS- Underwater BG.png differ diff --git a/public/images/battle_anims/Rock Tomb.png b/public/images/battle_anims/Rock Tomb.png index d5d654a8dc8..02b7dbf403b 100644 Binary files a/public/images/battle_anims/Rock Tomb.png and b/public/images/battle_anims/Rock Tomb.png differ diff --git a/public/images/battle_anims/Sword6.png b/public/images/battle_anims/Sword6.png index 2a08c846b05..7f93457434c 100644 Binary files a/public/images/battle_anims/Sword6.png and b/public/images/battle_anims/Sword6.png differ diff --git a/public/images/battle_anims/Thunder2.png b/public/images/battle_anims/Thunder2.png index 97677830322..16284d300e8 100644 Binary files a/public/images/battle_anims/Thunder2.png and b/public/images/battle_anims/Thunder2.png differ diff --git a/public/images/battle_anims/Trovao.png b/public/images/battle_anims/Trovao.png index 14210e47551..7c3b67967d7 100644 Binary files a/public/images/battle_anims/Trovao.png and b/public/images/battle_anims/Trovao.png differ diff --git a/public/images/battle_anims/block.png b/public/images/battle_anims/block.png index ca4d8874df4..d2496d7390d 100644 Binary files a/public/images/battle_anims/block.png and b/public/images/battle_anims/block.png differ diff --git a/public/images/battle_anims/confused.png b/public/images/battle_anims/confused.png index 8e4823b79e1..267765e5701 100644 Binary files a/public/images/battle_anims/confused.png and b/public/images/battle_anims/confused.png differ diff --git a/public/images/battle_anims/finger.spoon.png b/public/images/battle_anims/finger.spoon.png index f8ea58fca81..268d3169ba0 100644 Binary files a/public/images/battle_anims/finger.spoon.png and b/public/images/battle_anims/finger.spoon.png differ diff --git a/public/images/battle_anims/fly copy.png b/public/images/battle_anims/fly copy.png index 78909303837..d3431d209e4 100644 Binary files a/public/images/battle_anims/fly copy.png and b/public/images/battle_anims/fly copy.png differ diff --git a/public/images/battle_anims/ghost2.png b/public/images/battle_anims/ghost2.png index eb58d716dc5..f2a29364257 100644 Binary files a/public/images/battle_anims/ghost2.png and b/public/images/battle_anims/ghost2.png differ diff --git a/public/images/battle_anims/mixed status.png b/public/images/battle_anims/mixed status.png index e95d3b457d2..c08f9f64dec 100644 Binary files a/public/images/battle_anims/mixed status.png and b/public/images/battle_anims/mixed status.png differ diff --git a/public/images/battle_anims/mixed.png b/public/images/battle_anims/mixed.png index 074dc10ae72..9b4da7d5626 100644 Binary files a/public/images/battle_anims/mixed.png and b/public/images/battle_anims/mixed.png differ diff --git a/public/images/battle_anims/terastallize.png b/public/images/battle_anims/terastallize.png index 78fbb7ec335..b322a8a89d0 100644 Binary files a/public/images/battle_anims/terastallize.png and b/public/images/battle_anims/terastallize.png differ diff --git a/public/images/categories.png b/public/images/categories.png index 0f683399495..56903fddd07 100644 Binary files a/public/images/categories.png and b/public/images/categories.png differ diff --git a/public/images/categories_legacy.png b/public/images/categories_legacy.png index 94ba1d4c4bc..54c90c36b18 100644 Binary files a/public/images/categories_legacy.png and b/public/images/categories_legacy.png differ diff --git a/public/images/cg/end_f.png b/public/images/cg/end_f.png index 84f94d7e05d..9fbb4514007 100644 Binary files a/public/images/cg/end_f.png and b/public/images/cg/end_f.png differ diff --git a/public/images/cg/end_m.png b/public/images/cg/end_m.png index abdcb49aff9..4ba892011b5 100644 Binary files a/public/images/cg/end_m.png and b/public/images/cg/end_m.png differ diff --git a/public/images/character/rival_f.png b/public/images/character/rival_f.png index b165cd8a760..2c78dd39fff 100644 Binary files a/public/images/character/rival_f.png and b/public/images/character/rival_f.png differ diff --git a/public/images/character/rival_m.png b/public/images/character/rival_m.png index b12e2787287..46fb12b53ba 100644 Binary files a/public/images/character/rival_m.png and b/public/images/character/rival_m.png differ diff --git a/public/images/effects/battle_stats.png b/public/images/effects/battle_stats.png index cc3bc279641..e1d4fd4eea7 100644 Binary files a/public/images/effects/battle_stats.png and b/public/images/effects/battle_stats.png differ diff --git a/public/images/effects/evo_sparkle.png b/public/images/effects/evo_sparkle.png index 46e1aebba81..b8e791f13e1 100644 Binary files a/public/images/effects/evo_sparkle.png and b/public/images/effects/evo_sparkle.png differ diff --git a/public/images/effects/pb_particles.png b/public/images/effects/pb_particles.png index 386ca9b29e7..a25b3cf7a09 100644 Binary files a/public/images/effects/pb_particles.png and b/public/images/effects/pb_particles.png differ diff --git a/public/images/effects/shiny.png b/public/images/effects/shiny.png index e508fcdf58a..fb05ab39eba 100644 Binary files a/public/images/effects/shiny.png and b/public/images/effects/shiny.png differ diff --git a/public/images/effects/shiny_2.png b/public/images/effects/shiny_2.png index 3f9e37c1643..717cf936685 100644 Binary files a/public/images/effects/shiny_2.png and b/public/images/effects/shiny_2.png differ diff --git a/public/images/effects/shiny_3.png b/public/images/effects/shiny_3.png index 53000bdb3d1..52cafa35259 100644 Binary files a/public/images/effects/shiny_3.png and b/public/images/effects/shiny_3.png differ diff --git a/public/images/effects/tera.png b/public/images/effects/tera.png index e378d1c051c..343565f0005 100644 Binary files a/public/images/effects/tera.png and b/public/images/effects/tera.png differ diff --git a/public/images/effects/tera_sparkle.png b/public/images/effects/tera_sparkle.png index 3385aaf23ad..2d6769a291b 100644 Binary files a/public/images/effects/tera_sparkle.png and b/public/images/effects/tera_sparkle.png differ diff --git a/public/images/egg/default.png b/public/images/egg/default.png index 160ac2542eb..94d6b34a4dc 100644 Binary files a/public/images/egg/default.png and b/public/images/egg/default.png differ diff --git a/public/images/egg/egg.png b/public/images/egg/egg.png index 2943e83b5ea..696b5a232cf 100644 Binary files a/public/images/egg/egg.png and b/public/images/egg/egg.png differ diff --git a/public/images/egg/egg_crack.png b/public/images/egg/egg_crack.png index b23b7703307..9d553ebf378 100644 Binary files a/public/images/egg/egg_crack.png and b/public/images/egg/egg_crack.png differ diff --git a/public/images/egg/egg_icons.png b/public/images/egg/egg_icons.png index c69187baa51..59891a9629e 100644 Binary files a/public/images/egg/egg_icons.png and b/public/images/egg/egg_icons.png differ diff --git a/public/images/egg/egg_lightrays.png b/public/images/egg/egg_lightrays.png index a457625454d..892134b8701 100644 Binary files a/public/images/egg/egg_lightrays.png and b/public/images/egg/egg_lightrays.png differ diff --git a/public/images/egg/egg_shard.png b/public/images/egg/egg_shard.png index a4084af17e1..774763be241 100644 Binary files a/public/images/egg/egg_shard.png and b/public/images/egg/egg_shard.png differ diff --git a/public/images/egg/gacha_eggs.png b/public/images/egg/gacha_eggs.png index 6e26addc450..8aa6420be1a 100644 Binary files a/public/images/egg/gacha_eggs.png and b/public/images/egg/gacha_eggs.png differ diff --git a/public/images/egg/gacha_glass.png b/public/images/egg/gacha_glass.png index b8c4a423454..ddeeb68f3a1 100644 Binary files a/public/images/egg/gacha_glass.png and b/public/images/egg/gacha_glass.png differ diff --git a/public/images/egg/gacha_hatch.png b/public/images/egg/gacha_hatch.png index 39fa7de58c1..0bdcae840e0 100644 Binary files a/public/images/egg/gacha_hatch.png and b/public/images/egg/gacha_hatch.png differ diff --git a/public/images/egg/gacha_knob.png b/public/images/egg/gacha_knob.png index 9b20c081cf2..10107ebd27f 100644 Binary files a/public/images/egg/gacha_knob.png and b/public/images/egg/gacha_knob.png differ diff --git a/public/images/egg/gacha_legendary.png b/public/images/egg/gacha_legendary.png index 6eb41e55099..f0ff27095b6 100644 Binary files a/public/images/egg/gacha_legendary.png and b/public/images/egg/gacha_legendary.png differ diff --git a/public/images/egg/gacha_move.png b/public/images/egg/gacha_move.png index 19b3668424a..f0559192a75 100644 Binary files a/public/images/egg/gacha_move.png and b/public/images/egg/gacha_move.png differ diff --git a/public/images/egg/gacha_shiny.png b/public/images/egg/gacha_shiny.png index ffc1314f597..a884fee5927 100644 Binary files a/public/images/egg/gacha_shiny.png and b/public/images/egg/gacha_shiny.png differ diff --git a/public/images/egg/gacha_underlay_legendary.png b/public/images/egg/gacha_underlay_legendary.png index 5d116d3e11f..0e7d29b1a79 100644 Binary files a/public/images/egg/gacha_underlay_legendary.png and b/public/images/egg/gacha_underlay_legendary.png differ diff --git a/public/images/egg/gacha_underlay_move.png b/public/images/egg/gacha_underlay_move.png index 437b2bca3ab..b3ce6b4a666 100644 Binary files a/public/images/egg/gacha_underlay_move.png and b/public/images/egg/gacha_underlay_move.png differ diff --git a/public/images/egg/gacha_underlay_shiny.png b/public/images/egg/gacha_underlay_shiny.png index a2b40fa2b0b..f9ea3733ca0 100644 Binary files a/public/images/egg/gacha_underlay_shiny.png and b/public/images/egg/gacha_underlay_shiny.png differ diff --git a/public/images/egg/open_hatch.png b/public/images/egg/open_hatch.png index fd91aca14d3..535f2fabff2 100644 Binary files a/public/images/egg/open_hatch.png and b/public/images/egg/open_hatch.png differ diff --git a/public/images/events/aprf25-de.png b/public/images/events/aprf25-de.png index d4bb7ebdc50..af0d2a63f70 100644 Binary files a/public/images/events/aprf25-de.png and b/public/images/events/aprf25-de.png differ diff --git a/public/images/events/aprf25-en.png b/public/images/events/aprf25-en.png index 8f7268b01b6..bb61b156a7b 100644 Binary files a/public/images/events/aprf25-en.png and b/public/images/events/aprf25-en.png differ diff --git a/public/images/events/aprf25-es-ES.png b/public/images/events/aprf25-es-ES.png index a6136a2c8de..c8f2e6a0d5a 100644 Binary files a/public/images/events/aprf25-es-ES.png and b/public/images/events/aprf25-es-ES.png differ diff --git a/public/images/events/aprf25-es-MX.png b/public/images/events/aprf25-es-MX.png index a6136a2c8de..c8f2e6a0d5a 100644 Binary files a/public/images/events/aprf25-es-MX.png and b/public/images/events/aprf25-es-MX.png differ diff --git a/public/images/events/aprf25-fr.png b/public/images/events/aprf25-fr.png index c68264c75dd..cad3045cf68 100644 Binary files a/public/images/events/aprf25-fr.png and b/public/images/events/aprf25-fr.png differ diff --git a/public/images/events/aprf25-it.png b/public/images/events/aprf25-it.png index 01bc0d2a1f0..70c57f92845 100644 Binary files a/public/images/events/aprf25-it.png and b/public/images/events/aprf25-it.png differ diff --git a/public/images/events/aprf25-ja.png b/public/images/events/aprf25-ja.png index c6b62a3672e..ae99dd72e52 100644 Binary files a/public/images/events/aprf25-ja.png and b/public/images/events/aprf25-ja.png differ diff --git a/public/images/events/aprf25-ko.png b/public/images/events/aprf25-ko.png index bcc87e33ac1..1c76f7ce6e9 100644 Binary files a/public/images/events/aprf25-ko.png and b/public/images/events/aprf25-ko.png differ diff --git a/public/images/events/aprf25-pt-BR.png b/public/images/events/aprf25-pt-BR.png index f56f5b5c1e9..bee4ed3f1e2 100644 Binary files a/public/images/events/aprf25-pt-BR.png and b/public/images/events/aprf25-pt-BR.png differ diff --git a/public/images/events/aprf25-zh-CN.png b/public/images/events/aprf25-zh-CN.png index 57b2c3ec5be..23322d12760 100644 Binary files a/public/images/events/aprf25-zh-CN.png and b/public/images/events/aprf25-zh-CN.png differ diff --git a/public/images/events/august-variant-update.png b/public/images/events/august-variant-update.png index 1c78aa2fe14..e2e429ae2e6 100644 Binary files a/public/images/events/august-variant-update.png and b/public/images/events/august-variant-update.png differ diff --git a/public/images/events/egg-update_de.png b/public/images/events/egg-update_de.png index 5de94877d5c..5b7c8e50881 100644 Binary files a/public/images/events/egg-update_de.png and b/public/images/events/egg-update_de.png differ diff --git a/public/images/events/egg-update_en.png b/public/images/events/egg-update_en.png index 7104d340ca0..5b407cf7c75 100644 Binary files a/public/images/events/egg-update_en.png and b/public/images/events/egg-update_en.png differ diff --git a/public/images/events/egg-update_es.png b/public/images/events/egg-update_es.png index ec5f5c46d17..4d1bfe7b2e8 100644 Binary files a/public/images/events/egg-update_es.png and b/public/images/events/egg-update_es.png differ diff --git a/public/images/events/egg-update_fr.png b/public/images/events/egg-update_fr.png index e0505fa96dd..e214ef46cbe 100644 Binary files a/public/images/events/egg-update_fr.png and b/public/images/events/egg-update_fr.png differ diff --git a/public/images/events/egg-update_it.png b/public/images/events/egg-update_it.png index fc347bce9cf..4665d7e9657 100644 Binary files a/public/images/events/egg-update_it.png and b/public/images/events/egg-update_it.png differ diff --git a/public/images/events/egg-update_ja.png b/public/images/events/egg-update_ja.png index 2259cbb4d9a..2a1403e752f 100644 Binary files a/public/images/events/egg-update_ja.png and b/public/images/events/egg-update_ja.png differ diff --git a/public/images/events/egg-update_ko.png b/public/images/events/egg-update_ko.png index 99dcc662402..5e22550cea8 100644 Binary files a/public/images/events/egg-update_ko.png and b/public/images/events/egg-update_ko.png differ diff --git a/public/images/events/egg-update_pt-BR.png b/public/images/events/egg-update_pt-BR.png index ee347d35654..68f3633f16a 100644 Binary files a/public/images/events/egg-update_pt-BR.png and b/public/images/events/egg-update_pt-BR.png differ diff --git a/public/images/events/egg-update_zh-CN.png b/public/images/events/egg-update_zh-CN.png index 02d780fab89..e646562f4cf 100644 Binary files a/public/images/events/egg-update_zh-CN.png and b/public/images/events/egg-update_zh-CN.png differ diff --git a/public/images/events/halloween2024-event-de.png b/public/images/events/halloween2024-event-de.png index a56053c73cc..8291991091e 100644 Binary files a/public/images/events/halloween2024-event-de.png and b/public/images/events/halloween2024-event-de.png differ diff --git a/public/images/events/halloween2024-event-en.png b/public/images/events/halloween2024-event-en.png index 3886fa796ae..dc2d241fb09 100644 Binary files a/public/images/events/halloween2024-event-en.png and b/public/images/events/halloween2024-event-en.png differ diff --git a/public/images/events/halloween2024-event-es-ES.png b/public/images/events/halloween2024-event-es-ES.png index dc9bac86cd0..5d3c4a1734f 100644 Binary files a/public/images/events/halloween2024-event-es-ES.png and b/public/images/events/halloween2024-event-es-ES.png differ diff --git a/public/images/events/halloween2024-event-fr.png b/public/images/events/halloween2024-event-fr.png index 21df18c7471..198e5d28de5 100644 Binary files a/public/images/events/halloween2024-event-fr.png and b/public/images/events/halloween2024-event-fr.png differ diff --git a/public/images/events/halloween2024-event-it.png b/public/images/events/halloween2024-event-it.png index ce2316b2ce5..461e9faf704 100644 Binary files a/public/images/events/halloween2024-event-it.png and b/public/images/events/halloween2024-event-it.png differ diff --git a/public/images/events/halloween2024-event-ja.png b/public/images/events/halloween2024-event-ja.png index 363f3b33b0d..4bd49b3d28a 100644 Binary files a/public/images/events/halloween2024-event-ja.png and b/public/images/events/halloween2024-event-ja.png differ diff --git a/public/images/events/halloween2024-event-ko.png b/public/images/events/halloween2024-event-ko.png index 3f52526a296..94b9859da97 100644 Binary files a/public/images/events/halloween2024-event-ko.png and b/public/images/events/halloween2024-event-ko.png differ diff --git a/public/images/events/halloween2024-event-pt-BR.png b/public/images/events/halloween2024-event-pt-BR.png index 764a9c1daa9..a89cfe5231f 100644 Binary files a/public/images/events/halloween2024-event-pt-BR.png and b/public/images/events/halloween2024-event-pt-BR.png differ diff --git a/public/images/events/halloween2024-event-zh-CN.png b/public/images/events/halloween2024-event-zh-CN.png index c2096a05d98..61c11b4ee95 100644 Binary files a/public/images/events/halloween2024-event-zh-CN.png and b/public/images/events/halloween2024-event-zh-CN.png differ diff --git a/public/images/events/pkmnday2025event-de.png b/public/images/events/pkmnday2025event-de.png index 4cc53546752..b3746a4623e 100644 Binary files a/public/images/events/pkmnday2025event-de.png and b/public/images/events/pkmnday2025event-de.png differ diff --git a/public/images/events/pkmnday2025event-en.png b/public/images/events/pkmnday2025event-en.png index e9caa9e19d6..54b6e7280ac 100644 Binary files a/public/images/events/pkmnday2025event-en.png and b/public/images/events/pkmnday2025event-en.png differ diff --git a/public/images/events/pkmnday2025event-es-ES.png b/public/images/events/pkmnday2025event-es-ES.png index e1ab096dffc..afdf051dc79 100644 Binary files a/public/images/events/pkmnday2025event-es-ES.png and b/public/images/events/pkmnday2025event-es-ES.png differ diff --git a/public/images/events/pkmnday2025event-fr.png b/public/images/events/pkmnday2025event-fr.png index 037d1e06e61..52ef02d474a 100644 Binary files a/public/images/events/pkmnday2025event-fr.png and b/public/images/events/pkmnday2025event-fr.png differ diff --git a/public/images/events/pkmnday2025event-it.png b/public/images/events/pkmnday2025event-it.png index f38a60330fa..6f3e8642e3d 100644 Binary files a/public/images/events/pkmnday2025event-it.png and b/public/images/events/pkmnday2025event-it.png differ diff --git a/public/images/events/pkmnday2025event-ja.png b/public/images/events/pkmnday2025event-ja.png index 94b02ad93a0..e7633edb5ed 100644 Binary files a/public/images/events/pkmnday2025event-ja.png and b/public/images/events/pkmnday2025event-ja.png differ diff --git a/public/images/events/pkmnday2025event-ko.png b/public/images/events/pkmnday2025event-ko.png index aed9ee3fb28..343687706ac 100644 Binary files a/public/images/events/pkmnday2025event-ko.png and b/public/images/events/pkmnday2025event-ko.png differ diff --git a/public/images/events/pkmnday2025event-pt-BR.png b/public/images/events/pkmnday2025event-pt-BR.png index 2190bbac535..67559a80068 100644 Binary files a/public/images/events/pkmnday2025event-pt-BR.png and b/public/images/events/pkmnday2025event-pt-BR.png differ diff --git a/public/images/events/pkmnday2025event-zh-CN.png b/public/images/events/pkmnday2025event-zh-CN.png index a3430482dd0..fc92405a149 100644 Binary files a/public/images/events/pkmnday2025event-zh-CN.png and b/public/images/events/pkmnday2025event-zh-CN.png differ diff --git a/public/images/events/pride-update.png b/public/images/events/pride-update.png index face6df1790..253101bd63a 100644 Binary files a/public/images/events/pride-update.png and b/public/images/events/pride-update.png differ diff --git a/public/images/events/pride2025-de.png b/public/images/events/pride2025-de.png index 8d205821b3c..06044f3c94e 100644 Binary files a/public/images/events/pride2025-de.png and b/public/images/events/pride2025-de.png differ diff --git a/public/images/events/pride2025-en.png b/public/images/events/pride2025-en.png index 5a81a32be5f..1dd3d3d97f1 100644 Binary files a/public/images/events/pride2025-en.png and b/public/images/events/pride2025-en.png differ diff --git a/public/images/events/pride2025-es-ES.png b/public/images/events/pride2025-es-ES.png index 52eba6ffe45..cd0f972266a 100644 Binary files a/public/images/events/pride2025-es-ES.png and b/public/images/events/pride2025-es-ES.png differ diff --git a/public/images/events/pride2025-es-MX.png b/public/images/events/pride2025-es-MX.png index cb2100cd601..4408bbcf9e1 100644 Binary files a/public/images/events/pride2025-es-MX.png and b/public/images/events/pride2025-es-MX.png differ diff --git a/public/images/events/pride2025-fr.png b/public/images/events/pride2025-fr.png index f583c83bbbc..78084fdbe89 100644 Binary files a/public/images/events/pride2025-fr.png and b/public/images/events/pride2025-fr.png differ diff --git a/public/images/events/pride2025-it.png b/public/images/events/pride2025-it.png index 293d1f92ad8..68d3910782e 100644 Binary files a/public/images/events/pride2025-it.png and b/public/images/events/pride2025-it.png differ diff --git a/public/images/events/pride2025-ja.png b/public/images/events/pride2025-ja.png index 562e43e7d8e..af4f837ab74 100644 Binary files a/public/images/events/pride2025-ja.png and b/public/images/events/pride2025-ja.png differ diff --git a/public/images/events/pride2025-ko.png b/public/images/events/pride2025-ko.png index 74be30ba4ef..fe8733dc2ae 100644 Binary files a/public/images/events/pride2025-ko.png and b/public/images/events/pride2025-ko.png differ diff --git a/public/images/events/pride2025-pt-BR.png b/public/images/events/pride2025-pt-BR.png index 99269bd1495..bc08dea5e6f 100644 Binary files a/public/images/events/pride2025-pt-BR.png and b/public/images/events/pride2025-pt-BR.png differ diff --git a/public/images/events/pride2025-zh-CN.png b/public/images/events/pride2025-zh-CN.png index 5be66a544cf..fbd9e9ff278 100644 Binary files a/public/images/events/pride2025-zh-CN.png and b/public/images/events/pride2025-zh-CN.png differ diff --git a/public/images/events/pride2025-zh-TW.png b/public/images/events/pride2025-zh-TW.png index aeffc354c7d..e64be2cd79c 100644 Binary files a/public/images/events/pride2025-zh-TW.png and b/public/images/events/pride2025-zh-TW.png differ diff --git a/public/images/events/september-update-de.png b/public/images/events/september-update-de.png index 1ecb46e408c..9947adaa222 100644 Binary files a/public/images/events/september-update-de.png and b/public/images/events/september-update-de.png differ diff --git a/public/images/events/september-update-en.png b/public/images/events/september-update-en.png index 57dd130b98d..1bcced18f68 100644 Binary files a/public/images/events/september-update-en.png and b/public/images/events/september-update-en.png differ diff --git a/public/images/events/september-update-es.png b/public/images/events/september-update-es.png index 8c294d21403..a43ae8d15fa 100644 Binary files a/public/images/events/september-update-es.png and b/public/images/events/september-update-es.png differ diff --git a/public/images/events/september-update-fr.png b/public/images/events/september-update-fr.png index 4be33c85e9a..7a6ae965226 100644 Binary files a/public/images/events/september-update-fr.png and b/public/images/events/september-update-fr.png differ diff --git a/public/images/events/september-update-it.png b/public/images/events/september-update-it.png index 62542f4eb9b..c97001c28d5 100644 Binary files a/public/images/events/september-update-it.png and b/public/images/events/september-update-it.png differ diff --git a/public/images/events/september-update-ja.png b/public/images/events/september-update-ja.png index 93e18c51223..11d1f34abdf 100644 Binary files a/public/images/events/september-update-ja.png and b/public/images/events/september-update-ja.png differ diff --git a/public/images/events/september-update-ko.png b/public/images/events/september-update-ko.png index 13585327fce..6e8adf31fcb 100644 Binary files a/public/images/events/september-update-ko.png and b/public/images/events/september-update-ko.png differ diff --git a/public/images/events/september-update-pt-BR.png b/public/images/events/september-update-pt-BR.png index 8dd8b8759e9..2960bb1c605 100644 Binary files a/public/images/events/september-update-pt-BR.png and b/public/images/events/september-update-pt-BR.png differ diff --git a/public/images/events/september-update-zh-CN.png b/public/images/events/september-update-zh-CN.png index ee56d644d24..31f660ceace 100644 Binary files a/public/images/events/september-update-zh-CN.png and b/public/images/events/september-update-zh-CN.png differ diff --git a/public/images/events/spr25event-de.png b/public/images/events/spr25event-de.png index 1ccd9557460..a91202d7334 100644 Binary files a/public/images/events/spr25event-de.png and b/public/images/events/spr25event-de.png differ diff --git a/public/images/events/spr25event-en.png b/public/images/events/spr25event-en.png index 0e73f9247e3..cb30dac4638 100644 Binary files a/public/images/events/spr25event-en.png and b/public/images/events/spr25event-en.png differ diff --git a/public/images/events/spr25event-es-ES.png b/public/images/events/spr25event-es-ES.png index 137f1c6e743..9d16d9ced6d 100644 Binary files a/public/images/events/spr25event-es-ES.png and b/public/images/events/spr25event-es-ES.png differ diff --git a/public/images/events/spr25event-es-MX.png b/public/images/events/spr25event-es-MX.png index 137f1c6e743..9d16d9ced6d 100644 Binary files a/public/images/events/spr25event-es-MX.png and b/public/images/events/spr25event-es-MX.png differ diff --git a/public/images/events/spr25event-fr.png b/public/images/events/spr25event-fr.png index 7730e16d4a3..f7b2618a3e3 100644 Binary files a/public/images/events/spr25event-fr.png and b/public/images/events/spr25event-fr.png differ diff --git a/public/images/events/spr25event-it.png b/public/images/events/spr25event-it.png index 2664b4367cc..384078c3e82 100644 Binary files a/public/images/events/spr25event-it.png and b/public/images/events/spr25event-it.png differ diff --git a/public/images/events/spr25event-ja.png b/public/images/events/spr25event-ja.png index 90b02af8050..523dc529539 100644 Binary files a/public/images/events/spr25event-ja.png and b/public/images/events/spr25event-ja.png differ diff --git a/public/images/events/spr25event-ko.png b/public/images/events/spr25event-ko.png index a8fe279617a..d8a470ffd7e 100644 Binary files a/public/images/events/spr25event-ko.png and b/public/images/events/spr25event-ko.png differ diff --git a/public/images/events/spr25event-pt-BR.png b/public/images/events/spr25event-pt-BR.png index ae195fecc97..42d4a404ca7 100644 Binary files a/public/images/events/spr25event-pt-BR.png and b/public/images/events/spr25event-pt-BR.png differ diff --git a/public/images/events/spr25event-zh-CN.png b/public/images/events/spr25event-zh-CN.png index 1d8ad35c166..88f9023170b 100644 Binary files a/public/images/events/spr25event-zh-CN.png and b/public/images/events/spr25event-zh-CN.png differ diff --git a/public/images/events/valentines2025event-de.png b/public/images/events/valentines2025event-de.png index 8dd41924a77..1c8696b1e37 100644 Binary files a/public/images/events/valentines2025event-de.png and b/public/images/events/valentines2025event-de.png differ diff --git a/public/images/events/valentines2025event-en.png b/public/images/events/valentines2025event-en.png index dc36dad3394..ab1d2c7fa19 100644 Binary files a/public/images/events/valentines2025event-en.png and b/public/images/events/valentines2025event-en.png differ diff --git a/public/images/events/valentines2025event-es-ES.png b/public/images/events/valentines2025event-es-ES.png index 0915299b7bd..905d7de92ff 100644 Binary files a/public/images/events/valentines2025event-es-ES.png and b/public/images/events/valentines2025event-es-ES.png differ diff --git a/public/images/events/valentines2025event-fr.png b/public/images/events/valentines2025event-fr.png index dbaf23101ed..f6300b01d28 100644 Binary files a/public/images/events/valentines2025event-fr.png and b/public/images/events/valentines2025event-fr.png differ diff --git a/public/images/events/valentines2025event-it.png b/public/images/events/valentines2025event-it.png index b10eb8083be..06927ff8d88 100644 Binary files a/public/images/events/valentines2025event-it.png and b/public/images/events/valentines2025event-it.png differ diff --git a/public/images/events/valentines2025event-ja.png b/public/images/events/valentines2025event-ja.png index 7e9fe5ad3d4..6c7cd449c76 100644 Binary files a/public/images/events/valentines2025event-ja.png and b/public/images/events/valentines2025event-ja.png differ diff --git a/public/images/events/valentines2025event-ko.png b/public/images/events/valentines2025event-ko.png index 558a402fc77..199a0538dd3 100644 Binary files a/public/images/events/valentines2025event-ko.png and b/public/images/events/valentines2025event-ko.png differ diff --git a/public/images/events/valentines2025event-pt-BR.png b/public/images/events/valentines2025event-pt-BR.png index 45fa40f30fc..bae78d11849 100644 Binary files a/public/images/events/valentines2025event-pt-BR.png and b/public/images/events/valentines2025event-pt-BR.png differ diff --git a/public/images/events/valentines2025event-zh-CN.png b/public/images/events/valentines2025event-zh-CN.png index 13256da720c..c8302d06cd6 100644 Binary files a/public/images/events/valentines2025event-zh-CN.png and b/public/images/events/valentines2025event-zh-CN.png differ diff --git a/public/images/events/winter_holidays2024-event-de.png b/public/images/events/winter_holidays2024-event-de.png index 1c2e10086f2..0d900058705 100644 Binary files a/public/images/events/winter_holidays2024-event-de.png and b/public/images/events/winter_holidays2024-event-de.png differ diff --git a/public/images/events/winter_holidays2024-event-en.png b/public/images/events/winter_holidays2024-event-en.png index 3a361e99bee..fa4e693f54c 100644 Binary files a/public/images/events/winter_holidays2024-event-en.png and b/public/images/events/winter_holidays2024-event-en.png differ diff --git a/public/images/events/winter_holidays2024-event-es-ES.png b/public/images/events/winter_holidays2024-event-es-ES.png index f7e64268ad5..6923e1e2c5f 100644 Binary files a/public/images/events/winter_holidays2024-event-es-ES.png and b/public/images/events/winter_holidays2024-event-es-ES.png differ diff --git a/public/images/events/winter_holidays2024-event-fr.png b/public/images/events/winter_holidays2024-event-fr.png index 278f5f2afd4..cf68c7221a8 100644 Binary files a/public/images/events/winter_holidays2024-event-fr.png and b/public/images/events/winter_holidays2024-event-fr.png differ diff --git a/public/images/events/winter_holidays2024-event-it.png b/public/images/events/winter_holidays2024-event-it.png index f3062f40d51..5c58c806dda 100644 Binary files a/public/images/events/winter_holidays2024-event-it.png and b/public/images/events/winter_holidays2024-event-it.png differ diff --git a/public/images/events/winter_holidays2024-event-ja.png b/public/images/events/winter_holidays2024-event-ja.png index 0a217c81d03..29cc129f7c2 100644 Binary files a/public/images/events/winter_holidays2024-event-ja.png and b/public/images/events/winter_holidays2024-event-ja.png differ diff --git a/public/images/events/winter_holidays2024-event-ko.png b/public/images/events/winter_holidays2024-event-ko.png index 83c9a8525f0..93033288093 100644 Binary files a/public/images/events/winter_holidays2024-event-ko.png and b/public/images/events/winter_holidays2024-event-ko.png differ diff --git a/public/images/events/winter_holidays2024-event-pt-BR.png b/public/images/events/winter_holidays2024-event-pt-BR.png index 1f003be5986..7a160f9efbd 100644 Binary files a/public/images/events/winter_holidays2024-event-pt-BR.png and b/public/images/events/winter_holidays2024-event-pt-BR.png differ diff --git a/public/images/events/winter_holidays2024-event-zh-CN.png b/public/images/events/winter_holidays2024-event-zh-CN.png index 03a9f57c6f2..2308ff99a38 100644 Binary files a/public/images/events/winter_holidays2024-event-zh-CN.png and b/public/images/events/winter_holidays2024-event-zh-CN.png differ diff --git a/public/images/events/yearofthesnakeevent-de.png b/public/images/events/yearofthesnakeevent-de.png index f5e684bbcd5..08cf799303d 100644 Binary files a/public/images/events/yearofthesnakeevent-de.png and b/public/images/events/yearofthesnakeevent-de.png differ diff --git a/public/images/events/yearofthesnakeevent-en.png b/public/images/events/yearofthesnakeevent-en.png index ddd34023b94..2b846528790 100644 Binary files a/public/images/events/yearofthesnakeevent-en.png and b/public/images/events/yearofthesnakeevent-en.png differ diff --git a/public/images/events/yearofthesnakeevent-es-ES.png b/public/images/events/yearofthesnakeevent-es-ES.png index 76474c475c5..b3d3330f05c 100644 Binary files a/public/images/events/yearofthesnakeevent-es-ES.png and b/public/images/events/yearofthesnakeevent-es-ES.png differ diff --git a/public/images/events/yearofthesnakeevent-fr.png b/public/images/events/yearofthesnakeevent-fr.png index 88ad3e770ca..edabe05527c 100644 Binary files a/public/images/events/yearofthesnakeevent-fr.png and b/public/images/events/yearofthesnakeevent-fr.png differ diff --git a/public/images/events/yearofthesnakeevent-it.png b/public/images/events/yearofthesnakeevent-it.png index a02e7048d2a..32c5d02ebb6 100644 Binary files a/public/images/events/yearofthesnakeevent-it.png and b/public/images/events/yearofthesnakeevent-it.png differ diff --git a/public/images/events/yearofthesnakeevent-ja.png b/public/images/events/yearofthesnakeevent-ja.png index ba13b5d281f..250efd2bd43 100644 Binary files a/public/images/events/yearofthesnakeevent-ja.png and b/public/images/events/yearofthesnakeevent-ja.png differ diff --git a/public/images/events/yearofthesnakeevent-ko.png b/public/images/events/yearofthesnakeevent-ko.png index dca6bcbbd15..072f310273b 100644 Binary files a/public/images/events/yearofthesnakeevent-ko.png and b/public/images/events/yearofthesnakeevent-ko.png differ diff --git a/public/images/events/yearofthesnakeevent-pt-BR.png b/public/images/events/yearofthesnakeevent-pt-BR.png index a535aec2aa5..9b192361373 100644 Binary files a/public/images/events/yearofthesnakeevent-pt-BR.png and b/public/images/events/yearofthesnakeevent-pt-BR.png differ diff --git a/public/images/events/yearofthesnakeevent-zh-CN.png b/public/images/events/yearofthesnakeevent-zh-CN.png index 3ab4fc565a7..49dc32e8141 100644 Binary files a/public/images/events/yearofthesnakeevent-zh-CN.png and b/public/images/events/yearofthesnakeevent-zh-CN.png differ diff --git a/public/images/inputs/dualshock.png b/public/images/inputs/dualshock.png index 264f03a298e..acdfd37aa79 100644 Binary files a/public/images/inputs/dualshock.png and b/public/images/inputs/dualshock.png differ diff --git a/public/images/inputs/keyboard.png b/public/images/inputs/keyboard.png index 0c33e579006..a1532bf667e 100644 Binary files a/public/images/inputs/keyboard.png and b/public/images/inputs/keyboard.png differ diff --git a/public/images/inputs/xbox.png b/public/images/inputs/xbox.png index 037fd8515ae..6f47d27fe5b 100644 Binary files a/public/images/inputs/xbox.png and b/public/images/inputs/xbox.png differ diff --git a/public/images/items.json b/public/images/items.json index 4312f2a58c4..7b332442f04 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -4,8 +4,8 @@ "image": "items.png", "format": "RGBA8888", "size": { - "w": 432, - "h": 432 + "w": 434, + "h": 434 }, "scale": 1, "frames": [ @@ -409,7 +409,7 @@ } }, { - "filename": "choice_specs", + "filename": "chipped_pot", "rotated": false, "trimmed": true, "sourceSize": { @@ -417,16 +417,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 24, - "h": 18 + "x": 3, + "y": 6, + "w": 26, + "h": 20 }, "frame": { "x": 0, "y": 414, - "w": 24, - "h": 18 + "w": 26, + "h": 20 } }, { @@ -598,7 +598,7 @@ } }, { - "filename": "chipped_pot", + "filename": "cracked_pot", "rotated": false, "trimmed": true, "sourceSize": { @@ -618,27 +618,6 @@ "h": 20 } }, - { - "filename": "cracked_pot", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 3, - "y": 6, - "w": 26, - "h": 20 - }, - "frame": { - "x": 358, - "y": 0, - "w": 26, - "h": 20 - } - }, { "filename": "legend_plate", "rotated": false, @@ -654,14 +633,35 @@ "h": 20 }, "frame": { - "x": 384, + "x": 358, "y": 0, "w": 25, "h": 20 } }, { - "filename": "big_root", + "filename": "mystical_rock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 25, + "h": 23 + }, + "frame": { + "x": 383, + "y": 0, + "w": 25, + "h": 23 + } + }, + { + "filename": "blank_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -671,13 +671,13 @@ "spriteSourceSize": { "x": 4, "y": 4, - "w": 23, + "w": 24, "h": 24 }, "frame": { - "x": 409, + "x": 408, "y": 0, - "w": 23, + "w": 24, "h": 24 } }, @@ -829,7 +829,7 @@ } }, { - "filename": "blank_plate", + "filename": "big_root", "rotated": false, "trimmed": true, "sourceSize": { @@ -839,13 +839,13 @@ "spriteSourceSize": { "x": 4, "y": 4, - "w": 24, + "w": 23, "h": 24 }, "frame": { "x": 22, "y": 290, - "w": 24, + "w": 23, "h": 24 } }, @@ -934,7 +934,7 @@ } }, { - "filename": "exp_balance", + "filename": "fist_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -943,15 +943,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 5, + "y": 4, "w": 24, - "h": 22 + "h": 24 }, "frame": { - "x": 24, + "x": 26, "y": 410, "w": 24, - "h": 22 + "h": 24 } }, { @@ -997,7 +997,7 @@ } }, { - "filename": "mystical_rock", + "filename": "choice_specs", "rotated": false, "trimmed": true, "sourceSize": { @@ -1006,15 +1006,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 5, - "w": 25, - "h": 23 + "y": 8, + "w": 24, + "h": 18 }, "frame": { "x": 59, "y": 53, - "w": 25, - "h": 23 + "w": 24, + "h": 18 } }, { @@ -1081,7 +1081,7 @@ } }, { - "filename": "fist_plate", + "filename": "flame_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1102,7 +1102,7 @@ } }, { - "filename": "flame_plate", + "filename": "focus_band", "rotated": false, "trimmed": true, "sourceSize": { @@ -1123,7 +1123,7 @@ } }, { - "filename": "focus_band", + "filename": "golden_punch", "rotated": false, "trimmed": true, "sourceSize": { @@ -1144,7 +1144,7 @@ } }, { - "filename": "golden_punch", + "filename": "gracidea", "rotated": false, "trimmed": true, "sourceSize": { @@ -1165,7 +1165,7 @@ } }, { - "filename": "gracidea", + "filename": "grip_claw", "rotated": false, "trimmed": true, "sourceSize": { @@ -1185,27 +1185,6 @@ "h": 24 } }, - { - "filename": "grip_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 55, - "y": 76, - "w": 24, - "h": 24 - } - }, { "filename": "icicle_plate", "rotated": false, @@ -1221,8 +1200,8 @@ "h": 24 }, "frame": { - "x": 55, - "y": 100, + "x": 45, + "y": 278, "w": 24, "h": 24 } @@ -1243,7 +1222,7 @@ }, "frame": { "x": 46, - "y": 278, + "y": 302, "w": 24, "h": 24 } @@ -1264,7 +1243,7 @@ }, "frame": { "x": 46, - "y": 302, + "y": 326, "w": 24, "h": 24 } @@ -1285,7 +1264,7 @@ }, "frame": { "x": 46, - "y": 326, + "y": 350, "w": 24, "h": 24 } @@ -1306,13 +1285,13 @@ }, "frame": { "x": 46, - "y": 350, + "y": 374, "w": 24, "h": 24 } }, { - "filename": "lucky_punch_master", + "filename": "silver_powder", "rotated": false, "trimmed": true, "sourceSize": { @@ -1321,14 +1300,119 @@ }, "spriteSourceSize": { "x": 4, - "y": 4, + "y": 11, "w": 24, + "h": 15 + }, + "frame": { + "x": 48, + "y": 71, + "w": 24, + "h": 15 + } + }, + { + "filename": "elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, "h": 24 }, "frame": { - "x": 46, - "y": 374, - "w": 24, + "x": 55, + "y": 86, + "w": 18, + "h": 24 + } + }, + { + "filename": "ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 55, + "y": 110, + "w": 18, + "h": 24 + } + }, + { + "filename": "full_restore", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 60, + "y": 134, + "w": 18, + "h": 24 + } + }, + { + "filename": "hp_up", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 63, + "y": 158, + "w": 16, + "h": 24 + } + }, + { + "filename": "iron", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 63, + "y": 182, + "w": 16, "h": 24 } }, @@ -1347,12 +1431,33 @@ "h": 24 }, "frame": { - "x": 48, - "y": 398, + "x": 68, + "y": 206, "w": 23, "h": 24 } }, + { + "filename": "lucky_punch_master", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 68, + "y": 230, + "w": 24, + "h": 24 + } + }, { "filename": "lucky_punch_ultra", "rotated": false, @@ -1368,8 +1473,8 @@ "h": 24 }, "frame": { - "x": 60, - "y": 124, + "x": 68, + "y": 254, "w": 24, "h": 24 } @@ -1389,8 +1494,8 @@ "h": 24 }, "frame": { - "x": 63, - "y": 148, + "x": 69, + "y": 278, "w": 24, "h": 24 } @@ -1410,33 +1515,12 @@ "h": 24 }, "frame": { - "x": 63, - "y": 172, + "x": 70, + "y": 302, "w": 24, "h": 24 } }, - { - "filename": "max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 24 - }, - "frame": { - "x": 68, - "y": 196, - "w": 22, - "h": 24 - } - }, { "filename": "mind_plate", "rotated": false, @@ -1452,8 +1536,8 @@ "h": 24 }, "frame": { - "x": 68, - "y": 220, + "x": 70, + "y": 326, "w": 24, "h": 24 } @@ -1473,8 +1557,8 @@ "h": 24 }, "frame": { - "x": 68, - "y": 244, + "x": 70, + "y": 350, "w": 24, "h": 24 } @@ -1495,7 +1579,7 @@ }, "frame": { "x": 70, - "y": 268, + "y": 374, "w": 24, "h": 24 } @@ -1515,665 +1599,14 @@ "h": 24 }, "frame": { - "x": 70, - "y": 292, + "x": 50, + "y": 398, "w": 24, "h": 24 } }, { - "filename": "scanner", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 316, - "w": 24, - "h": 24 - } - }, - { - "filename": "silk_scarf", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 340, - "w": 24, - "h": 24 - } - }, - { - "filename": "sky_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 364, - "w": 24, - "h": 24 - } - }, - { - "filename": "reveal_glass", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 71, - "y": 388, - "w": 23, - "h": 24 - } - }, - { - "filename": "icy_reins_of_unity", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 71, - "y": 412, - "w": 24, - "h": 20 - } - }, - { - "filename": "elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 79, - "y": 76, - "w": 18, - "h": 24 - } - }, - { - "filename": "ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 79, - "y": 100, - "w": 18, - "h": 24 - } - }, - { - "filename": "full_restore", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 84, - "y": 124, - "w": 18, - "h": 24 - } - }, - { - "filename": "hp_up", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 87, - "y": 148, - "w": 16, - "h": 24 - } - }, - { - "filename": "iron", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 87, - "y": 172, - "w": 16, - "h": 24 - } - }, - { - "filename": "lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 90, - "y": 196, - "w": 17, - "h": 24 - } - }, - { - "filename": "max_elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 92, - "y": 220, - "w": 18, - "h": 24 - } - }, - { - "filename": "max_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 92, - "y": 244, - "w": 18, - "h": 24 - } - }, - { - "filename": "max_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 94, - "y": 268, - "w": 17, - "h": 24 - } - }, - { - "filename": "max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 94, - "y": 292, - "w": 18, - "h": 24 - } - }, - { - "filename": "oval_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 94, - "y": 316, - "w": 21, - "h": 24 - } - }, - { - "filename": "shiny_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 94, - "y": 340, - "w": 21, - "h": 24 - } - }, - { - "filename": "splash_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 94, - "y": 364, - "w": 24, - "h": 24 - } - }, - { - "filename": "spooky_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 94, - "y": 388, - "w": 24, - "h": 24 - } - }, - { - "filename": "metal_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 95, - "y": 412, - "w": 24, - "h": 20 - } - }, - { - "filename": "berry_pouch", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 84, - "y": 53, - "w": 23, - "h": 23 - } - }, - { - "filename": "max_repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 97, - "y": 76, - "w": 16, - "h": 24 - } - }, - { - "filename": "pp_max", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 97, - "y": 100, - "w": 16, - "h": 24 - } - }, - { - "filename": "pp_up", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 102, - "y": 124, - "w": 16, - "h": 24 - } - }, - { - "filename": "protein", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 103, - "y": 148, - "w": 16, - "h": 24 - } - }, - { - "filename": "red_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 20, - "h": 24 - }, - "frame": { - "x": 103, - "y": 172, - "w": 20, - "h": 24 - } - }, - { - "filename": "repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 107, - "y": 196, - "w": 16, - "h": 24 - } - }, - { - "filename": "stone_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 110, - "y": 220, - "w": 24, - "h": 24 - } - }, - { - "filename": "sun_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 110, - "y": 244, - "w": 24, - "h": 24 - } - }, - { - "filename": "toxic_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 111, - "y": 268, - "w": 24, - "h": 24 - } - }, - { - "filename": "zap_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 112, - "y": 292, - "w": 24, - "h": 24 - } - }, - { - "filename": "black_belt", + "filename": "max_revive", "rotated": false, "trimmed": true, "sourceSize": { @@ -2184,117 +1617,12 @@ "x": 5, "y": 4, "w": 22, - "h": 23 - }, - "frame": { - "x": 115, - "y": 316, - "w": 22, - "h": 23 - } - }, - { - "filename": "bug_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 115, - "y": 339, - "w": 22, - "h": 23 - } - }, - { - "filename": "clefairy_doll", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 118, - "y": 362, - "w": 24, - "h": 23 - } - }, - { - "filename": "coin_case", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 118, - "y": 385, - "w": 24, - "h": 23 - } - }, - { - "filename": "super_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, "h": 24 }, "frame": { - "x": 119, - "y": 408, - "w": 17, - "h": 24 - } - }, - { - "filename": "super_repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 136, - "y": 408, - "w": 16, + "x": 74, + "y": 398, + "w": 22, "h": 24 } }, @@ -2320,7 +1648,7 @@ } }, { - "filename": "black_glasses", + "filename": "lure", "rotated": false, "trimmed": true, "sourceSize": { @@ -2328,20 +1656,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 + "x": 8, + "y": 4, + "w": 17, + "h": 24 }, "frame": { "x": 86, - "y": 36, - "w": 23, - "h": 17 + "y": 27, + "w": 17, + "h": 24 } }, { - "filename": "expert_belt", + "filename": "scanner", "rotated": false, "trimmed": true, "sourceSize": { @@ -2352,15 +1680,78 @@ "x": 4, "y": 4, "w": 24, + "h": 24 + }, + "frame": { + "x": 103, + "y": 26, + "w": 24, + "h": 24 + } + }, + { + "filename": "clefairy_doll", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, "h": 23 }, "frame": { - "x": 109, - "y": 26, + "x": 127, + "y": 36, "w": 24, "h": 23 } }, + { + "filename": "coin_case", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 23 + }, + "frame": { + "x": 103, + "y": 50, + "w": 24, + "h": 23 + } + }, + { + "filename": "big_nugget", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 83, + "y": 53, + "w": 20, + "h": 20 + } + }, { "filename": "dragon_scale", "rotated": false, @@ -2376,12 +1767,264 @@ "h": 18 }, "frame": { - "x": 133, - "y": 36, + "x": 127, + "y": 59, "w": 24, "h": 18 } }, + { + "filename": "max_elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 151, + "y": 36, + "w": 18, + "h": 24 + } + }, + { + "filename": "adamant_crystal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 151, + "y": 60, + "w": 23, + "h": 21 + } + }, + { + "filename": "silk_scarf", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 169, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "sky_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 193, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "splash_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 217, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "spooky_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 241, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "stone_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 265, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "sun_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 289, + "y": 21, + "w": 24, + "h": 24 + } + }, + { + "filename": "oval_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 313, + "y": 21, + "w": 21, + "h": 24 + } + }, + { + "filename": "toxic_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 334, + "y": 20, + "w": 24, + "h": 24 + } + }, + { + "filename": "zap_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 358, + "y": 20, + "w": 24, + "h": 24 + } + }, + { + "filename": "exp_balance", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 22 + }, + "frame": { + "x": 382, + "y": 23, + "w": 24, + "h": 22 + } + }, { "filename": "exp_share", "rotated": false, @@ -2397,14 +2040,35 @@ "h": 22 }, "frame": { - "x": 109, - "y": 49, + "x": 406, + "y": 24, "w": 24, "h": 22 } }, { - "filename": "golden_net", + "filename": "candy_overlay", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 12, + "w": 16, + "h": 15 + }, + "frame": { + "x": 169, + "y": 45, + "w": 16, + "h": 15 + } + }, + { + "filename": "expert_belt", "rotated": false, "trimmed": true, "sourceSize": { @@ -2413,98 +2077,14 @@ }, "spriteSourceSize": { "x": 4, - "y": 5, - "w": 24, - "h": 21 - }, - "frame": { - "x": 133, - "y": 54, - "w": 24, - "h": 21 - } - }, - { - "filename": "mystic_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 23 - }, - "frame": { - "x": 113, - "y": 71, - "w": 20, - "h": 23 - } - }, - { - "filename": "dark_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, "y": 4, - "w": 22, + "w": 24, "h": 23 }, "frame": { - "x": 113, - "y": 94, - "w": 22, - "h": 23 - } - }, - { - "filename": "coupon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 133, - "y": 75, - "w": 23, - "h": 19 - } - }, - { - "filename": "dragon_fang", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 135, - "y": 94, - "w": 21, + "x": 185, + "y": 45, + "w": 24, "h": 23 } }, @@ -2523,75 +2103,12 @@ "h": 23 }, "frame": { - "x": 118, - "y": 117, + "x": 209, + "y": 45, "w": 24, "h": 23 } }, - { - "filename": "dynamax_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 23, - "h": 23 - }, - "frame": { - "x": 119, - "y": 140, - "w": 23, - "h": 23 - } - }, - { - "filename": "unknown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 142, - "y": 117, - "w": 16, - "h": 24 - } - }, - { - "filename": "berry_pot", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 18, - "h": 22 - }, - "frame": { - "x": 142, - "y": 141, - "w": 18, - "h": 22 - } - }, { "filename": "leppa_berry", "rotated": false, @@ -2607,8 +2124,8 @@ "h": 23 }, "frame": { - "x": 123, - "y": 163, + "x": 233, + "y": 45, "w": 24, "h": 23 } @@ -2628,96 +2145,12 @@ "h": 23 }, "frame": { - "x": 123, - "y": 186, + "x": 257, + "y": 45, "w": 24, "h": 23 } }, - { - "filename": "relic_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 15, - "h": 11 - }, - "frame": { - "x": 123, - "y": 209, - "w": 15, - "h": 11 - } - }, - { - "filename": "zinc", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 147, - "y": 163, - "w": 16, - "h": 24 - } - }, - { - "filename": "bug_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 147, - "y": 187, - "w": 22, - "h": 22 - } - }, - { - "filename": "peat_block", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 138, - "y": 209, - "w": 24, - "h": 22 - } - }, { "filename": "twisted_spoon", "rotated": false, @@ -2733,56 +2166,14 @@ "h": 23 }, "frame": { - "x": 134, - "y": 231, + "x": 281, + "y": 45, "w": 24, "h": 23 } }, { - "filename": "griseous_core", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 135, - "y": 254, - "w": 23, - "h": 23 - } - }, - { - "filename": "silver_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 24, - "h": 15 - }, - "frame": { - "x": 135, - "y": 277, - "w": 24, - "h": 15 - } - }, - { - "filename": "leek", + "filename": "berry_pouch", "rotated": false, "trimmed": true, "sourceSize": { @@ -2796,98 +2187,14 @@ "h": 23 }, "frame": { - "x": 136, - "y": 292, + "x": 305, + "y": 45, "w": 23, "h": 23 } }, { - "filename": "dragon_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 137, - "y": 315, - "w": 22, - "h": 23 - } - }, - { - "filename": "electric_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 137, - "y": 338, - "w": 22, - "h": 23 - } - }, - { - "filename": "fairy_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 142, - "y": 361, - "w": 22, - "h": 23 - } - }, - { - "filename": "fighting_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 142, - "y": 384, - "w": 22, - "h": 23 - } - }, - { - "filename": "fire_stone", + "filename": "black_belt", "rotated": false, "trimmed": true, "sourceSize": { @@ -2896,19 +2203,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 5, + "y": 4, "w": 22, "h": 23 }, "frame": { - "x": 152, - "y": 407, + "x": 328, + "y": 45, "w": 22, "h": 23 } }, { - "filename": "charcoal", + "filename": "reveal_glass", "rotated": false, "trimmed": true, "sourceSize": { @@ -2916,20 +2223,83 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 4, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 350, + "y": 44, + "w": 23, + "h": 24 + } + }, + { + "filename": "dynamax_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 23, + "h": 23 + }, + "frame": { + "x": 373, + "y": 45, + "w": 23, + "h": 23 + } + }, + { + "filename": "peat_block", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, "y": 5, - "w": 22, + "w": 24, "h": 22 }, "frame": { - "x": 162, - "y": 209, - "w": 22, + "x": 396, + "y": 46, + "w": 24, "h": 22 } }, { - "filename": "macho_brace", + "filename": "revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 12, + "h": 17 + }, + "frame": { + "x": 420, + "y": 46, + "w": 12, + "h": 17 + } + }, + { + "filename": "golden_net", "rotated": false, "trimmed": true, "sourceSize": { @@ -2939,287 +2309,14 @@ "spriteSourceSize": { "x": 4, "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 158, - "y": 231, - "w": 23, - "h": 23 - } - }, - { - "filename": "rare_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 158, - "y": 254, - "w": 23, - "h": 23 - } - }, - { - "filename": "fire_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 159, - "y": 277, - "w": 22, - "h": 23 - } - }, - { - "filename": "focus_sash", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 159, - "y": 300, - "w": 22, - "h": 23 - } - }, - { - "filename": "ghost_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 159, - "y": 323, - "w": 22, - "h": 23 - } - }, - { - "filename": "candy_overlay", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 16, - "h": 15 - }, - "frame": { - "x": 159, - "y": 346, - "w": 16, - "h": 15 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 164, - "y": 361, - "w": 15, - "h": 23 - } - }, - { - "filename": "grass_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 164, - "y": 384, - "w": 22, - "h": 23 - } - }, - { - "filename": "ground_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 174, - "y": 407, - "w": 22, - "h": 23 - } - }, - { - "filename": "hyper_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 157, - "y": 36, - "w": 17, - "h": 23 - } - }, - { - "filename": "adamant_crystal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, + "w": 24, "h": 21 }, - "frame": { - "x": 157, - "y": 59, - "w": 23, - "h": 21 - } - }, - { - "filename": "rarer_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 156, - "y": 80, - "w": 23, - "h": 23 - } - }, - { - "filename": "healing_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 23, - "h": 22 - }, "frame": { "x": 174, - "y": 21, - "w": 23, - "h": 22 - } - }, - { - "filename": "rusted_sword", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 22 - }, - "frame": { - "x": 197, - "y": 21, - "w": 23, - "h": 22 + "y": 68, + "w": 24, + "h": 21 } }, { @@ -3237,8 +2334,8 @@ "h": 21 }, "frame": { - "x": 220, - "y": 21, + "x": 198, + "y": 68, "w": 23, "h": 21 } @@ -3258,14 +2355,35 @@ "h": 21 }, "frame": { - "x": 243, - "y": 21, + "x": 221, + "y": 68, "w": 23, "h": 21 } }, { - "filename": "moon_stone", + "filename": "griseous_core", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 244, + "y": 68, + "w": 23, + "h": 23 + } + }, + { + "filename": "leek", "rotated": false, "trimmed": true, "sourceSize": { @@ -3274,19 +2392,19 @@ }, "spriteSourceSize": { "x": 4, - "y": 6, + "y": 5, "w": 23, - "h": 21 + "h": 23 }, "frame": { - "x": 266, - "y": 21, + "x": 267, + "y": 68, "w": 23, - "h": 21 + "h": 23 } }, { - "filename": "n_lunarizer", + "filename": "macho_brace", "rotated": false, "trimmed": true, "sourceSize": { @@ -3295,19 +2413,103 @@ }, "spriteSourceSize": { "x": 4, - "y": 6, + "y": 5, "w": 23, - "h": 21 + "h": 23 }, "frame": { - "x": 289, - "y": 21, + "x": 290, + "y": 68, "w": 23, - "h": 21 + "h": 23 } }, { - "filename": "berry_juice", + "filename": "rare_candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 313, + "y": 68, + "w": 23, + "h": 23 + } + }, + { + "filename": "rarer_candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 336, + "y": 68, + "w": 23, + "h": 23 + } + }, + { + "filename": "bug_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 359, + "y": 68, + "w": 22, + "h": 23 + } + }, + { + "filename": "dark_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 381, + "y": 68, + "w": 22, + "h": 23 + } + }, + { + "filename": "dragon_fang", "rotated": false, "trimmed": true, "sourceSize": { @@ -3317,18 +2519,18 @@ "spriteSourceSize": { "x": 5, "y": 5, - "w": 22, - "h": 21 + "w": 21, + "h": 23 }, "frame": { - "x": 312, - "y": 21, - "w": 22, - "h": 21 + "x": 403, + "y": 68, + "w": 21, + "h": 23 } }, { - "filename": "dark_memory", + "filename": "relic_gold", "rotated": false, "trimmed": true, "sourceSize": { @@ -3336,20 +2538,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 9, + "y": 11, + "w": 15, + "h": 11 }, "frame": { - "x": 334, - "y": 20, - "w": 22, - "h": 22 + "x": 50, + "y": 422, + "w": 15, + "h": 11 } }, { - "filename": "dire_hit", + "filename": "icy_reins_of_unity", "rotated": false, "trimmed": true, "sourceSize": { @@ -3357,41 +2559,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 4, + "y": 7, + "w": 24, + "h": 20 }, "frame": { - "x": 356, - "y": 20, - "w": 22, - "h": 22 + "x": 91, + "y": 73, + "w": 24, + "h": 20 } }, { - "filename": "dna_splicers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 378, - "y": 20, - "w": 22, - "h": 22 - } - }, - { - "filename": "relic_band", + "filename": "max_ether", "rotated": false, "trimmed": true, "sourceSize": { @@ -3400,15 +2581,78 @@ }, "spriteSourceSize": { "x": 7, - "y": 9, - "w": 17, - "h": 16 + "y": 4, + "w": 18, + "h": 24 }, "frame": { - "x": 174, - "y": 43, - "w": 17, - "h": 16 + "x": 73, + "y": 73, + "w": 18, + "h": 24 + } + }, + { + "filename": "max_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 73, + "y": 97, + "w": 18, + "h": 24 + } + }, + { + "filename": "dragon_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 91, + "y": 93, + "w": 22, + "h": 23 + } + }, + { + "filename": "metal_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 115, + "y": 77, + "w": 24, + "h": 20 } }, { @@ -3426,12 +2670,33 @@ "h": 20 }, "frame": { - "x": 191, - "y": 43, + "x": 113, + "y": 97, "w": 24, "h": 20 } }, + { + "filename": "berry_juice_bad", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 21 + }, + "frame": { + "x": 91, + "y": 116, + "w": 22, + "h": 21 + } + }, { "filename": "rusted_shield", "rotated": false, @@ -3447,12 +2712,264 @@ "h": 20 }, "frame": { - "x": 400, - "y": 24, + "x": 113, + "y": 117, "w": 24, "h": 20 } }, + { + "filename": "moon_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 78, + "y": 137, + "w": 23, + "h": 21 + } + }, + { + "filename": "electric_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 79, + "y": 158, + "w": 22, + "h": 23 + } + }, + { + "filename": "fairy_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 101, + "y": 137, + "w": 22, + "h": 23 + } + }, + { + "filename": "fighting_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 79, + "y": 181, + "w": 22, + "h": 23 + } + }, + { + "filename": "fire_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 23 + }, + "frame": { + "x": 101, + "y": 160, + "w": 22, + "h": 23 + } + }, + { + "filename": "berry_juice_good", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 21 + }, + "frame": { + "x": 101, + "y": 183, + "w": 22, + "h": 21 + } + }, + { + "filename": "fire_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 91, + "y": 204, + "w": 22, + "h": 23 + } + }, + { + "filename": "shiny_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 92, + "y": 227, + "w": 21, + "h": 24 + } + }, + { + "filename": "focus_sash", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 92, + "y": 251, + "w": 22, + "h": 23 + } + }, + { + "filename": "ghost_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 93, + "y": 274, + "w": 22, + "h": 23 + } + }, + { + "filename": "grass_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 297, + "w": 22, + "h": 23 + } + }, + { + "filename": "ground_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 320, + "w": 22, + "h": 23 + } + }, { "filename": "ice_tera_shard", "rotated": false, @@ -3468,35 +2985,14 @@ "h": 23 }, "frame": { - "x": 158, - "y": 103, + "x": 94, + "y": 343, "w": 22, "h": 23 } }, { - "filename": "eviolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 158, - "y": 126, - "w": 15, - "h": 15 - } - }, - { - "filename": "dragon_memory", + "filename": "never_melt_ice", "rotated": false, "trimmed": true, "sourceSize": { @@ -3507,15 +3003,120 @@ "x": 5, "y": 5, "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 366, + "w": 22, + "h": 23 + } + }, + { + "filename": "max_lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 123, + "y": 137, + "w": 17, + "h": 24 + } + }, + { + "filename": "red_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 20, + "h": 24 + }, + "frame": { + "x": 123, + "y": 161, + "w": 20, + "h": 24 + } + }, + { + "filename": "binding_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 23, + "h": 20 + }, + "frame": { + "x": 123, + "y": 185, + "w": 23, + "h": 20 + } + }, + { + "filename": "healing_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 23, "h": 22 }, "frame": { - "x": 160, - "y": 141, - "w": 22, + "x": 113, + "y": 205, + "w": 23, "h": 22 } }, + { + "filename": "normal_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 113, + "y": 227, + "w": 22, + "h": 23 + } + }, { "filename": "lansat_berry", "rotated": false, @@ -3531,8 +3132,8 @@ "h": 23 }, "frame": { - "x": 163, - "y": 163, + "x": 114, + "y": 250, "w": 21, "h": 23 } @@ -3552,77 +3153,14 @@ "h": 23 }, "frame": { - "x": 169, - "y": 186, + "x": 115, + "y": 273, "w": 21, "h": 23 } }, { - "filename": "prism_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 173, - "y": 126, - "w": 15, - "h": 15 - } - }, - { - "filename": "electirizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 182, - "y": 141, - "w": 22, - "h": 22 - } - }, - { - "filename": "never_melt_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 184, - "y": 163, - "w": 22, - "h": 23 - } - }, - { - "filename": "normal_tera_shard", + "filename": "mystic_water", "rotated": false, "trimmed": true, "sourceSize": { @@ -3631,36 +3169,15 @@ }, "spriteSourceSize": { "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 190, - "y": 186, - "w": 22, - "h": 23 - } - }, - { - "filename": "electric_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, "y": 5, - "w": 22, - "h": 22 + "w": 20, + "h": 23 }, "frame": { - "x": 184, - "y": 209, - "w": 22, - "h": 22 + "x": 116, + "y": 296, + "w": 20, + "h": 23 } }, { @@ -3678,8 +3195,8 @@ "h": 23 }, "frame": { - "x": 181, - "y": 231, + "x": 116, + "y": 319, "w": 22, "h": 23 } @@ -3699,8 +3216,8 @@ "h": 23 }, "frame": { - "x": 181, - "y": 254, + "x": 116, + "y": 342, "w": 22, "h": 23 } @@ -3720,8 +3237,8 @@ "h": 23 }, "frame": { - "x": 181, - "y": 277, + "x": 116, + "y": 365, "w": 22, "h": 23 } @@ -3741,12 +3258,33 @@ "h": 23 }, "frame": { - "x": 181, - "y": 300, + "x": 96, + "y": 389, "w": 22, "h": 23 } }, + { + "filename": "rusted_sword", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 22 + }, + "frame": { + "x": 96, + "y": 412, + "w": 23, + "h": 22 + } + }, { "filename": "rock_tera_shard", "rotated": false, @@ -3762,54 +3300,12 @@ "h": 23 }, "frame": { - "x": 181, - "y": 323, + "x": 118, + "y": 388, "w": 22, "h": 23 } }, - { - "filename": "sacred_ash", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 180, - "y": 63, - "w": 24, - "h": 20 - } - }, - { - "filename": "shadow_reins_of_unity", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 179, - "y": 83, - "w": 24, - "h": 20 - } - }, { "filename": "steel_tera_shard", "rotated": false, @@ -3825,14 +3321,14 @@ "h": 23 }, "frame": { - "x": 180, - "y": 103, + "x": 119, + "y": 411, "w": 22, "h": 23 } }, { - "filename": "apicot_berry", + "filename": "berry_pot", "rotated": false, "trimmed": true, "sourceSize": { @@ -3840,37 +3336,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 + "x": 7, + "y": 5, + "w": 18, + "h": 22 }, "frame": { - "x": 204, - "y": 63, - "w": 19, - "h": 20 - } - }, - { - "filename": "big_nugget", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 203, - "y": 83, - "w": 20, - "h": 20 + "x": 136, + "y": 205, + "w": 18, + "h": 22 } }, { @@ -3888,54 +3363,12 @@ "h": 23 }, "frame": { - "x": 202, - "y": 103, + "x": 135, + "y": 227, "w": 21, "h": 23 } }, - { - "filename": "binding_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 23, - "h": 20 - }, - "frame": { - "x": 215, - "y": 43, - "w": 23, - "h": 20 - } - }, - { - "filename": "n_solarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 238, - "y": 42, - "w": 23, - "h": 21 - } - }, { "filename": "stellar_tera_shard", "rotated": false, @@ -3951,8 +3384,8 @@ "h": 23 }, "frame": { - "x": 223, - "y": 63, + "x": 135, + "y": 250, "w": 22, "h": 23 } @@ -3972,12 +3405,306 @@ "h": 23 }, "frame": { - "x": 223, - "y": 86, + "x": 136, + "y": 273, "w": 22, "h": 23 } }, + { + "filename": "wide_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 136, + "y": 296, + "w": 22, + "h": 23 + } + }, + { + "filename": "whipped_dream", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 21, + "h": 23 + }, + "frame": { + "x": 138, + "y": 319, + "w": 21, + "h": 23 + } + }, + { + "filename": "bug_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 138, + "y": 342, + "w": 22, + "h": 22 + } + }, + { + "filename": "charcoal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 138, + "y": 364, + "w": 22, + "h": 22 + } + }, + { + "filename": "dark_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 140, + "y": 386, + "w": 22, + "h": 22 + } + }, + { + "filename": "dire_hit", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 141, + "y": 408, + "w": 22, + "h": 22 + } + }, + { + "filename": "blue_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 154, + "y": 81, + "w": 20, + "h": 20 + } + }, + { + "filename": "full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 15, + "h": 23 + }, + "frame": { + "x": 139, + "y": 81, + "w": 15, + "h": 23 + } + }, + { + "filename": "super_lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 137, + "y": 104, + "w": 17, + "h": 24 + } + }, + { + "filename": "dna_splicers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 154, + "y": 101, + "w": 22, + "h": 22 + } + }, + { + "filename": "dragon_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 176, + "y": 89, + "w": 22, + "h": 22 + } + }, + { + "filename": "electirizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 198, + "y": 89, + "w": 22, + "h": 22 + } + }, + { + "filename": "sacred_ash", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 220, + "y": 89, + "w": 24, + "h": 20 + } + }, + { + "filename": "shadow_reins_of_unity", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 244, + "y": 91, + "w": 24, + "h": 20 + } + }, { "filename": "soft_sand", "rotated": false, @@ -3993,14 +3720,14 @@ "h": 20 }, "frame": { - "x": 261, - "y": 42, + "x": 268, + "y": 91, "w": 24, "h": 20 } }, { - "filename": "reviver_seed", + "filename": "n_lunarizer", "rotated": false, "trimmed": true, "sourceSize": { @@ -4008,20 +3735,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 8, + "x": 4, + "y": 6, "w": 23, - "h": 20 + "h": 21 }, "frame": { - "x": 285, - "y": 42, + "x": 292, + "y": 91, "w": 23, - "h": 20 + "h": 21 } }, { - "filename": "shell_bell", + "filename": "n_solarizer", "rotated": false, "trimmed": true, "sourceSize": { @@ -4029,16 +3756,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 7, + "x": 4, + "y": 6, "w": 23, - "h": 20 + "h": 21 }, "frame": { - "x": 308, - "y": 42, + "x": 315, + "y": 91, "w": 23, - "h": 20 + "h": 21 } }, { @@ -4056,8 +3783,8 @@ "h": 21 }, "frame": { - "x": 331, - "y": 42, + "x": 338, + "y": 91, "w": 23, "h": 21 } @@ -4077,12 +3804,33 @@ "h": 21 }, "frame": { - "x": 354, - "y": 42, + "x": 361, + "y": 91, "w": 22, "h": 21 } }, + { + "filename": "electric_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 383, + "y": 91, + "w": 22, + "h": 22 + } + }, { "filename": "enigma_berry", "rotated": false, @@ -4098,14 +3846,14 @@ "h": 22 }, "frame": { - "x": 376, - "y": 42, + "x": 405, + "y": 91, "w": 22, "h": 22 } }, { - "filename": "deep_sea_scale", + "filename": "black_glasses", "rotated": false, "trimmed": true, "sourceSize": { @@ -4113,16 +3861,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 + "x": 4, + "y": 8, + "w": 23, + "h": 17 }, "frame": { - "x": 398, - "y": 44, - "w": 22, - "h": 20 + "x": 176, + "y": 111, + "w": 23, + "h": 17 } }, { @@ -4140,35 +3888,14 @@ "h": 19 }, "frame": { - "x": 223, - "y": 109, + "x": 199, + "y": 111, "w": 22, "h": 19 } }, { - "filename": "potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 245, - "y": 63, - "w": 17, - "h": 23 - } - }, - { - "filename": "wide_lens", + "filename": "reviver_seed", "rotated": false, "trimmed": true, "sourceSize": { @@ -4177,439 +3904,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 262, - "y": 62, - "w": 22, - "h": 23 - } - }, - { - "filename": "fairy_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 284, - "y": 62, - "w": 22, - "h": 22 - } - }, - { - "filename": "fighting_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 306, - "y": 62, - "w": 22, - "h": 22 - } - }, - { - "filename": "fire_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 328, - "y": 63, - "w": 22, - "h": 22 - } - }, - { - "filename": "flying_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 350, - "y": 63, - "w": 22, - "h": 22 - } - }, - { - "filename": "sachet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 18, - "h": 23 - }, - "frame": { - "x": 245, - "y": 86, - "w": 18, - "h": 23 - } - }, - { - "filename": "whipped_dream", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 263, - "y": 85, - "w": 21, - "h": 23 - } - }, - { - "filename": "ganlon_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 284, - "y": 84, - "w": 22, - "h": 22 - } - }, - { - "filename": "ghost_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 306, - "y": 84, - "w": 22, - "h": 22 - } - }, - { - "filename": "grass_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 328, - "y": 85, - "w": 22, - "h": 22 - } - }, - { - "filename": "ground_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 350, - "y": 85, - "w": 22, - "h": 22 - } - }, - { - "filename": "guard_spec", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 372, - "y": 64, - "w": 22, - "h": 22 - } - }, - { - "filename": "ice_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 394, - "y": 64, - "w": 22, - "h": 22 - } - }, - { - "filename": "ice_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 372, - "y": 86, - "w": 22, - "h": 22 - } - }, - { - "filename": "magmarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 394, - "y": 86, - "w": 22, - "h": 22 - } - }, - { - "filename": "mystery_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, "y": 8, - "w": 16, - "h": 18 - }, - "frame": { - "x": 416, - "y": 64, - "w": 16, - "h": 18 - } - }, - { - "filename": "abomasite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 416, - "y": 82, - "w": 16, - "h": 16 - } - }, - { - "filename": "absolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 416, - "y": 98, - "w": 16, - "h": 16 - } - }, - { - "filename": "revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 12, - "h": 17 - }, - "frame": { - "x": 420, - "y": 44, - "w": 12, - "h": 17 - } - }, - { - "filename": "big_mushroom", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 19 - }, - "frame": { - "x": 245, - "y": 109, - "w": 19, - "h": 19 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, + "w": 23, "h": 20 }, "frame": { - "x": 264, - "y": 108, - "w": 20, + "x": 221, + "y": 109, + "w": 23, "h": 20 } }, { - "filename": "mini_black_hole", + "filename": "coupon", "rotated": false, "trimmed": true, "sourceSize": { @@ -4617,499 +3924,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 4, + "y": 7, + "w": 23, + "h": 19 }, "frame": { - "x": 284, - "y": 106, - "w": 22, - "h": 22 - } - }, - { - "filename": "moon_flute", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 306, - "y": 106, - "w": 22, - "h": 22 - } - }, - { - "filename": "liechi_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 328, - "y": 107, - "w": 22, - "h": 21 - } - }, - { - "filename": "normal_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 350, - "y": 107, - "w": 22, - "h": 22 - } - }, - { - "filename": "poison_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 372, - "y": 108, - "w": 22, - "h": 22 - } - }, - { - "filename": "protector", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 394, - "y": 108, - "w": 22, - "h": 22 - } - }, - { - "filename": "aerodactylite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 416, - "y": 114, - "w": 16, - "h": 16 - } - }, - { - "filename": "psychic_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 179, - "y": 346, - "w": 22, - "h": 22 - } - }, - { - "filename": "aggronite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 179, - "y": 368, - "w": 16, - "h": 16 - } - }, - { - "filename": "super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 186, - "y": 384, - "w": 17, - "h": 23 - } - }, - { - "filename": "alakazite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 195, - "y": 368, - "w": 16, - "h": 16 - } - }, - { - "filename": "hard_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 201, - "y": 346, - "w": 20, - "h": 22 - } - }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 203, - "y": 384, - "w": 15, - "h": 22 - } - }, - { - "filename": "altarianite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 211, - "y": 368, - "w": 16, - "h": 16 - } - }, - { - "filename": "lock_capsule", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 218, - "y": 384, - "w": 19, - "h": 22 - } - }, - { - "filename": "metal_coat", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 196, - "y": 407, - "w": 19, - "h": 22 - } - }, - { - "filename": "rock_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 215, - "y": 406, - "w": 22, - "h": 22 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 206, - "y": 209, - "w": 17, - "h": 22 - } - }, - { - "filename": "scroll_of_darkness", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 203, - "y": 231, - "w": 22, - "h": 22 - } - }, - { - "filename": "scroll_of_waters", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 203, - "y": 253, - "w": 22, - "h": 22 - } - }, - { - "filename": "shed_shell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 203, - "y": 275, - "w": 22, - "h": 22 - } - }, - { - "filename": "starf_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 203, - "y": 297, - "w": 22, - "h": 22 - } - }, - { - "filename": "steel_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 203, - "y": 319, - "w": 22, - "h": 22 - } - }, - { - "filename": "quick_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 204, - "y": 126, - "w": 19, - "h": 21 + "x": 244, + "y": 111, + "w": 23, + "h": 19 } }, { @@ -5127,8 +3951,8 @@ "h": 19 }, "frame": { - "x": 223, - "y": 128, + "x": 267, + "y": 111, "w": 23, "h": 19 } @@ -5148,8 +3972,8 @@ "h": 19 }, "frame": { - "x": 246, - "y": 128, + "x": 290, + "y": 112, "w": 23, "h": 19 } @@ -5169,12 +3993,54 @@ "h": 19 }, "frame": { - "x": 269, - "y": 128, + "x": 313, + "y": 112, "w": 23, "h": 19 } }, + { + "filename": "shell_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 23, + "h": 20 + }, + "frame": { + "x": 336, + "y": 112, + "w": 23, + "h": 20 + } + }, + { + "filename": "deep_sea_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 359, + "y": 112, + "w": 22, + "h": 20 + } + }, { "filename": "blunder_policy", "rotated": false, @@ -5190,8 +4056,8 @@ "h": 19 }, "frame": { - "x": 292, - "y": 128, + "x": 381, + "y": 113, "w": 22, "h": 19 } @@ -5211,14 +4077,14 @@ "h": 19 }, "frame": { - "x": 314, - "y": 128, + "x": 403, + "y": 113, "w": 22, "h": 19 } }, { - "filename": "ampharosite", + "filename": "fairy_feather", "rotated": false, "trimmed": true, "sourceSize": { @@ -5226,16 +4092,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 + "x": 5, + "y": 7, + "w": 22, + "h": 20 }, "frame": { - "x": 204, - "y": 147, - "w": 16, - "h": 16 + "x": 154, + "y": 123, + "w": 22, + "h": 20 } }, { @@ -5253,8 +4119,8 @@ "h": 17 }, "frame": { - "x": 220, - "y": 147, + "x": 176, + "y": 128, "w": 23, "h": 17 } @@ -5274,14 +4140,14 @@ "h": 17 }, "frame": { - "x": 243, - "y": 147, + "x": 199, + "y": 130, "w": 23, "h": 17 } }, { - "filename": "douse_drive", + "filename": "fairy_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -5289,16 +4155,184 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 222, + "y": 129, + "w": 22, + "h": 22 + } + }, + { + "filename": "fighting_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 244, + "y": 130, + "w": 22, + "h": 22 + } + }, + { + "filename": "fire_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { "x": 266, - "y": 147, - "w": 23, - "h": 17 + "y": 130, + "w": 22, + "h": 22 + } + }, + { + "filename": "flying_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 288, + "y": 131, + "w": 22, + "h": 22 + } + }, + { + "filename": "ganlon_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 310, + "y": 131, + "w": 22, + "h": 22 + } + }, + { + "filename": "ghost_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 332, + "y": 132, + "w": 22, + "h": 22 + } + }, + { + "filename": "grass_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 354, + "y": 132, + "w": 22, + "h": 22 + } + }, + { + "filename": "ground_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 376, + "y": 132, + "w": 22, + "h": 22 + } + }, + { + "filename": "guard_spec", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 398, + "y": 132, + "w": 22, + "h": 22 } }, { @@ -5316,14 +4350,140 @@ "h": 18 }, "frame": { - "x": 289, - "y": 147, + "x": 140, + "y": 143, "w": 23, "h": 18 } }, { - "filename": "fairy_feather", + "filename": "max_repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 143, + "y": 161, + "w": 16, + "h": 24 + } + }, + { + "filename": "apicot_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 146, + "y": 185, + "w": 19, + "h": 20 + } + }, + { + "filename": "hard_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 154, + "y": 205, + "w": 20, + "h": 22 + } + }, + { + "filename": "sachet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 18, + "h": 23 + }, + "frame": { + "x": 156, + "y": 227, + "w": 18, + "h": 23 + } + }, + { + "filename": "hyper_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 157, + "y": 250, + "w": 17, + "h": 23 + } + }, + { + "filename": "pp_max", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 158, + "y": 273, + "w": 16, + "h": 24 + } + }, + { + "filename": "ice_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -5332,19 +4492,586 @@ }, "spriteSourceSize": { "x": 5, - "y": 7, + "y": 5, "w": 22, + "h": 22 + }, + "frame": { + "x": 158, + "y": 297, + "w": 22, + "h": 22 + } + }, + { + "filename": "ice_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 159, + "y": 319, + "w": 22, + "h": 22 + } + }, + { + "filename": "magmarizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 160, + "y": 341, + "w": 22, + "h": 22 + } + }, + { + "filename": "mini_black_hole", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 160, + "y": 363, + "w": 22, + "h": 22 + } + }, + { + "filename": "moon_flute", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 162, + "y": 385, + "w": 22, + "h": 22 + } + }, + { + "filename": "normal_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 163, + "y": 407, + "w": 22, + "h": 22 + } + }, + { + "filename": "pp_up", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 159, + "y": 161, + "w": 16, + "h": 24 + } + }, + { + "filename": "candy_jar", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, "h": 20 }, "frame": { - "x": 312, + "x": 165, + "y": 185, + "w": 19, + "h": 20 + } + }, + { + "filename": "protein", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 174, + "y": 205, + "w": 16, + "h": 24 + } + }, + { + "filename": "repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 174, + "y": 229, + "w": 16, + "h": 24 + } + }, + { + "filename": "super_repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 174, + "y": 253, + "w": 16, + "h": 24 + } + }, + { + "filename": "gb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 174, + "y": 277, + "w": 20, + "h": 20 + } + }, + { + "filename": "leftovers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 15, + "h": 22 + }, + "frame": { + "x": 180, + "y": 297, + "w": 15, + "h": 22 + } + }, + { + "filename": "lock_capsule", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 181, + "y": 319, + "w": 19, + "h": 22 + } + }, + { + "filename": "metal_coat", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 182, + "y": 341, + "w": 19, + "h": 22 + } + }, + { + "filename": "poison_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 182, + "y": 363, + "w": 22, + "h": 22 + } + }, + { + "filename": "protector", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 184, + "y": 385, + "w": 22, + "h": 22 + } + }, + { + "filename": "psychic_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 185, + "y": 407, + "w": 22, + "h": 22 + } + }, + { + "filename": "relic_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 9, + "w": 17, + "h": 16 + }, + "frame": { + "x": 163, + "y": 145, + "w": 17, + "h": 16 + } + }, + { + "filename": "unknown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 175, + "y": 161, + "w": 16, + "h": 24 + } + }, + { + "filename": "abomasite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 180, + "y": 145, + "w": 16, + "h": 16 + } + }, + { + "filename": "douse_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 196, "y": 147, + "w": 23, + "h": 17 + } + }, + { + "filename": "liechi_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 191, + "y": 164, + "w": 22, + "h": 21 + } + }, + { + "filename": "malicious_armor", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 184, + "y": 185, "w": 22, "h": 20 } }, { - "filename": "sun_flute", + "filename": "zinc", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 190, + "y": 205, + "w": 16, + "h": 24 + } + }, + { + "filename": "potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 190, + "y": 229, + "w": 17, + "h": 23 + } + }, + { + "filename": "super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 190, + "y": 252, + "w": 17, + "h": 23 + } + }, + { + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 194, + "y": 275, + "w": 17, + "h": 22 + } + }, + { + "filename": "rock_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -5358,14 +5085,14 @@ "h": 22 }, "frame": { - "x": 206, - "y": 164, + "x": 195, + "y": 297, "w": 22, "h": 22 } }, { - "filename": "thick_club", + "filename": "scroll_of_darkness", "rotated": false, "trimmed": true, "sourceSize": { @@ -5379,14 +5106,14 @@ "h": 22 }, "frame": { - "x": 228, - "y": 164, + "x": 200, + "y": 319, "w": 22, "h": 22 } }, { - "filename": "thunder_stone", + "filename": "scroll_of_waters", "rotated": false, "trimmed": true, "sourceSize": { @@ -5400,14 +5127,14 @@ "h": 22 }, "frame": { - "x": 212, - "y": 186, + "x": 201, + "y": 341, "w": 22, "h": 22 } }, { - "filename": "tm_bug", + "filename": "shed_shell", "rotated": false, "trimmed": true, "sourceSize": { @@ -5421,50 +5148,8 @@ "h": 22 }, "frame": { - "x": 250, - "y": 164, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_dark", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 234, - "y": 186, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_dragon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 223, - "y": 208, + "x": 204, + "y": 363, "w": 22, "h": 22 } @@ -5484,14 +5169,14 @@ "h": 22 }, "frame": { - "x": 225, - "y": 230, + "x": 206, + "y": 385, "w": 20, "h": 22 } }, { - "filename": "tm_electric", + "filename": "starf_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -5505,978 +5190,12 @@ "h": 22 }, "frame": { - "x": 225, - "y": 252, + "x": 207, + "y": 407, "w": 22, "h": 22 } }, - { - "filename": "tm_fairy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 225, - "y": 274, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_fighting", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 225, - "y": 296, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_fire", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 225, - "y": 318, - "w": 22, - "h": 22 - } - }, - { - "filename": "soothe_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 272, - "y": 164, - "w": 17, - "h": 22 - } - }, - { - "filename": "tm_flying", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 256, - "y": 186, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_ghost", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 245, - "y": 208, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_grass", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 245, - "y": 230, - "w": 22, - "h": 22 - } - }, - { - "filename": "sweet_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 289, - "y": 165, - "w": 22, - "h": 21 - } - }, - { - "filename": "tm_ground", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 278, - "y": 186, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 267, - "y": 208, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_normal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 267, - "y": 230, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_poison", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 247, - "y": 252, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_psychic", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 247, - "y": 274, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_rock", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 247, - "y": 296, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_steel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 247, - "y": 318, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 269, - "y": 252, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 269, - "y": 274, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 269, - "y": 296, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_accuracy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 269, - "y": 318, - "w": 22, - "h": 22 - } - }, - { - "filename": "malicious_armor", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 311, - "y": 167, - "w": 22, - "h": 20 - } - }, - { - "filename": "x_attack", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 289, - "y": 208, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_defense", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 289, - "y": 230, - "w": 22, - "h": 22 - } - }, - { - "filename": "syrupy_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 300, - "y": 187, - "w": 22, - "h": 21 - } - }, - { - "filename": "x_sp_atk", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 291, - "y": 252, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_sp_def", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 291, - "y": 274, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_speed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 291, - "y": 296, - "w": 22, - "h": 22 - } - }, - { - "filename": "tart_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 291, - "y": 318, - "w": 22, - "h": 21 - } - }, - { - "filename": "dawn_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 21 - }, - "frame": { - "x": 322, - "y": 187, - "w": 20, - "h": 21 - } - }, - { - "filename": "dusk_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 311, - "y": 208, - "w": 21, - "h": 21 - } - }, - { - "filename": "poison_barb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 311, - "y": 229, - "w": 21, - "h": 21 - } - }, - { - "filename": "flying_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 21 - }, - "frame": { - "x": 313, - "y": 250, - "w": 20, - "h": 21 - } - }, - { - "filename": "shiny_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 313, - "y": 271, - "w": 21, - "h": 21 - } - }, - { - "filename": "zoom_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 313, - "y": 292, - "w": 21, - "h": 21 - } - }, - { - "filename": "tera_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 313, - "y": 313, - "w": 22, - "h": 20 - } - }, - { - "filename": "spell_tag", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 332, - "y": 208, - "w": 19, - "h": 21 - } - }, - { - "filename": "candy_jar", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 332, - "y": 229, - "w": 19, - "h": 20 - } - }, - { - "filename": "gb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 333, - "y": 249, - "w": 20, - "h": 20 - } - }, - { - "filename": "hard_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 334, - "y": 269, - "w": 19, - "h": 20 - } - }, - { - "filename": "magnet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 334, - "y": 289, - "w": 20, - "h": 20 - } - }, - { - "filename": "mb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 335, - "y": 309, - "w": 20, - "h": 20 - } - }, - { - "filename": "golden_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 333, - "y": 167, - "w": 17, - "h": 20 - } - }, - { - "filename": "lucky_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 334, - "y": 147, - "w": 17, - "h": 20 - } - }, - { - "filename": "masterpiece_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 336, - "y": 129, - "w": 21, - "h": 18 - } - }, - { - "filename": "pb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 342, - "y": 187, - "w": 20, - "h": 20 - } - }, - { - "filename": "pb_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 350, - "y": 167, - "w": 20, - "h": 20 - } - }, - { - "filename": "pb_silver", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 351, - "y": 147, - "w": 20, - "h": 20 - } - }, { "filename": "shock_drive", "rotated": false, @@ -6492,8 +5211,8 @@ "h": 17 }, "frame": { - "x": 357, - "y": 130, + "x": 219, + "y": 151, "w": 23, "h": 17 } @@ -6513,12 +5232,201 @@ "h": 17 }, "frame": { - "x": 380, - "y": 130, + "x": 242, + "y": 152, "w": 23, "h": 17 } }, + { + "filename": "steel_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 265, + "y": 152, + "w": 22, + "h": 22 + } + }, + { + "filename": "sun_flute", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 287, + "y": 153, + "w": 22, + "h": 22 + } + }, + { + "filename": "thick_club", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 309, + "y": 153, + "w": 22, + "h": 22 + } + }, + { + "filename": "sweet_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 331, + "y": 154, + "w": 22, + "h": 21 + } + }, + { + "filename": "syrupy_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 353, + "y": 154, + "w": 22, + "h": 21 + } + }, + { + "filename": "tart_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 375, + "y": 154, + "w": 22, + "h": 21 + } + }, + { + "filename": "thunder_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 397, + "y": 154, + "w": 22, + "h": 22 + } + }, + { + "filename": "tera_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 213, + "y": 168, + "w": 22, + "h": 20 + } + }, + { + "filename": "tm_bug", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 206, + "y": 188, + "w": 22, + "h": 22 + } + }, { "filename": "upgrade", "rotated": false, @@ -6534,12 +5442,663 @@ "h": 19 }, "frame": { - "x": 371, - "y": 147, + "x": 206, + "y": 210, "w": 22, "h": 19 } }, + { + "filename": "tm_dark", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 207, + "y": 229, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_dragon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 207, + "y": 251, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_electric", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 211, + "y": 273, + "w": 22, + "h": 22 + } + }, + { + "filename": "soothe_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 217, + "y": 295, + "w": 17, + "h": 22 + } + }, + { + "filename": "tm_fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 235, + "y": 169, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_fighting", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 228, + "y": 191, + "w": 22, + "h": 22 + } + }, + { + "filename": "absolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 228, + "y": 213, + "w": 16, + "h": 16 + } + }, + { + "filename": "tm_fire", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 229, + "y": 229, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_flying", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 229, + "y": 251, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_ghost", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 233, + "y": 273, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_grass", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 234, + "y": 295, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_ground", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 222, + "y": 317, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_ice", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 339, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_normal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 226, + "y": 361, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_poison", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 226, + "y": 383, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_psychic", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 229, + "y": 405, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_rock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 244, + "y": 317, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_steel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 245, + "y": 339, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 248, + "y": 361, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 248, + "y": 383, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 251, + "y": 405, + "w": 22, + "h": 22 + } + }, + { + "filename": "eviolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 419, + "y": 154, + "w": 15, + "h": 15 + } + }, + { + "filename": "prism_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 419, + "y": 169, + "w": 15, + "h": 15 + } + }, + { + "filename": "x_accuracy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 257, + "y": 174, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_attack", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 279, + "y": 175, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_defense", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 301, + "y": 175, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_sp_atk", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 323, + "y": 175, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_sp_def", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 345, + "y": 175, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_speed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 367, + "y": 175, + "w": 22, + "h": 22 + } + }, + { + "filename": "dusk_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 389, + "y": 176, + "w": 21, + "h": 21 + } + }, + { + "filename": "masterpiece_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 250, + "y": 196, + "w": 21, + "h": 18 + } + }, { "filename": "metal_alloy", "rotated": false, @@ -6555,14 +6114,98 @@ "h": 19 }, "frame": { - "x": 403, - "y": 130, + "x": 271, + "y": 197, "w": 21, "h": 19 } }, { - "filename": "razor_fang", + "filename": "poison_barb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 292, + "y": 197, + "w": 21, + "h": 21 + } + }, + { + "filename": "shiny_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 313, + "y": 197, + "w": 21, + "h": 21 + } + }, + { + "filename": "zoom_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 334, + "y": 197, + "w": 21, + "h": 21 + } + }, + { + "filename": "dawn_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 21 + }, + "frame": { + "x": 355, + "y": 197, + "w": 20, + "h": 21 + } + }, + { + "filename": "flying_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -6571,19 +6214,40 @@ }, "spriteSourceSize": { "x": 7, - "y": 6, - "w": 18, - "h": 20 + "y": 5, + "w": 20, + "h": 21 }, "frame": { - "x": 351, - "y": 207, - "w": 18, - "h": 20 + "x": 375, + "y": 197, + "w": 20, + "h": 21 } }, { - "filename": "rb", + "filename": "quick_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 395, + "y": 197, + "w": 19, + "h": 21 + } + }, + { + "filename": "magnet", "rotated": false, "trimmed": true, "sourceSize": { @@ -6597,35 +6261,14 @@ "h": 20 }, "frame": { - "x": 351, - "y": 227, + "x": 414, + "y": 184, "w": 20, "h": 20 } }, { - "filename": "smooth_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 353, - "y": 247, - "w": 20, - "h": 20 - } - }, - { - "filename": "strange_ball", + "filename": "mb", "rotated": false, "trimmed": true, "sourceSize": { @@ -6639,14 +6282,14 @@ "h": 20 }, "frame": { - "x": 353, - "y": 267, + "x": 414, + "y": 204, "w": 20, "h": 20 } }, { - "filename": "ub", + "filename": "pb", "rotated": false, "trimmed": true, "sourceSize": { @@ -6660,8 +6303,50 @@ "h": 20 }, "frame": { - "x": 354, - "y": 287, + "x": 251, + "y": 214, + "w": 20, + "h": 20 + } + }, + { + "filename": "old_gateau", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 21, + "h": 18 + }, + "frame": { + "x": 271, + "y": 216, + "w": 21, + "h": 18 + } + }, + { + "filename": "pb_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 251, + "y": 234, "w": 20, "h": 20 } @@ -6681,14 +6366,98 @@ "h": 19 }, "frame": { - "x": 355, - "y": 307, + "x": 251, + "y": 254, "w": 20, "h": 19 } }, { - "filename": "old_gateau", + "filename": "pb_silver", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 271, + "y": 234, + "w": 20, + "h": 20 + } + }, + { + "filename": "power_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 271, + "y": 254, + "w": 20, + "h": 19 + } + }, + { + "filename": "rb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 255, + "y": 273, + "w": 20, + "h": 20 + } + }, + { + "filename": "spell_tag", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 256, + "y": 293, + "w": 19, + "h": 21 + } + }, + { + "filename": "sharp_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -6702,14 +6471,14 @@ "h": 18 }, "frame": { - "x": 393, - "y": 149, + "x": 292, + "y": 218, "w": 21, "h": 18 } }, { - "filename": "oval_stone", + "filename": "smooth_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -6718,14 +6487,182 @@ }, "spriteSourceSize": { "x": 7, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 291, + "y": 236, + "w": 20, + "h": 20 + } + }, + { + "filename": "unremarkable_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, "y": 7, - "w": 18, + "w": 21, + "h": 18 + }, + "frame": { + "x": 313, + "y": 218, + "w": 21, + "h": 18 + } + }, + { + "filename": "strange_ball", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 311, + "y": 236, + "w": 20, + "h": 20 + } + }, + { + "filename": "razor_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, "h": 19 }, "frame": { - "x": 414, - "y": 149, - "w": 18, + "x": 334, + "y": 218, + "w": 20, + "h": 19 + } + }, + { + "filename": "ub", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 354, + "y": 218, + "w": 20, + "h": 20 + } + }, + { + "filename": "hard_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 374, + "y": 218, + "w": 19, + "h": 20 + } + }, + { + "filename": "white_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 393, + "y": 218, + "w": 20, + "h": 19 + } + }, + { + "filename": "everstone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 17 + }, + "frame": { + "x": 291, + "y": 256, + "w": 20, + "h": 17 + } + }, + { + "filename": "big_mushroom", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 19 + }, + "frame": { + "x": 275, + "y": 273, + "w": 19, "h": 19 } }, @@ -6744,14 +6681,14 @@ "h": 19 }, "frame": { - "x": 362, - "y": 187, + "x": 275, + "y": 292, "w": 19, "h": 19 } }, { - "filename": "power_herb", + "filename": "golden_egg", "rotated": false, "trimmed": true, "sourceSize": { @@ -6759,20 +6696,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 + "x": 7, + "y": 6, + "w": 17, + "h": 20 }, "frame": { - "x": 369, - "y": 206, - "w": 20, - "h": 19 + "x": 294, + "y": 273, + "w": 17, + "h": 20 } }, { - "filename": "razor_claw", + "filename": "razor_fang", "rotated": false, "trimmed": true, "sourceSize": { @@ -6780,20 +6717,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 + "x": 7, + "y": 6, + "w": 18, + "h": 20 }, "frame": { - "x": 371, - "y": 225, - "w": 20, - "h": 19 + "x": 311, + "y": 256, + "w": 18, + "h": 20 } }, { - "filename": "white_herb", + "filename": "baton", "rotated": false, "trimmed": true, "sourceSize": { @@ -6801,57 +6738,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, + "x": 7, "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 373, - "y": 244, - "w": 20, - "h": 19 - } - }, - { - "filename": "sharp_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 21, + "w": 18, "h": 18 }, "frame": { - "x": 373, - "y": 263, - "w": 21, - "h": 18 - } - }, - { - "filename": "unremarkable_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 374, - "y": 281, - "w": 21, + "x": 294, + "y": 293, + "w": 18, "h": 18 } }, @@ -6870,33 +6765,12 @@ "h": 18 }, "frame": { - "x": 375, - "y": 299, + "x": 331, + "y": 237, "w": 20, "h": 18 } }, - { - "filename": "everstone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 17 - }, - "frame": { - "x": 375, - "y": 317, - "w": 20, - "h": 17 - } - }, { "filename": "wl_antidote", "rotated": false, @@ -6912,8 +6786,8 @@ "h": 18 }, "frame": { - "x": 355, - "y": 326, + "x": 351, + "y": 238, "w": 20, "h": 18 } @@ -6933,12 +6807,54 @@ "h": 18 }, "frame": { - "x": 335, - "y": 329, + "x": 371, + "y": 238, "w": 20, "h": 18 } }, + { + "filename": "aerodactylite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 311, + "y": 276, + "w": 16, + "h": 16 + } + }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 312, + "y": 292, + "w": 17, + "h": 20 + } + }, { "filename": "wl_burn_heal", "rotated": false, @@ -6954,8 +6870,8 @@ "h": 18 }, "frame": { - "x": 375, - "y": 334, + "x": 413, + "y": 224, "w": 20, "h": 18 } @@ -6975,8 +6891,8 @@ "h": 18 }, "frame": { - "x": 355, - "y": 344, + "x": 393, + "y": 237, "w": 20, "h": 18 } @@ -6996,12 +6912,33 @@ "h": 18 }, "frame": { - "x": 375, - "y": 352, + "x": 413, + "y": 242, "w": 20, "h": 18 } }, + { + "filename": "oval_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 19 + }, + "frame": { + "x": 266, + "y": 314, + "w": 18, + "h": 19 + } + }, { "filename": "wl_elixir", "rotated": false, @@ -7017,12 +6954,33 @@ "h": 18 }, "frame": { - "x": 313, - "y": 333, + "x": 284, + "y": 311, "w": 20, "h": 18 } }, + { + "filename": "candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 11, + "w": 18, + "h": 18 + }, + "frame": { + "x": 267, + "y": 333, + "w": 18, + "h": 18 + } + }, { "filename": "wl_ether", "rotated": false, @@ -7038,12 +6996,96 @@ "h": 18 }, "frame": { - "x": 333, - "y": 347, + "x": 285, + "y": 329, "w": 20, "h": 18 } }, + { + "filename": "aggronite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 304, + "y": 312, + "w": 16, + "h": 16 + } + }, + { + "filename": "dark_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 305, + "y": 328, + "w": 18, + "h": 18 + } + }, + { + "filename": "alakazite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 320, + "y": 312, + "w": 16, + "h": 16 + } + }, + { + "filename": "flame_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 323, + "y": 328, + "w": 18, + "h": 18 + } + }, { "filename": "wl_full_heal", "rotated": false, @@ -7059,8 +7101,8 @@ "h": 18 }, "frame": { - "x": 371, - "y": 166, + "x": 285, + "y": 347, "w": 20, "h": 18 } @@ -7080,12 +7122,33 @@ "h": 18 }, "frame": { - "x": 391, - "y": 167, + "x": 305, + "y": 346, "w": 20, "h": 18 } }, + { + "filename": "light_ball", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 325, + "y": 346, + "w": 18, + "h": 18 + } + }, { "filename": "wl_guard_spec", "rotated": false, @@ -7101,8 +7164,8 @@ "h": 18 }, "frame": { - "x": 411, - "y": 168, + "x": 270, + "y": 365, "w": 20, "h": 18 } @@ -7122,14 +7185,14 @@ "h": 18 }, "frame": { - "x": 381, - "y": 185, + "x": 270, + "y": 383, "w": 20, "h": 18 } }, { - "filename": "baton", + "filename": "light_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -7143,71 +7206,8 @@ "h": 18 }, "frame": { - "x": 389, - "y": 203, - "w": 18, - "h": 18 - } - }, - { - "filename": "candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 11, - "w": 18, - "h": 18 - }, - "frame": { - "x": 391, - "y": 221, - "w": 18, - "h": 18 - } - }, - { - "filename": "dark_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 393, - "y": 239, - "w": 18, - "h": 18 - } - }, - { - "filename": "flame_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 394, - "y": 257, + "x": 290, + "y": 365, "w": 18, "h": 18 } @@ -7227,12 +7227,33 @@ "h": 18 }, "frame": { - "x": 412, - "y": 186, + "x": 308, + "y": 364, "w": 20, "h": 18 } }, + { + "filename": "toxic_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 290, + "y": 383, + "w": 18, + "h": 18 + } + }, { "filename": "wl_item_drop", "rotated": false, @@ -7248,8 +7269,8 @@ "h": 18 }, "frame": { - "x": 412, - "y": 204, + "x": 308, + "y": 382, "w": 20, "h": 18 } @@ -7269,12 +7290,33 @@ "h": 18 }, "frame": { - "x": 412, - "y": 222, + "x": 273, + "y": 401, "w": 20, "h": 18 } }, + { + "filename": "mystery_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 18 + }, + "frame": { + "x": 328, + "y": 364, + "w": 16, + "h": 18 + } + }, { "filename": "wl_max_elixir", "rotated": false, @@ -7290,8 +7332,8 @@ "h": 18 }, "frame": { - "x": 412, - "y": 240, + "x": 328, + "y": 382, "w": 20, "h": 18 } @@ -7311,8 +7353,92 @@ "h": 18 }, "frame": { - "x": 412, - "y": 258, + "x": 293, + "y": 401, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_max_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 313, + "y": 400, + "w": 20, + "h": 18 + } + }, + { + "filename": "altarianite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 313, + "y": 418, + "w": 16, + "h": 16 + } + }, + { + "filename": "ampharosite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 329, + "y": 418, + "w": 16, + "h": 16 + } + }, + { + "filename": "wl_max_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 333, + "y": 400, "w": 20, "h": 18 } @@ -7332,117 +7458,12 @@ "h": 16 }, "frame": { - "x": 395, - "y": 275, + "x": 345, + "y": 418, "w": 16, "h": 16 } }, - { - "filename": "light_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 395, - "y": 291, - "w": 18, - "h": 18 - } - }, - { - "filename": "light_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 395, - "y": 309, - "w": 18, - "h": 18 - } - }, - { - "filename": "toxic_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 395, - "y": 327, - "w": 18, - "h": 18 - } - }, - { - "filename": "wl_max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 395, - "y": 345, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 395, - "y": 363, - "w": 20, - "h": 18 - } - }, { "filename": "wl_paralyze_heal", "rotated": false, @@ -7458,8 +7479,8 @@ "h": 18 }, "frame": { - "x": 225, - "y": 340, + "x": 331, + "y": 255, "w": 20, "h": 18 } @@ -7479,8 +7500,8 @@ "h": 18 }, "frame": { - "x": 245, - "y": 340, + "x": 351, + "y": 256, "w": 20, "h": 18 } @@ -7500,8 +7521,8 @@ "h": 18 }, "frame": { - "x": 265, - "y": 340, + "x": 371, + "y": 256, "w": 20, "h": 18 } @@ -7521,8 +7542,8 @@ "h": 18 }, "frame": { - "x": 227, - "y": 358, + "x": 391, + "y": 255, "w": 20, "h": 18 } @@ -7542,8 +7563,8 @@ "h": 18 }, "frame": { - "x": 247, - "y": 358, + "x": 411, + "y": 260, "w": 20, "h": 18 } @@ -7563,8 +7584,8 @@ "h": 16 }, "frame": { - "x": 267, - "y": 358, + "x": 327, + "y": 276, "w": 16, "h": 16 } @@ -7584,8 +7605,8 @@ "h": 16 }, "frame": { - "x": 237, - "y": 376, + "x": 329, + "y": 292, "w": 16, "h": 16 } @@ -7605,8 +7626,8 @@ "h": 16 }, "frame": { - "x": 237, - "y": 392, + "x": 343, + "y": 274, "w": 16, "h": 16 } @@ -7626,8 +7647,8 @@ "h": 16 }, "frame": { - "x": 237, - "y": 408, + "x": 359, + "y": 274, "w": 16, "h": 16 } @@ -7647,8 +7668,8 @@ "h": 16 }, "frame": { - "x": 253, - "y": 376, + "x": 375, + "y": 274, "w": 16, "h": 16 } @@ -7668,8 +7689,8 @@ "h": 16 }, "frame": { - "x": 253, - "y": 392, + "x": 391, + "y": 273, "w": 16, "h": 16 } @@ -7689,8 +7710,8 @@ "h": 16 }, "frame": { - "x": 253, - "y": 408, + "x": 345, + "y": 290, "w": 16, "h": 16 } @@ -7710,8 +7731,8 @@ "h": 16 }, "frame": { - "x": 269, - "y": 374, + "x": 361, + "y": 290, "w": 16, "h": 16 } @@ -7731,8 +7752,8 @@ "h": 16 }, "frame": { - "x": 269, - "y": 390, + "x": 377, + "y": 290, "w": 16, "h": 16 } @@ -7752,8 +7773,8 @@ "h": 16 }, "frame": { - "x": 269, - "y": 406, + "x": 393, + "y": 289, "w": 16, "h": 16 } @@ -7773,8 +7794,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 340, + "x": 336, + "y": 308, "w": 16, "h": 16 } @@ -7794,8 +7815,8 @@ "h": 16 }, "frame": { - "x": 283, - "y": 358, + "x": 352, + "y": 306, "w": 16, "h": 16 } @@ -7815,8 +7836,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 374, + "x": 368, + "y": 306, "w": 16, "h": 16 } @@ -7836,8 +7857,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 390, + "x": 384, + "y": 306, "w": 16, "h": 16 } @@ -7857,8 +7878,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 406, + "x": 400, + "y": 305, "w": 16, "h": 16 } @@ -7878,8 +7899,8 @@ "h": 16 }, "frame": { - "x": 353, - "y": 362, + "x": 341, + "y": 324, "w": 16, "h": 16 } @@ -7899,8 +7920,8 @@ "h": 16 }, "frame": { - "x": 369, - "y": 370, + "x": 357, + "y": 322, "w": 16, "h": 16 } @@ -7920,8 +7941,8 @@ "h": 16 }, "frame": { - "x": 413, - "y": 276, + "x": 373, + "y": 322, "w": 16, "h": 16 } @@ -7941,8 +7962,8 @@ "h": 16 }, "frame": { - "x": 413, - "y": 292, + "x": 389, + "y": 322, "w": 16, "h": 16 } @@ -7962,8 +7983,8 @@ "h": 16 }, "frame": { - "x": 413, - "y": 308, + "x": 405, + "y": 321, "w": 16, "h": 16 } @@ -7983,8 +8004,8 @@ "h": 16 }, "frame": { - "x": 413, - "y": 324, + "x": 343, + "y": 340, "w": 16, "h": 16 } @@ -8004,8 +8025,8 @@ "h": 16 }, "frame": { - "x": 415, - "y": 340, + "x": 359, + "y": 338, "w": 16, "h": 16 } @@ -8025,8 +8046,8 @@ "h": 16 }, "frame": { - "x": 415, - "y": 356, + "x": 375, + "y": 338, "w": 16, "h": 16 } @@ -8046,8 +8067,8 @@ "h": 16 }, "frame": { - "x": 415, - "y": 372, + "x": 391, + "y": 338, "w": 16, "h": 16 } @@ -8067,8 +8088,8 @@ "h": 16 }, "frame": { - "x": 299, - "y": 356, + "x": 407, + "y": 337, "w": 16, "h": 16 } @@ -8088,8 +8109,8 @@ "h": 16 }, "frame": { - "x": 301, - "y": 372, + "x": 344, + "y": 356, "w": 16, "h": 16 } @@ -8109,8 +8130,8 @@ "h": 16 }, "frame": { - "x": 301, - "y": 388, + "x": 360, + "y": 354, "w": 16, "h": 16 } @@ -8130,8 +8151,8 @@ "h": 16 }, "frame": { - "x": 301, - "y": 404, + "x": 376, + "y": 354, "w": 16, "h": 16 } @@ -8151,8 +8172,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 351, + "x": 392, + "y": 354, "w": 16, "h": 16 } @@ -8172,8 +8193,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 367, + "x": 408, + "y": 353, "w": 16, "h": 16 } @@ -8193,8 +8214,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 365, + "x": 409, + "y": 278, "w": 16, "h": 16 } @@ -8214,8 +8235,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 383, + "x": 348, + "y": 372, "w": 16, "h": 16 } @@ -8235,8 +8256,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 381, + "x": 364, + "y": 370, "w": 16, "h": 16 } @@ -8256,8 +8277,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 399, + "x": 380, + "y": 370, "w": 16, "h": 16 } @@ -8277,8 +8298,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 397, + "x": 396, + "y": 370, "w": 16, "h": 16 } @@ -8298,8 +8319,8 @@ "h": 16 }, "frame": { - "x": 349, - "y": 378, + "x": 412, + "y": 369, "w": 16, "h": 16 } @@ -8319,8 +8340,8 @@ "h": 16 }, "frame": { - "x": 349, - "y": 394, + "x": 364, + "y": 386, "w": 16, "h": 16 } @@ -8340,7 +8361,7 @@ "h": 16 }, "frame": { - "x": 365, + "x": 380, "y": 386, "w": 16, "h": 16 @@ -8361,8 +8382,8 @@ "h": 16 }, "frame": { - "x": 365, - "y": 402, + "x": 396, + "y": 386, "w": 16, "h": 16 } @@ -8382,8 +8403,8 @@ "h": 16 }, "frame": { - "x": 349, - "y": 410, + "x": 412, + "y": 385, "w": 16, "h": 16 } @@ -8403,8 +8424,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 413, + "x": 353, + "y": 402, "w": 16, "h": 16 } @@ -8424,8 +8445,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 415, + "x": 361, + "y": 418, "w": 16, "h": 16 } @@ -8445,8 +8466,8 @@ "h": 16 }, "frame": { - "x": 381, - "y": 386, + "x": 369, + "y": 402, "w": 16, "h": 16 } @@ -8457,6 +8478,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:c228145ca625236e53edc95aac265d56:86524cdf0e3043482141d77259bc4d47:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:9b6fc7b241128f4f61686fe287e090cd:46e9caafcc91f3c30ff85a6e8d3f5227:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index eb9878a5bfc..f0f11e9443e 100644 Binary files a/public/images/items.png and b/public/images/items.png differ diff --git a/public/images/items/ability_capsule.png b/public/images/items/ability_capsule.png index ee8aec6b346..92171d5296a 100644 Binary files a/public/images/items/ability_capsule.png and b/public/images/items/ability_capsule.png differ diff --git a/public/images/items/ability_charm.png b/public/images/items/ability_charm.png index 2e9e0368667..c8a168e315d 100644 Binary files a/public/images/items/ability_charm.png and b/public/images/items/ability_charm.png differ diff --git a/public/images/items/abomasite.png b/public/images/items/abomasite.png index 0758786bb0f..eae8422bdaf 100644 Binary files a/public/images/items/abomasite.png and b/public/images/items/abomasite.png differ diff --git a/public/images/items/absolite.png b/public/images/items/absolite.png index f7fe8b7ac12..8043b2969bb 100644 Binary files a/public/images/items/absolite.png and b/public/images/items/absolite.png differ diff --git a/public/images/items/adamant_crystal.png b/public/images/items/adamant_crystal.png index eb35af1540e..f9aea1fda30 100644 Binary files a/public/images/items/adamant_crystal.png and b/public/images/items/adamant_crystal.png differ diff --git a/public/images/items/aerodactylite.png b/public/images/items/aerodactylite.png index 4feb04f3702..8065e19de5e 100644 Binary files a/public/images/items/aerodactylite.png and b/public/images/items/aerodactylite.png differ diff --git a/public/images/items/aggronite.png b/public/images/items/aggronite.png index 8feb64633ec..a7c1f990ffa 100644 Binary files a/public/images/items/aggronite.png and b/public/images/items/aggronite.png differ diff --git a/public/images/items/alakazite.png b/public/images/items/alakazite.png index ed6acab02ca..470987bc13b 100644 Binary files a/public/images/items/alakazite.png and b/public/images/items/alakazite.png differ diff --git a/public/images/items/altarianite.png b/public/images/items/altarianite.png index 0bb6be23819..03ef291faa5 100644 Binary files a/public/images/items/altarianite.png and b/public/images/items/altarianite.png differ diff --git a/public/images/items/ampharosite.png b/public/images/items/ampharosite.png index 3084c05c1a9..154f7ea5609 100644 Binary files a/public/images/items/ampharosite.png and b/public/images/items/ampharosite.png differ diff --git a/public/images/items/amulet_coin.png b/public/images/items/amulet_coin.png index 3cda2b2a53b..727562836c4 100644 Binary files a/public/images/items/amulet_coin.png and b/public/images/items/amulet_coin.png differ diff --git a/public/images/items/apicot_berry.png b/public/images/items/apicot_berry.png index 4719671eaf3..7681645c114 100644 Binary files a/public/images/items/apicot_berry.png and b/public/images/items/apicot_berry.png differ diff --git a/public/images/items/audinite.png b/public/images/items/audinite.png index f7c21bf8e3b..b73c11bfbf2 100644 Binary files a/public/images/items/audinite.png and b/public/images/items/audinite.png differ diff --git a/public/images/items/auspicious_armor.png b/public/images/items/auspicious_armor.png index 27c40ae0bd0..d484b01f8b8 100644 Binary files a/public/images/items/auspicious_armor.png and b/public/images/items/auspicious_armor.png differ diff --git a/public/images/items/banettite.png b/public/images/items/banettite.png index b6bcadd72fc..9361e0e3da5 100644 Binary files a/public/images/items/banettite.png and b/public/images/items/banettite.png differ diff --git a/public/images/items/baton.png b/public/images/items/baton.png index ece81f82b2f..f814d93728e 100644 Binary files a/public/images/items/baton.png and b/public/images/items/baton.png differ diff --git a/public/images/items/beedrillite.png b/public/images/items/beedrillite.png index 99e516446d7..f1eb43f5488 100644 Binary files a/public/images/items/beedrillite.png and b/public/images/items/beedrillite.png differ diff --git a/public/images/items/berry_juice.png b/public/images/items/berry_juice.png deleted file mode 100644 index 2f6272eca7c..00000000000 Binary files a/public/images/items/berry_juice.png and /dev/null differ diff --git a/public/images/items/berry_juice_bad.png b/public/images/items/berry_juice_bad.png new file mode 100644 index 00000000000..3b0805fff81 Binary files /dev/null and b/public/images/items/berry_juice_bad.png differ diff --git a/public/images/items/berry_juice_good.png b/public/images/items/berry_juice_good.png new file mode 100644 index 00000000000..b4d9907576f Binary files /dev/null and b/public/images/items/berry_juice_good.png differ diff --git a/public/images/items/berry_pot.png b/public/images/items/berry_pot.png index 3cb9b90dc53..d80ac4a4cc0 100644 Binary files a/public/images/items/berry_pot.png and b/public/images/items/berry_pot.png differ diff --git a/public/images/items/berry_pouch.png b/public/images/items/berry_pouch.png index d14d71358a9..6fd84402bca 100644 Binary files a/public/images/items/berry_pouch.png and b/public/images/items/berry_pouch.png differ diff --git a/public/images/items/big_mushroom.png b/public/images/items/big_mushroom.png index eb203a7374c..37b53cdb6bb 100644 Binary files a/public/images/items/big_mushroom.png and b/public/images/items/big_mushroom.png differ diff --git a/public/images/items/big_nugget.png b/public/images/items/big_nugget.png index e8839daa0b6..21f83672a2e 100644 Binary files a/public/images/items/big_nugget.png and b/public/images/items/big_nugget.png differ diff --git a/public/images/items/big_root.png b/public/images/items/big_root.png index 24b863ee238..8f32b006495 100644 Binary files a/public/images/items/big_root.png and b/public/images/items/big_root.png differ diff --git a/public/images/items/binding_band.png b/public/images/items/binding_band.png index b33256b7b8c..ee91f14a36c 100644 Binary files a/public/images/items/binding_band.png and b/public/images/items/binding_band.png differ diff --git a/public/images/items/black_augurite.png b/public/images/items/black_augurite.png index b34127e2942..d448c7b888c 100644 Binary files a/public/images/items/black_augurite.png and b/public/images/items/black_augurite.png differ diff --git a/public/images/items/black_belt.png b/public/images/items/black_belt.png index 9dde3ff83cd..4d303e2f645 100644 Binary files a/public/images/items/black_belt.png and b/public/images/items/black_belt.png differ diff --git a/public/images/items/black_glasses.png b/public/images/items/black_glasses.png index 4b18d828964..fd512c9763d 100644 Binary files a/public/images/items/black_glasses.png and b/public/images/items/black_glasses.png differ diff --git a/public/images/items/black_sludge.png b/public/images/items/black_sludge.png index fe308586dd3..04a1cabd9f5 100644 Binary files a/public/images/items/black_sludge.png and b/public/images/items/black_sludge.png differ diff --git a/public/images/items/blank_plate.png b/public/images/items/blank_plate.png index c08d25aa3c3..aa881d228f2 100644 Binary files a/public/images/items/blank_plate.png and b/public/images/items/blank_plate.png differ diff --git a/public/images/items/blastoisinite.png b/public/images/items/blastoisinite.png index ea2ddef0640..9d4f29cce8f 100644 Binary files a/public/images/items/blastoisinite.png and b/public/images/items/blastoisinite.png differ diff --git a/public/images/items/blazikenite.png b/public/images/items/blazikenite.png index 9b6e9e59212..4269cb18710 100644 Binary files a/public/images/items/blazikenite.png and b/public/images/items/blazikenite.png differ diff --git a/public/images/items/blue_orb.png b/public/images/items/blue_orb.png index 34c19c8aea8..f523a868c30 100644 Binary files a/public/images/items/blue_orb.png and b/public/images/items/blue_orb.png differ diff --git a/public/images/items/blunder_policy.png b/public/images/items/blunder_policy.png index 8d5b11cb3f6..8104d00c40c 100644 Binary files a/public/images/items/blunder_policy.png and b/public/images/items/blunder_policy.png differ diff --git a/public/images/items/bronze_ribbon.png b/public/images/items/bronze_ribbon.png index cab218e09f5..13eeb7254ca 100644 Binary files a/public/images/items/bronze_ribbon.png and b/public/images/items/bronze_ribbon.png differ diff --git a/public/images/items/bug_memory.png b/public/images/items/bug_memory.png index 004b499ba7a..a71637ceb5a 100644 Binary files a/public/images/items/bug_memory.png and b/public/images/items/bug_memory.png differ diff --git a/public/images/items/bug_tera_shard.png b/public/images/items/bug_tera_shard.png index 5711fe193bd..bc722e4698f 100644 Binary files a/public/images/items/bug_tera_shard.png and b/public/images/items/bug_tera_shard.png differ diff --git a/public/images/items/burn_drive.png b/public/images/items/burn_drive.png index 02ee18c02bd..b45ffc76d7e 100644 Binary files a/public/images/items/burn_drive.png and b/public/images/items/burn_drive.png differ diff --git a/public/images/items/calcium.png b/public/images/items/calcium.png index 4c42228d724..0ec70b68fec 100644 Binary files a/public/images/items/calcium.png and b/public/images/items/calcium.png differ diff --git a/public/images/items/cameruptite.png b/public/images/items/cameruptite.png index 9eda37e14e1..1653ff8f33a 100644 Binary files a/public/images/items/cameruptite.png and b/public/images/items/cameruptite.png differ diff --git a/public/images/items/candy.png b/public/images/items/candy.png index 81cf5e19ee2..fcbdaa1b13d 100644 Binary files a/public/images/items/candy.png and b/public/images/items/candy.png differ diff --git a/public/images/items/candy_jar.png b/public/images/items/candy_jar.png index 0338b64a86d..ddd7922bdce 100644 Binary files a/public/images/items/candy_jar.png and b/public/images/items/candy_jar.png differ diff --git a/public/images/items/candy_overlay.png b/public/images/items/candy_overlay.png index a1cb428cdee..567df8b3606 100644 Binary files a/public/images/items/candy_overlay.png and b/public/images/items/candy_overlay.png differ diff --git a/public/images/items/carbos.png b/public/images/items/carbos.png index 7dd09f2ec12..c824ddd5b82 100644 Binary files a/public/images/items/carbos.png and b/public/images/items/carbos.png differ diff --git a/public/images/items/catching_charm.png b/public/images/items/catching_charm.png index 57545622131..4699b8c3d85 100644 Binary files a/public/images/items/catching_charm.png and b/public/images/items/catching_charm.png differ diff --git a/public/images/items/charcoal.png b/public/images/items/charcoal.png index e10f8f20fd6..ce456f7b1fc 100644 Binary files a/public/images/items/charcoal.png and b/public/images/items/charcoal.png differ diff --git a/public/images/items/charizardite_x.png b/public/images/items/charizardite_x.png index d238a77a9e2..37838c5919c 100644 Binary files a/public/images/items/charizardite_x.png and b/public/images/items/charizardite_x.png differ diff --git a/public/images/items/charizardite_y.png b/public/images/items/charizardite_y.png index 5a3ea59d091..ba4672dd3f3 100644 Binary files a/public/images/items/charizardite_y.png and b/public/images/items/charizardite_y.png differ diff --git a/public/images/items/chill_drive.png b/public/images/items/chill_drive.png index fbf7462479a..6126b295328 100644 Binary files a/public/images/items/chill_drive.png and b/public/images/items/chill_drive.png differ diff --git a/public/images/items/chipped_pot.png b/public/images/items/chipped_pot.png index 969c3dc3c16..c78d7581ea8 100644 Binary files a/public/images/items/chipped_pot.png and b/public/images/items/chipped_pot.png differ diff --git a/public/images/items/choice_scarf.png b/public/images/items/choice_scarf.png index 2ddf7d3be16..e4be436ca73 100644 Binary files a/public/images/items/choice_scarf.png and b/public/images/items/choice_scarf.png differ diff --git a/public/images/items/choice_specs.png b/public/images/items/choice_specs.png index 09b58f64381..24163f48bf9 100644 Binary files a/public/images/items/choice_specs.png and b/public/images/items/choice_specs.png differ diff --git a/public/images/items/clefairy_doll.png b/public/images/items/clefairy_doll.png index 3b54c83df47..cd618cd80ab 100644 Binary files a/public/images/items/clefairy_doll.png and b/public/images/items/clefairy_doll.png differ diff --git a/public/images/items/coin_case.png b/public/images/items/coin_case.png index 3c17c2b13f8..733a0c95e76 100644 Binary files a/public/images/items/coin_case.png and b/public/images/items/coin_case.png differ diff --git a/public/images/items/cornerstone_mask.png b/public/images/items/cornerstone_mask.png index 205bdef9805..e92be7ea813 100644 Binary files a/public/images/items/cornerstone_mask.png and b/public/images/items/cornerstone_mask.png differ diff --git a/public/images/items/coupon.png b/public/images/items/coupon.png index b08fa65fecc..81cfa676705 100644 Binary files a/public/images/items/coupon.png and b/public/images/items/coupon.png differ diff --git a/public/images/items/cracked_pot.png b/public/images/items/cracked_pot.png index 4afc2caf2c8..36a73cd0884 100644 Binary files a/public/images/items/cracked_pot.png and b/public/images/items/cracked_pot.png differ diff --git a/public/images/items/dark_memory.png b/public/images/items/dark_memory.png index e1e503bd036..0d0ccb42664 100644 Binary files a/public/images/items/dark_memory.png and b/public/images/items/dark_memory.png differ diff --git a/public/images/items/dark_stone.png b/public/images/items/dark_stone.png index c28a93ecabc..f97da5f3394 100644 Binary files a/public/images/items/dark_stone.png and b/public/images/items/dark_stone.png differ diff --git a/public/images/items/dark_tera_shard.png b/public/images/items/dark_tera_shard.png index ca24664b74e..2472fc812f9 100644 Binary files a/public/images/items/dark_tera_shard.png and b/public/images/items/dark_tera_shard.png differ diff --git a/public/images/items/dawn_stone.png b/public/images/items/dawn_stone.png index b29d2016a56..91b50dca7b2 100644 Binary files a/public/images/items/dawn_stone.png and b/public/images/items/dawn_stone.png differ diff --git a/public/images/items/deep_sea_scale.png b/public/images/items/deep_sea_scale.png index cf40e652319..6027c618afc 100644 Binary files a/public/images/items/deep_sea_scale.png and b/public/images/items/deep_sea_scale.png differ diff --git a/public/images/items/deep_sea_tooth.png b/public/images/items/deep_sea_tooth.png index 2cd1980aeb4..0a7a74f0c9a 100644 Binary files a/public/images/items/deep_sea_tooth.png and b/public/images/items/deep_sea_tooth.png differ diff --git a/public/images/items/diancite.png b/public/images/items/diancite.png index 293d327524e..fbb0267fb59 100644 Binary files a/public/images/items/diancite.png and b/public/images/items/diancite.png differ diff --git a/public/images/items/dire_hit.png b/public/images/items/dire_hit.png index 0c060710ff2..49011c7f86d 100644 Binary files a/public/images/items/dire_hit.png and b/public/images/items/dire_hit.png differ diff --git a/public/images/items/dna_splicers.png b/public/images/items/dna_splicers.png index 51c1524076e..918c156ba53 100644 Binary files a/public/images/items/dna_splicers.png and b/public/images/items/dna_splicers.png differ diff --git a/public/images/items/douse_drive.png b/public/images/items/douse_drive.png index fb8e7623184..6ecf6125fcf 100644 Binary files a/public/images/items/douse_drive.png and b/public/images/items/douse_drive.png differ diff --git a/public/images/items/draco_plate.png b/public/images/items/draco_plate.png index b4702aadba3..0dfae0ef0e1 100644 Binary files a/public/images/items/draco_plate.png and b/public/images/items/draco_plate.png differ diff --git a/public/images/items/dragon_fang.png b/public/images/items/dragon_fang.png index 4a9904e0ef3..b5c028d1e92 100644 Binary files a/public/images/items/dragon_fang.png and b/public/images/items/dragon_fang.png differ diff --git a/public/images/items/dragon_memory.png b/public/images/items/dragon_memory.png index 01a14cadf3e..9a383fc5f0a 100644 Binary files a/public/images/items/dragon_memory.png and b/public/images/items/dragon_memory.png differ diff --git a/public/images/items/dragon_scale.png b/public/images/items/dragon_scale.png index 9cec39b22f6..6b82ce88b01 100644 Binary files a/public/images/items/dragon_scale.png and b/public/images/items/dragon_scale.png differ diff --git a/public/images/items/dragon_tera_shard.png b/public/images/items/dragon_tera_shard.png index 51f20d9f5cb..c140671fbfc 100644 Binary files a/public/images/items/dragon_tera_shard.png and b/public/images/items/dragon_tera_shard.png differ diff --git a/public/images/items/dread_plate.png b/public/images/items/dread_plate.png index 6537320bc0a..ce40b216eea 100644 Binary files a/public/images/items/dread_plate.png and b/public/images/items/dread_plate.png differ diff --git a/public/images/items/dubious_disc.png b/public/images/items/dubious_disc.png index 122958f9550..eb907164f05 100644 Binary files a/public/images/items/dubious_disc.png and b/public/images/items/dubious_disc.png differ diff --git a/public/images/items/dusk_stone.png b/public/images/items/dusk_stone.png index e2cf73d04f3..fd4879d48fe 100644 Binary files a/public/images/items/dusk_stone.png and b/public/images/items/dusk_stone.png differ diff --git a/public/images/items/dynamax_band.png b/public/images/items/dynamax_band.png index 57b9a0caef5..df9f4460884 100644 Binary files a/public/images/items/dynamax_band.png and b/public/images/items/dynamax_band.png differ diff --git a/public/images/items/earth_plate.png b/public/images/items/earth_plate.png index 79fbbad5a84..8d298d92722 100644 Binary files a/public/images/items/earth_plate.png and b/public/images/items/earth_plate.png differ diff --git a/public/images/items/electirizer.png b/public/images/items/electirizer.png index a5589a27e42..2ef85308755 100644 Binary files a/public/images/items/electirizer.png and b/public/images/items/electirizer.png differ diff --git a/public/images/items/electric_memory.png b/public/images/items/electric_memory.png index d0595356482..2801bb7d03c 100644 Binary files a/public/images/items/electric_memory.png and b/public/images/items/electric_memory.png differ diff --git a/public/images/items/electric_tera_shard.png b/public/images/items/electric_tera_shard.png index 2195be32929..a836a5587f8 100644 Binary files a/public/images/items/electric_tera_shard.png and b/public/images/items/electric_tera_shard.png differ diff --git a/public/images/items/elixir.png b/public/images/items/elixir.png index b4bf7834e17..2d365d259af 100644 Binary files a/public/images/items/elixir.png and b/public/images/items/elixir.png differ diff --git a/public/images/items/enigma_berry.png b/public/images/items/enigma_berry.png index 1a1a20854ed..7a13e643d68 100644 Binary files a/public/images/items/enigma_berry.png and b/public/images/items/enigma_berry.png differ diff --git a/public/images/items/ether.png b/public/images/items/ether.png index 44a6e79de8e..76003bb7696 100644 Binary files a/public/images/items/ether.png and b/public/images/items/ether.png differ diff --git a/public/images/items/everstone.png b/public/images/items/everstone.png index 194f7b63baf..27c205f9132 100644 Binary files a/public/images/items/everstone.png and b/public/images/items/everstone.png differ diff --git a/public/images/items/eviolite.png b/public/images/items/eviolite.png index 8591791a5a1..30da75fd71d 100644 Binary files a/public/images/items/eviolite.png and b/public/images/items/eviolite.png differ diff --git a/public/images/items/exp_balance.png b/public/images/items/exp_balance.png index 6a03556a3a2..a00a5d599a4 100644 Binary files a/public/images/items/exp_balance.png and b/public/images/items/exp_balance.png differ diff --git a/public/images/items/exp_charm.png b/public/images/items/exp_charm.png index 2635e4d8020..1c3fe829993 100644 Binary files a/public/images/items/exp_charm.png and b/public/images/items/exp_charm.png differ diff --git a/public/images/items/exp_share.png b/public/images/items/exp_share.png index 6b866c63427..611056743e6 100644 Binary files a/public/images/items/exp_share.png and b/public/images/items/exp_share.png differ diff --git a/public/images/items/expert_belt.png b/public/images/items/expert_belt.png index 86cfd7170de..e4e903d7507 100644 Binary files a/public/images/items/expert_belt.png and b/public/images/items/expert_belt.png differ diff --git a/public/images/items/fairy_feather.png b/public/images/items/fairy_feather.png index 44923a1db94..7e087db48d5 100644 Binary files a/public/images/items/fairy_feather.png and b/public/images/items/fairy_feather.png differ diff --git a/public/images/items/fairy_memory.png b/public/images/items/fairy_memory.png index b5d0b32ff95..20d088762e6 100644 Binary files a/public/images/items/fairy_memory.png and b/public/images/items/fairy_memory.png differ diff --git a/public/images/items/fairy_tera_shard.png b/public/images/items/fairy_tera_shard.png index 36fb5e88f1c..1984581c74d 100644 Binary files a/public/images/items/fairy_tera_shard.png and b/public/images/items/fairy_tera_shard.png differ diff --git a/public/images/items/fighting_memory.png b/public/images/items/fighting_memory.png index 13789836880..58dd3ecf8a1 100644 Binary files a/public/images/items/fighting_memory.png and b/public/images/items/fighting_memory.png differ diff --git a/public/images/items/fighting_tera_shard.png b/public/images/items/fighting_tera_shard.png index 8423a41cbe8..c76f50b48e4 100644 Binary files a/public/images/items/fighting_tera_shard.png and b/public/images/items/fighting_tera_shard.png differ diff --git a/public/images/items/fire_memory.png b/public/images/items/fire_memory.png index 4f341417b6e..c066c1be42b 100644 Binary files a/public/images/items/fire_memory.png and b/public/images/items/fire_memory.png differ diff --git a/public/images/items/fire_stone.png b/public/images/items/fire_stone.png index 3db1942dfed..2f9cb737064 100644 Binary files a/public/images/items/fire_stone.png and b/public/images/items/fire_stone.png differ diff --git a/public/images/items/fire_tera_shard.png b/public/images/items/fire_tera_shard.png index 5783a5e9720..4a620eec220 100644 Binary files a/public/images/items/fire_tera_shard.png and b/public/images/items/fire_tera_shard.png differ diff --git a/public/images/items/fist_plate.png b/public/images/items/fist_plate.png index d992e4ab1c2..c3304c41e07 100644 Binary files a/public/images/items/fist_plate.png and b/public/images/items/fist_plate.png differ diff --git a/public/images/items/flame_orb.png b/public/images/items/flame_orb.png index 5132bbb6153..6c9d05fc8a0 100644 Binary files a/public/images/items/flame_orb.png and b/public/images/items/flame_orb.png differ diff --git a/public/images/items/flame_plate.png b/public/images/items/flame_plate.png index 7633773eee1..c7b7eb6bef1 100644 Binary files a/public/images/items/flame_plate.png and b/public/images/items/flame_plate.png differ diff --git a/public/images/items/flying_memory.png b/public/images/items/flying_memory.png index 8d3efbf1401..8b73abd8264 100644 Binary files a/public/images/items/flying_memory.png and b/public/images/items/flying_memory.png differ diff --git a/public/images/items/flying_tera_shard.png b/public/images/items/flying_tera_shard.png index 97d3013f70b..42e2b16c4d2 100644 Binary files a/public/images/items/flying_tera_shard.png and b/public/images/items/flying_tera_shard.png differ diff --git a/public/images/items/focus_band.png b/public/images/items/focus_band.png index 3a04ae17023..c49d74a5ae9 100644 Binary files a/public/images/items/focus_band.png and b/public/images/items/focus_band.png differ diff --git a/public/images/items/focus_sash.png b/public/images/items/focus_sash.png index 7700ceecf6b..17dc0fd8fe6 100644 Binary files a/public/images/items/focus_sash.png and b/public/images/items/focus_sash.png differ diff --git a/public/images/items/full_heal.png b/public/images/items/full_heal.png index 4e59dcf44fa..e378291efe0 100644 Binary files a/public/images/items/full_heal.png and b/public/images/items/full_heal.png differ diff --git a/public/images/items/full_restore.png b/public/images/items/full_restore.png index ba64f2edad8..d6e8c7f33e8 100644 Binary files a/public/images/items/full_restore.png and b/public/images/items/full_restore.png differ diff --git a/public/images/items/galarica_cuff.png b/public/images/items/galarica_cuff.png index 56523da5230..4706279c43d 100644 Binary files a/public/images/items/galarica_cuff.png and b/public/images/items/galarica_cuff.png differ diff --git a/public/images/items/galarica_wreath.png b/public/images/items/galarica_wreath.png index 20356bafd8a..6b1838c78dc 100644 Binary files a/public/images/items/galarica_wreath.png and b/public/images/items/galarica_wreath.png differ diff --git a/public/images/items/galladite.png b/public/images/items/galladite.png index 15c93cd9c7e..3fccc2d9b61 100644 Binary files a/public/images/items/galladite.png and b/public/images/items/galladite.png differ diff --git a/public/images/items/ganlon_berry.png b/public/images/items/ganlon_berry.png index f9b88fc563a..72dfb41c4a5 100644 Binary files a/public/images/items/ganlon_berry.png and b/public/images/items/ganlon_berry.png differ diff --git a/public/images/items/garchompite.png b/public/images/items/garchompite.png index 4276146a858..b1938a6979d 100644 Binary files a/public/images/items/garchompite.png and b/public/images/items/garchompite.png differ diff --git a/public/images/items/gardevoirite.png b/public/images/items/gardevoirite.png index 66a90b93bce..9180b369df4 100644 Binary files a/public/images/items/gardevoirite.png and b/public/images/items/gardevoirite.png differ diff --git a/public/images/items/gb.png b/public/images/items/gb.png index 4b437277849..bcc88593fad 100644 Binary files a/public/images/items/gb.png and b/public/images/items/gb.png differ diff --git a/public/images/items/gengarite.png b/public/images/items/gengarite.png index 4ccaae6ee8d..4a6a40d2f99 100644 Binary files a/public/images/items/gengarite.png and b/public/images/items/gengarite.png differ diff --git a/public/images/items/ghost_memory.png b/public/images/items/ghost_memory.png index cb3c31ea4de..cde6f45e735 100644 Binary files a/public/images/items/ghost_memory.png and b/public/images/items/ghost_memory.png differ diff --git a/public/images/items/ghost_tera_shard.png b/public/images/items/ghost_tera_shard.png index 8b9c6e750a9..859c0b9778f 100644 Binary files a/public/images/items/ghost_tera_shard.png and b/public/images/items/ghost_tera_shard.png differ diff --git a/public/images/items/glalitite.png b/public/images/items/glalitite.png index ed7858c631c..e3830e7b6e7 100644 Binary files a/public/images/items/glalitite.png and b/public/images/items/glalitite.png differ diff --git a/public/images/items/golden_egg.png b/public/images/items/golden_egg.png index 5727b4cb513..743a32729f3 100644 Binary files a/public/images/items/golden_egg.png and b/public/images/items/golden_egg.png differ diff --git a/public/images/items/golden_exp_charm.png b/public/images/items/golden_exp_charm.png index 1895bfaa6b9..75067eb0eea 100644 Binary files a/public/images/items/golden_exp_charm.png and b/public/images/items/golden_exp_charm.png differ diff --git a/public/images/items/golden_mystic_ticket.png b/public/images/items/golden_mystic_ticket.png index 57f5b9efca6..6886c815099 100644 Binary files a/public/images/items/golden_mystic_ticket.png and b/public/images/items/golden_mystic_ticket.png differ diff --git a/public/images/items/golden_net.png b/public/images/items/golden_net.png index 3205d545e27..95ac08c1132 100644 Binary files a/public/images/items/golden_net.png and b/public/images/items/golden_net.png differ diff --git a/public/images/items/golden_punch.png b/public/images/items/golden_punch.png index 291ff7e248f..80c4729a227 100644 Binary files a/public/images/items/golden_punch.png and b/public/images/items/golden_punch.png differ diff --git a/public/images/items/gracidea.png b/public/images/items/gracidea.png index 6e464390f17..3fca2378d87 100644 Binary files a/public/images/items/gracidea.png and b/public/images/items/gracidea.png differ diff --git a/public/images/items/grass_memory.png b/public/images/items/grass_memory.png index 9d7d27de770..aacb8e7e98a 100644 Binary files a/public/images/items/grass_memory.png and b/public/images/items/grass_memory.png differ diff --git a/public/images/items/grass_tera_shard.png b/public/images/items/grass_tera_shard.png index 30e8762b5b6..218474660c4 100644 Binary files a/public/images/items/grass_tera_shard.png and b/public/images/items/grass_tera_shard.png differ diff --git a/public/images/items/great_ribbon.png b/public/images/items/great_ribbon.png index e7d7e452122..e246d8db981 100644 Binary files a/public/images/items/great_ribbon.png and b/public/images/items/great_ribbon.png differ diff --git a/public/images/items/grip_claw.png b/public/images/items/grip_claw.png index 61cbb89e9b2..ccef5828f8d 100644 Binary files a/public/images/items/grip_claw.png and b/public/images/items/grip_claw.png differ diff --git a/public/images/items/griseous_core.png b/public/images/items/griseous_core.png index 7f3f683eda8..b067ffc7ef6 100644 Binary files a/public/images/items/griseous_core.png and b/public/images/items/griseous_core.png differ diff --git a/public/images/items/ground_memory.png b/public/images/items/ground_memory.png index 808a4e13eeb..66d31446dd9 100644 Binary files a/public/images/items/ground_memory.png and b/public/images/items/ground_memory.png differ diff --git a/public/images/items/ground_tera_shard.png b/public/images/items/ground_tera_shard.png index d7760f7dc1b..46a1cab5f4e 100644 Binary files a/public/images/items/ground_tera_shard.png and b/public/images/items/ground_tera_shard.png differ diff --git a/public/images/items/guard_spec.png b/public/images/items/guard_spec.png index 95a37d0d28e..d65f5a8c07f 100644 Binary files a/public/images/items/guard_spec.png and b/public/images/items/guard_spec.png differ diff --git a/public/images/items/gyaradosite.png b/public/images/items/gyaradosite.png index e4cae1366ab..7765f443dbd 100644 Binary files a/public/images/items/gyaradosite.png and b/public/images/items/gyaradosite.png differ diff --git a/public/images/items/hard_meteorite.png b/public/images/items/hard_meteorite.png index 8e8a2b5688b..5a38e06c9ac 100644 Binary files a/public/images/items/hard_meteorite.png and b/public/images/items/hard_meteorite.png differ diff --git a/public/images/items/hard_stone.png b/public/images/items/hard_stone.png index 571d96afaa8..98463a6a391 100644 Binary files a/public/images/items/hard_stone.png and b/public/images/items/hard_stone.png differ diff --git a/public/images/items/healing_charm.png b/public/images/items/healing_charm.png index 4601eeef593..42bcc0eab06 100644 Binary files a/public/images/items/healing_charm.png and b/public/images/items/healing_charm.png differ diff --git a/public/images/items/hearthflame_mask.png b/public/images/items/hearthflame_mask.png index deed3d0cde8..b76556c8baf 100644 Binary files a/public/images/items/hearthflame_mask.png and b/public/images/items/hearthflame_mask.png differ diff --git a/public/images/items/heracronite.png b/public/images/items/heracronite.png index ffcfc440c19..29c8e8285fb 100644 Binary files a/public/images/items/heracronite.png and b/public/images/items/heracronite.png differ diff --git a/public/images/items/houndoominite.png b/public/images/items/houndoominite.png index c9ea1954dfa..9ae4982694e 100644 Binary files a/public/images/items/houndoominite.png and b/public/images/items/houndoominite.png differ diff --git a/public/images/items/hp_up.png b/public/images/items/hp_up.png index 5c6baff0673..b9284383978 100644 Binary files a/public/images/items/hp_up.png and b/public/images/items/hp_up.png differ diff --git a/public/images/items/hyper_potion.png b/public/images/items/hyper_potion.png index 197aaac690c..140c74b638e 100644 Binary files a/public/images/items/hyper_potion.png and b/public/images/items/hyper_potion.png differ diff --git a/public/images/items/ice_memory.png b/public/images/items/ice_memory.png index 812548cf36c..e41e27c888b 100644 Binary files a/public/images/items/ice_memory.png and b/public/images/items/ice_memory.png differ diff --git a/public/images/items/ice_stone.png b/public/images/items/ice_stone.png index 945714b759c..6920d78ab37 100644 Binary files a/public/images/items/ice_stone.png and b/public/images/items/ice_stone.png differ diff --git a/public/images/items/ice_tera_shard.png b/public/images/items/ice_tera_shard.png index 5644d647c2b..946d5e2dd0c 100644 Binary files a/public/images/items/ice_tera_shard.png and b/public/images/items/ice_tera_shard.png differ diff --git a/public/images/items/icicle_plate.png b/public/images/items/icicle_plate.png index f9797d91f6a..dc4bb74c1d6 100644 Binary files a/public/images/items/icicle_plate.png and b/public/images/items/icicle_plate.png differ diff --git a/public/images/items/icy_reins_of_unity.png b/public/images/items/icy_reins_of_unity.png index b7af48d6a81..1b7d9c6399d 100644 Binary files a/public/images/items/icy_reins_of_unity.png and b/public/images/items/icy_reins_of_unity.png differ diff --git a/public/images/items/insect_plate.png b/public/images/items/insect_plate.png index 5bcc0eebaf5..23e8012d580 100644 Binary files a/public/images/items/insect_plate.png and b/public/images/items/insect_plate.png differ diff --git a/public/images/items/inverse.png b/public/images/items/inverse.png index 0d77ce77dde..059cb2f22ea 100644 Binary files a/public/images/items/inverse.png and b/public/images/items/inverse.png differ diff --git a/public/images/items/iron.png b/public/images/items/iron.png index 5cfff11b059..9c459388385 100644 Binary files a/public/images/items/iron.png and b/public/images/items/iron.png differ diff --git a/public/images/items/iron_plate.png b/public/images/items/iron_plate.png index 65d660e34eb..1a8365ac2af 100644 Binary files a/public/images/items/iron_plate.png and b/public/images/items/iron_plate.png differ diff --git a/public/images/items/kangaskhanite.png b/public/images/items/kangaskhanite.png index 3d65d2cc5ca..8e27c75332c 100644 Binary files a/public/images/items/kangaskhanite.png and b/public/images/items/kangaskhanite.png differ diff --git a/public/images/items/kings_rock.png b/public/images/items/kings_rock.png index bfda9d559d3..52dd6d04938 100644 Binary files a/public/images/items/kings_rock.png and b/public/images/items/kings_rock.png differ diff --git a/public/images/items/lansat_berry.png b/public/images/items/lansat_berry.png index 223bff3eb26..b9c244194b7 100644 Binary files a/public/images/items/lansat_berry.png and b/public/images/items/lansat_berry.png differ diff --git a/public/images/items/latiasite.png b/public/images/items/latiasite.png index 486a542576f..055e74d50bb 100644 Binary files a/public/images/items/latiasite.png and b/public/images/items/latiasite.png differ diff --git a/public/images/items/latiosite.png b/public/images/items/latiosite.png index 69708835c7b..60fdf81f708 100644 Binary files a/public/images/items/latiosite.png and b/public/images/items/latiosite.png differ diff --git a/public/images/items/leaders_crest.png b/public/images/items/leaders_crest.png index 86b71f1eb6d..3f9ddd4b89a 100644 Binary files a/public/images/items/leaders_crest.png and b/public/images/items/leaders_crest.png differ diff --git a/public/images/items/leaf_stone.png b/public/images/items/leaf_stone.png index dc68e916bcf..e4c85f2b0ba 100644 Binary files a/public/images/items/leaf_stone.png and b/public/images/items/leaf_stone.png differ diff --git a/public/images/items/leek.png b/public/images/items/leek.png index 1cb136aa78c..528392264c7 100644 Binary files a/public/images/items/leek.png and b/public/images/items/leek.png differ diff --git a/public/images/items/leftovers.png b/public/images/items/leftovers.png index 4732d149b64..c06eaf073d9 100644 Binary files a/public/images/items/leftovers.png and b/public/images/items/leftovers.png differ diff --git a/public/images/items/legend_plate.png b/public/images/items/legend_plate.png index e96f12b45f5..2304d587346 100644 Binary files a/public/images/items/legend_plate.png and b/public/images/items/legend_plate.png differ diff --git a/public/images/items/leppa_berry.png b/public/images/items/leppa_berry.png index 7a0f13b5199..611acba6a1a 100644 Binary files a/public/images/items/leppa_berry.png and b/public/images/items/leppa_berry.png differ diff --git a/public/images/items/liechi_berry.png b/public/images/items/liechi_berry.png index 192d8f001ef..8ce788a36aa 100644 Binary files a/public/images/items/liechi_berry.png and b/public/images/items/liechi_berry.png differ diff --git a/public/images/items/light_ball.png b/public/images/items/light_ball.png index 7748f735d37..a5fe2859e63 100644 Binary files a/public/images/items/light_ball.png and b/public/images/items/light_ball.png differ diff --git a/public/images/items/light_stone.png b/public/images/items/light_stone.png index 97258cf3970..f9f40130b48 100644 Binary files a/public/images/items/light_stone.png and b/public/images/items/light_stone.png differ diff --git a/public/images/items/linking_cord.png b/public/images/items/linking_cord.png index 4cde9da3f14..414378222f7 100644 Binary files a/public/images/items/linking_cord.png and b/public/images/items/linking_cord.png differ diff --git a/public/images/items/lock_capsule.png b/public/images/items/lock_capsule.png index 25d0cc14cff..be5a5e02c99 100644 Binary files a/public/images/items/lock_capsule.png and b/public/images/items/lock_capsule.png differ diff --git a/public/images/items/lopunnite.png b/public/images/items/lopunnite.png index bd67cf159ed..7f9364e3755 100644 Binary files a/public/images/items/lopunnite.png and b/public/images/items/lopunnite.png differ diff --git a/public/images/items/lucarionite.png b/public/images/items/lucarionite.png index b97161df68c..b964cb0b039 100644 Binary files a/public/images/items/lucarionite.png and b/public/images/items/lucarionite.png differ diff --git a/public/images/items/lucky_egg.png b/public/images/items/lucky_egg.png index fa9a1e4b390..8f99d3f37d0 100644 Binary files a/public/images/items/lucky_egg.png and b/public/images/items/lucky_egg.png differ diff --git a/public/images/items/lucky_punch.png b/public/images/items/lucky_punch.png index 97887631d9a..c70c084e3bc 100644 Binary files a/public/images/items/lucky_punch.png and b/public/images/items/lucky_punch.png differ diff --git a/public/images/items/lucky_punch_great.png b/public/images/items/lucky_punch_great.png index 77c77daf535..2b2c60d6f3a 100644 Binary files a/public/images/items/lucky_punch_great.png and b/public/images/items/lucky_punch_great.png differ diff --git a/public/images/items/lucky_punch_master.png b/public/images/items/lucky_punch_master.png index 89fc1b67cf4..97d5d22aea0 100644 Binary files a/public/images/items/lucky_punch_master.png and b/public/images/items/lucky_punch_master.png differ diff --git a/public/images/items/lucky_punch_ultra.png b/public/images/items/lucky_punch_ultra.png index a95c23666bb..fbe09c35cd1 100644 Binary files a/public/images/items/lucky_punch_ultra.png and b/public/images/items/lucky_punch_ultra.png differ diff --git a/public/images/items/lum_berry.png b/public/images/items/lum_berry.png index d19c4fba583..97d3167eec4 100644 Binary files a/public/images/items/lum_berry.png and b/public/images/items/lum_berry.png differ diff --git a/public/images/items/lure.png b/public/images/items/lure.png index 1c3ea6cc8e8..883181158e5 100644 Binary files a/public/images/items/lure.png and b/public/images/items/lure.png differ diff --git a/public/images/items/lustrous_globe.png b/public/images/items/lustrous_globe.png index 2a854db742b..0aee9500272 100644 Binary files a/public/images/items/lustrous_globe.png and b/public/images/items/lustrous_globe.png differ diff --git a/public/images/items/macho_brace.png b/public/images/items/macho_brace.png index 760139cf7f8..b5063258fc2 100644 Binary files a/public/images/items/macho_brace.png and b/public/images/items/macho_brace.png differ diff --git a/public/images/items/magmarizer.png b/public/images/items/magmarizer.png index 4f4d5f45851..785816b6ad5 100644 Binary files a/public/images/items/magmarizer.png and b/public/images/items/magmarizer.png differ diff --git a/public/images/items/magnet.png b/public/images/items/magnet.png index 9ce8b686e9f..f8668e72abd 100644 Binary files a/public/images/items/magnet.png and b/public/images/items/magnet.png differ diff --git a/public/images/items/malicious_armor.png b/public/images/items/malicious_armor.png index b7bfb55195b..62c93b3b817 100644 Binary files a/public/images/items/malicious_armor.png and b/public/images/items/malicious_armor.png differ diff --git a/public/images/items/manectite.png b/public/images/items/manectite.png index c9df61669bc..ca82d2cee9f 100644 Binary files a/public/images/items/manectite.png and b/public/images/items/manectite.png differ diff --git a/public/images/items/map.png b/public/images/items/map.png index dc2fc6b95d8..9fda9174744 100644 Binary files a/public/images/items/map.png and b/public/images/items/map.png differ diff --git a/public/images/items/master_ribbon.png b/public/images/items/master_ribbon.png index 0443215c1f9..a8a181a52a0 100644 Binary files a/public/images/items/master_ribbon.png and b/public/images/items/master_ribbon.png differ diff --git a/public/images/items/masterpiece_teacup.png b/public/images/items/masterpiece_teacup.png index ec2455c0763..71d8e5a8cf8 100644 Binary files a/public/images/items/masterpiece_teacup.png and b/public/images/items/masterpiece_teacup.png differ diff --git a/public/images/items/mawilite.png b/public/images/items/mawilite.png index 60ea4017f56..da9f74a43be 100644 Binary files a/public/images/items/mawilite.png and b/public/images/items/mawilite.png differ diff --git a/public/images/items/max_elixir.png b/public/images/items/max_elixir.png index 1f528c2bb61..7afaf6581ea 100644 Binary files a/public/images/items/max_elixir.png and b/public/images/items/max_elixir.png differ diff --git a/public/images/items/max_ether.png b/public/images/items/max_ether.png index 8cee7152a86..451e7fa302f 100644 Binary files a/public/images/items/max_ether.png and b/public/images/items/max_ether.png differ diff --git a/public/images/items/max_lure.png b/public/images/items/max_lure.png index 812229d140a..dfc86583dd3 100644 Binary files a/public/images/items/max_lure.png and b/public/images/items/max_lure.png differ diff --git a/public/images/items/max_mushrooms.png b/public/images/items/max_mushrooms.png index 8d5c015c963..10ff9f5a72f 100644 Binary files a/public/images/items/max_mushrooms.png and b/public/images/items/max_mushrooms.png differ diff --git a/public/images/items/max_potion.png b/public/images/items/max_potion.png index d8fadc9cb93..94f3828e7eb 100644 Binary files a/public/images/items/max_potion.png and b/public/images/items/max_potion.png differ diff --git a/public/images/items/max_repel.png b/public/images/items/max_repel.png index 4d88ae6d0b5..6e0621f5f0f 100644 Binary files a/public/images/items/max_repel.png and b/public/images/items/max_repel.png differ diff --git a/public/images/items/max_revive.png b/public/images/items/max_revive.png index 609fd17c3b9..e440b940dec 100644 Binary files a/public/images/items/max_revive.png and b/public/images/items/max_revive.png differ diff --git a/public/images/items/mb.png b/public/images/items/mb.png index d80b3b89108..2ba762c5853 100644 Binary files a/public/images/items/mb.png and b/public/images/items/mb.png differ diff --git a/public/images/items/meadow_plate.png b/public/images/items/meadow_plate.png index e2b991776ba..f3358e3df96 100644 Binary files a/public/images/items/meadow_plate.png and b/public/images/items/meadow_plate.png differ diff --git a/public/images/items/medichamite.png b/public/images/items/medichamite.png index 42f22ed6cd6..f99b8af2edd 100644 Binary files a/public/images/items/medichamite.png and b/public/images/items/medichamite.png differ diff --git a/public/images/items/mega_bracelet.png b/public/images/items/mega_bracelet.png index 5e8ff02be8c..674c59a8f5a 100644 Binary files a/public/images/items/mega_bracelet.png and b/public/images/items/mega_bracelet.png differ diff --git a/public/images/items/metagrossite.png b/public/images/items/metagrossite.png index c245368758d..1ec22fb09db 100644 Binary files a/public/images/items/metagrossite.png and b/public/images/items/metagrossite.png differ diff --git a/public/images/items/metal_alloy.png b/public/images/items/metal_alloy.png index 41f22df4254..33b0f556d70 100644 Binary files a/public/images/items/metal_alloy.png and b/public/images/items/metal_alloy.png differ diff --git a/public/images/items/metal_coat.png b/public/images/items/metal_coat.png index 88f055c3fb3..07344dfe724 100644 Binary files a/public/images/items/metal_coat.png and b/public/images/items/metal_coat.png differ diff --git a/public/images/items/metal_powder.png b/public/images/items/metal_powder.png index 64af0b144fe..a56b6aa1d70 100644 Binary files a/public/images/items/metal_powder.png and b/public/images/items/metal_powder.png differ diff --git a/public/images/items/metronome.png b/public/images/items/metronome.png index 837d75b4c03..2ae5ed6d1fa 100644 Binary files a/public/images/items/metronome.png and b/public/images/items/metronome.png differ diff --git a/public/images/items/mewtwonite_x.png b/public/images/items/mewtwonite_x.png index 507d17e54e8..0639c28e33e 100644 Binary files a/public/images/items/mewtwonite_x.png and b/public/images/items/mewtwonite_x.png differ diff --git a/public/images/items/mewtwonite_y.png b/public/images/items/mewtwonite_y.png index ff0c53396d2..f78e7d7e1fc 100644 Binary files a/public/images/items/mewtwonite_y.png and b/public/images/items/mewtwonite_y.png differ diff --git a/public/images/items/mind_plate.png b/public/images/items/mind_plate.png index ff6fcc4f3af..e8964d64816 100644 Binary files a/public/images/items/mind_plate.png and b/public/images/items/mind_plate.png differ diff --git a/public/images/items/mini_black_hole.png b/public/images/items/mini_black_hole.png index 6edcaad16e3..16589cad227 100644 Binary files a/public/images/items/mini_black_hole.png and b/public/images/items/mini_black_hole.png differ diff --git a/public/images/items/mint_atk.png b/public/images/items/mint_atk.png index 88315c0cd07..f42e0cccfc0 100644 Binary files a/public/images/items/mint_atk.png and b/public/images/items/mint_atk.png differ diff --git a/public/images/items/mint_def.png b/public/images/items/mint_def.png index 584c610ffd7..5e45103f199 100644 Binary files a/public/images/items/mint_def.png and b/public/images/items/mint_def.png differ diff --git a/public/images/items/mint_neutral.png b/public/images/items/mint_neutral.png index f5287539e78..ccf97113000 100644 Binary files a/public/images/items/mint_neutral.png and b/public/images/items/mint_neutral.png differ diff --git a/public/images/items/mint_spatk.png b/public/images/items/mint_spatk.png index 90a57c5608b..94b60ed1597 100644 Binary files a/public/images/items/mint_spatk.png and b/public/images/items/mint_spatk.png differ diff --git a/public/images/items/mint_spd.png b/public/images/items/mint_spd.png index 5420b0997b3..f3f1a1308a9 100644 Binary files a/public/images/items/mint_spd.png and b/public/images/items/mint_spd.png differ diff --git a/public/images/items/mint_spdef.png b/public/images/items/mint_spdef.png index 54a1b4ed21d..4b378953644 100644 Binary files a/public/images/items/mint_spdef.png and b/public/images/items/mint_spdef.png differ diff --git a/public/images/items/miracle_seed.png b/public/images/items/miracle_seed.png index 8be7ba72d33..fac8d792c85 100644 Binary files a/public/images/items/miracle_seed.png and b/public/images/items/miracle_seed.png differ diff --git a/public/images/items/moon_flute.png b/public/images/items/moon_flute.png index 5d3c0caf5f2..e22ea1e0c84 100644 Binary files a/public/images/items/moon_flute.png and b/public/images/items/moon_flute.png differ diff --git a/public/images/items/moon_stone.png b/public/images/items/moon_stone.png index f002fb18073..1f1644946f8 100644 Binary files a/public/images/items/moon_stone.png and b/public/images/items/moon_stone.png differ diff --git a/public/images/items/muscle_band.png b/public/images/items/muscle_band.png index 0be9d010767..0038fc4d65d 100644 Binary files a/public/images/items/muscle_band.png and b/public/images/items/muscle_band.png differ diff --git a/public/images/items/mystery_egg.png b/public/images/items/mystery_egg.png index ac631cec7c8..1630e9c69f3 100644 Binary files a/public/images/items/mystery_egg.png and b/public/images/items/mystery_egg.png differ diff --git a/public/images/items/mystic_ticket.png b/public/images/items/mystic_ticket.png index cab03fa5470..8a1e4ccd937 100644 Binary files a/public/images/items/mystic_ticket.png and b/public/images/items/mystic_ticket.png differ diff --git a/public/images/items/mystic_water.png b/public/images/items/mystic_water.png index 2128895b385..9ab0c0544dc 100644 Binary files a/public/images/items/mystic_water.png and b/public/images/items/mystic_water.png differ diff --git a/public/images/items/mystical_rock.png b/public/images/items/mystical_rock.png index 81a397e4c2d..39c5da781dd 100644 Binary files a/public/images/items/mystical_rock.png and b/public/images/items/mystical_rock.png differ diff --git a/public/images/items/n_lunarizer.png b/public/images/items/n_lunarizer.png index a03b48ad387..fc67271346c 100644 Binary files a/public/images/items/n_lunarizer.png and b/public/images/items/n_lunarizer.png differ diff --git a/public/images/items/n_solarizer.png b/public/images/items/n_solarizer.png index 69153fd38dd..787d2b49bc4 100644 Binary files a/public/images/items/n_solarizer.png and b/public/images/items/n_solarizer.png differ diff --git a/public/images/items/never_melt_ice.png b/public/images/items/never_melt_ice.png index 35b1ada771d..58bbb01788a 100644 Binary files a/public/images/items/never_melt_ice.png and b/public/images/items/never_melt_ice.png differ diff --git a/public/images/items/normal_memory.png b/public/images/items/normal_memory.png index e1ff89a9993..e7aa8336820 100644 Binary files a/public/images/items/normal_memory.png and b/public/images/items/normal_memory.png differ diff --git a/public/images/items/normal_tera_shard.png b/public/images/items/normal_tera_shard.png index 1c7a41ea0ec..b63640ca94a 100644 Binary files a/public/images/items/normal_tera_shard.png and b/public/images/items/normal_tera_shard.png differ diff --git a/public/images/items/nugget.png b/public/images/items/nugget.png index 0340f5d4000..ce70209b164 100644 Binary files a/public/images/items/nugget.png and b/public/images/items/nugget.png differ diff --git a/public/images/items/old_gateau.png b/public/images/items/old_gateau.png index dd5f0e05a94..52aaf5a3b73 100644 Binary files a/public/images/items/old_gateau.png and b/public/images/items/old_gateau.png differ diff --git a/public/images/items/oval_charm.png b/public/images/items/oval_charm.png index fcdb914ec22..5c47c169157 100644 Binary files a/public/images/items/oval_charm.png and b/public/images/items/oval_charm.png differ diff --git a/public/images/items/oval_stone.png b/public/images/items/oval_stone.png index d58ffde18e3..6437a6dd821 100644 Binary files a/public/images/items/oval_stone.png and b/public/images/items/oval_stone.png differ diff --git a/public/images/items/pair_of_tickets.png b/public/images/items/pair_of_tickets.png index b06c9a8727f..68274208196 100644 Binary files a/public/images/items/pair_of_tickets.png and b/public/images/items/pair_of_tickets.png differ diff --git a/public/images/items/pb.png b/public/images/items/pb.png index 37c37edb8c1..d54f6b02b5b 100644 Binary files a/public/images/items/pb.png and b/public/images/items/pb.png differ diff --git a/public/images/items/pb_gold.png b/public/images/items/pb_gold.png index 6dff6824158..86842760431 100644 Binary files a/public/images/items/pb_gold.png and b/public/images/items/pb_gold.png differ diff --git a/public/images/items/pb_silver.png b/public/images/items/pb_silver.png index 9528517a77a..674cf646fd3 100644 Binary files a/public/images/items/pb_silver.png and b/public/images/items/pb_silver.png differ diff --git a/public/images/items/peat_block.png b/public/images/items/peat_block.png index f3c65449b87..357a9496aa8 100644 Binary files a/public/images/items/peat_block.png and b/public/images/items/peat_block.png differ diff --git a/public/images/items/petaya_berry.png b/public/images/items/petaya_berry.png index 0fba884c2e2..434165e6e20 100644 Binary files a/public/images/items/petaya_berry.png and b/public/images/items/petaya_berry.png differ diff --git a/public/images/items/pidgeotite.png b/public/images/items/pidgeotite.png index 52cb40852d6..cd7c1d2d01c 100644 Binary files a/public/images/items/pidgeotite.png and b/public/images/items/pidgeotite.png differ diff --git a/public/images/items/pinsirite.png b/public/images/items/pinsirite.png index 88ecc8e9ea9..5a63465f882 100644 Binary files a/public/images/items/pinsirite.png and b/public/images/items/pinsirite.png differ diff --git a/public/images/items/pixie_plate.png b/public/images/items/pixie_plate.png index e123ae6a49a..7d29f030a76 100644 Binary files a/public/images/items/pixie_plate.png and b/public/images/items/pixie_plate.png differ diff --git a/public/images/items/poison_barb.png b/public/images/items/poison_barb.png index 3f9d714b08c..b85f7d3b777 100644 Binary files a/public/images/items/poison_barb.png and b/public/images/items/poison_barb.png differ diff --git a/public/images/items/poison_memory.png b/public/images/items/poison_memory.png index 6c2b0aea77b..4de0993adbf 100644 Binary files a/public/images/items/poison_memory.png and b/public/images/items/poison_memory.png differ diff --git a/public/images/items/poison_tera_shard.png b/public/images/items/poison_tera_shard.png index 4f21d158a41..bd213f1a91e 100644 Binary files a/public/images/items/poison_tera_shard.png and b/public/images/items/poison_tera_shard.png differ diff --git a/public/images/items/potion.png b/public/images/items/potion.png index f69c3210f03..e24a483970e 100644 Binary files a/public/images/items/potion.png and b/public/images/items/potion.png differ diff --git a/public/images/items/power_herb.png b/public/images/items/power_herb.png index ba156c62122..477e32e3271 100644 Binary files a/public/images/items/power_herb.png and b/public/images/items/power_herb.png differ diff --git a/public/images/items/pp_max.png b/public/images/items/pp_max.png index 787641e26ac..9c449b2e564 100644 Binary files a/public/images/items/pp_max.png and b/public/images/items/pp_max.png differ diff --git a/public/images/items/pp_up.png b/public/images/items/pp_up.png index 463c3a0578d..16f1c7558fd 100644 Binary files a/public/images/items/pp_up.png and b/public/images/items/pp_up.png differ diff --git a/public/images/items/prism_scale.png b/public/images/items/prism_scale.png index 2436e96bf73..807031bf268 100644 Binary files a/public/images/items/prism_scale.png and b/public/images/items/prism_scale.png differ diff --git a/public/images/items/prison_bottle.png b/public/images/items/prison_bottle.png index 06217988364..e8f24568da3 100644 Binary files a/public/images/items/prison_bottle.png and b/public/images/items/prison_bottle.png differ diff --git a/public/images/items/protector.png b/public/images/items/protector.png index 8f65be09b2f..028fd793988 100644 Binary files a/public/images/items/protector.png and b/public/images/items/protector.png differ diff --git a/public/images/items/protein.png b/public/images/items/protein.png index 54df13c6753..6165202f37d 100644 Binary files a/public/images/items/protein.png and b/public/images/items/protein.png differ diff --git a/public/images/items/psychic_memory.png b/public/images/items/psychic_memory.png index 21d62daa483..5ac912cfb10 100644 Binary files a/public/images/items/psychic_memory.png and b/public/images/items/psychic_memory.png differ diff --git a/public/images/items/psychic_tera_shard.png b/public/images/items/psychic_tera_shard.png index f4b1cf7b259..cb575d7bb69 100644 Binary files a/public/images/items/psychic_tera_shard.png and b/public/images/items/psychic_tera_shard.png differ diff --git a/public/images/items/quick_claw.png b/public/images/items/quick_claw.png index ff4f76d0576..94999fa3c09 100644 Binary files a/public/images/items/quick_claw.png and b/public/images/items/quick_claw.png differ diff --git a/public/images/items/quick_powder.png b/public/images/items/quick_powder.png index 58a8a1187a9..731d6fce025 100644 Binary files a/public/images/items/quick_powder.png and b/public/images/items/quick_powder.png differ diff --git a/public/images/items/rare_candy.png b/public/images/items/rare_candy.png index a860a116905..cd27e2002ba 100644 Binary files a/public/images/items/rare_candy.png and b/public/images/items/rare_candy.png differ diff --git a/public/images/items/rarer_candy.png b/public/images/items/rarer_candy.png index 4424caa659a..92af7fe43bf 100644 Binary files a/public/images/items/rarer_candy.png and b/public/images/items/rarer_candy.png differ diff --git a/public/images/items/rayquazite.png b/public/images/items/rayquazite.png index b39957b857d..6617924c434 100644 Binary files a/public/images/items/rayquazite.png and b/public/images/items/rayquazite.png differ diff --git a/public/images/items/razor_claw.png b/public/images/items/razor_claw.png index 0ba506e8706..48fc80d7f00 100644 Binary files a/public/images/items/razor_claw.png and b/public/images/items/razor_claw.png differ diff --git a/public/images/items/razor_fang.png b/public/images/items/razor_fang.png index 75306db5c4c..a55dbb3ee13 100644 Binary files a/public/images/items/razor_fang.png and b/public/images/items/razor_fang.png differ diff --git a/public/images/items/rb.png b/public/images/items/rb.png index 020f2ff878b..2dcccceb57e 100644 Binary files a/public/images/items/rb.png and b/public/images/items/rb.png differ diff --git a/public/images/items/reaper_cloth.png b/public/images/items/reaper_cloth.png index 9caf7665e95..8bb722c241d 100644 Binary files a/public/images/items/reaper_cloth.png and b/public/images/items/reaper_cloth.png differ diff --git a/public/images/items/red_orb.png b/public/images/items/red_orb.png index cee83740ca5..8e9a3bd4f74 100644 Binary files a/public/images/items/red_orb.png and b/public/images/items/red_orb.png differ diff --git a/public/images/items/relic_band.png b/public/images/items/relic_band.png index adbd73bc46b..7f783d26647 100644 Binary files a/public/images/items/relic_band.png and b/public/images/items/relic_band.png differ diff --git a/public/images/items/relic_crown.png b/public/images/items/relic_crown.png index de090041c1c..1f3c9c3a7df 100644 Binary files a/public/images/items/relic_crown.png and b/public/images/items/relic_crown.png differ diff --git a/public/images/items/relic_gold.png b/public/images/items/relic_gold.png index af5b1797938..650c610d4e2 100644 Binary files a/public/images/items/relic_gold.png and b/public/images/items/relic_gold.png differ diff --git a/public/images/items/repel.png b/public/images/items/repel.png index 80c7738e9ff..12c0111b794 100644 Binary files a/public/images/items/repel.png and b/public/images/items/repel.png differ diff --git a/public/images/items/reveal_glass.png b/public/images/items/reveal_glass.png index 469d20a2752..2f9d45a8932 100644 Binary files a/public/images/items/reveal_glass.png and b/public/images/items/reveal_glass.png differ diff --git a/public/images/items/revive.png b/public/images/items/revive.png index e84659867d8..2e2185088d5 100644 Binary files a/public/images/items/revive.png and b/public/images/items/revive.png differ diff --git a/public/images/items/reviver_seed.png b/public/images/items/reviver_seed.png index 10bc5c32451..2ba0919ea5a 100644 Binary files a/public/images/items/reviver_seed.png and b/public/images/items/reviver_seed.png differ diff --git a/public/images/items/ribbon_gen1.png b/public/images/items/ribbon_gen1.png index 1d731cf2e98..cb992589d36 100644 Binary files a/public/images/items/ribbon_gen1.png and b/public/images/items/ribbon_gen1.png differ diff --git a/public/images/items/ribbon_gen2.png b/public/images/items/ribbon_gen2.png index 2ff54112105..26268987fbe 100644 Binary files a/public/images/items/ribbon_gen2.png and b/public/images/items/ribbon_gen2.png differ diff --git a/public/images/items/ribbon_gen3.png b/public/images/items/ribbon_gen3.png index 14a5eb09f7d..501885bc952 100644 Binary files a/public/images/items/ribbon_gen3.png and b/public/images/items/ribbon_gen3.png differ diff --git a/public/images/items/ribbon_gen4.png b/public/images/items/ribbon_gen4.png index c482ac1d04a..ad4cb139a7e 100644 Binary files a/public/images/items/ribbon_gen4.png and b/public/images/items/ribbon_gen4.png differ diff --git a/public/images/items/ribbon_gen5.png b/public/images/items/ribbon_gen5.png index 52560cde636..a4f99ffb7f2 100644 Binary files a/public/images/items/ribbon_gen5.png and b/public/images/items/ribbon_gen5.png differ diff --git a/public/images/items/ribbon_gen6.png b/public/images/items/ribbon_gen6.png index 3dd04927acd..4c9281fa8a7 100644 Binary files a/public/images/items/ribbon_gen6.png and b/public/images/items/ribbon_gen6.png differ diff --git a/public/images/items/ribbon_gen7.png b/public/images/items/ribbon_gen7.png index 225b40da419..5d088392bd5 100644 Binary files a/public/images/items/ribbon_gen7.png and b/public/images/items/ribbon_gen7.png differ diff --git a/public/images/items/ribbon_gen8.png b/public/images/items/ribbon_gen8.png index 32aaa803699..537d64605b3 100644 Binary files a/public/images/items/ribbon_gen8.png and b/public/images/items/ribbon_gen8.png differ diff --git a/public/images/items/ribbon_gen9.png b/public/images/items/ribbon_gen9.png index ad489005e23..a702b6241c3 100644 Binary files a/public/images/items/ribbon_gen9.png and b/public/images/items/ribbon_gen9.png differ diff --git a/public/images/items/rock_memory.png b/public/images/items/rock_memory.png index cfa71c851c0..963ea849d8c 100644 Binary files a/public/images/items/rock_memory.png and b/public/images/items/rock_memory.png differ diff --git a/public/images/items/rock_tera_shard.png b/public/images/items/rock_tera_shard.png index a07ca7955a4..279722e455b 100644 Binary files a/public/images/items/rock_tera_shard.png and b/public/images/items/rock_tera_shard.png differ diff --git a/public/images/items/rogue_ribbon.png b/public/images/items/rogue_ribbon.png index c3cfdcd85f3..6864b6a1fb7 100644 Binary files a/public/images/items/rogue_ribbon.png and b/public/images/items/rogue_ribbon.png differ diff --git a/public/images/items/rusted_shield.png b/public/images/items/rusted_shield.png index 3748f13dc4b..065309fc1b7 100644 Binary files a/public/images/items/rusted_shield.png and b/public/images/items/rusted_shield.png differ diff --git a/public/images/items/rusted_sword.png b/public/images/items/rusted_sword.png index 249233374e0..ad7123dc56d 100644 Binary files a/public/images/items/rusted_sword.png and b/public/images/items/rusted_sword.png differ diff --git a/public/images/items/sablenite.png b/public/images/items/sablenite.png index 48eac2d5875..771480aa918 100644 Binary files a/public/images/items/sablenite.png and b/public/images/items/sablenite.png differ diff --git a/public/images/items/sachet.png b/public/images/items/sachet.png index 6d91fa210fc..3d9d6e6c7c7 100644 Binary files a/public/images/items/sachet.png and b/public/images/items/sachet.png differ diff --git a/public/images/items/sacred_ash.png b/public/images/items/sacred_ash.png index 4fab9dc0dc7..741afe59ef2 100644 Binary files a/public/images/items/sacred_ash.png and b/public/images/items/sacred_ash.png differ diff --git a/public/images/items/salac_berry.png b/public/images/items/salac_berry.png index 846dbb4f160..a01a824b4ba 100644 Binary files a/public/images/items/salac_berry.png and b/public/images/items/salac_berry.png differ diff --git a/public/images/items/salamencite.png b/public/images/items/salamencite.png index f175f39d351..f54a8d3dae9 100644 Binary files a/public/images/items/salamencite.png and b/public/images/items/salamencite.png differ diff --git a/public/images/items/scanner.png b/public/images/items/scanner.png index a291d3f43f8..f56f696638c 100644 Binary files a/public/images/items/scanner.png and b/public/images/items/scanner.png differ diff --git a/public/images/items/sceptilite.png b/public/images/items/sceptilite.png index ff81c4a55fe..4b255b1ff6d 100644 Binary files a/public/images/items/sceptilite.png and b/public/images/items/sceptilite.png differ diff --git a/public/images/items/scizorite.png b/public/images/items/scizorite.png index 8ffca143835..9d357bbd0b6 100644 Binary files a/public/images/items/scizorite.png and b/public/images/items/scizorite.png differ diff --git a/public/images/items/scope_lens.png b/public/images/items/scope_lens.png index d3b1f354313..2cf77cbde77 100644 Binary files a/public/images/items/scope_lens.png and b/public/images/items/scope_lens.png differ diff --git a/public/images/items/scroll_of_darkness.png b/public/images/items/scroll_of_darkness.png index ff6a6065e40..72044c62d8b 100644 Binary files a/public/images/items/scroll_of_darkness.png and b/public/images/items/scroll_of_darkness.png differ diff --git a/public/images/items/scroll_of_waters.png b/public/images/items/scroll_of_waters.png index e4a0cb725eb..9195a26abe1 100644 Binary files a/public/images/items/scroll_of_waters.png and b/public/images/items/scroll_of_waters.png differ diff --git a/public/images/items/shadow_reins_of_unity.png b/public/images/items/shadow_reins_of_unity.png index 85cbc446851..668033ff166 100644 Binary files a/public/images/items/shadow_reins_of_unity.png and b/public/images/items/shadow_reins_of_unity.png differ diff --git a/public/images/items/sharp_beak.png b/public/images/items/sharp_beak.png index f22019e658b..a19e77d2e6e 100644 Binary files a/public/images/items/sharp_beak.png and b/public/images/items/sharp_beak.png differ diff --git a/public/images/items/sharp_meteorite.png b/public/images/items/sharp_meteorite.png index 61a139fab05..97f89e5893b 100644 Binary files a/public/images/items/sharp_meteorite.png and b/public/images/items/sharp_meteorite.png differ diff --git a/public/images/items/sharpedonite.png b/public/images/items/sharpedonite.png index b3eabd5650f..c409b9afef7 100644 Binary files a/public/images/items/sharpedonite.png and b/public/images/items/sharpedonite.png differ diff --git a/public/images/items/shed_shell.png b/public/images/items/shed_shell.png index 17981864897..855b9034e49 100644 Binary files a/public/images/items/shed_shell.png and b/public/images/items/shed_shell.png differ diff --git a/public/images/items/shell_bell.png b/public/images/items/shell_bell.png index 6367d134672..9a99aeebae6 100644 Binary files a/public/images/items/shell_bell.png and b/public/images/items/shell_bell.png differ diff --git a/public/images/items/shiny_charm.png b/public/images/items/shiny_charm.png index 9f692828d04..de2ca39bf5b 100644 Binary files a/public/images/items/shiny_charm.png and b/public/images/items/shiny_charm.png differ diff --git a/public/images/items/shiny_stone.png b/public/images/items/shiny_stone.png index d2fabe6ec8b..01919d39107 100644 Binary files a/public/images/items/shiny_stone.png and b/public/images/items/shiny_stone.png differ diff --git a/public/images/items/shock_drive.png b/public/images/items/shock_drive.png index 592b0085a8f..6bf2602386f 100644 Binary files a/public/images/items/shock_drive.png and b/public/images/items/shock_drive.png differ diff --git a/public/images/items/silk_scarf.png b/public/images/items/silk_scarf.png index 83f056c8b41..993e9841dd7 100644 Binary files a/public/images/items/silk_scarf.png and b/public/images/items/silk_scarf.png differ diff --git a/public/images/items/silver_powder.png b/public/images/items/silver_powder.png index eb717fc6776..fee080a8612 100644 Binary files a/public/images/items/silver_powder.png and b/public/images/items/silver_powder.png differ diff --git a/public/images/items/sitrus_berry.png b/public/images/items/sitrus_berry.png index 703657ad8dd..d26f506e403 100644 Binary files a/public/images/items/sitrus_berry.png and b/public/images/items/sitrus_berry.png differ diff --git a/public/images/items/sky_plate.png b/public/images/items/sky_plate.png index 293bb8f5dc2..be463d5dbdf 100644 Binary files a/public/images/items/sky_plate.png and b/public/images/items/sky_plate.png differ diff --git a/public/images/items/slowbronite.png b/public/images/items/slowbronite.png index 3dc21ffb550..6e0ff2ad294 100644 Binary files a/public/images/items/slowbronite.png and b/public/images/items/slowbronite.png differ diff --git a/public/images/items/smooth_meteorite.png b/public/images/items/smooth_meteorite.png index 059e8e91a30..9e0361e8be2 100644 Binary files a/public/images/items/smooth_meteorite.png and b/public/images/items/smooth_meteorite.png differ diff --git a/public/images/items/soft_sand.png b/public/images/items/soft_sand.png index feda8cd3858..380be0052a5 100644 Binary files a/public/images/items/soft_sand.png and b/public/images/items/soft_sand.png differ diff --git a/public/images/items/soothe_bell.png b/public/images/items/soothe_bell.png index fbceb808c56..d21da03f663 100644 Binary files a/public/images/items/soothe_bell.png and b/public/images/items/soothe_bell.png differ diff --git a/public/images/items/soul_dew.png b/public/images/items/soul_dew.png index 56b9655714b..8458cb04cfd 100644 Binary files a/public/images/items/soul_dew.png and b/public/images/items/soul_dew.png differ diff --git a/public/images/items/spell_tag.png b/public/images/items/spell_tag.png index f9270c6e0a6..0dc730371d5 100644 Binary files a/public/images/items/spell_tag.png and b/public/images/items/spell_tag.png differ diff --git a/public/images/items/splash_plate.png b/public/images/items/splash_plate.png index d86ae5eab57..57a310c7d4f 100644 Binary files a/public/images/items/splash_plate.png and b/public/images/items/splash_plate.png differ diff --git a/public/images/items/spooky_plate.png b/public/images/items/spooky_plate.png index d7df7d25e32..0de26ac30ea 100644 Binary files a/public/images/items/spooky_plate.png and b/public/images/items/spooky_plate.png differ diff --git a/public/images/items/starf_berry.png b/public/images/items/starf_berry.png index cbf423e8343..6c1fb5fd0d4 100644 Binary files a/public/images/items/starf_berry.png and b/public/images/items/starf_berry.png differ diff --git a/public/images/items/steel_memory.png b/public/images/items/steel_memory.png index 8c45bc8b640..764ba8d6022 100644 Binary files a/public/images/items/steel_memory.png and b/public/images/items/steel_memory.png differ diff --git a/public/images/items/steel_tera_shard.png b/public/images/items/steel_tera_shard.png index c93f8c52f60..1cd37754648 100644 Binary files a/public/images/items/steel_tera_shard.png and b/public/images/items/steel_tera_shard.png differ diff --git a/public/images/items/steelixite.png b/public/images/items/steelixite.png index 429c668e656..92a73b854b7 100644 Binary files a/public/images/items/steelixite.png and b/public/images/items/steelixite.png differ diff --git a/public/images/items/stellar_tera_shard.png b/public/images/items/stellar_tera_shard.png index 1d5982f5b94..7d0644646a1 100644 Binary files a/public/images/items/stellar_tera_shard.png and b/public/images/items/stellar_tera_shard.png differ diff --git a/public/images/items/stone_plate.png b/public/images/items/stone_plate.png index dfc3a0cd132..782874f7ca7 100644 Binary files a/public/images/items/stone_plate.png and b/public/images/items/stone_plate.png differ diff --git a/public/images/items/strange_ball.png b/public/images/items/strange_ball.png index 2ddce33d7ac..3c8c7d71aa4 100644 Binary files a/public/images/items/strange_ball.png and b/public/images/items/strange_ball.png differ diff --git a/public/images/items/strawberry_sweet.png b/public/images/items/strawberry_sweet.png index 5df6e1cd8be..0089247b6b9 100644 Binary files a/public/images/items/strawberry_sweet.png and b/public/images/items/strawberry_sweet.png differ diff --git a/public/images/items/sun_flute.png b/public/images/items/sun_flute.png index f81d35fde9c..a792d42244c 100644 Binary files a/public/images/items/sun_flute.png and b/public/images/items/sun_flute.png differ diff --git a/public/images/items/sun_stone.png b/public/images/items/sun_stone.png index 33a7e8f8387..cc26cdf4a63 100644 Binary files a/public/images/items/sun_stone.png and b/public/images/items/sun_stone.png differ diff --git a/public/images/items/super_exp_charm.png b/public/images/items/super_exp_charm.png index ccd73f558cc..fd12fcdbc34 100644 Binary files a/public/images/items/super_exp_charm.png and b/public/images/items/super_exp_charm.png differ diff --git a/public/images/items/super_lure.png b/public/images/items/super_lure.png index 9c2213cd964..56c9abe59d2 100644 Binary files a/public/images/items/super_lure.png and b/public/images/items/super_lure.png differ diff --git a/public/images/items/super_potion.png b/public/images/items/super_potion.png index 16205cb0fff..def35f4d363 100644 Binary files a/public/images/items/super_potion.png and b/public/images/items/super_potion.png differ diff --git a/public/images/items/super_repel.png b/public/images/items/super_repel.png index 628cdbc5c1d..a8a0394832c 100644 Binary files a/public/images/items/super_repel.png and b/public/images/items/super_repel.png differ diff --git a/public/images/items/swampertite.png b/public/images/items/swampertite.png index d4d6415f6f4..5b4b422e034 100644 Binary files a/public/images/items/swampertite.png and b/public/images/items/swampertite.png differ diff --git a/public/images/items/sweet_apple.png b/public/images/items/sweet_apple.png index cfdf79a9eac..de183d1c644 100644 Binary files a/public/images/items/sweet_apple.png and b/public/images/items/sweet_apple.png differ diff --git a/public/images/items/syrupy_apple.png b/public/images/items/syrupy_apple.png index 9c9d05e2e0a..e6bebc8f3c8 100644 Binary files a/public/images/items/syrupy_apple.png and b/public/images/items/syrupy_apple.png differ diff --git a/public/images/items/tart_apple.png b/public/images/items/tart_apple.png index ad9a1728bb5..67f90802382 100644 Binary files a/public/images/items/tart_apple.png and b/public/images/items/tart_apple.png differ diff --git a/public/images/items/tera_orb.png b/public/images/items/tera_orb.png index d8720e41669..52348dc95fc 100644 Binary files a/public/images/items/tera_orb.png and b/public/images/items/tera_orb.png differ diff --git a/public/images/items/thick_club.png b/public/images/items/thick_club.png index f15885f0190..193540ff0b1 100644 Binary files a/public/images/items/thick_club.png and b/public/images/items/thick_club.png differ diff --git a/public/images/items/thunder_stone.png b/public/images/items/thunder_stone.png index 9e87909516b..dc54772c294 100644 Binary files a/public/images/items/thunder_stone.png and b/public/images/items/thunder_stone.png differ diff --git a/public/images/items/tm_bug.png b/public/images/items/tm_bug.png index 229230198c4..191f6b93c82 100644 Binary files a/public/images/items/tm_bug.png and b/public/images/items/tm_bug.png differ diff --git a/public/images/items/tm_dark.png b/public/images/items/tm_dark.png index 6856b1d5444..f2aab224ff9 100644 Binary files a/public/images/items/tm_dark.png and b/public/images/items/tm_dark.png differ diff --git a/public/images/items/tm_dragon.png b/public/images/items/tm_dragon.png index 62405519132..bf31cf2565e 100644 Binary files a/public/images/items/tm_dragon.png and b/public/images/items/tm_dragon.png differ diff --git a/public/images/items/tm_electric.png b/public/images/items/tm_electric.png index 34e4fe8de7b..96022151293 100644 Binary files a/public/images/items/tm_electric.png and b/public/images/items/tm_electric.png differ diff --git a/public/images/items/tm_fairy.png b/public/images/items/tm_fairy.png index 056783a8d63..f62104d0ba0 100644 Binary files a/public/images/items/tm_fairy.png and b/public/images/items/tm_fairy.png differ diff --git a/public/images/items/tm_fighting.png b/public/images/items/tm_fighting.png index 144d75826cd..0359997d820 100644 Binary files a/public/images/items/tm_fighting.png and b/public/images/items/tm_fighting.png differ diff --git a/public/images/items/tm_fire.png b/public/images/items/tm_fire.png index ae19e381873..f4880a6c888 100644 Binary files a/public/images/items/tm_fire.png and b/public/images/items/tm_fire.png differ diff --git a/public/images/items/tm_flying.png b/public/images/items/tm_flying.png index 3db06a69e68..ebadc39a88a 100644 Binary files a/public/images/items/tm_flying.png and b/public/images/items/tm_flying.png differ diff --git a/public/images/items/tm_ghost.png b/public/images/items/tm_ghost.png index a7e8928aa7e..ef0fd9f6927 100644 Binary files a/public/images/items/tm_ghost.png and b/public/images/items/tm_ghost.png differ diff --git a/public/images/items/tm_grass.png b/public/images/items/tm_grass.png index 42f11f590eb..f81d27b7ff0 100644 Binary files a/public/images/items/tm_grass.png and b/public/images/items/tm_grass.png differ diff --git a/public/images/items/tm_ground.png b/public/images/items/tm_ground.png index ff9dc7c7384..e8bf5d4da04 100644 Binary files a/public/images/items/tm_ground.png and b/public/images/items/tm_ground.png differ diff --git a/public/images/items/tm_ice.png b/public/images/items/tm_ice.png index 4edbb447a3e..eece8623654 100644 Binary files a/public/images/items/tm_ice.png and b/public/images/items/tm_ice.png differ diff --git a/public/images/items/tm_normal.png b/public/images/items/tm_normal.png index 19afe85d442..cede4cb96e2 100644 Binary files a/public/images/items/tm_normal.png and b/public/images/items/tm_normal.png differ diff --git a/public/images/items/tm_poison.png b/public/images/items/tm_poison.png index 83cb4488f33..5cea7211e95 100644 Binary files a/public/images/items/tm_poison.png and b/public/images/items/tm_poison.png differ diff --git a/public/images/items/tm_psychic.png b/public/images/items/tm_psychic.png index 4296914eda2..8767e879b21 100644 Binary files a/public/images/items/tm_psychic.png and b/public/images/items/tm_psychic.png differ diff --git a/public/images/items/tm_rock.png b/public/images/items/tm_rock.png index 6e13a1835e7..1a6be381a1a 100644 Binary files a/public/images/items/tm_rock.png and b/public/images/items/tm_rock.png differ diff --git a/public/images/items/tm_steel.png b/public/images/items/tm_steel.png index 32071b0843b..3fbb61b47df 100644 Binary files a/public/images/items/tm_steel.png and b/public/images/items/tm_steel.png differ diff --git a/public/images/items/tm_water.png b/public/images/items/tm_water.png index 85403f20be7..3f9122a3862 100644 Binary files a/public/images/items/tm_water.png and b/public/images/items/tm_water.png differ diff --git a/public/images/items/toxic_orb.png b/public/images/items/toxic_orb.png index 3483c13ba2b..8b8a1026f92 100644 Binary files a/public/images/items/toxic_orb.png and b/public/images/items/toxic_orb.png differ diff --git a/public/images/items/toxic_plate.png b/public/images/items/toxic_plate.png index efb2cff129c..721b275f7d7 100644 Binary files a/public/images/items/toxic_plate.png and b/public/images/items/toxic_plate.png differ diff --git a/public/images/items/twisted_spoon.png b/public/images/items/twisted_spoon.png index 9bb23b04386..07908d6cd0f 100644 Binary files a/public/images/items/twisted_spoon.png and b/public/images/items/twisted_spoon.png differ diff --git a/public/images/items/tyranitarite.png b/public/images/items/tyranitarite.png index 691f8c6123d..3873e7784b1 100644 Binary files a/public/images/items/tyranitarite.png and b/public/images/items/tyranitarite.png differ diff --git a/public/images/items/ub.png b/public/images/items/ub.png index 6ad0702223a..76fe1f28675 100644 Binary files a/public/images/items/ub.png and b/public/images/items/ub.png differ diff --git a/public/images/items/ultra_ribbon.png b/public/images/items/ultra_ribbon.png index a63ec63e394..f1342eb3bae 100644 Binary files a/public/images/items/ultra_ribbon.png and b/public/images/items/ultra_ribbon.png differ diff --git a/public/images/items/ultranecrozium_z.png b/public/images/items/ultranecrozium_z.png index cdcb29e6c26..f95d722873e 100644 Binary files a/public/images/items/ultranecrozium_z.png and b/public/images/items/ultranecrozium_z.png differ diff --git a/public/images/items/unknown.png b/public/images/items/unknown.png index 2d0637048ae..17697d0d640 100644 Binary files a/public/images/items/unknown.png and b/public/images/items/unknown.png differ diff --git a/public/images/items/unremarkable_teacup.png b/public/images/items/unremarkable_teacup.png index 340214b6a5c..154d8284e0d 100644 Binary files a/public/images/items/unremarkable_teacup.png and b/public/images/items/unremarkable_teacup.png differ diff --git a/public/images/items/upgrade.png b/public/images/items/upgrade.png index 11a7ade4d6a..3fe4f03d15a 100644 Binary files a/public/images/items/upgrade.png and b/public/images/items/upgrade.png differ diff --git a/public/images/items/venusaurite.png b/public/images/items/venusaurite.png index 845d1cef75a..853b37417e3 100644 Binary files a/public/images/items/venusaurite.png and b/public/images/items/venusaurite.png differ diff --git a/public/images/items/water_memory.png b/public/images/items/water_memory.png index a0cf8b3c5c9..fb3d33dfbce 100644 Binary files a/public/images/items/water_memory.png and b/public/images/items/water_memory.png differ diff --git a/public/images/items/water_stone.png b/public/images/items/water_stone.png index fa3d924960b..6972b36573c 100644 Binary files a/public/images/items/water_stone.png and b/public/images/items/water_stone.png differ diff --git a/public/images/items/water_tera_shard.png b/public/images/items/water_tera_shard.png index eec8caf1245..1cab0ad8f33 100644 Binary files a/public/images/items/water_tera_shard.png and b/public/images/items/water_tera_shard.png differ diff --git a/public/images/items/wellspring_mask.png b/public/images/items/wellspring_mask.png index 921c71e1493..bec005856e3 100644 Binary files a/public/images/items/wellspring_mask.png and b/public/images/items/wellspring_mask.png differ diff --git a/public/images/items/whipped_dream.png b/public/images/items/whipped_dream.png index 16090ccaac0..d02a3fb9807 100644 Binary files a/public/images/items/whipped_dream.png and b/public/images/items/whipped_dream.png differ diff --git a/public/images/items/white_herb.png b/public/images/items/white_herb.png index 829c64f188e..0729602fd15 100644 Binary files a/public/images/items/white_herb.png and b/public/images/items/white_herb.png differ diff --git a/public/images/items/wide_lens.png b/public/images/items/wide_lens.png index f7dbe9843fa..dcfb43378a0 100644 Binary files a/public/images/items/wide_lens.png and b/public/images/items/wide_lens.png differ diff --git a/public/images/items/wise_glasses.png b/public/images/items/wise_glasses.png index b48d2cb3ffd..765c88524d0 100644 Binary files a/public/images/items/wise_glasses.png and b/public/images/items/wise_glasses.png differ diff --git a/public/images/items/wl_ability_urge.png b/public/images/items/wl_ability_urge.png index b6b2c8bd398..7312de48a94 100644 Binary files a/public/images/items/wl_ability_urge.png and b/public/images/items/wl_ability_urge.png differ diff --git a/public/images/items/wl_antidote.png b/public/images/items/wl_antidote.png index 93e4757938b..18a74292dcf 100644 Binary files a/public/images/items/wl_antidote.png and b/public/images/items/wl_antidote.png differ diff --git a/public/images/items/wl_awakening.png b/public/images/items/wl_awakening.png index 57e83b30cda..4fa59e2d10d 100644 Binary files a/public/images/items/wl_awakening.png and b/public/images/items/wl_awakening.png differ diff --git a/public/images/items/wl_burn_heal.png b/public/images/items/wl_burn_heal.png index 3565af12d62..98fa6c11766 100644 Binary files a/public/images/items/wl_burn_heal.png and b/public/images/items/wl_burn_heal.png differ diff --git a/public/images/items/wl_custom_spliced.png b/public/images/items/wl_custom_spliced.png index 8aef30edb27..de344ae5704 100644 Binary files a/public/images/items/wl_custom_spliced.png and b/public/images/items/wl_custom_spliced.png differ diff --git a/public/images/items/wl_custom_thief.png b/public/images/items/wl_custom_thief.png index de6d62a48bc..aa6f9e0acc7 100644 Binary files a/public/images/items/wl_custom_thief.png and b/public/images/items/wl_custom_thief.png differ diff --git a/public/images/items/wl_elixir.png b/public/images/items/wl_elixir.png index 05eb590c484..e9b7b48ab87 100644 Binary files a/public/images/items/wl_elixir.png and b/public/images/items/wl_elixir.png differ diff --git a/public/images/items/wl_ether.png b/public/images/items/wl_ether.png index 114e8ffc941..457f2bf0649 100644 Binary files a/public/images/items/wl_ether.png and b/public/images/items/wl_ether.png differ diff --git a/public/images/items/wl_full_heal.png b/public/images/items/wl_full_heal.png index 215f9801d65..361d432781a 100644 Binary files a/public/images/items/wl_full_heal.png and b/public/images/items/wl_full_heal.png differ diff --git a/public/images/items/wl_full_restore.png b/public/images/items/wl_full_restore.png index d6d4ffe720f..0e9399f7c77 100644 Binary files a/public/images/items/wl_full_restore.png and b/public/images/items/wl_full_restore.png differ diff --git a/public/images/items/wl_guard_spec.png b/public/images/items/wl_guard_spec.png index 9d22029e927..4207e5094b9 100644 Binary files a/public/images/items/wl_guard_spec.png and b/public/images/items/wl_guard_spec.png differ diff --git a/public/images/items/wl_hyper_potion.png b/public/images/items/wl_hyper_potion.png index 1eb075445e4..016c257a1eb 100644 Binary files a/public/images/items/wl_hyper_potion.png and b/public/images/items/wl_hyper_potion.png differ diff --git a/public/images/items/wl_ice_heal.png b/public/images/items/wl_ice_heal.png index 9ac297c0e7c..619d30d9ca9 100644 Binary files a/public/images/items/wl_ice_heal.png and b/public/images/items/wl_ice_heal.png differ diff --git a/public/images/items/wl_item_drop.png b/public/images/items/wl_item_drop.png index 73425749670..66e81677917 100644 Binary files a/public/images/items/wl_item_drop.png and b/public/images/items/wl_item_drop.png differ diff --git a/public/images/items/wl_item_urge.png b/public/images/items/wl_item_urge.png index 17a6343058f..766751fa329 100644 Binary files a/public/images/items/wl_item_urge.png and b/public/images/items/wl_item_urge.png differ diff --git a/public/images/items/wl_max_elixir.png b/public/images/items/wl_max_elixir.png index 4a33dc853fd..4df3eaff10a 100644 Binary files a/public/images/items/wl_max_elixir.png and b/public/images/items/wl_max_elixir.png differ diff --git a/public/images/items/wl_max_ether.png b/public/images/items/wl_max_ether.png index d3f69ce053d..0e174f9eba4 100644 Binary files a/public/images/items/wl_max_ether.png and b/public/images/items/wl_max_ether.png differ diff --git a/public/images/items/wl_max_potion.png b/public/images/items/wl_max_potion.png index 6b58c07e86a..2f334795489 100644 Binary files a/public/images/items/wl_max_potion.png and b/public/images/items/wl_max_potion.png differ diff --git a/public/images/items/wl_max_revive.png b/public/images/items/wl_max_revive.png index f2eabaa8d5b..89528b0fe8a 100644 Binary files a/public/images/items/wl_max_revive.png and b/public/images/items/wl_max_revive.png differ diff --git a/public/images/items/wl_paralyze_heal.png b/public/images/items/wl_paralyze_heal.png index 8e89e54c156..ba9c172fa17 100644 Binary files a/public/images/items/wl_paralyze_heal.png and b/public/images/items/wl_paralyze_heal.png differ diff --git a/public/images/items/wl_potion.png b/public/images/items/wl_potion.png index 619c5d2b014..a70bba17b2f 100644 Binary files a/public/images/items/wl_potion.png and b/public/images/items/wl_potion.png differ diff --git a/public/images/items/wl_reset_urge.png b/public/images/items/wl_reset_urge.png index 0d2f94504f0..197bcc829fa 100644 Binary files a/public/images/items/wl_reset_urge.png and b/public/images/items/wl_reset_urge.png differ diff --git a/public/images/items/wl_revive.png b/public/images/items/wl_revive.png index c01b5f68853..36ac67d5a60 100644 Binary files a/public/images/items/wl_revive.png and b/public/images/items/wl_revive.png differ diff --git a/public/images/items/wl_super_potion.png b/public/images/items/wl_super_potion.png index eaf60d6c882..5bf5dd8fc89 100644 Binary files a/public/images/items/wl_super_potion.png and b/public/images/items/wl_super_potion.png differ diff --git a/public/images/items/x_accuracy.png b/public/images/items/x_accuracy.png index e7af52fc684..4e51324faef 100644 Binary files a/public/images/items/x_accuracy.png and b/public/images/items/x_accuracy.png differ diff --git a/public/images/items/x_attack.png b/public/images/items/x_attack.png index 711aca7269c..a66167121f6 100644 Binary files a/public/images/items/x_attack.png and b/public/images/items/x_attack.png differ diff --git a/public/images/items/x_defense.png b/public/images/items/x_defense.png index ee8d3fa5e2d..a4f9675fc82 100644 Binary files a/public/images/items/x_defense.png and b/public/images/items/x_defense.png differ diff --git a/public/images/items/x_sp_atk.png b/public/images/items/x_sp_atk.png index f0e674e4d45..853df12a909 100644 Binary files a/public/images/items/x_sp_atk.png and b/public/images/items/x_sp_atk.png differ diff --git a/public/images/items/x_sp_def.png b/public/images/items/x_sp_def.png index 2ab43574af9..04cb91cd73f 100644 Binary files a/public/images/items/x_sp_def.png and b/public/images/items/x_sp_def.png differ diff --git a/public/images/items/x_speed.png b/public/images/items/x_speed.png index daebb295d77..7d9541531d3 100644 Binary files a/public/images/items/x_speed.png and b/public/images/items/x_speed.png differ diff --git a/public/images/items/zap_plate.png b/public/images/items/zap_plate.png index a966fb76cd3..a291624b7a3 100644 Binary files a/public/images/items/zap_plate.png and b/public/images/items/zap_plate.png differ diff --git a/public/images/items/zinc.png b/public/images/items/zinc.png index c1dfd47a9fc..fccbc59e9e8 100644 Binary files a/public/images/items/zinc.png and b/public/images/items/zinc.png differ diff --git a/public/images/items/zoom_lens.png b/public/images/items/zoom_lens.png index 30b3ed55bdf..4f1e5cc9e4b 100644 Binary files a/public/images/items/zoom_lens.png and b/public/images/items/zoom_lens.png differ diff --git a/public/images/logo.png b/public/images/logo.png index 16e559d9c38..bf6c0b19ef2 100644 Binary files a/public/images/logo.png and b/public/images/logo.png differ diff --git a/public/images/mystery-encounters/berries_abound_bush.png b/public/images/mystery-encounters/berries_abound_bush.png index e9be20b4863..741191d71c9 100644 Binary files a/public/images/mystery-encounters/berries_abound_bush.png and b/public/images/mystery-encounters/berries_abound_bush.png differ diff --git a/public/images/mystery-encounters/dark_deal_porygon.png b/public/images/mystery-encounters/dark_deal_porygon.png index 168999fb0f4..97013af65a1 100644 Binary files a/public/images/mystery-encounters/dark_deal_porygon.png and b/public/images/mystery-encounters/dark_deal_porygon.png differ diff --git a/public/images/mystery-encounters/dark_deal_scientist.png b/public/images/mystery-encounters/dark_deal_scientist.png index 453cb767ec1..980f5109f30 100644 Binary files a/public/images/mystery-encounters/dark_deal_scientist.png and b/public/images/mystery-encounters/dark_deal_scientist.png differ diff --git a/public/images/mystery-encounters/department_store_sale_lady.png b/public/images/mystery-encounters/department_store_sale_lady.png index 9dcc1281c9e..b11034d3c99 100644 Binary files a/public/images/mystery-encounters/department_store_sale_lady.png and b/public/images/mystery-encounters/department_store_sale_lady.png differ diff --git a/public/images/mystery-encounters/encounter_exclaim.png b/public/images/mystery-encounters/encounter_exclaim.png index a7727f4da2e..25b288c9700 100644 Binary files a/public/images/mystery-encounters/encounter_exclaim.png and b/public/images/mystery-encounters/encounter_exclaim.png differ diff --git a/public/images/mystery-encounters/encounter_radar.png b/public/images/mystery-encounters/encounter_radar.png index deb9426c269..9af44cd452e 100644 Binary files a/public/images/mystery-encounters/encounter_radar.png and b/public/images/mystery-encounters/encounter_radar.png differ diff --git a/public/images/mystery-encounters/field_trip_teacher.png b/public/images/mystery-encounters/field_trip_teacher.png index b4332bc0032..22a6a316297 100644 Binary files a/public/images/mystery-encounters/field_trip_teacher.png and b/public/images/mystery-encounters/field_trip_teacher.png differ diff --git a/public/images/mystery-encounters/fun_and_games_game.png b/public/images/mystery-encounters/fun_and_games_game.png index 03a3b9c9cbc..8b087c7a31b 100644 Binary files a/public/images/mystery-encounters/fun_and_games_game.png and b/public/images/mystery-encounters/fun_and_games_game.png differ diff --git a/public/images/mystery-encounters/fun_and_games_man.png b/public/images/mystery-encounters/fun_and_games_man.png index 05f94dbd33d..42c3b4996fa 100644 Binary files a/public/images/mystery-encounters/fun_and_games_man.png and b/public/images/mystery-encounters/fun_and_games_man.png differ diff --git a/public/images/mystery-encounters/fun_and_games_wobbuffet.png b/public/images/mystery-encounters/fun_and_games_wobbuffet.png index 37e7220196a..71997be5692 100644 Binary files a/public/images/mystery-encounters/fun_and_games_wobbuffet.png and b/public/images/mystery-encounters/fun_and_games_wobbuffet.png differ diff --git a/public/images/mystery-encounters/global_trade_system.png b/public/images/mystery-encounters/global_trade_system.png index cb0ffb0ab20..fb6815c0240 100644 Binary files a/public/images/mystery-encounters/global_trade_system.png and b/public/images/mystery-encounters/global_trade_system.png differ diff --git a/public/images/mystery-encounters/lost_at_sea_buoy.png b/public/images/mystery-encounters/lost_at_sea_buoy.png index fb957ac29f0..0c7400b7760 100644 Binary files a/public/images/mystery-encounters/lost_at_sea_buoy.png and b/public/images/mystery-encounters/lost_at_sea_buoy.png differ diff --git a/public/images/mystery-encounters/mysterious_chest_blue.png b/public/images/mystery-encounters/mysterious_chest_blue.png index e67bdcafa04..f959db13aa7 100644 Binary files a/public/images/mystery-encounters/mysterious_chest_blue.png and b/public/images/mystery-encounters/mysterious_chest_blue.png differ diff --git a/public/images/mystery-encounters/mysterious_chest_red.png b/public/images/mystery-encounters/mysterious_chest_red.png index c20a8218be6..c312911040e 100644 Binary files a/public/images/mystery-encounters/mysterious_chest_red.png and b/public/images/mystery-encounters/mysterious_chest_red.png differ diff --git a/public/images/mystery-encounters/part_timer_crate.png b/public/images/mystery-encounters/part_timer_crate.png index fb70a6e534a..dd02c243613 100644 Binary files a/public/images/mystery-encounters/part_timer_crate.png and b/public/images/mystery-encounters/part_timer_crate.png differ diff --git a/public/images/mystery-encounters/pokemon_salesman.png b/public/images/mystery-encounters/pokemon_salesman.png index 1251dd8eda7..e5195cf6999 100644 Binary files a/public/images/mystery-encounters/pokemon_salesman.png and b/public/images/mystery-encounters/pokemon_salesman.png differ diff --git a/public/images/mystery-encounters/safari_zone.png b/public/images/mystery-encounters/safari_zone.png index 375d66ebbe9..54bf097656c 100644 Binary files a/public/images/mystery-encounters/safari_zone.png and b/public/images/mystery-encounters/safari_zone.png differ diff --git a/public/images/mystery-encounters/safari_zone_bait.png b/public/images/mystery-encounters/safari_zone_bait.png index 7de9169d187..2133aea6567 100644 Binary files a/public/images/mystery-encounters/safari_zone_bait.png and b/public/images/mystery-encounters/safari_zone_bait.png differ diff --git a/public/images/mystery-encounters/safari_zone_mud.png b/public/images/mystery-encounters/safari_zone_mud.png index 2ba7cb00047..750ff585353 100644 Binary files a/public/images/mystery-encounters/safari_zone_mud.png and b/public/images/mystery-encounters/safari_zone_mud.png differ diff --git a/public/images/mystery-encounters/shady_vitamin_dealer.png b/public/images/mystery-encounters/shady_vitamin_dealer.png index 967d82973e6..f63eadc6799 100644 Binary files a/public/images/mystery-encounters/shady_vitamin_dealer.png and b/public/images/mystery-encounters/shady_vitamin_dealer.png differ diff --git a/public/images/mystery-encounters/teleporting_hijinks_teleporter.png b/public/images/mystery-encounters/teleporting_hijinks_teleporter.png index 9a049c30ab1..178f1571194 100644 Binary files a/public/images/mystery-encounters/teleporting_hijinks_teleporter.png and b/public/images/mystery-encounters/teleporting_hijinks_teleporter.png differ diff --git a/public/images/mystery-encounters/training_session_gear.png b/public/images/mystery-encounters/training_session_gear.png index 42c3a9bb7d4..9ebc7530f64 100644 Binary files a/public/images/mystery-encounters/training_session_gear.png and b/public/images/mystery-encounters/training_session_gear.png differ diff --git a/public/images/mystery-encounters/weird_dream_woman.png b/public/images/mystery-encounters/weird_dream_woman.png index 50d04667152..65fe2f30841 100644 Binary files a/public/images/mystery-encounters/weird_dream_woman.png and b/public/images/mystery-encounters/weird_dream_woman.png differ diff --git a/public/images/pb.png b/public/images/pb.png index acb3a96f066..364d864f7fe 100644 Binary files a/public/images/pb.png and b/public/images/pb.png differ diff --git a/public/images/pokeball/gb.png b/public/images/pokeball/gb.png index 94f278c8ef1..54980d63766 100644 Binary files a/public/images/pokeball/gb.png and b/public/images/pokeball/gb.png differ diff --git a/public/images/pokeball/gb_open.png b/public/images/pokeball/gb_open.png index 26d8956fd27..c5a6e43e705 100644 Binary files a/public/images/pokeball/gb_open.png and b/public/images/pokeball/gb_open.png differ diff --git a/public/images/pokeball/gb_opening.png b/public/images/pokeball/gb_opening.png index 9947f7ceb37..075aafeb460 100644 Binary files a/public/images/pokeball/gb_opening.png and b/public/images/pokeball/gb_opening.png differ diff --git a/public/images/pokeball/lb.png b/public/images/pokeball/lb.png index 1a5639d48e9..2aedfafec3c 100644 Binary files a/public/images/pokeball/lb.png and b/public/images/pokeball/lb.png differ diff --git a/public/images/pokeball/lb_open.png b/public/images/pokeball/lb_open.png index fd2b9c71953..6ec104da2d8 100644 Binary files a/public/images/pokeball/lb_open.png and b/public/images/pokeball/lb_open.png differ diff --git a/public/images/pokeball/lb_opening.png b/public/images/pokeball/lb_opening.png index e62517bd67a..b5f0cffa55a 100644 Binary files a/public/images/pokeball/lb_opening.png and b/public/images/pokeball/lb_opening.png differ diff --git a/public/images/pokeball/mb.png b/public/images/pokeball/mb.png index de374e0fd7c..cc7b7d3164b 100644 Binary files a/public/images/pokeball/mb.png and b/public/images/pokeball/mb.png differ diff --git a/public/images/pokeball/mb_open.png b/public/images/pokeball/mb_open.png index f4e0145dcd4..b0d21c800c2 100644 Binary files a/public/images/pokeball/mb_open.png and b/public/images/pokeball/mb_open.png differ diff --git a/public/images/pokeball/mb_opening.png b/public/images/pokeball/mb_opening.png index 9c483fbbb9c..a13437ba70a 100644 Binary files a/public/images/pokeball/mb_opening.png and b/public/images/pokeball/mb_opening.png differ diff --git a/public/images/pokeball/pb.png b/public/images/pokeball/pb.png index 684ed6b0a5f..7404b3129b7 100644 Binary files a/public/images/pokeball/pb.png and b/public/images/pokeball/pb.png differ diff --git a/public/images/pokeball/pb_open.png b/public/images/pokeball/pb_open.png index 62db56cddde..0fdfa7b98fa 100644 Binary files a/public/images/pokeball/pb_open.png and b/public/images/pokeball/pb_open.png differ diff --git a/public/images/pokeball/pb_opening.png b/public/images/pokeball/pb_opening.png index 4f13a9b46cb..1a7136f186d 100644 Binary files a/public/images/pokeball/pb_opening.png and b/public/images/pokeball/pb_opening.png differ diff --git a/public/images/pokeball/rb.png b/public/images/pokeball/rb.png index e2e1f1b077e..5c4c616d72d 100644 Binary files a/public/images/pokeball/rb.png and b/public/images/pokeball/rb.png differ diff --git a/public/images/pokeball/rb_open.png b/public/images/pokeball/rb_open.png index 94d480b5828..3fe5f6e81c7 100644 Binary files a/public/images/pokeball/rb_open.png and b/public/images/pokeball/rb_open.png differ diff --git a/public/images/pokeball/rb_opening.png b/public/images/pokeball/rb_opening.png index 76aae3c1772..76f4989755d 100644 Binary files a/public/images/pokeball/rb_opening.png and b/public/images/pokeball/rb_opening.png differ diff --git a/public/images/pokeball/ub.png b/public/images/pokeball/ub.png index 306cb168428..92f11481dce 100644 Binary files a/public/images/pokeball/ub.png and b/public/images/pokeball/ub.png differ diff --git a/public/images/pokeball/ub_open.png b/public/images/pokeball/ub_open.png index 2ccf2ae366d..29e5ddac92f 100644 Binary files a/public/images/pokeball/ub_open.png and b/public/images/pokeball/ub_open.png differ diff --git a/public/images/pokeball/ub_opening.png b/public/images/pokeball/ub_opening.png index 6c8e7711201..0696c666d95 100644 Binary files a/public/images/pokeball/ub_opening.png and b/public/images/pokeball/ub_opening.png differ diff --git a/public/images/pokemon_icons_0.png b/public/images/pokemon_icons_0.png index 134fe8c1bee..dd91c8358a2 100644 Binary files a/public/images/pokemon_icons_0.png and b/public/images/pokemon_icons_0.png differ diff --git a/public/images/pokemon_icons_1.png b/public/images/pokemon_icons_1.png index 4091f15165f..de7342ad141 100644 Binary files a/public/images/pokemon_icons_1.png and b/public/images/pokemon_icons_1.png differ diff --git a/public/images/pokemon_icons_1v.png b/public/images/pokemon_icons_1v.png index 025c1ab025a..8e62eef7c76 100644 Binary files a/public/images/pokemon_icons_1v.png and b/public/images/pokemon_icons_1v.png differ diff --git a/public/images/pokemon_icons_2.png b/public/images/pokemon_icons_2.png index fed696d7154..218c4345069 100644 Binary files a/public/images/pokemon_icons_2.png and b/public/images/pokemon_icons_2.png differ diff --git a/public/images/pokemon_icons_2v.png b/public/images/pokemon_icons_2v.png index fea0fb339ce..e6b0a58fe96 100644 Binary files a/public/images/pokemon_icons_2v.png and b/public/images/pokemon_icons_2v.png differ diff --git a/public/images/pokemon_icons_3.png b/public/images/pokemon_icons_3.png index a0eec0a5290..cf0c1b2c3c9 100644 Binary files a/public/images/pokemon_icons_3.png and b/public/images/pokemon_icons_3.png differ diff --git a/public/images/pokemon_icons_3v.png b/public/images/pokemon_icons_3v.png index b151e36e72c..ba4bcfdeb57 100644 Binary files a/public/images/pokemon_icons_3v.png and b/public/images/pokemon_icons_3v.png differ diff --git a/public/images/pokemon_icons_4.png b/public/images/pokemon_icons_4.png index 5b271a3930c..38bb31617f9 100644 Binary files a/public/images/pokemon_icons_4.png and b/public/images/pokemon_icons_4.png differ diff --git a/public/images/pokemon_icons_4v.png b/public/images/pokemon_icons_4v.png index 972bff8b777..0b207777830 100644 Binary files a/public/images/pokemon_icons_4v.png and b/public/images/pokemon_icons_4v.png differ diff --git a/public/images/pokemon_icons_5.png b/public/images/pokemon_icons_5.png index 12fa2e99e24..5f80eaaff67 100644 Binary files a/public/images/pokemon_icons_5.png and b/public/images/pokemon_icons_5.png differ diff --git a/public/images/pokemon_icons_5v.png b/public/images/pokemon_icons_5v.png index 881f430447e..7ca76312c2b 100644 Binary files a/public/images/pokemon_icons_5v.png and b/public/images/pokemon_icons_5v.png differ diff --git a/public/images/pokemon_icons_6.png b/public/images/pokemon_icons_6.png index 272018b672b..47a5274c3d1 100644 Binary files a/public/images/pokemon_icons_6.png and b/public/images/pokemon_icons_6.png differ diff --git a/public/images/pokemon_icons_6v.png b/public/images/pokemon_icons_6v.png index d8bd10eaa71..be2bd1621fb 100644 Binary files a/public/images/pokemon_icons_6v.png and b/public/images/pokemon_icons_6v.png differ diff --git a/public/images/pokemon_icons_7.png b/public/images/pokemon_icons_7.png index e541af01c2d..c2ffe6ad9f3 100644 Binary files a/public/images/pokemon_icons_7.png and b/public/images/pokemon_icons_7.png differ diff --git a/public/images/pokemon_icons_7v.png b/public/images/pokemon_icons_7v.png index 732e67e5219..8164a6bb37b 100644 Binary files a/public/images/pokemon_icons_7v.png and b/public/images/pokemon_icons_7v.png differ diff --git a/public/images/pokemon_icons_8.png b/public/images/pokemon_icons_8.png index 3003f3b9e0e..4bcbb68ac35 100644 Binary files a/public/images/pokemon_icons_8.png and b/public/images/pokemon_icons_8.png differ diff --git a/public/images/pokemon_icons_8v.png b/public/images/pokemon_icons_8v.png index 4017b0945d6..183a87bbad4 100644 Binary files a/public/images/pokemon_icons_8v.png and b/public/images/pokemon_icons_8v.png differ diff --git a/public/images/pokemon_icons_9.png b/public/images/pokemon_icons_9.png index 2985fb800d6..2cfc7c6272d 100644 Binary files a/public/images/pokemon_icons_9.png and b/public/images/pokemon_icons_9.png differ diff --git a/public/images/pokemon_icons_9v.png b/public/images/pokemon_icons_9v.png index 3636c3059d8..cc93a28d120 100644 Binary files a/public/images/pokemon_icons_9v.png and b/public/images/pokemon_icons_9v.png differ diff --git a/public/images/statuses.png b/public/images/statuses.png index d372b989be9..f678b9cb700 100644 Binary files a/public/images/statuses.png and b/public/images/statuses.png differ diff --git a/public/images/statuses/burn.png b/public/images/statuses/burn.png deleted file mode 100644 index e77cd88af3b..00000000000 Binary files a/public/images/statuses/burn.png and /dev/null differ diff --git a/public/images/statuses/faint.png b/public/images/statuses/faint.png deleted file mode 100644 index 12297bdab22..00000000000 Binary files a/public/images/statuses/faint.png and /dev/null differ diff --git a/public/images/statuses/freeze.png b/public/images/statuses/freeze.png deleted file mode 100644 index 5f768a5cef4..00000000000 Binary files a/public/images/statuses/freeze.png and /dev/null differ diff --git a/public/images/statuses/paralysis.png b/public/images/statuses/paralysis.png deleted file mode 100644 index ceda9071708..00000000000 Binary files a/public/images/statuses/paralysis.png and /dev/null differ diff --git a/public/images/statuses/poison.png b/public/images/statuses/poison.png deleted file mode 100644 index b9ed12adcfc..00000000000 Binary files a/public/images/statuses/poison.png and /dev/null differ diff --git a/public/images/statuses/pokerus.png b/public/images/statuses/pokerus.png deleted file mode 100644 index 2963d0081f8..00000000000 Binary files a/public/images/statuses/pokerus.png and /dev/null differ diff --git a/public/images/statuses/sleep.png b/public/images/statuses/sleep.png deleted file mode 100644 index 8a54bbab717..00000000000 Binary files a/public/images/statuses/sleep.png and /dev/null differ diff --git a/public/images/statuses/toxic.png b/public/images/statuses/toxic.png deleted file mode 100644 index c34fcb71d85..00000000000 Binary files a/public/images/statuses/toxic.png and /dev/null differ diff --git a/public/images/statuses_ca.png b/public/images/statuses_ca.png index fe05e243f7a..d6fb8fd0415 100644 Binary files a/public/images/statuses_ca.png and b/public/images/statuses_ca.png differ diff --git a/public/images/statuses_da.png b/public/images/statuses_da.png index 02780bddd98..274e960a565 100644 Binary files a/public/images/statuses_da.png and b/public/images/statuses_da.png differ diff --git a/public/images/statuses_de.png b/public/images/statuses_de.png index ab85384d591..140ba6326ca 100644 Binary files a/public/images/statuses_de.png and b/public/images/statuses_de.png differ diff --git a/public/images/statuses_es-ES.png b/public/images/statuses_es-ES.png index dc845d6fb1f..fbe87996506 100644 Binary files a/public/images/statuses_es-ES.png and b/public/images/statuses_es-ES.png differ diff --git a/public/images/statuses_es-MX.png b/public/images/statuses_es-MX.png index dc845d6fb1f..fbe87996506 100644 Binary files a/public/images/statuses_es-MX.png and b/public/images/statuses_es-MX.png differ diff --git a/public/images/statuses_fr.png b/public/images/statuses_fr.png index 95989cd5d97..611e6226a17 100644 Binary files a/public/images/statuses_fr.png and b/public/images/statuses_fr.png differ diff --git a/public/images/statuses_it.png b/public/images/statuses_it.png index af3107018f8..4ca1e908d94 100644 Binary files a/public/images/statuses_it.png and b/public/images/statuses_it.png differ diff --git a/public/images/statuses_ja.png b/public/images/statuses_ja.png index 305fbe9168c..433c54f04bd 100644 Binary files a/public/images/statuses_ja.png and b/public/images/statuses_ja.png differ diff --git a/public/images/statuses_ko.png b/public/images/statuses_ko.png index d372b989be9..a600231df7b 100644 Binary files a/public/images/statuses_ko.png and b/public/images/statuses_ko.png differ diff --git a/public/images/statuses_pt-BR.png b/public/images/statuses_pt-BR.png index 3073540e8a2..0c5124f12d8 100644 Binary files a/public/images/statuses_pt-BR.png and b/public/images/statuses_pt-BR.png differ diff --git a/public/images/statuses_ro.png b/public/images/statuses_ro.png index 537b634c520..0eb8e1fc32c 100644 Binary files a/public/images/statuses_ro.png and b/public/images/statuses_ro.png differ diff --git a/public/images/statuses_ru.png b/public/images/statuses_ru.png index 1da8b66a4f8..c579e454c0f 100644 Binary files a/public/images/statuses_ru.png and b/public/images/statuses_ru.png differ diff --git a/public/images/statuses_tr.png b/public/images/statuses_tr.png index d372b989be9..f678b9cb700 100644 Binary files a/public/images/statuses_tr.png and b/public/images/statuses_tr.png differ diff --git a/public/images/statuses_zh-CN.png b/public/images/statuses_zh-CN.png index d372b989be9..f678b9cb700 100644 Binary files a/public/images/statuses_zh-CN.png and b/public/images/statuses_zh-CN.png differ diff --git a/public/images/statuses_zh-TW.png b/public/images/statuses_zh-TW.png index d372b989be9..f678b9cb700 100644 Binary files a/public/images/statuses_zh-TW.png and b/public/images/statuses_zh-TW.png differ diff --git a/public/images/trainer/aaron.png b/public/images/trainer/aaron.png index a63f49849a6..e392942b43b 100644 Binary files a/public/images/trainer/aaron.png and b/public/images/trainer/aaron.png differ diff --git a/public/images/trainer/ace_trainer_f.png b/public/images/trainer/ace_trainer_f.png index 8774ba08044..c4bd56a4e97 100644 Binary files a/public/images/trainer/ace_trainer_f.png and b/public/images/trainer/ace_trainer_f.png differ diff --git a/public/images/trainer/ace_trainer_m.png b/public/images/trainer/ace_trainer_m.png index a7922b1b2d1..4b8524bd83c 100644 Binary files a/public/images/trainer/ace_trainer_m.png and b/public/images/trainer/ace_trainer_m.png differ diff --git a/public/images/trainer/acerola.png b/public/images/trainer/acerola.png index 3804d2986a3..49215970f25 100644 Binary files a/public/images/trainer/acerola.png and b/public/images/trainer/acerola.png differ diff --git a/public/images/trainer/aether_grunt_f.png b/public/images/trainer/aether_grunt_f.png index a5088c9e1de..30e1adea75f 100644 Binary files a/public/images/trainer/aether_grunt_f.png and b/public/images/trainer/aether_grunt_f.png differ diff --git a/public/images/trainer/aether_grunt_m.png b/public/images/trainer/aether_grunt_m.png index 1b1e092a340..c61970f4c98 100644 Binary files a/public/images/trainer/aether_grunt_m.png and b/public/images/trainer/aether_grunt_m.png differ diff --git a/public/images/trainer/agatha.png b/public/images/trainer/agatha.png index 71eed676318..d342f258577 100644 Binary files a/public/images/trainer/agatha.png and b/public/images/trainer/agatha.png differ diff --git a/public/images/trainer/alder.png b/public/images/trainer/alder.png index 552fe694929..83f4025d90c 100644 Binary files a/public/images/trainer/alder.png and b/public/images/trainer/alder.png differ diff --git a/public/images/trainer/allister.png b/public/images/trainer/allister.png index 07c20c1b275..c29781660d1 100644 Binary files a/public/images/trainer/allister.png and b/public/images/trainer/allister.png differ diff --git a/public/images/trainer/amarys.png b/public/images/trainer/amarys.png index a342c909f69..e4b43a2c82b 100644 Binary files a/public/images/trainer/amarys.png and b/public/images/trainer/amarys.png differ diff --git a/public/images/trainer/aqua_grunt_f.png b/public/images/trainer/aqua_grunt_f.png index 4db237bf9e7..61d6fd258bc 100644 Binary files a/public/images/trainer/aqua_grunt_f.png and b/public/images/trainer/aqua_grunt_f.png differ diff --git a/public/images/trainer/aqua_grunt_m.png b/public/images/trainer/aqua_grunt_m.png index 4fe230ca119..bddb6e9fa3c 100644 Binary files a/public/images/trainer/aqua_grunt_m.png and b/public/images/trainer/aqua_grunt_m.png differ diff --git a/public/images/trainer/archie.png b/public/images/trainer/archie.png index d4c003fd348..7b047322bfb 100644 Binary files a/public/images/trainer/archie.png and b/public/images/trainer/archie.png differ diff --git a/public/images/trainer/artist.png b/public/images/trainer/artist.png index 3843508aa36..99e5264d250 100644 Binary files a/public/images/trainer/artist.png and b/public/images/trainer/artist.png differ diff --git a/public/images/trainer/atticus.png b/public/images/trainer/atticus.png index 75cd70b72d8..6ed82233976 100644 Binary files a/public/images/trainer/atticus.png and b/public/images/trainer/atticus.png differ diff --git a/public/images/trainer/backers_f.png b/public/images/trainer/backers_f.png index 2d9a08ea89a..ac39e36e80b 100644 Binary files a/public/images/trainer/backers_f.png and b/public/images/trainer/backers_f.png differ diff --git a/public/images/trainer/backers_m.png b/public/images/trainer/backers_m.png index 0b5fda670ca..26d2bf9c70a 100644 Binary files a/public/images/trainer/backers_m.png and b/public/images/trainer/backers_m.png differ diff --git a/public/images/trainer/backpacker_f.png b/public/images/trainer/backpacker_f.png index 11e471a4649..89d70dca204 100644 Binary files a/public/images/trainer/backpacker_f.png and b/public/images/trainer/backpacker_f.png differ diff --git a/public/images/trainer/backpacker_m.png b/public/images/trainer/backpacker_m.png index b7397aa2555..295046b42c1 100644 Binary files a/public/images/trainer/backpacker_m.png and b/public/images/trainer/backpacker_m.png differ diff --git a/public/images/trainer/baker.png b/public/images/trainer/baker.png index 1f97fea2323..16a3079d181 100644 Binary files a/public/images/trainer/baker.png and b/public/images/trainer/baker.png differ diff --git a/public/images/trainer/bea.png b/public/images/trainer/bea.png index 844abc3df9f..1054b16f590 100644 Binary files a/public/images/trainer/bea.png and b/public/images/trainer/bea.png differ diff --git a/public/images/trainer/beauty.png b/public/images/trainer/beauty.png index 881bbead794..c3e209e30f2 100644 Binary files a/public/images/trainer/beauty.png and b/public/images/trainer/beauty.png differ diff --git a/public/images/trainer/bede.png b/public/images/trainer/bede.png index 7db54c785d9..b48920e8098 100644 Binary files a/public/images/trainer/bede.png and b/public/images/trainer/bede.png differ diff --git a/public/images/trainer/benga.png b/public/images/trainer/benga.png index a3ad1a93c8c..e9a268a105b 100644 Binary files a/public/images/trainer/benga.png and b/public/images/trainer/benga.png differ diff --git a/public/images/trainer/bertha.png b/public/images/trainer/bertha.png index 6b8f0b3e3e5..83a9f23d3fa 100644 Binary files a/public/images/trainer/bertha.png and b/public/images/trainer/bertha.png differ diff --git a/public/images/trainer/biker.png b/public/images/trainer/biker.png index 79ec6d15ec9..9b067958d8d 100644 Binary files a/public/images/trainer/biker.png and b/public/images/trainer/biker.png differ diff --git a/public/images/trainer/black_belt_f.png b/public/images/trainer/black_belt_f.png index 8567cd35cf7..b4a333c9d41 100644 Binary files a/public/images/trainer/black_belt_f.png and b/public/images/trainer/black_belt_f.png differ diff --git a/public/images/trainer/black_belt_m.png b/public/images/trainer/black_belt_m.png index c620c2d0e77..3d84251773b 100644 Binary files a/public/images/trainer/black_belt_m.png and b/public/images/trainer/black_belt_m.png differ diff --git a/public/images/trainer/blaine.png b/public/images/trainer/blaine.png index 86b1e3fef2d..389f8fc331c 100644 Binary files a/public/images/trainer/blaine.png and b/public/images/trainer/blaine.png differ diff --git a/public/images/trainer/blue.png b/public/images/trainer/blue.png index ff5c0883e13..0a1b7296d69 100644 Binary files a/public/images/trainer/blue.png and b/public/images/trainer/blue.png differ diff --git a/public/images/trainer/brassius.png b/public/images/trainer/brassius.png index 9e736d32f45..0b1c928b47a 100644 Binary files a/public/images/trainer/brassius.png and b/public/images/trainer/brassius.png differ diff --git a/public/images/trainer/brawly.png b/public/images/trainer/brawly.png index 11b2f7807ca..1a31cfa4d06 100644 Binary files a/public/images/trainer/brawly.png and b/public/images/trainer/brawly.png differ diff --git a/public/images/trainer/breeder_f.png b/public/images/trainer/breeder_f.png index 9995c61c217..2fa7ebdd36c 100644 Binary files a/public/images/trainer/breeder_f.png and b/public/images/trainer/breeder_f.png differ diff --git a/public/images/trainer/breeder_m.png b/public/images/trainer/breeder_m.png index 0ae024e3767..16588225352 100644 Binary files a/public/images/trainer/breeder_m.png and b/public/images/trainer/breeder_m.png differ diff --git a/public/images/trainer/brock.png b/public/images/trainer/brock.png index e4e06a63c55..91790be27ad 100644 Binary files a/public/images/trainer/brock.png and b/public/images/trainer/brock.png differ diff --git a/public/images/trainer/bruno.png b/public/images/trainer/bruno.png index 5dc90107ec2..10c394fbd68 100644 Binary files a/public/images/trainer/bruno.png and b/public/images/trainer/bruno.png differ diff --git a/public/images/trainer/brycen.png b/public/images/trainer/brycen.png index 7b26705b0e7..3f8e2be9100 100644 Binary files a/public/images/trainer/brycen.png and b/public/images/trainer/brycen.png differ diff --git a/public/images/trainer/buck.png b/public/images/trainer/buck.png index 2384fb42a33..fcf66000b47 100644 Binary files a/public/images/trainer/buck.png and b/public/images/trainer/buck.png differ diff --git a/public/images/trainer/bug_type_superfan.png b/public/images/trainer/bug_type_superfan.png index 0d2fd7a68ae..db622948152 100644 Binary files a/public/images/trainer/bug_type_superfan.png and b/public/images/trainer/bug_type_superfan.png differ diff --git a/public/images/trainer/bugsy.png b/public/images/trainer/bugsy.png index 59316fe6ed8..091f8a1219c 100644 Binary files a/public/images/trainer/bugsy.png and b/public/images/trainer/bugsy.png differ diff --git a/public/images/trainer/burgh.png b/public/images/trainer/burgh.png index fc41dafa7e3..24fd8f7bb31 100644 Binary files a/public/images/trainer/burgh.png and b/public/images/trainer/burgh.png differ diff --git a/public/images/trainer/byron.png b/public/images/trainer/byron.png index 0d815c94eaf..52830777c00 100644 Binary files a/public/images/trainer/byron.png and b/public/images/trainer/byron.png differ diff --git a/public/images/trainer/caitlin.png b/public/images/trainer/caitlin.png index 78ed7cc94dd..ecf96cb8a68 100644 Binary files a/public/images/trainer/caitlin.png and b/public/images/trainer/caitlin.png differ diff --git a/public/images/trainer/candice.png b/public/images/trainer/candice.png index 32539cdea8c..c0337b94a71 100644 Binary files a/public/images/trainer/candice.png and b/public/images/trainer/candice.png differ diff --git a/public/images/trainer/cheren.png b/public/images/trainer/cheren.png index cb230d910c4..f56accbb01b 100644 Binary files a/public/images/trainer/cheren.png and b/public/images/trainer/cheren.png differ diff --git a/public/images/trainer/cheryl.png b/public/images/trainer/cheryl.png index c46505f6b25..390ae7d88af 100644 Binary files a/public/images/trainer/cheryl.png and b/public/images/trainer/cheryl.png differ diff --git a/public/images/trainer/chili.png b/public/images/trainer/chili.png index f66b5aa841d..20f26201c44 100644 Binary files a/public/images/trainer/chili.png and b/public/images/trainer/chili.png differ diff --git a/public/images/trainer/chuck.png b/public/images/trainer/chuck.png index 511283ff5f5..852bc80a69c 100644 Binary files a/public/images/trainer/chuck.png and b/public/images/trainer/chuck.png differ diff --git a/public/images/trainer/cilan.png b/public/images/trainer/cilan.png index b5249c30e47..cbcf6f14b5d 100644 Binary files a/public/images/trainer/cilan.png and b/public/images/trainer/cilan.png differ diff --git a/public/images/trainer/clair.png b/public/images/trainer/clair.png index 9299c73a2f7..6e70dfab397 100644 Binary files a/public/images/trainer/clair.png and b/public/images/trainer/clair.png differ diff --git a/public/images/trainer/clay.png b/public/images/trainer/clay.png index 1aa788d7d72..a6418f382e2 100644 Binary files a/public/images/trainer/clay.png and b/public/images/trainer/clay.png differ diff --git a/public/images/trainer/clemont.png b/public/images/trainer/clemont.png index a93aa428002..8cc7e3cf8fb 100644 Binary files a/public/images/trainer/clemont.png and b/public/images/trainer/clemont.png differ diff --git a/public/images/trainer/clerk_f.png b/public/images/trainer/clerk_f.png index 7d522ed31a2..431dac64d75 100644 Binary files a/public/images/trainer/clerk_f.png and b/public/images/trainer/clerk_f.png differ diff --git a/public/images/trainer/clerk_m.png b/public/images/trainer/clerk_m.png index 69531b2a0dd..765094fea95 100644 Binary files a/public/images/trainer/clerk_m.png and b/public/images/trainer/clerk_m.png differ diff --git a/public/images/trainer/clerk_m_2.png b/public/images/trainer/clerk_m_2.png index 13f2f76c7cd..4ee3e07e810 100644 Binary files a/public/images/trainer/clerk_m_2.png and b/public/images/trainer/clerk_m_2.png differ diff --git a/public/images/trainer/colress.png b/public/images/trainer/colress.png index 49a316c95d5..f9bc0ecb01b 100644 Binary files a/public/images/trainer/colress.png and b/public/images/trainer/colress.png differ diff --git a/public/images/trainer/courtney.png b/public/images/trainer/courtney.png index 3db5151e61c..5e24995ebee 100644 Binary files a/public/images/trainer/courtney.png and b/public/images/trainer/courtney.png differ diff --git a/public/images/trainer/crasher_wake.png b/public/images/trainer/crasher_wake.png index 42bbf3a69c6..41ef2bff7c4 100644 Binary files a/public/images/trainer/crasher_wake.png and b/public/images/trainer/crasher_wake.png differ diff --git a/public/images/trainer/cress.png b/public/images/trainer/cress.png index 97448e0c561..6183dafdc4d 100644 Binary files a/public/images/trainer/cress.png and b/public/images/trainer/cress.png differ diff --git a/public/images/trainer/crispin.png b/public/images/trainer/crispin.png index 865e1ac1990..d6b21a30edc 100644 Binary files a/public/images/trainer/crispin.png and b/public/images/trainer/crispin.png differ diff --git a/public/images/trainer/cyclist_f.png b/public/images/trainer/cyclist_f.png index 8a809efb985..c1fad64a8b6 100644 Binary files a/public/images/trainer/cyclist_f.png and b/public/images/trainer/cyclist_f.png differ diff --git a/public/images/trainer/cyclist_m.png b/public/images/trainer/cyclist_m.png index 002b880babf..78015bf74a6 100644 Binary files a/public/images/trainer/cyclist_m.png and b/public/images/trainer/cyclist_m.png differ diff --git a/public/images/trainer/cynthia.png b/public/images/trainer/cynthia.png index 078c4522abd..6dbef7cc81b 100644 Binary files a/public/images/trainer/cynthia.png and b/public/images/trainer/cynthia.png differ diff --git a/public/images/trainer/cyrus.png b/public/images/trainer/cyrus.png index f7351340568..fc087b3d428 100644 Binary files a/public/images/trainer/cyrus.png and b/public/images/trainer/cyrus.png differ diff --git a/public/images/trainer/dancer.png b/public/images/trainer/dancer.png index f43feed6972..ae22fc4926d 100644 Binary files a/public/images/trainer/dancer.png and b/public/images/trainer/dancer.png differ diff --git a/public/images/trainer/depot_agent.png b/public/images/trainer/depot_agent.png index 6a7ac6326e9..41670eeb681 100644 Binary files a/public/images/trainer/depot_agent.png and b/public/images/trainer/depot_agent.png differ diff --git a/public/images/trainer/diantha.png b/public/images/trainer/diantha.png index 663eb3613ba..25ccaa08347 100644 Binary files a/public/images/trainer/diantha.png and b/public/images/trainer/diantha.png differ diff --git a/public/images/trainer/doctor_f.png b/public/images/trainer/doctor_f.png index eb9730f17fc..c386059cf6a 100644 Binary files a/public/images/trainer/doctor_f.png and b/public/images/trainer/doctor_f.png differ diff --git a/public/images/trainer/doctor_m.png b/public/images/trainer/doctor_m.png index 6fd41266ab1..03dc3c1add0 100644 Binary files a/public/images/trainer/doctor_m.png and b/public/images/trainer/doctor_m.png differ diff --git a/public/images/trainer/drake.png b/public/images/trainer/drake.png index 80d663d1149..322e9e247e5 100644 Binary files a/public/images/trainer/drake.png and b/public/images/trainer/drake.png differ diff --git a/public/images/trainer/drasna.png b/public/images/trainer/drasna.png index a6e1824f3a8..799c77c7536 100644 Binary files a/public/images/trainer/drasna.png and b/public/images/trainer/drasna.png differ diff --git a/public/images/trainer/drayden.png b/public/images/trainer/drayden.png index 76845bb7a93..97c56b4e3c1 100644 Binary files a/public/images/trainer/drayden.png and b/public/images/trainer/drayden.png differ diff --git a/public/images/trainer/drayton.png b/public/images/trainer/drayton.png index bd575b25937..e4108f87744 100644 Binary files a/public/images/trainer/drayton.png and b/public/images/trainer/drayton.png differ diff --git a/public/images/trainer/elesa.png b/public/images/trainer/elesa.png index 84c3f31b687..c0ff42ea16d 100644 Binary files a/public/images/trainer/elesa.png and b/public/images/trainer/elesa.png differ diff --git a/public/images/trainer/emmet.png b/public/images/trainer/emmet.png index 15019e1b4c7..dd18339a28d 100644 Binary files a/public/images/trainer/emmet.png and b/public/images/trainer/emmet.png differ diff --git a/public/images/trainer/eri.png b/public/images/trainer/eri.png index cb38f96b030..1ad9c948ef9 100644 Binary files a/public/images/trainer/eri.png and b/public/images/trainer/eri.png differ diff --git a/public/images/trainer/erika.png b/public/images/trainer/erika.png index f3018cde454..408da0444cd 100644 Binary files a/public/images/trainer/erika.png and b/public/images/trainer/erika.png differ diff --git a/public/images/trainer/expert_pokemon_breeder.png b/public/images/trainer/expert_pokemon_breeder.png index 0625f5255c3..0c53da6f2dc 100644 Binary files a/public/images/trainer/expert_pokemon_breeder.png and b/public/images/trainer/expert_pokemon_breeder.png differ diff --git a/public/images/trainer/faba.png b/public/images/trainer/faba.png index 1c509da8a78..c5dc107fe99 100644 Binary files a/public/images/trainer/faba.png and b/public/images/trainer/faba.png differ diff --git a/public/images/trainer/falkner.png b/public/images/trainer/falkner.png index b5ca8944620..ab628366d2c 100644 Binary files a/public/images/trainer/falkner.png and b/public/images/trainer/falkner.png differ diff --git a/public/images/trainer/fantina.png b/public/images/trainer/fantina.png index a0bea745d6e..f0f53f8e042 100644 Binary files a/public/images/trainer/fantina.png and b/public/images/trainer/fantina.png differ diff --git a/public/images/trainer/firebreather.png b/public/images/trainer/firebreather.png index 829496bd56e..daf0d2d1c1f 100644 Binary files a/public/images/trainer/firebreather.png and b/public/images/trainer/firebreather.png differ diff --git a/public/images/trainer/fisherman.png b/public/images/trainer/fisherman.png index b2656dbf360..b7344ac611a 100644 Binary files a/public/images/trainer/fisherman.png and b/public/images/trainer/fisherman.png differ diff --git a/public/images/trainer/flannery.png b/public/images/trainer/flannery.png index d909ee145fe..f538797b539 100644 Binary files a/public/images/trainer/flannery.png and b/public/images/trainer/flannery.png differ diff --git a/public/images/trainer/flare_grunt_f.png b/public/images/trainer/flare_grunt_f.png index e2a4dd7fe92..c7a3177d45f 100644 Binary files a/public/images/trainer/flare_grunt_f.png and b/public/images/trainer/flare_grunt_f.png differ diff --git a/public/images/trainer/flare_grunt_m.png b/public/images/trainer/flare_grunt_m.png index eb14dbed0e2..88dd10d10c5 100644 Binary files a/public/images/trainer/flare_grunt_m.png and b/public/images/trainer/flare_grunt_m.png differ diff --git a/public/images/trainer/flint.png b/public/images/trainer/flint.png index 46fc32c5796..d3d775da7f6 100644 Binary files a/public/images/trainer/flint.png and b/public/images/trainer/flint.png differ diff --git a/public/images/trainer/future_self_f.png b/public/images/trainer/future_self_f.png index e75c5e5e65b..9493d712ce0 100644 Binary files a/public/images/trainer/future_self_f.png and b/public/images/trainer/future_self_f.png differ diff --git a/public/images/trainer/future_self_m.png b/public/images/trainer/future_self_m.png index edd15cb6ea3..f6e7be1c38d 100644 Binary files a/public/images/trainer/future_self_m.png and b/public/images/trainer/future_self_m.png differ diff --git a/public/images/trainer/galactic_grunt_f.png b/public/images/trainer/galactic_grunt_f.png index 209d8ae09ba..27fe101d0e7 100644 Binary files a/public/images/trainer/galactic_grunt_f.png and b/public/images/trainer/galactic_grunt_f.png differ diff --git a/public/images/trainer/galactic_grunt_m.png b/public/images/trainer/galactic_grunt_m.png index 1fff818266c..5ccbc2ff194 100644 Binary files a/public/images/trainer/galactic_grunt_m.png and b/public/images/trainer/galactic_grunt_m.png differ diff --git a/public/images/trainer/gardenia.png b/public/images/trainer/gardenia.png index 3d28f2257f8..13cbdfccc47 100644 Binary files a/public/images/trainer/gardenia.png and b/public/images/trainer/gardenia.png differ diff --git a/public/images/trainer/geeta.png b/public/images/trainer/geeta.png index c14ff9deb56..2319cb21ecd 100644 Binary files a/public/images/trainer/geeta.png and b/public/images/trainer/geeta.png differ diff --git a/public/images/trainer/ghetsis.png b/public/images/trainer/ghetsis.png index e17bd7d7b95..00597b74a1e 100644 Binary files a/public/images/trainer/ghetsis.png and b/public/images/trainer/ghetsis.png differ diff --git a/public/images/trainer/giacomo.png b/public/images/trainer/giacomo.png index 352acaddf95..2a756c804b1 100644 Binary files a/public/images/trainer/giacomo.png and b/public/images/trainer/giacomo.png differ diff --git a/public/images/trainer/giovanni.png b/public/images/trainer/giovanni.png index 360f444c409..d372fcc0db5 100644 Binary files a/public/images/trainer/giovanni.png and b/public/images/trainer/giovanni.png differ diff --git a/public/images/trainer/glacia.png b/public/images/trainer/glacia.png index c73b9b69bab..3a6203096d5 100644 Binary files a/public/images/trainer/glacia.png and b/public/images/trainer/glacia.png differ diff --git a/public/images/trainer/gordie.png b/public/images/trainer/gordie.png index a91f7dacaa7..c588c956209 100644 Binary files a/public/images/trainer/gordie.png and b/public/images/trainer/gordie.png differ diff --git a/public/images/trainer/grant.png b/public/images/trainer/grant.png index 60cf5b8d493..56092076792 100644 Binary files a/public/images/trainer/grant.png and b/public/images/trainer/grant.png differ diff --git a/public/images/trainer/grimsley.png b/public/images/trainer/grimsley.png index 02b04d486d4..e8abca3adc1 100644 Binary files a/public/images/trainer/grimsley.png and b/public/images/trainer/grimsley.png differ diff --git a/public/images/trainer/grusha.png b/public/images/trainer/grusha.png index 73d6a8708b3..80963763465 100644 Binary files a/public/images/trainer/grusha.png and b/public/images/trainer/grusha.png differ diff --git a/public/images/trainer/guitarist.png b/public/images/trainer/guitarist.png index 94fdeb0fafd..7b86da2d9de 100644 Binary files a/public/images/trainer/guitarist.png and b/public/images/trainer/guitarist.png differ diff --git a/public/images/trainer/guzma.png b/public/images/trainer/guzma.png index 1ae6d8eb8d1..ffb17930b47 100644 Binary files a/public/images/trainer/guzma.png and b/public/images/trainer/guzma.png differ diff --git a/public/images/trainer/hala.png b/public/images/trainer/hala.png index 4f26cbb8ff3..d0972fb43ae 100644 Binary files a/public/images/trainer/hala.png and b/public/images/trainer/hala.png differ diff --git a/public/images/trainer/harlequin.png b/public/images/trainer/harlequin.png index e810db5c849..bed24c10510 100644 Binary files a/public/images/trainer/harlequin.png and b/public/images/trainer/harlequin.png differ diff --git a/public/images/trainer/hassel.png b/public/images/trainer/hassel.png index d78bc4dd5db..3c3d42ef8f8 100644 Binary files a/public/images/trainer/hassel.png and b/public/images/trainer/hassel.png differ diff --git a/public/images/trainer/hau.png b/public/images/trainer/hau.png index 7aa673a6fe9..4f4696779ad 100644 Binary files a/public/images/trainer/hau.png and b/public/images/trainer/hau.png differ diff --git a/public/images/trainer/hex_maniac.png b/public/images/trainer/hex_maniac.png index 7557dbf7797..5ed9deeea92 100644 Binary files a/public/images/trainer/hex_maniac.png and b/public/images/trainer/hex_maniac.png differ diff --git a/public/images/trainer/hiker.png b/public/images/trainer/hiker.png index 4d151cdf6f7..44d87b45e32 100644 Binary files a/public/images/trainer/hiker.png and b/public/images/trainer/hiker.png differ diff --git a/public/images/trainer/hooligans.png b/public/images/trainer/hooligans.png index e639e46f3ed..4a216f17a63 100644 Binary files a/public/images/trainer/hooligans.png and b/public/images/trainer/hooligans.png differ diff --git a/public/images/trainer/hoopster.png b/public/images/trainer/hoopster.png index c9ab835029c..5576224d0aa 100644 Binary files a/public/images/trainer/hoopster.png and b/public/images/trainer/hoopster.png differ diff --git a/public/images/trainer/hugh.png b/public/images/trainer/hugh.png index 638c7d350d8..8fa09efef64 100644 Binary files a/public/images/trainer/hugh.png and b/public/images/trainer/hugh.png differ diff --git a/public/images/trainer/infielder.png b/public/images/trainer/infielder.png index 0a8e22ac10c..7ad913cd0bf 100644 Binary files a/public/images/trainer/infielder.png and b/public/images/trainer/infielder.png differ diff --git a/public/images/trainer/ingo.png b/public/images/trainer/ingo.png index 638833acba6..d4e31c4c344 100644 Binary files a/public/images/trainer/ingo.png and b/public/images/trainer/ingo.png differ diff --git a/public/images/trainer/iono.png b/public/images/trainer/iono.png index e2637beba08..5e78c70dea4 100644 Binary files a/public/images/trainer/iono.png and b/public/images/trainer/iono.png differ diff --git a/public/images/trainer/iris.png b/public/images/trainer/iris.png index 943f49846b0..49526059f30 100644 Binary files a/public/images/trainer/iris.png and b/public/images/trainer/iris.png differ diff --git a/public/images/trainer/janine.png b/public/images/trainer/janine.png index 7caa31d487b..79a728d2c96 100644 Binary files a/public/images/trainer/janine.png and b/public/images/trainer/janine.png differ diff --git a/public/images/trainer/janitor.png b/public/images/trainer/janitor.png index c95f2f2e97e..0cb2f399944 100644 Binary files a/public/images/trainer/janitor.png and b/public/images/trainer/janitor.png differ diff --git a/public/images/trainer/jasmine.png b/public/images/trainer/jasmine.png index bb14acd7bcf..2fd744fe3f4 100644 Binary files a/public/images/trainer/jasmine.png and b/public/images/trainer/jasmine.png differ diff --git a/public/images/trainer/juan.png b/public/images/trainer/juan.png index fb7a376693b..add2de7bf5a 100644 Binary files a/public/images/trainer/juan.png and b/public/images/trainer/juan.png differ diff --git a/public/images/trainer/kabu.png b/public/images/trainer/kabu.png index bffcec47e1e..c6de5929d2a 100644 Binary files a/public/images/trainer/kabu.png and b/public/images/trainer/kabu.png differ diff --git a/public/images/trainer/kahili.png b/public/images/trainer/kahili.png index 94dea140f4c..c60873d31be 100644 Binary files a/public/images/trainer/kahili.png and b/public/images/trainer/kahili.png differ diff --git a/public/images/trainer/karen.png b/public/images/trainer/karen.png index 3ed1ef2d8ad..a5d90c9fc94 100644 Binary files a/public/images/trainer/karen.png and b/public/images/trainer/karen.png differ diff --git a/public/images/trainer/katy.png b/public/images/trainer/katy.png index 74da023252b..534abca0397 100644 Binary files a/public/images/trainer/katy.png and b/public/images/trainer/katy.png differ diff --git a/public/images/trainer/kieran.png b/public/images/trainer/kieran.png index c857052e2eb..9c167c4b30f 100644 Binary files a/public/images/trainer/kieran.png and b/public/images/trainer/kieran.png differ diff --git a/public/images/trainer/kofu.png b/public/images/trainer/kofu.png index ae7f364f7f6..bfbc8898ede 100644 Binary files a/public/images/trainer/kofu.png and b/public/images/trainer/kofu.png differ diff --git a/public/images/trainer/koga.png b/public/images/trainer/koga.png index f763d7eff6d..4ed714d63c0 100644 Binary files a/public/images/trainer/koga.png and b/public/images/trainer/koga.png differ diff --git a/public/images/trainer/korrina.png b/public/images/trainer/korrina.png index 8994afdddb1..db7d5102ce6 100644 Binary files a/public/images/trainer/korrina.png and b/public/images/trainer/korrina.png differ diff --git a/public/images/trainer/kukui.png b/public/images/trainer/kukui.png index aacf1c197ee..e06972b1dbb 100644 Binary files a/public/images/trainer/kukui.png and b/public/images/trainer/kukui.png differ diff --git a/public/images/trainer/lacey.png b/public/images/trainer/lacey.png index 0e71e6976c9..b62b524fa86 100644 Binary files a/public/images/trainer/lacey.png and b/public/images/trainer/lacey.png differ diff --git a/public/images/trainer/lance.png b/public/images/trainer/lance.png index 464a8da55b2..091dd5f8790 100644 Binary files a/public/images/trainer/lance.png and b/public/images/trainer/lance.png differ diff --git a/public/images/trainer/larry.png b/public/images/trainer/larry.png index 08dfa0bdc4b..486acf68078 100644 Binary files a/public/images/trainer/larry.png and b/public/images/trainer/larry.png differ diff --git a/public/images/trainer/lenora.png b/public/images/trainer/lenora.png index 11a58822296..df01b993925 100644 Binary files a/public/images/trainer/lenora.png and b/public/images/trainer/lenora.png differ diff --git a/public/images/trainer/leon.png b/public/images/trainer/leon.png index c907f0dc87c..c16132a95c3 100644 Binary files a/public/images/trainer/leon.png and b/public/images/trainer/leon.png differ diff --git a/public/images/trainer/linebacker.png b/public/images/trainer/linebacker.png index e82dd76b25c..315ca22c8f1 100644 Binary files a/public/images/trainer/linebacker.png and b/public/images/trainer/linebacker.png differ diff --git a/public/images/trainer/liza.png b/public/images/trainer/liza.png index 525607739cc..9412f90dc1f 100644 Binary files a/public/images/trainer/liza.png and b/public/images/trainer/liza.png differ diff --git a/public/images/trainer/lorelei.png b/public/images/trainer/lorelei.png index 30b6643d834..9dc7852887d 100644 Binary files a/public/images/trainer/lorelei.png and b/public/images/trainer/lorelei.png differ diff --git a/public/images/trainer/lt_surge.png b/public/images/trainer/lt_surge.png index df207e92bdd..483ba989759 100644 Binary files a/public/images/trainer/lt_surge.png and b/public/images/trainer/lt_surge.png differ diff --git a/public/images/trainer/lucian.png b/public/images/trainer/lucian.png index fabf59aad08..d10db6ea578 100644 Binary files a/public/images/trainer/lucian.png and b/public/images/trainer/lucian.png differ diff --git a/public/images/trainer/lusamine.png b/public/images/trainer/lusamine.png index 1619dc11e2d..b1b35f1fbeb 100644 Binary files a/public/images/trainer/lusamine.png and b/public/images/trainer/lusamine.png differ diff --git a/public/images/trainer/lysandre.png b/public/images/trainer/lysandre.png index fe6dafb00f8..71ee7998de2 100644 Binary files a/public/images/trainer/lysandre.png and b/public/images/trainer/lysandre.png differ diff --git a/public/images/trainer/macro_grunt_f.png b/public/images/trainer/macro_grunt_f.png index 85586126da1..0a7e20b8ab5 100644 Binary files a/public/images/trainer/macro_grunt_f.png and b/public/images/trainer/macro_grunt_f.png differ diff --git a/public/images/trainer/macro_grunt_m.png b/public/images/trainer/macro_grunt_m.png index 464c735c75d..980db918ac6 100644 Binary files a/public/images/trainer/macro_grunt_m.png and b/public/images/trainer/macro_grunt_m.png differ diff --git a/public/images/trainer/magma_grunt_f.png b/public/images/trainer/magma_grunt_f.png index 5ea582ad067..fa3d74b4049 100644 Binary files a/public/images/trainer/magma_grunt_f.png and b/public/images/trainer/magma_grunt_f.png differ diff --git a/public/images/trainer/magma_grunt_m.png b/public/images/trainer/magma_grunt_m.png index b2432a79d28..c2477104fb8 100644 Binary files a/public/images/trainer/magma_grunt_m.png and b/public/images/trainer/magma_grunt_m.png differ diff --git a/public/images/trainer/maid.png b/public/images/trainer/maid.png index 824c2f1a46c..b17a90a2358 100644 Binary files a/public/images/trainer/maid.png and b/public/images/trainer/maid.png differ diff --git a/public/images/trainer/malva.png b/public/images/trainer/malva.png index 850818e761b..cdff3f0cf58 100644 Binary files a/public/images/trainer/malva.png and b/public/images/trainer/malva.png differ diff --git a/public/images/trainer/marley.png b/public/images/trainer/marley.png index 8e78e11e8ad..2fc9ce88a76 100644 Binary files a/public/images/trainer/marley.png and b/public/images/trainer/marley.png differ diff --git a/public/images/trainer/marlon.png b/public/images/trainer/marlon.png index 657ae5eef0e..edf735d8d1e 100644 Binary files a/public/images/trainer/marlon.png and b/public/images/trainer/marlon.png differ diff --git a/public/images/trainer/marnie.png b/public/images/trainer/marnie.png index 1cbac5f67a5..65b03c2a66c 100644 Binary files a/public/images/trainer/marnie.png and b/public/images/trainer/marnie.png differ diff --git a/public/images/trainer/marshal.png b/public/images/trainer/marshal.png index b705a547a28..5bfcdb032d0 100644 Binary files a/public/images/trainer/marshal.png and b/public/images/trainer/marshal.png differ diff --git a/public/images/trainer/maxie.png b/public/images/trainer/maxie.png index 232082f869f..741c439ff97 100644 Binary files a/public/images/trainer/maxie.png and b/public/images/trainer/maxie.png differ diff --git a/public/images/trainer/maylene.png b/public/images/trainer/maylene.png index 09fbbd72982..69ab563b79c 100644 Binary files a/public/images/trainer/maylene.png and b/public/images/trainer/maylene.png differ diff --git a/public/images/trainer/mela.png b/public/images/trainer/mela.png index a98547d6380..7e2eceef1a1 100644 Binary files a/public/images/trainer/mela.png and b/public/images/trainer/mela.png differ diff --git a/public/images/trainer/melony.png b/public/images/trainer/melony.png index 01a148396da..3a58cf1c431 100644 Binary files a/public/images/trainer/melony.png and b/public/images/trainer/melony.png differ diff --git a/public/images/trainer/milo.png b/public/images/trainer/milo.png index dbc7878185e..859500024da 100644 Binary files a/public/images/trainer/milo.png and b/public/images/trainer/milo.png differ diff --git a/public/images/trainer/mira.png b/public/images/trainer/mira.png index 5c1afe5d241..4e6b08d7c87 100644 Binary files a/public/images/trainer/mira.png and b/public/images/trainer/mira.png differ diff --git a/public/images/trainer/misty.png b/public/images/trainer/misty.png index 56b9beae28c..9c90330a4c3 100644 Binary files a/public/images/trainer/misty.png and b/public/images/trainer/misty.png differ diff --git a/public/images/trainer/molayne.png b/public/images/trainer/molayne.png index c400e5be33d..89ff5dd4678 100644 Binary files a/public/images/trainer/molayne.png and b/public/images/trainer/molayne.png differ diff --git a/public/images/trainer/morty.png b/public/images/trainer/morty.png index 5aad41ca1b6..15ec18c6822 100644 Binary files a/public/images/trainer/morty.png and b/public/images/trainer/morty.png differ diff --git a/public/images/trainer/musician.png b/public/images/trainer/musician.png index 72f5ec495c2..db9373c9764 100644 Binary files a/public/images/trainer/musician.png and b/public/images/trainer/musician.png differ diff --git a/public/images/trainer/mustard.png b/public/images/trainer/mustard.png index 0acba02db45..2a58de341dd 100644 Binary files a/public/images/trainer/mustard.png and b/public/images/trainer/mustard.png differ diff --git a/public/images/trainer/nate.png b/public/images/trainer/nate.png index aebbe7a2da9..6798b80efdd 100644 Binary files a/public/images/trainer/nate.png and b/public/images/trainer/nate.png differ diff --git a/public/images/trainer/nemona.png b/public/images/trainer/nemona.png index 83a9e803df4..c30b46602d3 100644 Binary files a/public/images/trainer/nemona.png and b/public/images/trainer/nemona.png differ diff --git a/public/images/trainer/nessa.png b/public/images/trainer/nessa.png index b645ff7159a..53f4f1edf94 100644 Binary files a/public/images/trainer/nessa.png and b/public/images/trainer/nessa.png differ diff --git a/public/images/trainer/norman.png b/public/images/trainer/norman.png index 16dd4d5b2ba..39ab24236bf 100644 Binary files a/public/images/trainer/norman.png and b/public/images/trainer/norman.png differ diff --git a/public/images/trainer/nursery_aide.png b/public/images/trainer/nursery_aide.png index a0cbb549864..a49b12c0729 100644 Binary files a/public/images/trainer/nursery_aide.png and b/public/images/trainer/nursery_aide.png differ diff --git a/public/images/trainer/officer.png b/public/images/trainer/officer.png index 6a6da4cdcc0..b2b2af2f0c3 100644 Binary files a/public/images/trainer/officer.png and b/public/images/trainer/officer.png differ diff --git a/public/images/trainer/oleana.png b/public/images/trainer/oleana.png index e74fb6ab2eb..811e5a81b15 100644 Binary files a/public/images/trainer/oleana.png and b/public/images/trainer/oleana.png differ diff --git a/public/images/trainer/olivia.png b/public/images/trainer/olivia.png index 60b4336efdb..d9e47ae3ca5 100644 Binary files a/public/images/trainer/olivia.png and b/public/images/trainer/olivia.png differ diff --git a/public/images/trainer/olympia.png b/public/images/trainer/olympia.png index 5faa566d31b..a84068a7148 100644 Binary files a/public/images/trainer/olympia.png and b/public/images/trainer/olympia.png differ diff --git a/public/images/trainer/opal.png b/public/images/trainer/opal.png index 8fbf608526f..f7eb342bdf8 100644 Binary files a/public/images/trainer/opal.png and b/public/images/trainer/opal.png differ diff --git a/public/images/trainer/ortega.png b/public/images/trainer/ortega.png index cede7b6c311..8b9b6ce7409 100644 Binary files a/public/images/trainer/ortega.png and b/public/images/trainer/ortega.png differ diff --git a/public/images/trainer/parasol_lady.png b/public/images/trainer/parasol_lady.png index 8610320dca4..53855f00100 100644 Binary files a/public/images/trainer/parasol_lady.png and b/public/images/trainer/parasol_lady.png differ diff --git a/public/images/trainer/penny.png b/public/images/trainer/penny.png index 67c90a41462..1fd8dd8aa4d 100644 Binary files a/public/images/trainer/penny.png and b/public/images/trainer/penny.png differ diff --git a/public/images/trainer/phoebe.png b/public/images/trainer/phoebe.png index eb0bf9f61bb..44506d6bc66 100644 Binary files a/public/images/trainer/phoebe.png and b/public/images/trainer/phoebe.png differ diff --git a/public/images/trainer/piers.png b/public/images/trainer/piers.png index 7730a2d54ca..155b39bcb09 100644 Binary files a/public/images/trainer/piers.png and b/public/images/trainer/piers.png differ diff --git a/public/images/trainer/pilot.png b/public/images/trainer/pilot.png index 0956c048ed9..393742c4941 100644 Binary files a/public/images/trainer/pilot.png and b/public/images/trainer/pilot.png differ diff --git a/public/images/trainer/plasma_grunt_f.png b/public/images/trainer/plasma_grunt_f.png index 7fa804cc39d..c2198d815ad 100644 Binary files a/public/images/trainer/plasma_grunt_f.png and b/public/images/trainer/plasma_grunt_f.png differ diff --git a/public/images/trainer/plasma_grunt_m.png b/public/images/trainer/plasma_grunt_m.png index 801e91b1d6d..040b602e272 100644 Binary files a/public/images/trainer/plasma_grunt_m.png and b/public/images/trainer/plasma_grunt_m.png differ diff --git a/public/images/trainer/player_alt_f.png b/public/images/trainer/player_alt_f.png index b244076da83..c03b7792be1 100644 Binary files a/public/images/trainer/player_alt_f.png and b/public/images/trainer/player_alt_f.png differ diff --git a/public/images/trainer/player_alt_m.png b/public/images/trainer/player_alt_m.png index 935c7b8ba1b..659f21aa530 100644 Binary files a/public/images/trainer/player_alt_m.png and b/public/images/trainer/player_alt_m.png differ diff --git a/public/images/trainer/player_f.png b/public/images/trainer/player_f.png index f2f9169d6aa..c775fdf84ea 100644 Binary files a/public/images/trainer/player_f.png and b/public/images/trainer/player_f.png differ diff --git a/public/images/trainer/player_m.png b/public/images/trainer/player_m.png index 7dd60695fb2..96e73e271f1 100644 Binary files a/public/images/trainer/player_m.png and b/public/images/trainer/player_m.png differ diff --git a/public/images/trainer/plumeria.png b/public/images/trainer/plumeria.png index 4528c3e2053..f1903a9ca8e 100644 Binary files a/public/images/trainer/plumeria.png and b/public/images/trainer/plumeria.png differ diff --git a/public/images/trainer/pokefan_f.png b/public/images/trainer/pokefan_f.png index 9464d4ee858..4818584f15f 100644 Binary files a/public/images/trainer/pokefan_f.png and b/public/images/trainer/pokefan_f.png differ diff --git a/public/images/trainer/pokefan_m.png b/public/images/trainer/pokefan_m.png index ffba222874d..8057f5417b2 100644 Binary files a/public/images/trainer/pokefan_m.png and b/public/images/trainer/pokefan_m.png differ diff --git a/public/images/trainer/poppy.png b/public/images/trainer/poppy.png index 750043084c0..8055562ed19 100644 Binary files a/public/images/trainer/poppy.png and b/public/images/trainer/poppy.png differ diff --git a/public/images/trainer/preschooler_f.png b/public/images/trainer/preschooler_f.png index 1d265ffc97f..0668e5a22a0 100644 Binary files a/public/images/trainer/preschooler_f.png and b/public/images/trainer/preschooler_f.png differ diff --git a/public/images/trainer/preschooler_m.png b/public/images/trainer/preschooler_m.png index bfbc6d8fb53..3b2376c7ae3 100644 Binary files a/public/images/trainer/preschooler_m.png and b/public/images/trainer/preschooler_m.png differ diff --git a/public/images/trainer/pryce.png b/public/images/trainer/pryce.png index 90d884085e5..0da1d3f13f8 100644 Binary files a/public/images/trainer/pryce.png and b/public/images/trainer/pryce.png differ diff --git a/public/images/trainer/psychic_f.png b/public/images/trainer/psychic_f.png index 45978bc4ee1..f3e8ea6c795 100644 Binary files a/public/images/trainer/psychic_f.png and b/public/images/trainer/psychic_f.png differ diff --git a/public/images/trainer/psychic_m.png b/public/images/trainer/psychic_m.png index af62c9ae440..a871f261ff6 100644 Binary files a/public/images/trainer/psychic_m.png and b/public/images/trainer/psychic_m.png differ diff --git a/public/images/trainer/raihan.png b/public/images/trainer/raihan.png index a23c2b61e14..651482ff439 100644 Binary files a/public/images/trainer/raihan.png and b/public/images/trainer/raihan.png differ diff --git a/public/images/trainer/ramos.png b/public/images/trainer/ramos.png index ad47c93cbe5..76855734a90 100644 Binary files a/public/images/trainer/ramos.png and b/public/images/trainer/ramos.png differ diff --git a/public/images/trainer/ranger_f.png b/public/images/trainer/ranger_f.png index 713d793e65e..82fe5bb82fd 100644 Binary files a/public/images/trainer/ranger_f.png and b/public/images/trainer/ranger_f.png differ diff --git a/public/images/trainer/ranger_m.png b/public/images/trainer/ranger_m.png index 785502a9326..1c03cc6800d 100644 Binary files a/public/images/trainer/ranger_m.png and b/public/images/trainer/ranger_m.png differ diff --git a/public/images/trainer/red.png b/public/images/trainer/red.png index fd0da367b25..8f2573dda89 100644 Binary files a/public/images/trainer/red.png and b/public/images/trainer/red.png differ diff --git a/public/images/trainer/rich_f.png b/public/images/trainer/rich_f.png index 05b4e4c0d87..2d781ec1982 100644 Binary files a/public/images/trainer/rich_f.png and b/public/images/trainer/rich_f.png differ diff --git a/public/images/trainer/rich_kid_f.png b/public/images/trainer/rich_kid_f.png index 9985a098e03..c172f40b969 100644 Binary files a/public/images/trainer/rich_kid_f.png and b/public/images/trainer/rich_kid_f.png differ diff --git a/public/images/trainer/rich_kid_m.png b/public/images/trainer/rich_kid_m.png index 86b5428ae99..0f1ba099691 100644 Binary files a/public/images/trainer/rich_kid_m.png and b/public/images/trainer/rich_kid_m.png differ diff --git a/public/images/trainer/rich_m.png b/public/images/trainer/rich_m.png index 56f674536eb..8718fdc669e 100644 Binary files a/public/images/trainer/rich_m.png and b/public/images/trainer/rich_m.png differ diff --git a/public/images/trainer/rika.png b/public/images/trainer/rika.png index f5848fda219..e7c0bea47f0 100644 Binary files a/public/images/trainer/rika.png and b/public/images/trainer/rika.png differ diff --git a/public/images/trainer/riley.png b/public/images/trainer/riley.png index a9f0e3b53a9..8f6088a3982 100644 Binary files a/public/images/trainer/riley.png and b/public/images/trainer/riley.png differ diff --git a/public/images/trainer/rival_f.png b/public/images/trainer/rival_f.png index 2495192d1fc..9b5519ac98c 100644 Binary files a/public/images/trainer/rival_f.png and b/public/images/trainer/rival_f.png differ diff --git a/public/images/trainer/rival_m.png b/public/images/trainer/rival_m.png index 86e4bfac879..243c9ca8534 100644 Binary files a/public/images/trainer/rival_m.png and b/public/images/trainer/rival_m.png differ diff --git a/public/images/trainer/roark.png b/public/images/trainer/roark.png index 37354c5afd7..c29d629f15f 100644 Binary files a/public/images/trainer/roark.png and b/public/images/trainer/roark.png differ diff --git a/public/images/trainer/rocket_grunt_f.png b/public/images/trainer/rocket_grunt_f.png index c4f6e96dec4..b69e4c95638 100644 Binary files a/public/images/trainer/rocket_grunt_f.png and b/public/images/trainer/rocket_grunt_f.png differ diff --git a/public/images/trainer/rocket_grunt_m.png b/public/images/trainer/rocket_grunt_m.png index 027da6cacb0..0dffe2aa7b4 100644 Binary files a/public/images/trainer/rocket_grunt_m.png and b/public/images/trainer/rocket_grunt_m.png differ diff --git a/public/images/trainer/rood.png b/public/images/trainer/rood.png index b2bc88aa1c4..198f32405ec 100644 Binary files a/public/images/trainer/rood.png and b/public/images/trainer/rood.png differ diff --git a/public/images/trainer/rosa.png b/public/images/trainer/rosa.png index 87d35510dda..7cba799b0c0 100644 Binary files a/public/images/trainer/rosa.png and b/public/images/trainer/rosa.png differ diff --git a/public/images/trainer/rose.png b/public/images/trainer/rose.png index 4c448e3302b..0365f1463ce 100644 Binary files a/public/images/trainer/rose.png and b/public/images/trainer/rose.png differ diff --git a/public/images/trainer/roughneck.png b/public/images/trainer/roughneck.png index 7ddff3373a0..5dee090991b 100644 Binary files a/public/images/trainer/roughneck.png and b/public/images/trainer/roughneck.png differ diff --git a/public/images/trainer/roxanne.png b/public/images/trainer/roxanne.png index ab0b88c6491..9bf9cad24be 100644 Binary files a/public/images/trainer/roxanne.png and b/public/images/trainer/roxanne.png differ diff --git a/public/images/trainer/roxie.png b/public/images/trainer/roxie.png index f0189c961b7..356377da04a 100644 Binary files a/public/images/trainer/roxie.png and b/public/images/trainer/roxie.png differ diff --git a/public/images/trainer/ryme.png b/public/images/trainer/ryme.png index f7756e94c94..efa70c27ad1 100644 Binary files a/public/images/trainer/ryme.png and b/public/images/trainer/ryme.png differ diff --git a/public/images/trainer/sabrina.png b/public/images/trainer/sabrina.png index da3e0550023..adaf8baf826 100644 Binary files a/public/images/trainer/sabrina.png and b/public/images/trainer/sabrina.png differ diff --git a/public/images/trainer/sailor.png b/public/images/trainer/sailor.png index ec61152bce3..80668a3e8fb 100644 Binary files a/public/images/trainer/sailor.png and b/public/images/trainer/sailor.png differ diff --git a/public/images/trainer/school_kid_f.png b/public/images/trainer/school_kid_f.png index 53814a045c3..4016ab433ea 100644 Binary files a/public/images/trainer/school_kid_f.png and b/public/images/trainer/school_kid_f.png differ diff --git a/public/images/trainer/school_kid_m.png b/public/images/trainer/school_kid_m.png index 3ae8eee89ae..8c80854078e 100644 Binary files a/public/images/trainer/school_kid_m.png and b/public/images/trainer/school_kid_m.png differ diff --git a/public/images/trainer/scientist_f.png b/public/images/trainer/scientist_f.png index b8d520f3e0c..e91b73fd5ce 100644 Binary files a/public/images/trainer/scientist_f.png and b/public/images/trainer/scientist_f.png differ diff --git a/public/images/trainer/scientist_m.png b/public/images/trainer/scientist_m.png index b4d200a0e27..184638085ac 100644 Binary files a/public/images/trainer/scientist_m.png and b/public/images/trainer/scientist_m.png differ diff --git a/public/images/trainer/shauntal.png b/public/images/trainer/shauntal.png index 9b64ff966f4..3bdad41bf68 100644 Binary files a/public/images/trainer/shauntal.png and b/public/images/trainer/shauntal.png differ diff --git a/public/images/trainer/shelly.png b/public/images/trainer/shelly.png index 95e6a07310d..4be79909123 100644 Binary files a/public/images/trainer/shelly.png and b/public/images/trainer/shelly.png differ diff --git a/public/images/trainer/sidney.png b/public/images/trainer/sidney.png index 72e6db5e4af..bc6226b58d1 100644 Binary files a/public/images/trainer/sidney.png and b/public/images/trainer/sidney.png differ diff --git a/public/images/trainer/siebold.png b/public/images/trainer/siebold.png index 0ac8038f87e..521a6fc911b 100644 Binary files a/public/images/trainer/siebold.png and b/public/images/trainer/siebold.png differ diff --git a/public/images/trainer/skull_grunt_f.png b/public/images/trainer/skull_grunt_f.png index c26e8d7f882..e92cdaf26ff 100644 Binary files a/public/images/trainer/skull_grunt_f.png and b/public/images/trainer/skull_grunt_f.png differ diff --git a/public/images/trainer/skull_grunt_m.png b/public/images/trainer/skull_grunt_m.png index 8babb09ec74..e1d3fc24713 100644 Binary files a/public/images/trainer/skull_grunt_m.png and b/public/images/trainer/skull_grunt_m.png differ diff --git a/public/images/trainer/skyla.png b/public/images/trainer/skyla.png index bdf9200eef3..209a672764c 100644 Binary files a/public/images/trainer/skyla.png and b/public/images/trainer/skyla.png differ diff --git a/public/images/trainer/smasher.png b/public/images/trainer/smasher.png index 7cda7f48365..74832f72bdb 100644 Binary files a/public/images/trainer/smasher.png and b/public/images/trainer/smasher.png differ diff --git a/public/images/trainer/snow_worker.png b/public/images/trainer/snow_worker.png index 0e39505cc8d..805d6e70e56 100644 Binary files a/public/images/trainer/snow_worker.png and b/public/images/trainer/snow_worker.png differ diff --git a/public/images/trainer/snow_worker_f.png b/public/images/trainer/snow_worker_f.png index e52dbf31514..02eab1ae027 100644 Binary files a/public/images/trainer/snow_worker_f.png and b/public/images/trainer/snow_worker_f.png differ diff --git a/public/images/trainer/snow_worker_m.png b/public/images/trainer/snow_worker_m.png index 0e39505cc8d..805d6e70e56 100644 Binary files a/public/images/trainer/snow_worker_m.png and b/public/images/trainer/snow_worker_m.png differ diff --git a/public/images/trainer/star_grunt_f.png b/public/images/trainer/star_grunt_f.png index ee0c25147cc..26f865a99f8 100644 Binary files a/public/images/trainer/star_grunt_f.png and b/public/images/trainer/star_grunt_f.png differ diff --git a/public/images/trainer/star_grunt_m.png b/public/images/trainer/star_grunt_m.png index 13fd4e88510..981d8af43e9 100644 Binary files a/public/images/trainer/star_grunt_m.png and b/public/images/trainer/star_grunt_m.png differ diff --git a/public/images/trainer/steven.png b/public/images/trainer/steven.png index 969d7b3d08c..ab18f202d3a 100644 Binary files a/public/images/trainer/steven.png and b/public/images/trainer/steven.png differ diff --git a/public/images/trainer/striker.png b/public/images/trainer/striker.png index 656a14833a1..2d9f87cf214 100644 Binary files a/public/images/trainer/striker.png and b/public/images/trainer/striker.png differ diff --git a/public/images/trainer/swimmer_f.png b/public/images/trainer/swimmer_f.png index 0a37d19172b..5514e1ac949 100644 Binary files a/public/images/trainer/swimmer_f.png and b/public/images/trainer/swimmer_f.png differ diff --git a/public/images/trainer/swimmer_m.png b/public/images/trainer/swimmer_m.png index 517ed5cfc65..ae028e93fe8 100644 Binary files a/public/images/trainer/swimmer_m.png and b/public/images/trainer/swimmer_m.png differ diff --git a/public/images/trainer/tate.png b/public/images/trainer/tate.png index 1702c2c7e7e..d1f66d77409 100644 Binary files a/public/images/trainer/tate.png and b/public/images/trainer/tate.png differ diff --git a/public/images/trainer/trainer_f_back.png b/public/images/trainer/trainer_f_back.png index 8c528ad5610..67f6c71b7d8 100644 Binary files a/public/images/trainer/trainer_f_back.png and b/public/images/trainer/trainer_f_back.png differ diff --git a/public/images/trainer/trainer_f_back_pb.png b/public/images/trainer/trainer_f_back_pb.png index e1c9b6d6ca4..f0497142d04 100644 Binary files a/public/images/trainer/trainer_f_back_pb.png and b/public/images/trainer/trainer_f_back_pb.png differ diff --git a/public/images/trainer/trainer_m_back.png b/public/images/trainer/trainer_m_back.png index 9e40d634ad7..5721107641e 100644 Binary files a/public/images/trainer/trainer_m_back.png and b/public/images/trainer/trainer_m_back.png differ diff --git a/public/images/trainer/trainer_m_back_pb.png b/public/images/trainer/trainer_m_back_pb.png index 8980a4e8249..06b1833e94a 100644 Binary files a/public/images/trainer/trainer_m_back_pb.png and b/public/images/trainer/trainer_m_back_pb.png differ diff --git a/public/images/trainer/tulip.png b/public/images/trainer/tulip.png index 87c9f3fd029..ed0fa56b494 100644 Binary files a/public/images/trainer/tulip.png and b/public/images/trainer/tulip.png differ diff --git a/public/images/trainer/twins.png b/public/images/trainer/twins.png index 9114c998285..8c3e3881458 100644 Binary files a/public/images/trainer/twins.png and b/public/images/trainer/twins.png differ diff --git a/public/images/trainer/unknown_f.png b/public/images/trainer/unknown_f.png index f7fb6d789a8..88cd0bb18c7 100644 Binary files a/public/images/trainer/unknown_f.png and b/public/images/trainer/unknown_f.png differ diff --git a/public/images/trainer/unknown_m.png b/public/images/trainer/unknown_m.png index a48f97af57a..df36d351b3d 100644 Binary files a/public/images/trainer/unknown_m.png and b/public/images/trainer/unknown_m.png differ diff --git a/public/images/trainer/valerie.png b/public/images/trainer/valerie.png index dc03beceb56..d8e652cdc82 100644 Binary files a/public/images/trainer/valerie.png and b/public/images/trainer/valerie.png differ diff --git a/public/images/trainer/veteran_f.png b/public/images/trainer/veteran_f.png index d6117581ff3..e49f49cb209 100644 Binary files a/public/images/trainer/veteran_f.png and b/public/images/trainer/veteran_f.png differ diff --git a/public/images/trainer/veteran_m.png b/public/images/trainer/veteran_m.png index 1f1d864859d..d30e3b18fed 100644 Binary files a/public/images/trainer/veteran_m.png and b/public/images/trainer/veteran_m.png differ diff --git a/public/images/trainer/vicky.png b/public/images/trainer/vicky.png index 3e2d6c13696..19020b9e204 100644 Binary files a/public/images/trainer/vicky.png and b/public/images/trainer/vicky.png differ diff --git a/public/images/trainer/victor.png b/public/images/trainer/victor.png index 3ffddea24bb..e7455126957 100644 Binary files a/public/images/trainer/victor.png and b/public/images/trainer/victor.png differ diff --git a/public/images/trainer/victoria.png b/public/images/trainer/victoria.png index e2874f266ad..8ee103e6376 100644 Binary files a/public/images/trainer/victoria.png and b/public/images/trainer/victoria.png differ diff --git a/public/images/trainer/viola.png b/public/images/trainer/viola.png index 0f6f19cadff..e9484ec0bca 100644 Binary files a/public/images/trainer/viola.png and b/public/images/trainer/viola.png differ diff --git a/public/images/trainer/vito.png b/public/images/trainer/vito.png index a7c6c0444f4..88fa724fc00 100644 Binary files a/public/images/trainer/vito.png and b/public/images/trainer/vito.png differ diff --git a/public/images/trainer/vivi.png b/public/images/trainer/vivi.png index cd97e676cfb..d4652590756 100644 Binary files a/public/images/trainer/vivi.png and b/public/images/trainer/vivi.png differ diff --git a/public/images/trainer/volkner.png b/public/images/trainer/volkner.png index d791513764a..b6fac546efa 100644 Binary files a/public/images/trainer/volkner.png and b/public/images/trainer/volkner.png differ diff --git a/public/images/trainer/waiter_f.png b/public/images/trainer/waiter_f.png index 2fbe15759e2..63aaa72f61e 100644 Binary files a/public/images/trainer/waiter_f.png and b/public/images/trainer/waiter_f.png differ diff --git a/public/images/trainer/waiter_m.png b/public/images/trainer/waiter_m.png index 2c9f01e9b49..24bf074d322 100644 Binary files a/public/images/trainer/waiter_m.png and b/public/images/trainer/waiter_m.png differ diff --git a/public/images/trainer/wallace.png b/public/images/trainer/wallace.png index c52997c700b..f5c366e07d9 100644 Binary files a/public/images/trainer/wallace.png and b/public/images/trainer/wallace.png differ diff --git a/public/images/trainer/wattson.png b/public/images/trainer/wattson.png index e31db579ac1..3a115e8e702 100644 Binary files a/public/images/trainer/wattson.png and b/public/images/trainer/wattson.png differ diff --git a/public/images/trainer/whitney.png b/public/images/trainer/whitney.png index 8177f4491ec..69a87bc1105 100644 Binary files a/public/images/trainer/whitney.png and b/public/images/trainer/whitney.png differ diff --git a/public/images/trainer/wikstrom.png b/public/images/trainer/wikstrom.png index c78630a3a11..bf0f5042eb9 100644 Binary files a/public/images/trainer/wikstrom.png and b/public/images/trainer/wikstrom.png differ diff --git a/public/images/trainer/will.png b/public/images/trainer/will.png index 918fe06431a..6095b21584e 100644 Binary files a/public/images/trainer/will.png and b/public/images/trainer/will.png differ diff --git a/public/images/trainer/winona.png b/public/images/trainer/winona.png index 362941ea5fa..4fa37c83799 100644 Binary files a/public/images/trainer/winona.png and b/public/images/trainer/winona.png differ diff --git a/public/images/trainer/worker_f.png b/public/images/trainer/worker_f.png index e52dbf31514..02eab1ae027 100644 Binary files a/public/images/trainer/worker_f.png and b/public/images/trainer/worker_f.png differ diff --git a/public/images/trainer/worker_m.png b/public/images/trainer/worker_m.png index 53bfd8047cd..0142c1053c8 100644 Binary files a/public/images/trainer/worker_m.png and b/public/images/trainer/worker_m.png differ diff --git a/public/images/trainer/wulfric.png b/public/images/trainer/wulfric.png index f379afa2260..6f146d502ac 100644 Binary files a/public/images/trainer/wulfric.png and b/public/images/trainer/wulfric.png differ diff --git a/public/images/trainer/youngster_f.png b/public/images/trainer/youngster_f.png index 40b5a947be8..886b1e90963 100644 Binary files a/public/images/trainer/youngster_f.png and b/public/images/trainer/youngster_f.png differ diff --git a/public/images/trainer/youngster_m.png b/public/images/trainer/youngster_m.png index 5f092f96ef1..543536c2412 100644 Binary files a/public/images/trainer/youngster_m.png and b/public/images/trainer/youngster_m.png differ diff --git a/public/images/trainer/zinzolin.png b/public/images/trainer/zinzolin.png index 6c866562989..a376a1cdd7d 100644 Binary files a/public/images/trainer/zinzolin.png and b/public/images/trainer/zinzolin.png differ diff --git a/public/images/types.png b/public/images/types.png index 8b644f1041c..26cb6111c8e 100644 Binary files a/public/images/types.png and b/public/images/types.png differ diff --git a/public/images/types_ca.png b/public/images/types_ca.png index a1c295d7b9b..363589b6421 100644 Binary files a/public/images/types_ca.png and b/public/images/types_ca.png differ diff --git a/public/images/types_da.png b/public/images/types_da.png index f2b7af8967a..c199e078376 100644 Binary files a/public/images/types_da.png and b/public/images/types_da.png differ diff --git a/public/images/types_de.png b/public/images/types_de.png index a033554029c..0e1d77e73b2 100755 Binary files a/public/images/types_de.png and b/public/images/types_de.png differ diff --git a/public/images/types_es-ES.png b/public/images/types_es-ES.png index 3596b7f6a0a..79797df11e0 100644 Binary files a/public/images/types_es-ES.png and b/public/images/types_es-ES.png differ diff --git a/public/images/types_es-MX.png b/public/images/types_es-MX.png index ad159a7c6bb..49c99bfaf35 100644 Binary files a/public/images/types_es-MX.png and b/public/images/types_es-MX.png differ diff --git a/public/images/types_fr.png b/public/images/types_fr.png index bf81bed1b22..4ee31a0fb36 100644 Binary files a/public/images/types_fr.png and b/public/images/types_fr.png differ diff --git a/public/images/types_it.png b/public/images/types_it.png index 3be03aeea68..61079be81c0 100644 Binary files a/public/images/types_it.png and b/public/images/types_it.png differ diff --git a/public/images/types_ja.png b/public/images/types_ja.png index a6f3b00607f..072e723f9a8 100644 Binary files a/public/images/types_ja.png and b/public/images/types_ja.png differ diff --git a/public/images/types_ko.png b/public/images/types_ko.png index 53342ddd3ec..d8f7b2a7761 100644 Binary files a/public/images/types_ko.png and b/public/images/types_ko.png differ diff --git a/public/images/types_pt-BR.png b/public/images/types_pt-BR.png index 71bb1471ff2..58b6b14bcf4 100644 Binary files a/public/images/types_pt-BR.png and b/public/images/types_pt-BR.png differ diff --git a/public/images/types_ro.png b/public/images/types_ro.png index 35b09a37035..4eaadd8877b 100644 Binary files a/public/images/types_ro.png and b/public/images/types_ro.png differ diff --git a/public/images/types_ru.png b/public/images/types_ru.png index 1e0615f721f..8ce9c06f905 100644 Binary files a/public/images/types_ru.png and b/public/images/types_ru.png differ diff --git a/public/images/types_tr.png b/public/images/types_tr.png index 93eae27caa0..828aeb0b741 100644 Binary files a/public/images/types_tr.png and b/public/images/types_tr.png differ diff --git a/public/images/types_zh-CN.png b/public/images/types_zh-CN.png index e1927ad9d3b..52403ed5892 100644 Binary files a/public/images/types_zh-CN.png and b/public/images/types_zh-CN.png differ diff --git a/public/images/types_zh-TW.png b/public/images/types_zh-TW.png index 8b644f1041c..9ebc29f0d4f 100644 Binary files a/public/images/types_zh-TW.png and b/public/images/types_zh-TW.png differ diff --git a/public/images/ui/ability_bar_left.png b/public/images/ui/ability_bar_left.png index 54abe4c5a94..de15f4910d6 100644 Binary files a/public/images/ui/ability_bar_left.png and b/public/images/ui/ability_bar_left.png differ diff --git a/public/images/ui/ability_bar_right.png b/public/images/ui/ability_bar_right.png index 282025bfc74..b8ab048c6b0 100644 Binary files a/public/images/ui/ability_bar_right.png and b/public/images/ui/ability_bar_right.png differ diff --git a/public/images/ui/achv_bar.png b/public/images/ui/achv_bar.png index c2ab2c5c63e..c5ba5fd2c4c 100644 Binary files a/public/images/ui/achv_bar.png and b/public/images/ui/achv_bar.png differ diff --git a/public/images/ui/achv_bar_2.png b/public/images/ui/achv_bar_2.png index 8505760d66e..b4d9af70614 100644 Binary files a/public/images/ui/achv_bar_2.png and b/public/images/ui/achv_bar_2.png differ diff --git a/public/images/ui/achv_bar_3.png b/public/images/ui/achv_bar_3.png index 723e533f741..2a3b26ebfb6 100644 Binary files a/public/images/ui/achv_bar_3.png and b/public/images/ui/achv_bar_3.png differ diff --git a/public/images/ui/achv_bar_4.png b/public/images/ui/achv_bar_4.png index f14026c7a34..2f079bb927c 100644 Binary files a/public/images/ui/achv_bar_4.png and b/public/images/ui/achv_bar_4.png differ diff --git a/public/images/ui/achv_bar_5.png b/public/images/ui/achv_bar_5.png index cc06af91620..0f27058fc7f 100644 Binary files a/public/images/ui/achv_bar_5.png and b/public/images/ui/achv_bar_5.png differ diff --git a/public/images/ui/bg.png b/public/images/ui/bg.png index 9c35e80ff8b..0f73a067181 100644 Binary files a/public/images/ui/bg.png and b/public/images/ui/bg.png differ diff --git a/public/images/ui/bgm_bar.png b/public/images/ui/bgm_bar.png index 54abe4c5a94..de15f4910d6 100644 Binary files a/public/images/ui/bgm_bar.png and b/public/images/ui/bgm_bar.png differ diff --git a/public/images/ui/bmenu_sel.png b/public/images/ui/bmenu_sel.png index c2e28ba2035..a77939dbac3 100644 Binary files a/public/images/ui/bmenu_sel.png and b/public/images/ui/bmenu_sel.png differ diff --git a/public/images/ui/boolean_sel.png b/public/images/ui/boolean_sel.png index d5b212a3088..7f1c86fbb4d 100644 Binary files a/public/images/ui/boolean_sel.png and b/public/images/ui/boolean_sel.png differ diff --git a/public/images/ui/button_tera.png b/public/images/ui/button_tera.png index c9672bafa39..61a65ed4813 100644 Binary files a/public/images/ui/button_tera.png and b/public/images/ui/button_tera.png differ diff --git a/public/images/ui/candy.png b/public/images/ui/candy.png index 6b633a19504..a37358a3f90 100644 Binary files a/public/images/ui/candy.png and b/public/images/ui/candy.png differ diff --git a/public/images/ui/candy_overlay.png b/public/images/ui/candy_overlay.png index 835cfccb05c..e8cadeabb73 100644 Binary files a/public/images/ui/candy_overlay.png and b/public/images/ui/candy_overlay.png differ diff --git a/public/images/ui/champion_ribbon.png b/public/images/ui/champion_ribbon.png index 96e36075d8f..533c20f6821 100644 Binary files a/public/images/ui/champion_ribbon.png and b/public/images/ui/champion_ribbon.png differ diff --git a/public/images/ui/cursor.png b/public/images/ui/cursor.png index 68283fcc22d..0e2f9d98a16 100644 Binary files a/public/images/ui/cursor.png and b/public/images/ui/cursor.png differ diff --git a/public/images/ui/cursor_reverse.png b/public/images/ui/cursor_reverse.png index 40c867e62a5..6048cdcdd86 100644 Binary files a/public/images/ui/cursor_reverse.png and b/public/images/ui/cursor_reverse.png differ diff --git a/public/images/ui/cursor_tera.png b/public/images/ui/cursor_tera.png index 34cbe095895..9d2be35cfac 100644 Binary files a/public/images/ui/cursor_tera.png and b/public/images/ui/cursor_tera.png differ diff --git a/public/images/ui/dawn_icon_bg.png b/public/images/ui/dawn_icon_bg.png index 2983c2744a3..7136582a3c8 100644 Binary files a/public/images/ui/dawn_icon_bg.png and b/public/images/ui/dawn_icon_bg.png differ diff --git a/public/images/ui/dawn_icon_fg.png b/public/images/ui/dawn_icon_fg.png index e6c195bd371..c64f0578073 100644 Binary files a/public/images/ui/dawn_icon_fg.png and b/public/images/ui/dawn_icon_fg.png differ diff --git a/public/images/ui/dawn_icon_mg.png b/public/images/ui/dawn_icon_mg.png index ff32b4418aa..63786fd68fe 100644 Binary files a/public/images/ui/dawn_icon_mg.png and b/public/images/ui/dawn_icon_mg.png differ diff --git a/public/images/ui/day_icon_bg.png b/public/images/ui/day_icon_bg.png index 9028e0309f7..34b812464af 100644 Binary files a/public/images/ui/day_icon_bg.png and b/public/images/ui/day_icon_bg.png differ diff --git a/public/images/ui/day_icon_fg.png b/public/images/ui/day_icon_fg.png index 523f7b1be7e..a480e4d274d 100644 Binary files a/public/images/ui/day_icon_fg.png and b/public/images/ui/day_icon_fg.png differ diff --git a/public/images/ui/day_icon_mg.png b/public/images/ui/day_icon_mg.png index 7a236fa48d3..43b5a011098 100644 Binary files a/public/images/ui/day_icon_mg.png and b/public/images/ui/day_icon_mg.png differ diff --git a/public/images/ui/discord.png b/public/images/ui/discord.png index 1cd833854b0..fd16df3646c 100644 Binary files a/public/images/ui/discord.png and b/public/images/ui/discord.png differ diff --git a/public/images/ui/dusk_icon_bg.png b/public/images/ui/dusk_icon_bg.png index 57d22ec2b21..31d637eb674 100644 Binary files a/public/images/ui/dusk_icon_bg.png and b/public/images/ui/dusk_icon_bg.png differ diff --git a/public/images/ui/dusk_icon_fg.png b/public/images/ui/dusk_icon_fg.png index e0ca1bce79b..b19cf6ae1f8 100644 Binary files a/public/images/ui/dusk_icon_fg.png and b/public/images/ui/dusk_icon_fg.png differ diff --git a/public/images/ui/dusk_icon_mg.png b/public/images/ui/dusk_icon_mg.png index 4e6a880b37f..298b9de6ff7 100644 Binary files a/public/images/ui/dusk_icon_mg.png and b/public/images/ui/dusk_icon_mg.png differ diff --git a/public/images/ui/egg_list_bg.png b/public/images/ui/egg_list_bg.png index 4ec09441866..3dd8ac7386a 100644 Binary files a/public/images/ui/egg_list_bg.png and b/public/images/ui/egg_list_bg.png differ diff --git a/public/images/ui/egg_summary_bg.png b/public/images/ui/egg_summary_bg.png index e81934b9d75..ed602d77bb6 100644 Binary files a/public/images/ui/egg_summary_bg.png and b/public/images/ui/egg_summary_bg.png differ diff --git a/public/images/ui/egg_summary_bg_blank.png b/public/images/ui/egg_summary_bg_blank.png index 09bcb63cfa3..77aed2cadc6 100644 Binary files a/public/images/ui/egg_summary_bg_blank.png and b/public/images/ui/egg_summary_bg_blank.png differ diff --git a/public/images/ui/favorite.png b/public/images/ui/favorite.png index d86dd58f5ab..3c0ea483b65 100644 Binary files a/public/images/ui/favorite.png and b/public/images/ui/favorite.png differ diff --git a/public/images/ui/friendship.png b/public/images/ui/friendship.png index 073adcadc76..16a50f93c79 100644 Binary files a/public/images/ui/friendship.png and b/public/images/ui/friendship.png differ diff --git a/public/images/ui/friendship_overlay.png b/public/images/ui/friendship_overlay.png index 4a4724fbdc9..b6957ded371 100644 Binary files a/public/images/ui/friendship_overlay.png and b/public/images/ui/friendship_overlay.png differ diff --git a/public/images/ui/google.png b/public/images/ui/google.png index 82760552886..3f5389086db 100644 Binary files a/public/images/ui/google.png and b/public/images/ui/google.png differ diff --git a/public/images/ui/ha_capsule.png b/public/images/ui/ha_capsule.png index cccc9cf51d3..a5b2a1e9d42 100644 Binary files a/public/images/ui/ha_capsule.png and b/public/images/ui/ha_capsule.png differ diff --git a/public/images/ui/hall_of_fame_blue.png b/public/images/ui/hall_of_fame_blue.png index 87fadf565fd..2b799070cb1 100644 Binary files a/public/images/ui/hall_of_fame_blue.png and b/public/images/ui/hall_of_fame_blue.png differ diff --git a/public/images/ui/hall_of_fame_red.png b/public/images/ui/hall_of_fame_red.png index 5d4d5e41e9c..df999ae631f 100644 Binary files a/public/images/ui/hall_of_fame_red.png and b/public/images/ui/hall_of_fame_red.png differ diff --git a/public/images/ui/icon_egg_move.png b/public/images/ui/icon_egg_move.png index a5b0bff4ace..3b79a6682d0 100644 Binary files a/public/images/ui/icon_egg_move.png and b/public/images/ui/icon_egg_move.png differ diff --git a/public/images/ui/icon_lock.png b/public/images/ui/icon_lock.png index 6a12efa15e8..6a09f41f182 100644 Binary files a/public/images/ui/icon_lock.png and b/public/images/ui/icon_lock.png differ diff --git a/public/images/ui/icon_owned.png b/public/images/ui/icon_owned.png index 37e2dfd08e6..f4592f33d0f 100644 Binary files a/public/images/ui/icon_owned.png and b/public/images/ui/icon_owned.png differ diff --git a/public/images/ui/icon_spliced.png b/public/images/ui/icon_spliced.png index 1db0d1136ba..edd3049a5c7 100644 Binary files a/public/images/ui/icon_spliced.png and b/public/images/ui/icon_spliced.png differ diff --git a/public/images/ui/icon_stop.png b/public/images/ui/icon_stop.png index 6d9c201695a..ff248e38c8e 100644 Binary files a/public/images/ui/icon_stop.png and b/public/images/ui/icon_stop.png differ diff --git a/public/images/ui/icon_tera.png b/public/images/ui/icon_tera.png index 97673434315..9d2be35cfac 100644 Binary files a/public/images/ui/icon_tera.png and b/public/images/ui/icon_tera.png differ diff --git a/public/images/ui/legacy/ability_bar_left.png b/public/images/ui/legacy/ability_bar_left.png index 7491a9ca527..18a3726eb28 100644 Binary files a/public/images/ui/legacy/ability_bar_left.png and b/public/images/ui/legacy/ability_bar_left.png differ diff --git a/public/images/ui/legacy/ability_bar_right.png b/public/images/ui/legacy/ability_bar_right.png index 3dcb845b3c9..8b5954d5624 100644 Binary files a/public/images/ui/legacy/ability_bar_right.png and b/public/images/ui/legacy/ability_bar_right.png differ diff --git a/public/images/ui/legacy/achv_bar.png b/public/images/ui/legacy/achv_bar.png index 442b43343cc..7e5f5d5f8c7 100644 Binary files a/public/images/ui/legacy/achv_bar.png and b/public/images/ui/legacy/achv_bar.png differ diff --git a/public/images/ui/legacy/achv_bar_2.png b/public/images/ui/legacy/achv_bar_2.png index 23ffca0a7d9..87d19379f01 100644 Binary files a/public/images/ui/legacy/achv_bar_2.png and b/public/images/ui/legacy/achv_bar_2.png differ diff --git a/public/images/ui/legacy/achv_bar_3.png b/public/images/ui/legacy/achv_bar_3.png index e2697234ecd..874e2384b1a 100644 Binary files a/public/images/ui/legacy/achv_bar_3.png and b/public/images/ui/legacy/achv_bar_3.png differ diff --git a/public/images/ui/legacy/achv_bar_4.png b/public/images/ui/legacy/achv_bar_4.png index d52fbfd055e..c0032288a00 100644 Binary files a/public/images/ui/legacy/achv_bar_4.png and b/public/images/ui/legacy/achv_bar_4.png differ diff --git a/public/images/ui/legacy/achv_bar_5.png b/public/images/ui/legacy/achv_bar_5.png index dae7a30dec0..ea6830b53ef 100644 Binary files a/public/images/ui/legacy/achv_bar_5.png and b/public/images/ui/legacy/achv_bar_5.png differ diff --git a/public/images/ui/legacy/bg.png b/public/images/ui/legacy/bg.png index 0b1ec4fac1a..d5b3f127ad8 100644 Binary files a/public/images/ui/legacy/bg.png and b/public/images/ui/legacy/bg.png differ diff --git a/public/images/ui/legacy/bgm_bar.png b/public/images/ui/legacy/bgm_bar.png index fedeb323fc2..bdcbf4cceee 100644 Binary files a/public/images/ui/legacy/bgm_bar.png and b/public/images/ui/legacy/bgm_bar.png differ diff --git a/public/images/ui/legacy/bmenu_sel.png b/public/images/ui/legacy/bmenu_sel.png index fe54fa918cf..8058a569e7d 100644 Binary files a/public/images/ui/legacy/bmenu_sel.png and b/public/images/ui/legacy/bmenu_sel.png differ diff --git a/public/images/ui/legacy/boolean_sel.png b/public/images/ui/legacy/boolean_sel.png index bd832fa1289..5ae1bba4bb2 100644 Binary files a/public/images/ui/legacy/boolean_sel.png and b/public/images/ui/legacy/boolean_sel.png differ diff --git a/public/images/ui/legacy/button_tera.png b/public/images/ui/legacy/button_tera.png index c9672bafa39..61a65ed4813 100644 Binary files a/public/images/ui/legacy/button_tera.png and b/public/images/ui/legacy/button_tera.png differ diff --git a/public/images/ui/legacy/candy.png b/public/images/ui/legacy/candy.png index 6b633a19504..a37358a3f90 100644 Binary files a/public/images/ui/legacy/candy.png and b/public/images/ui/legacy/candy.png differ diff --git a/public/images/ui/legacy/candy_overlay.png b/public/images/ui/legacy/candy_overlay.png index 835cfccb05c..e8cadeabb73 100644 Binary files a/public/images/ui/legacy/candy_overlay.png and b/public/images/ui/legacy/candy_overlay.png differ diff --git a/public/images/ui/legacy/champion_ribbon.png b/public/images/ui/legacy/champion_ribbon.png index 96e36075d8f..533c20f6821 100644 Binary files a/public/images/ui/legacy/champion_ribbon.png and b/public/images/ui/legacy/champion_ribbon.png differ diff --git a/public/images/ui/legacy/cursor.png b/public/images/ui/legacy/cursor.png index 39d7b7977ee..deadae321e3 100644 Binary files a/public/images/ui/legacy/cursor.png and b/public/images/ui/legacy/cursor.png differ diff --git a/public/images/ui/legacy/cursor_reverse.png b/public/images/ui/legacy/cursor_reverse.png index 57fb297747e..a13304f0956 100644 Binary files a/public/images/ui/legacy/cursor_reverse.png and b/public/images/ui/legacy/cursor_reverse.png differ diff --git a/public/images/ui/legacy/cursor_tera.png b/public/images/ui/legacy/cursor_tera.png index f2e77046137..cccdb7c68b1 100644 Binary files a/public/images/ui/legacy/cursor_tera.png and b/public/images/ui/legacy/cursor_tera.png differ diff --git a/public/images/ui/legacy/dawn_icon_bg.png b/public/images/ui/legacy/dawn_icon_bg.png index 87c4d75cd94..6521aa78765 100644 Binary files a/public/images/ui/legacy/dawn_icon_bg.png and b/public/images/ui/legacy/dawn_icon_bg.png differ diff --git a/public/images/ui/legacy/dawn_icon_fg.png b/public/images/ui/legacy/dawn_icon_fg.png index db985f953d3..1b82ad2dfcf 100644 Binary files a/public/images/ui/legacy/dawn_icon_fg.png and b/public/images/ui/legacy/dawn_icon_fg.png differ diff --git a/public/images/ui/legacy/dawn_icon_mg.png b/public/images/ui/legacy/dawn_icon_mg.png index 442cb1b674c..c259ea4e068 100644 Binary files a/public/images/ui/legacy/dawn_icon_mg.png and b/public/images/ui/legacy/dawn_icon_mg.png differ diff --git a/public/images/ui/legacy/day_icon_bg.png b/public/images/ui/legacy/day_icon_bg.png index 3db0dab4589..9c79cabfe26 100644 Binary files a/public/images/ui/legacy/day_icon_bg.png and b/public/images/ui/legacy/day_icon_bg.png differ diff --git a/public/images/ui/legacy/day_icon_fg.png b/public/images/ui/legacy/day_icon_fg.png index 657c82e5dbd..79872f95202 100644 Binary files a/public/images/ui/legacy/day_icon_fg.png and b/public/images/ui/legacy/day_icon_fg.png differ diff --git a/public/images/ui/legacy/day_icon_mg.png b/public/images/ui/legacy/day_icon_mg.png index 90fce4a761c..132d23bd8a3 100644 Binary files a/public/images/ui/legacy/day_icon_mg.png and b/public/images/ui/legacy/day_icon_mg.png differ diff --git a/public/images/ui/legacy/discord.png b/public/images/ui/legacy/discord.png index eac801cdd9d..1bb254b1594 100644 Binary files a/public/images/ui/legacy/discord.png and b/public/images/ui/legacy/discord.png differ diff --git a/public/images/ui/legacy/dusk_icon_bg.png b/public/images/ui/legacy/dusk_icon_bg.png index 7610a2e67f8..d5e29169fa8 100644 Binary files a/public/images/ui/legacy/dusk_icon_bg.png and b/public/images/ui/legacy/dusk_icon_bg.png differ diff --git a/public/images/ui/legacy/dusk_icon_fg.png b/public/images/ui/legacy/dusk_icon_fg.png index d4bbb98fdd1..f8adaf11a83 100644 Binary files a/public/images/ui/legacy/dusk_icon_fg.png and b/public/images/ui/legacy/dusk_icon_fg.png differ diff --git a/public/images/ui/legacy/dusk_icon_mg.png b/public/images/ui/legacy/dusk_icon_mg.png index dc603f8ca79..b889cab0e4f 100644 Binary files a/public/images/ui/legacy/dusk_icon_mg.png and b/public/images/ui/legacy/dusk_icon_mg.png differ diff --git a/public/images/ui/legacy/egg_list_bg.png b/public/images/ui/legacy/egg_list_bg.png index 932d01d335f..2d08756679e 100644 Binary files a/public/images/ui/legacy/egg_list_bg.png and b/public/images/ui/legacy/egg_list_bg.png differ diff --git a/public/images/ui/legacy/egg_summary_bg.png b/public/images/ui/legacy/egg_summary_bg.png index 658f5df0e96..8affc928028 100644 Binary files a/public/images/ui/legacy/egg_summary_bg.png and b/public/images/ui/legacy/egg_summary_bg.png differ diff --git a/public/images/ui/legacy/favorite.png b/public/images/ui/legacy/favorite.png index d86dd58f5ab..3c0ea483b65 100644 Binary files a/public/images/ui/legacy/favorite.png and b/public/images/ui/legacy/favorite.png differ diff --git a/public/images/ui/legacy/friendship.png b/public/images/ui/legacy/friendship.png index 073adcadc76..16a50f93c79 100644 Binary files a/public/images/ui/legacy/friendship.png and b/public/images/ui/legacy/friendship.png differ diff --git a/public/images/ui/legacy/friendship_overlay.png b/public/images/ui/legacy/friendship_overlay.png index 4a4724fbdc9..b6957ded371 100644 Binary files a/public/images/ui/legacy/friendship_overlay.png and b/public/images/ui/legacy/friendship_overlay.png differ diff --git a/public/images/ui/legacy/google.png b/public/images/ui/legacy/google.png index 82760552886..3f5389086db 100644 Binary files a/public/images/ui/legacy/google.png and b/public/images/ui/legacy/google.png differ diff --git a/public/images/ui/legacy/ha_capsule.png b/public/images/ui/legacy/ha_capsule.png index cccc9cf51d3..a5b2a1e9d42 100644 Binary files a/public/images/ui/legacy/ha_capsule.png and b/public/images/ui/legacy/ha_capsule.png differ diff --git a/public/images/ui/legacy/hall_of_fame_blue.png b/public/images/ui/legacy/hall_of_fame_blue.png index 87fadf565fd..2b799070cb1 100644 Binary files a/public/images/ui/legacy/hall_of_fame_blue.png and b/public/images/ui/legacy/hall_of_fame_blue.png differ diff --git a/public/images/ui/legacy/hall_of_fame_red.png b/public/images/ui/legacy/hall_of_fame_red.png index 5d4d5e41e9c..df999ae631f 100644 Binary files a/public/images/ui/legacy/hall_of_fame_red.png and b/public/images/ui/legacy/hall_of_fame_red.png differ diff --git a/public/images/ui/legacy/icon_egg_move.png b/public/images/ui/legacy/icon_egg_move.png index a5b0bff4ace..3b79a6682d0 100644 Binary files a/public/images/ui/legacy/icon_egg_move.png and b/public/images/ui/legacy/icon_egg_move.png differ diff --git a/public/images/ui/legacy/icon_lock.png b/public/images/ui/legacy/icon_lock.png index 6a12efa15e8..6a09f41f182 100644 Binary files a/public/images/ui/legacy/icon_lock.png and b/public/images/ui/legacy/icon_lock.png differ diff --git a/public/images/ui/legacy/icon_owned.png b/public/images/ui/legacy/icon_owned.png index 21e03afec60..6fe9dacd3e7 100644 Binary files a/public/images/ui/legacy/icon_owned.png and b/public/images/ui/legacy/icon_owned.png differ diff --git a/public/images/ui/legacy/icon_spliced.png b/public/images/ui/legacy/icon_spliced.png index 1db0d1136ba..edd3049a5c7 100644 Binary files a/public/images/ui/legacy/icon_spliced.png and b/public/images/ui/legacy/icon_spliced.png differ diff --git a/public/images/ui/legacy/icon_stop.png b/public/images/ui/legacy/icon_stop.png index 6d9c201695a..ff248e38c8e 100644 Binary files a/public/images/ui/legacy/icon_stop.png and b/public/images/ui/legacy/icon_stop.png differ diff --git a/public/images/ui/legacy/icon_tera.png b/public/images/ui/legacy/icon_tera.png index d178ca8fcb9..90d30ee9db6 100644 Binary files a/public/images/ui/legacy/icon_tera.png and b/public/images/ui/legacy/icon_tera.png differ diff --git a/public/images/ui/legacy/link_icon.png b/public/images/ui/legacy/link_icon.png index 56081261b9c..1e20ab56ec7 100644 Binary files a/public/images/ui/legacy/link_icon.png and b/public/images/ui/legacy/link_icon.png differ diff --git a/public/images/ui/legacy/mmenu_sel.png b/public/images/ui/legacy/mmenu_sel.png index 26952f7ece9..64b388876da 100644 Binary files a/public/images/ui/legacy/mmenu_sel.png and b/public/images/ui/legacy/mmenu_sel.png differ diff --git a/public/images/ui/legacy/mystery_egg.png b/public/images/ui/legacy/mystery_egg.png index bb117a137b0..83ad626da7e 100644 Binary files a/public/images/ui/legacy/mystery_egg.png and b/public/images/ui/legacy/mystery_egg.png differ diff --git a/public/images/ui/legacy/namebox.png b/public/images/ui/legacy/namebox.png index 25659f9dd40..5eea1504d77 100644 Binary files a/public/images/ui/legacy/namebox.png and b/public/images/ui/legacy/namebox.png differ diff --git a/public/images/ui/legacy/night_icon_bg.png b/public/images/ui/legacy/night_icon_bg.png index c32ee1ebd2a..ea2febb3cc1 100644 Binary files a/public/images/ui/legacy/night_icon_bg.png and b/public/images/ui/legacy/night_icon_bg.png differ diff --git a/public/images/ui/legacy/night_icon_fg.png b/public/images/ui/legacy/night_icon_fg.png index 737cca3305e..d8eceed3e37 100644 Binary files a/public/images/ui/legacy/night_icon_fg.png and b/public/images/ui/legacy/night_icon_fg.png differ diff --git a/public/images/ui/legacy/normal_memory.png b/public/images/ui/legacy/normal_memory.png index ddc22d1d4ab..604371dcc7c 100644 Binary files a/public/images/ui/legacy/normal_memory.png and b/public/images/ui/legacy/normal_memory.png differ diff --git a/public/images/ui/legacy/numbers.png b/public/images/ui/legacy/numbers.png index ba48453fa60..6157774d58e 100644 Binary files a/public/images/ui/legacy/numbers.png and b/public/images/ui/legacy/numbers.png differ diff --git a/public/images/ui/legacy/numbers_alt.png b/public/images/ui/legacy/numbers_alt.png index 1512ef435d0..fb510465471 100644 Binary files a/public/images/ui/legacy/numbers_alt.png and b/public/images/ui/legacy/numbers_alt.png differ diff --git a/public/images/ui/legacy/numbers_red.png b/public/images/ui/legacy/numbers_red.png index acafb20c98a..f0d885d304d 100644 Binary files a/public/images/ui/legacy/numbers_red.png and b/public/images/ui/legacy/numbers_red.png differ diff --git a/public/images/ui/legacy/numbers_red_alt.png b/public/images/ui/legacy/numbers_red_alt.png index 90b6f109289..d9edb0468d8 100644 Binary files a/public/images/ui/legacy/numbers_red_alt.png and b/public/images/ui/legacy/numbers_red_alt.png differ diff --git a/public/images/ui/legacy/overlay_exp.png b/public/images/ui/legacy/overlay_exp.png index 51166cd9e62..85d0cad7bfb 100644 Binary files a/public/images/ui/legacy/overlay_exp.png and b/public/images/ui/legacy/overlay_exp.png differ diff --git a/public/images/ui/legacy/overlay_hp.png b/public/images/ui/legacy/overlay_hp.png index 9855c6106f4..75607c199f5 100644 Binary files a/public/images/ui/legacy/overlay_hp.png and b/public/images/ui/legacy/overlay_hp.png differ diff --git a/public/images/ui/legacy/overlay_hp_boss.png b/public/images/ui/legacy/overlay_hp_boss.png index d7dc3b74e0f..8bfb4524007 100644 Binary files a/public/images/ui/legacy/overlay_hp_boss.png and b/public/images/ui/legacy/overlay_hp_boss.png differ diff --git a/public/images/ui/legacy/overlay_lv.png b/public/images/ui/legacy/overlay_lv.png index d79614f41eb..fdcbb1c7082 100644 Binary files a/public/images/ui/legacy/overlay_lv.png and b/public/images/ui/legacy/overlay_lv.png differ diff --git a/public/images/ui/legacy/overlay_lv_alt.png b/public/images/ui/legacy/overlay_lv_alt.png index 75b90a2ee4c..f7a76955e7b 100644 Binary files a/public/images/ui/legacy/overlay_lv_alt.png and b/public/images/ui/legacy/overlay_lv_alt.png differ diff --git a/public/images/ui/legacy/overlay_message.png b/public/images/ui/legacy/overlay_message.png index 9e58d86fc8b..769b2ae8144 100644 Binary files a/public/images/ui/legacy/overlay_message.png and b/public/images/ui/legacy/overlay_message.png differ diff --git a/public/images/ui/legacy/party_bg.png b/public/images/ui/legacy/party_bg.png index 323de1856c8..cdade735b4a 100644 Binary files a/public/images/ui/legacy/party_bg.png and b/public/images/ui/legacy/party_bg.png differ diff --git a/public/images/ui/legacy/party_bg_double.png b/public/images/ui/legacy/party_bg_double.png index 89beff1edf0..8e00bd6c293 100644 Binary files a/public/images/ui/legacy/party_bg_double.png and b/public/images/ui/legacy/party_bg_double.png differ diff --git a/public/images/ui/legacy/party_cancel.png b/public/images/ui/legacy/party_cancel.png index 4219c8d33c4..9836b9ced2d 100644 Binary files a/public/images/ui/legacy/party_cancel.png and b/public/images/ui/legacy/party_cancel.png differ diff --git a/public/images/ui/legacy/party_exp_bar.png b/public/images/ui/legacy/party_exp_bar.png index 0a514b43393..61757a97c2d 100644 Binary files a/public/images/ui/legacy/party_exp_bar.png and b/public/images/ui/legacy/party_exp_bar.png differ diff --git a/public/images/ui/legacy/party_pb.png b/public/images/ui/legacy/party_pb.png index a1e5f7a9bf7..cd8b9726155 100644 Binary files a/public/images/ui/legacy/party_pb.png and b/public/images/ui/legacy/party_pb.png differ diff --git a/public/images/ui/legacy/party_slot.png b/public/images/ui/legacy/party_slot.png index c7a2eb5c082..162f321ece5 100644 Binary files a/public/images/ui/legacy/party_slot.png and b/public/images/ui/legacy/party_slot.png differ diff --git a/public/images/ui/legacy/party_slot_hp_bar.png b/public/images/ui/legacy/party_slot_hp_bar.png index 13ac7436cc7..6a37002744c 100644 Binary files a/public/images/ui/legacy/party_slot_hp_bar.png and b/public/images/ui/legacy/party_slot_hp_bar.png differ diff --git a/public/images/ui/legacy/party_slot_hp_overlay.png b/public/images/ui/legacy/party_slot_hp_overlay.png index 360a3cd17fa..859adab148c 100644 Binary files a/public/images/ui/legacy/party_slot_hp_overlay.png and b/public/images/ui/legacy/party_slot_hp_overlay.png differ diff --git a/public/images/ui/legacy/party_slot_main.png b/public/images/ui/legacy/party_slot_main.png index 06eb9b03a7d..cb4af766597 100644 Binary files a/public/images/ui/legacy/party_slot_main.png and b/public/images/ui/legacy/party_slot_main.png differ diff --git a/public/images/ui/legacy/party_slot_overlay_hp.png b/public/images/ui/legacy/party_slot_overlay_hp.png index 8c15c63f31a..2eebb557cce 100644 Binary files a/public/images/ui/legacy/party_slot_overlay_hp.png and b/public/images/ui/legacy/party_slot_overlay_hp.png differ diff --git a/public/images/ui/legacy/party_slot_overlay_lv.png b/public/images/ui/legacy/party_slot_overlay_lv.png index 57d901a7d3d..11bb545c7af 100644 Binary files a/public/images/ui/legacy/party_slot_overlay_lv.png and b/public/images/ui/legacy/party_slot_overlay_lv.png differ diff --git a/public/images/ui/legacy/party_slot_overlay_lv_alt.png b/public/images/ui/legacy/party_slot_overlay_lv_alt.png index a52b9c2fa60..387dfec6b22 100644 Binary files a/public/images/ui/legacy/party_slot_overlay_lv_alt.png and b/public/images/ui/legacy/party_slot_overlay_lv_alt.png differ diff --git a/public/images/ui/legacy/passive_bg.png b/public/images/ui/legacy/passive_bg.png index 1e18a47b881..4a3fa1ec72b 100644 Binary files a/public/images/ui/legacy/passive_bg.png and b/public/images/ui/legacy/passive_bg.png differ diff --git a/public/images/ui/legacy/pb_tray_ball.png b/public/images/ui/legacy/pb_tray_ball.png index 112184f8593..671db0c0b15 100644 Binary files a/public/images/ui/legacy/pb_tray_ball.png and b/public/images/ui/legacy/pb_tray_ball.png differ diff --git a/public/images/ui/legacy/pb_tray_overlay_enemy.png b/public/images/ui/legacy/pb_tray_overlay_enemy.png index c557ac736fc..86ac85f946f 100644 Binary files a/public/images/ui/legacy/pb_tray_overlay_enemy.png and b/public/images/ui/legacy/pb_tray_overlay_enemy.png differ diff --git a/public/images/ui/legacy/pb_tray_overlay_player.png b/public/images/ui/legacy/pb_tray_overlay_player.png index 4a065de5d7d..de0b01502c0 100644 Binary files a/public/images/ui/legacy/pb_tray_overlay_player.png and b/public/images/ui/legacy/pb_tray_overlay_player.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_boss.png b/public/images/ui/legacy/pbinfo_enemy_boss.png index cffefd32396..98b2f09f941 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_boss.png and b/public/images/ui/legacy/pbinfo_enemy_boss.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_boss_stats.png b/public/images/ui/legacy/pbinfo_enemy_boss_stats.png index faca8887ff5..bf4b3d61d12 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_boss_stats.png and b/public/images/ui/legacy/pbinfo_enemy_boss_stats.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_mini.png b/public/images/ui/legacy/pbinfo_enemy_mini.png index 908ea10606f..c1c002a4b21 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_mini.png and b/public/images/ui/legacy/pbinfo_enemy_mini.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_mini_stats.png b/public/images/ui/legacy/pbinfo_enemy_mini_stats.png index eb32e694134..e6eaaa3be9e 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_mini_stats.png and b/public/images/ui/legacy/pbinfo_enemy_mini_stats.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_type.png b/public/images/ui/legacy/pbinfo_enemy_type.png index d89e360af86..27cb481e861 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_type.png and b/public/images/ui/legacy/pbinfo_enemy_type.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_type1.png b/public/images/ui/legacy/pbinfo_enemy_type1.png index f882e56ac88..df44b5bad5d 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_type1.png and b/public/images/ui/legacy/pbinfo_enemy_type1.png differ diff --git a/public/images/ui/legacy/pbinfo_enemy_type2.png b/public/images/ui/legacy/pbinfo_enemy_type2.png index f882e56ac88..df44b5bad5d 100644 Binary files a/public/images/ui/legacy/pbinfo_enemy_type2.png and b/public/images/ui/legacy/pbinfo_enemy_type2.png differ diff --git a/public/images/ui/legacy/pbinfo_player.png b/public/images/ui/legacy/pbinfo_player.png index 825a9eafec2..60c92f886aa 100644 Binary files a/public/images/ui/legacy/pbinfo_player.png and b/public/images/ui/legacy/pbinfo_player.png differ diff --git a/public/images/ui/legacy/pbinfo_player_mini.png b/public/images/ui/legacy/pbinfo_player_mini.png index 1ccaf4efd65..120e9592ab9 100644 Binary files a/public/images/ui/legacy/pbinfo_player_mini.png and b/public/images/ui/legacy/pbinfo_player_mini.png differ diff --git a/public/images/ui/legacy/pbinfo_player_mini_stats.png b/public/images/ui/legacy/pbinfo_player_mini_stats.png index dd2b7e65ba3..00bb687296c 100644 Binary files a/public/images/ui/legacy/pbinfo_player_mini_stats.png and b/public/images/ui/legacy/pbinfo_player_mini_stats.png differ diff --git a/public/images/ui/legacy/pbinfo_player_stats.png b/public/images/ui/legacy/pbinfo_player_stats.png index 078248624bc..6bf984a94dc 100644 Binary files a/public/images/ui/legacy/pbinfo_player_stats.png and b/public/images/ui/legacy/pbinfo_player_stats.png differ diff --git a/public/images/ui/legacy/pbinfo_player_type.png b/public/images/ui/legacy/pbinfo_player_type.png index 1a8b82855f6..5bfc5545067 100644 Binary files a/public/images/ui/legacy/pbinfo_player_type.png and b/public/images/ui/legacy/pbinfo_player_type.png differ diff --git a/public/images/ui/legacy/pbinfo_player_type1.png b/public/images/ui/legacy/pbinfo_player_type1.png index f882e56ac88..df44b5bad5d 100644 Binary files a/public/images/ui/legacy/pbinfo_player_type1.png and b/public/images/ui/legacy/pbinfo_player_type1.png differ diff --git a/public/images/ui/legacy/pbinfo_player_type2.png b/public/images/ui/legacy/pbinfo_player_type2.png index f882e56ac88..df44b5bad5d 100644 Binary files a/public/images/ui/legacy/pbinfo_player_type2.png and b/public/images/ui/legacy/pbinfo_player_type2.png differ diff --git a/public/images/ui/legacy/pbinfo_stat.png b/public/images/ui/legacy/pbinfo_stat.png index 18b0ca314de..25c94f9a807 100644 Binary files a/public/images/ui/legacy/pbinfo_stat.png and b/public/images/ui/legacy/pbinfo_stat.png differ diff --git a/public/images/ui/legacy/pbinfo_stat_numbers.png b/public/images/ui/legacy/pbinfo_stat_numbers.png index b02dfbec72f..5d27caa8efa 100644 Binary files a/public/images/ui/legacy/pbinfo_stat_numbers.png and b/public/images/ui/legacy/pbinfo_stat_numbers.png differ diff --git a/public/images/ui/legacy/pokedex_summary_bg.png b/public/images/ui/legacy/pokedex_summary_bg.png index 690df1547c0..7323f99a913 100644 Binary files a/public/images/ui/legacy/pokedex_summary_bg.png and b/public/images/ui/legacy/pokedex_summary_bg.png differ diff --git a/public/images/ui/legacy/prompt.png b/public/images/ui/legacy/prompt.png index ee74128ce56..d70362d1958 100644 Binary files a/public/images/ui/legacy/prompt.png and b/public/images/ui/legacy/prompt.png differ diff --git a/public/images/ui/legacy/saving_icon.png b/public/images/ui/legacy/saving_icon.png index fddff8c2d39..772fa91fb8c 100644 Binary files a/public/images/ui/legacy/saving_icon.png and b/public/images/ui/legacy/saving_icon.png differ diff --git a/public/images/ui/legacy/scroll_bar.png b/public/images/ui/legacy/scroll_bar.png index e4e5eb34dd2..1e8f53cd29e 100644 Binary files a/public/images/ui/legacy/scroll_bar.png and b/public/images/ui/legacy/scroll_bar.png differ diff --git a/public/images/ui/legacy/scroll_bar_handle.png b/public/images/ui/legacy/scroll_bar_handle.png index 95a1726b3ba..18532fb4dc1 100644 Binary files a/public/images/ui/legacy/scroll_bar_handle.png and b/public/images/ui/legacy/scroll_bar_handle.png differ diff --git a/public/images/ui/legacy/select_cursor_highlight_thick.png b/public/images/ui/legacy/select_cursor_highlight_thick.png index d9a1532862e..6b013b3747e 100644 Binary files a/public/images/ui/legacy/select_cursor_highlight_thick.png and b/public/images/ui/legacy/select_cursor_highlight_thick.png differ diff --git a/public/images/ui/legacy/settings_icon.png b/public/images/ui/legacy/settings_icon.png index 21680cce7fc..5963317affa 100644 Binary files a/public/images/ui/legacy/settings_icon.png and b/public/images/ui/legacy/settings_icon.png differ diff --git a/public/images/ui/legacy/shiny.png b/public/images/ui/legacy/shiny.png index 26d7814fb0b..362f8f48ff1 100644 Binary files a/public/images/ui/legacy/shiny.png and b/public/images/ui/legacy/shiny.png differ diff --git a/public/images/ui/legacy/shiny_1.png b/public/images/ui/legacy/shiny_1.png index 9c7a583a7f9..5154761124c 100644 Binary files a/public/images/ui/legacy/shiny_1.png and b/public/images/ui/legacy/shiny_1.png differ diff --git a/public/images/ui/legacy/shiny_icons.png b/public/images/ui/legacy/shiny_icons.png index f601d908d32..685f7b8af97 100644 Binary files a/public/images/ui/legacy/shiny_icons.png and b/public/images/ui/legacy/shiny_icons.png differ diff --git a/public/images/ui/legacy/shiny_small.png b/public/images/ui/legacy/shiny_small.png index e3d8e260573..66a8b12b876 100644 Binary files a/public/images/ui/legacy/shiny_small.png and b/public/images/ui/legacy/shiny_small.png differ diff --git a/public/images/ui/legacy/starter_container_bg.png b/public/images/ui/legacy/starter_container_bg.png index 5b60fcf551e..887616a8c87 100644 Binary files a/public/images/ui/legacy/starter_container_bg.png and b/public/images/ui/legacy/starter_container_bg.png differ diff --git a/public/images/ui/legacy/starter_select_bg.png b/public/images/ui/legacy/starter_select_bg.png index a94e470df0d..501e892969e 100644 Binary files a/public/images/ui/legacy/starter_select_bg.png and b/public/images/ui/legacy/starter_select_bg.png differ diff --git a/public/images/ui/legacy/summary_bg.png b/public/images/ui/legacy/summary_bg.png index c482b5a1bf1..261b4974fb7 100644 Binary files a/public/images/ui/legacy/summary_bg.png and b/public/images/ui/legacy/summary_bg.png differ diff --git a/public/images/ui/legacy/summary_moves.png b/public/images/ui/legacy/summary_moves.png index 54ddf0fb2c7..ab909588160 100644 Binary files a/public/images/ui/legacy/summary_moves.png and b/public/images/ui/legacy/summary_moves.png differ diff --git a/public/images/ui/legacy/summary_moves_cursor.png b/public/images/ui/legacy/summary_moves_cursor.png index 6b8314b66ca..7375cedcdf6 100644 Binary files a/public/images/ui/legacy/summary_moves_cursor.png and b/public/images/ui/legacy/summary_moves_cursor.png differ diff --git a/public/images/ui/legacy/summary_moves_effect.png b/public/images/ui/legacy/summary_moves_effect.png index 8d6ef024cf3..34d59970c99 100644 Binary files a/public/images/ui/legacy/summary_moves_effect.png and b/public/images/ui/legacy/summary_moves_effect.png differ diff --git a/public/images/ui/legacy/summary_moves_overlay_pp.png b/public/images/ui/legacy/summary_moves_overlay_pp.png index a42c86f28ea..9babffc0429 100644 Binary files a/public/images/ui/legacy/summary_moves_overlay_pp.png and b/public/images/ui/legacy/summary_moves_overlay_pp.png differ diff --git a/public/images/ui/legacy/summary_moves_overlay_row.png b/public/images/ui/legacy/summary_moves_overlay_row.png index 5b3e3d3e940..221a0f99a53 100644 Binary files a/public/images/ui/legacy/summary_moves_overlay_row.png and b/public/images/ui/legacy/summary_moves_overlay_row.png differ diff --git a/public/images/ui/legacy/summary_overlay_shiny.png b/public/images/ui/legacy/summary_overlay_shiny.png index e4ab820730a..bb2436738a8 100644 Binary files a/public/images/ui/legacy/summary_overlay_shiny.png and b/public/images/ui/legacy/summary_overlay_shiny.png differ diff --git a/public/images/ui/legacy/summary_profile.png b/public/images/ui/legacy/summary_profile.png index 77d59dac177..0b5b2ffec05 100644 Binary files a/public/images/ui/legacy/summary_profile.png and b/public/images/ui/legacy/summary_profile.png differ diff --git a/public/images/ui/legacy/summary_profile_ability.png b/public/images/ui/legacy/summary_profile_ability.png index 84c41556ab0..a803323564b 100644 Binary files a/public/images/ui/legacy/summary_profile_ability.png and b/public/images/ui/legacy/summary_profile_ability.png differ diff --git a/public/images/ui/legacy/summary_profile_passive.png b/public/images/ui/legacy/summary_profile_passive.png index 8d70a54b2a4..1ff9ad8e09a 100644 Binary files a/public/images/ui/legacy/summary_profile_passive.png and b/public/images/ui/legacy/summary_profile_passive.png differ diff --git a/public/images/ui/legacy/summary_profile_prompt_a.png b/public/images/ui/legacy/summary_profile_prompt_a.png index 146f1a862e5..f40127ec5ab 100644 Binary files a/public/images/ui/legacy/summary_profile_prompt_a.png and b/public/images/ui/legacy/summary_profile_prompt_a.png differ diff --git a/public/images/ui/legacy/summary_profile_prompt_z.png b/public/images/ui/legacy/summary_profile_prompt_z.png index ed842718914..eaa158eaddf 100644 Binary files a/public/images/ui/legacy/summary_profile_prompt_z.png and b/public/images/ui/legacy/summary_profile_prompt_z.png differ diff --git a/public/images/ui/legacy/summary_stats.png b/public/images/ui/legacy/summary_stats.png index 53c6a18caab..660fbee6df6 100644 Binary files a/public/images/ui/legacy/summary_stats.png and b/public/images/ui/legacy/summary_stats.png differ diff --git a/public/images/ui/legacy/summary_status.png b/public/images/ui/legacy/summary_status.png index 4a5b27e9e2b..95bd7533258 100644 Binary files a/public/images/ui/legacy/summary_status.png and b/public/images/ui/legacy/summary_status.png differ diff --git a/public/images/ui/legacy/summary_tabs_1.png b/public/images/ui/legacy/summary_tabs_1.png index ff38096fcd8..379768ea310 100644 Binary files a/public/images/ui/legacy/summary_tabs_1.png and b/public/images/ui/legacy/summary_tabs_1.png differ diff --git a/public/images/ui/legacy/summary_tabs_2.png b/public/images/ui/legacy/summary_tabs_2.png index f147d1b8385..2a04879b32b 100644 Binary files a/public/images/ui/legacy/summary_tabs_2.png and b/public/images/ui/legacy/summary_tabs_2.png differ diff --git a/public/images/ui/legacy/summary_tabs_3.png b/public/images/ui/legacy/summary_tabs_3.png index cf882988664..ef65774f744 100644 Binary files a/public/images/ui/legacy/summary_tabs_3.png and b/public/images/ui/legacy/summary_tabs_3.png differ diff --git a/public/images/ui/legacy/type_bgs.png b/public/images/ui/legacy/type_bgs.png index 6e682d9c327..c060ac46428 100644 Binary files a/public/images/ui/legacy/type_bgs.png and b/public/images/ui/legacy/type_bgs.png differ diff --git a/public/images/ui/legacy/type_tera.png b/public/images/ui/legacy/type_tera.png index 5c35966a6f4..f7f644cec5d 100644 Binary files a/public/images/ui/legacy/type_tera.png and b/public/images/ui/legacy/type_tera.png differ diff --git a/public/images/ui/legacy/unlink_icon.png b/public/images/ui/legacy/unlink_icon.png index f0da5f8e3ed..36723390363 100644 Binary files a/public/images/ui/legacy/unlink_icon.png and b/public/images/ui/legacy/unlink_icon.png differ diff --git a/public/images/ui/legacy/windows/window_1.png b/public/images/ui/legacy/windows/window_1.png index edfef03f924..355a25c17b3 100644 Binary files a/public/images/ui/legacy/windows/window_1.png and b/public/images/ui/legacy/windows/window_1.png differ diff --git a/public/images/ui/legacy/windows/window_1_thin.png b/public/images/ui/legacy/windows/window_1_thin.png index 4648d812f17..1e71eae928b 100644 Binary files a/public/images/ui/legacy/windows/window_1_thin.png and b/public/images/ui/legacy/windows/window_1_thin.png differ diff --git a/public/images/ui/legacy/windows/window_1_xthin.png b/public/images/ui/legacy/windows/window_1_xthin.png index 0ee6ffd00e1..d9ee533e223 100644 Binary files a/public/images/ui/legacy/windows/window_1_xthin.png and b/public/images/ui/legacy/windows/window_1_xthin.png differ diff --git a/public/images/ui/legacy/windows/window_2.png b/public/images/ui/legacy/windows/window_2.png index 7da47310d64..dda8f20c8ac 100644 Binary files a/public/images/ui/legacy/windows/window_2.png and b/public/images/ui/legacy/windows/window_2.png differ diff --git a/public/images/ui/legacy/windows/window_2_thin.png b/public/images/ui/legacy/windows/window_2_thin.png index 281d538b997..f75186c9c56 100644 Binary files a/public/images/ui/legacy/windows/window_2_thin.png and b/public/images/ui/legacy/windows/window_2_thin.png differ diff --git a/public/images/ui/legacy/windows/window_2_xthin.png b/public/images/ui/legacy/windows/window_2_xthin.png index 01e95113338..2f831870d74 100644 Binary files a/public/images/ui/legacy/windows/window_2_xthin.png and b/public/images/ui/legacy/windows/window_2_xthin.png differ diff --git a/public/images/ui/legacy/windows/window_3.png b/public/images/ui/legacy/windows/window_3.png index 0764ea2d6b0..6270a0c0f25 100644 Binary files a/public/images/ui/legacy/windows/window_3.png and b/public/images/ui/legacy/windows/window_3.png differ diff --git a/public/images/ui/legacy/windows/window_3_thin.png b/public/images/ui/legacy/windows/window_3_thin.png index 12cc6d60778..61d0ccbfe72 100644 Binary files a/public/images/ui/legacy/windows/window_3_thin.png and b/public/images/ui/legacy/windows/window_3_thin.png differ diff --git a/public/images/ui/legacy/windows/window_3_xthin.png b/public/images/ui/legacy/windows/window_3_xthin.png index 57ac6427b79..b510e667b46 100644 Binary files a/public/images/ui/legacy/windows/window_3_xthin.png and b/public/images/ui/legacy/windows/window_3_xthin.png differ diff --git a/public/images/ui/legacy/windows/window_4.png b/public/images/ui/legacy/windows/window_4.png index 69c5bce9dc9..0952c229cb9 100644 Binary files a/public/images/ui/legacy/windows/window_4.png and b/public/images/ui/legacy/windows/window_4.png differ diff --git a/public/images/ui/legacy/windows/window_4_thin.png b/public/images/ui/legacy/windows/window_4_thin.png index b61e7d7b22c..13f689ab884 100644 Binary files a/public/images/ui/legacy/windows/window_4_thin.png and b/public/images/ui/legacy/windows/window_4_thin.png differ diff --git a/public/images/ui/legacy/windows/window_4_xthin.png b/public/images/ui/legacy/windows/window_4_xthin.png index 45fec09adb0..e4d9163174e 100644 Binary files a/public/images/ui/legacy/windows/window_4_xthin.png and b/public/images/ui/legacy/windows/window_4_xthin.png differ diff --git a/public/images/ui/legacy/windows/window_5.png b/public/images/ui/legacy/windows/window_5.png index edfef03f924..355a25c17b3 100644 Binary files a/public/images/ui/legacy/windows/window_5.png and b/public/images/ui/legacy/windows/window_5.png differ diff --git a/public/images/ui/legacy/windows/window_5_thin.png b/public/images/ui/legacy/windows/window_5_thin.png index 4648d812f17..1e71eae928b 100644 Binary files a/public/images/ui/legacy/windows/window_5_thin.png and b/public/images/ui/legacy/windows/window_5_thin.png differ diff --git a/public/images/ui/legacy/windows/window_5_xthin.png b/public/images/ui/legacy/windows/window_5_xthin.png index 0ee6ffd00e1..d9ee533e223 100644 Binary files a/public/images/ui/legacy/windows/window_5_xthin.png and b/public/images/ui/legacy/windows/window_5_xthin.png differ diff --git a/public/images/ui/link_icon.png b/public/images/ui/link_icon.png index 56081261b9c..1e20ab56ec7 100644 Binary files a/public/images/ui/link_icon.png and b/public/images/ui/link_icon.png differ diff --git a/public/images/ui/mmenu_sel.png b/public/images/ui/mmenu_sel.png index 584403e5d74..4b957c7b905 100644 Binary files a/public/images/ui/mmenu_sel.png and b/public/images/ui/mmenu_sel.png differ diff --git a/public/images/ui/mystery_egg.png b/public/images/ui/mystery_egg.png index bb117a137b0..83ad626da7e 100644 Binary files a/public/images/ui/mystery_egg.png and b/public/images/ui/mystery_egg.png differ diff --git a/public/images/ui/namebox.png b/public/images/ui/namebox.png index 87e5a0875b9..70a0e5e4698 100644 Binary files a/public/images/ui/namebox.png and b/public/images/ui/namebox.png differ diff --git a/public/images/ui/night_icon_bg.png b/public/images/ui/night_icon_bg.png index 1ce4007d54c..233da1c80ee 100644 Binary files a/public/images/ui/night_icon_bg.png and b/public/images/ui/night_icon_bg.png differ diff --git a/public/images/ui/night_icon_fg.png b/public/images/ui/night_icon_fg.png index 78741584312..ef184e24435 100644 Binary files a/public/images/ui/night_icon_fg.png and b/public/images/ui/night_icon_fg.png differ diff --git a/public/images/ui/night_icon_mg.png b/public/images/ui/night_icon_mg.png index 5583c8f799d..f808e50b2c7 100644 Binary files a/public/images/ui/night_icon_mg.png and b/public/images/ui/night_icon_mg.png differ diff --git a/public/images/ui/normal_memory.png b/public/images/ui/normal_memory.png index ddc22d1d4ab..604371dcc7c 100644 Binary files a/public/images/ui/normal_memory.png and b/public/images/ui/normal_memory.png differ diff --git a/public/images/ui/numbers.png b/public/images/ui/numbers.png index bde4fe28202..fd281962047 100644 Binary files a/public/images/ui/numbers.png and b/public/images/ui/numbers.png differ diff --git a/public/images/ui/numbers_red.png b/public/images/ui/numbers_red.png index 37cae6b4d60..4b7eabcbde6 100644 Binary files a/public/images/ui/numbers_red.png and b/public/images/ui/numbers_red.png differ diff --git a/public/images/ui/overlay_exp.png b/public/images/ui/overlay_exp.png index 6b745bedcef..10181d01d4c 100644 Binary files a/public/images/ui/overlay_exp.png and b/public/images/ui/overlay_exp.png differ diff --git a/public/images/ui/overlay_hp.png b/public/images/ui/overlay_hp.png index 08674fdcdb7..9612ed8c4c2 100644 Binary files a/public/images/ui/overlay_hp.png and b/public/images/ui/overlay_hp.png differ diff --git a/public/images/ui/overlay_hp_boss.png b/public/images/ui/overlay_hp_boss.png index 5f7a02a4814..5a011709e0c 100644 Binary files a/public/images/ui/overlay_hp_boss.png and b/public/images/ui/overlay_hp_boss.png differ diff --git a/public/images/ui/overlay_lv.png b/public/images/ui/overlay_lv.png index bbd406d957a..ee3f439f7be 100644 Binary files a/public/images/ui/overlay_lv.png and b/public/images/ui/overlay_lv.png differ diff --git a/public/images/ui/overlay_message.png b/public/images/ui/overlay_message.png index 37eb577402b..7057d08ec71 100644 Binary files a/public/images/ui/overlay_message.png and b/public/images/ui/overlay_message.png differ diff --git a/public/images/ui/party_bg.png b/public/images/ui/party_bg.png index 378b1302cb7..f916cd1e1f8 100644 Binary files a/public/images/ui/party_bg.png and b/public/images/ui/party_bg.png differ diff --git a/public/images/ui/party_bg_double.png b/public/images/ui/party_bg_double.png index e0411c1199b..c4d381d828c 100644 Binary files a/public/images/ui/party_bg_double.png and b/public/images/ui/party_bg_double.png differ diff --git a/public/images/ui/party_cancel.png b/public/images/ui/party_cancel.png index 4ea4bc6d65e..5409a1463ce 100644 Binary files a/public/images/ui/party_cancel.png and b/public/images/ui/party_cancel.png differ diff --git a/public/images/ui/party_exp_bar.png b/public/images/ui/party_exp_bar.png index 7815c684b3a..4703876c129 100644 Binary files a/public/images/ui/party_exp_bar.png and b/public/images/ui/party_exp_bar.png differ diff --git a/public/images/ui/party_pb.png b/public/images/ui/party_pb.png index 4169e9c70dc..b635d5f66aa 100644 Binary files a/public/images/ui/party_pb.png and b/public/images/ui/party_pb.png differ diff --git a/public/images/ui/party_slot.png b/public/images/ui/party_slot.png index ad137cead64..9cdea6f61ca 100644 Binary files a/public/images/ui/party_slot.png and b/public/images/ui/party_slot.png differ diff --git a/public/images/ui/party_slot_hp_bar.png b/public/images/ui/party_slot_hp_bar.png index 1771123c141..5381fe0a0b6 100644 Binary files a/public/images/ui/party_slot_hp_bar.png and b/public/images/ui/party_slot_hp_bar.png differ diff --git a/public/images/ui/party_slot_hp_overlay.png b/public/images/ui/party_slot_hp_overlay.png index 09e8c974ca3..8c6935e5540 100644 Binary files a/public/images/ui/party_slot_hp_overlay.png and b/public/images/ui/party_slot_hp_overlay.png differ diff --git a/public/images/ui/party_slot_main.png b/public/images/ui/party_slot_main.png index ab1f70d8034..1ab1e8ee926 100644 Binary files a/public/images/ui/party_slot_main.png and b/public/images/ui/party_slot_main.png differ diff --git a/public/images/ui/party_slot_overlay_hp.png b/public/images/ui/party_slot_overlay_hp.png index 09e8c974ca3..8c6935e5540 100644 Binary files a/public/images/ui/party_slot_overlay_hp.png and b/public/images/ui/party_slot_overlay_hp.png differ diff --git a/public/images/ui/party_slot_overlay_lv.png b/public/images/ui/party_slot_overlay_lv.png index 7051b1d84c4..3d7dafd6737 100644 Binary files a/public/images/ui/party_slot_overlay_lv.png and b/public/images/ui/party_slot_overlay_lv.png differ diff --git a/public/images/ui/passive_bg.png b/public/images/ui/passive_bg.png index 1e18a47b881..4a3fa1ec72b 100644 Binary files a/public/images/ui/passive_bg.png and b/public/images/ui/passive_bg.png differ diff --git a/public/images/ui/pb_tray_ball.png b/public/images/ui/pb_tray_ball.png index 554f9079042..b8f9cb072ba 100644 Binary files a/public/images/ui/pb_tray_ball.png and b/public/images/ui/pb_tray_ball.png differ diff --git a/public/images/ui/pb_tray_overlay_enemy.png b/public/images/ui/pb_tray_overlay_enemy.png index b6fdbcd1753..06bb9ed2e78 100644 Binary files a/public/images/ui/pb_tray_overlay_enemy.png and b/public/images/ui/pb_tray_overlay_enemy.png differ diff --git a/public/images/ui/pb_tray_overlay_player.png b/public/images/ui/pb_tray_overlay_player.png index ddbacced3d3..b3517543fd7 100644 Binary files a/public/images/ui/pb_tray_overlay_player.png and b/public/images/ui/pb_tray_overlay_player.png differ diff --git a/public/images/ui/pbinfo_enemy_boss.png b/public/images/ui/pbinfo_enemy_boss.png index ff5ba661450..ce829bf4631 100644 Binary files a/public/images/ui/pbinfo_enemy_boss.png and b/public/images/ui/pbinfo_enemy_boss.png differ diff --git a/public/images/ui/pbinfo_enemy_boss_stats.png b/public/images/ui/pbinfo_enemy_boss_stats.png index 7148a7af475..f1478a04172 100644 Binary files a/public/images/ui/pbinfo_enemy_boss_stats.png and b/public/images/ui/pbinfo_enemy_boss_stats.png differ diff --git a/public/images/ui/pbinfo_enemy_mini.png b/public/images/ui/pbinfo_enemy_mini.png index a7ff3cb9b82..ddf10776778 100644 Binary files a/public/images/ui/pbinfo_enemy_mini.png and b/public/images/ui/pbinfo_enemy_mini.png differ diff --git a/public/images/ui/pbinfo_enemy_mini_stats.png b/public/images/ui/pbinfo_enemy_mini_stats.png index 69ac5d49411..9c31d64faa4 100644 Binary files a/public/images/ui/pbinfo_enemy_mini_stats.png and b/public/images/ui/pbinfo_enemy_mini_stats.png differ diff --git a/public/images/ui/pbinfo_enemy_type.png b/public/images/ui/pbinfo_enemy_type.png index 9bac63e5e72..f5199ab6d0e 100644 Binary files a/public/images/ui/pbinfo_enemy_type.png and b/public/images/ui/pbinfo_enemy_type.png differ diff --git a/public/images/ui/pbinfo_enemy_type1.png b/public/images/ui/pbinfo_enemy_type1.png index 6d94871e8da..be28e1d2c65 100644 Binary files a/public/images/ui/pbinfo_enemy_type1.png and b/public/images/ui/pbinfo_enemy_type1.png differ diff --git a/public/images/ui/pbinfo_enemy_type2.png b/public/images/ui/pbinfo_enemy_type2.png index 56a1989f2cd..a4616dc6e86 100644 Binary files a/public/images/ui/pbinfo_enemy_type2.png and b/public/images/ui/pbinfo_enemy_type2.png differ diff --git a/public/images/ui/pbinfo_player.png b/public/images/ui/pbinfo_player.png index fee08f71044..c7b2227e800 100644 Binary files a/public/images/ui/pbinfo_player.png and b/public/images/ui/pbinfo_player.png differ diff --git a/public/images/ui/pbinfo_player_mini.png b/public/images/ui/pbinfo_player_mini.png index f12d46bb4ff..90d5e03dae8 100644 Binary files a/public/images/ui/pbinfo_player_mini.png and b/public/images/ui/pbinfo_player_mini.png differ diff --git a/public/images/ui/pbinfo_player_mini_stats.png b/public/images/ui/pbinfo_player_mini_stats.png index 7ce3bd1af1e..52295492eb7 100644 Binary files a/public/images/ui/pbinfo_player_mini_stats.png and b/public/images/ui/pbinfo_player_mini_stats.png differ diff --git a/public/images/ui/pbinfo_player_stats.png b/public/images/ui/pbinfo_player_stats.png index cda978979aa..10192b7752e 100644 Binary files a/public/images/ui/pbinfo_player_stats.png and b/public/images/ui/pbinfo_player_stats.png differ diff --git a/public/images/ui/pbinfo_player_type.png b/public/images/ui/pbinfo_player_type.png index b78d078de95..864848f843b 100644 Binary files a/public/images/ui/pbinfo_player_type.png and b/public/images/ui/pbinfo_player_type.png differ diff --git a/public/images/ui/pbinfo_player_type1.png b/public/images/ui/pbinfo_player_type1.png index 56a1989f2cd..a4616dc6e86 100644 Binary files a/public/images/ui/pbinfo_player_type1.png and b/public/images/ui/pbinfo_player_type1.png differ diff --git a/public/images/ui/pbinfo_player_type2.png b/public/images/ui/pbinfo_player_type2.png index 6d94871e8da..be28e1d2c65 100644 Binary files a/public/images/ui/pbinfo_player_type2.png and b/public/images/ui/pbinfo_player_type2.png differ diff --git a/public/images/ui/pbinfo_stat.png b/public/images/ui/pbinfo_stat.png index 1c20ed70e82..1f86cc6a958 100644 Binary files a/public/images/ui/pbinfo_stat.png and b/public/images/ui/pbinfo_stat.png differ diff --git a/public/images/ui/pbinfo_stat_numbers.png b/public/images/ui/pbinfo_stat_numbers.png index 1465f8b7a64..af244d5ce26 100644 Binary files a/public/images/ui/pbinfo_stat_numbers.png and b/public/images/ui/pbinfo_stat_numbers.png differ diff --git a/public/images/ui/pokedex_summary_bg.png b/public/images/ui/pokedex_summary_bg.png index 92e70bbee27..bb55127edf2 100644 Binary files a/public/images/ui/pokedex_summary_bg.png and b/public/images/ui/pokedex_summary_bg.png differ diff --git a/public/images/ui/prompt.png b/public/images/ui/prompt.png index 4c5e4c7a96c..c045c5e91fe 100644 Binary files a/public/images/ui/prompt.png and b/public/images/ui/prompt.png differ diff --git a/public/images/ui/saving_icon.png b/public/images/ui/saving_icon.png index fddff8c2d39..772fa91fb8c 100644 Binary files a/public/images/ui/saving_icon.png and b/public/images/ui/saving_icon.png differ diff --git a/public/images/ui/scroll_bar.png b/public/images/ui/scroll_bar.png index e4e5eb34dd2..1e8f53cd29e 100644 Binary files a/public/images/ui/scroll_bar.png and b/public/images/ui/scroll_bar.png differ diff --git a/public/images/ui/scroll_bar_handle.png b/public/images/ui/scroll_bar_handle.png index 95a1726b3ba..18532fb4dc1 100644 Binary files a/public/images/ui/scroll_bar_handle.png and b/public/images/ui/scroll_bar_handle.png differ diff --git a/public/images/ui/select_cursor.png b/public/images/ui/select_cursor.png index 41e52ac0d60..002973f2c9d 100644 Binary files a/public/images/ui/select_cursor.png and b/public/images/ui/select_cursor.png differ diff --git a/public/images/ui/select_cursor_highlight.png b/public/images/ui/select_cursor_highlight.png index a0263740509..b8094eecfa8 100644 Binary files a/public/images/ui/select_cursor_highlight.png and b/public/images/ui/select_cursor_highlight.png differ diff --git a/public/images/ui/select_cursor_highlight_thick.png b/public/images/ui/select_cursor_highlight_thick.png index 4081361b211..7730e58387e 100644 Binary files a/public/images/ui/select_cursor_highlight_thick.png and b/public/images/ui/select_cursor_highlight_thick.png differ diff --git a/public/images/ui/select_cursor_pokerus.png b/public/images/ui/select_cursor_pokerus.png index b84340fdde4..506d6deb249 100644 Binary files a/public/images/ui/select_cursor_pokerus.png and b/public/images/ui/select_cursor_pokerus.png differ diff --git a/public/images/ui/select_gen_cursor.png b/public/images/ui/select_gen_cursor.png index e4ed90ecc77..7f1c86fbb4d 100644 Binary files a/public/images/ui/select_gen_cursor.png and b/public/images/ui/select_gen_cursor.png differ diff --git a/public/images/ui/select_gen_cursor_highlight.png b/public/images/ui/select_gen_cursor_highlight.png index 8019de6f3cd..cecc543ae56 100644 Binary files a/public/images/ui/select_gen_cursor_highlight.png and b/public/images/ui/select_gen_cursor_highlight.png differ diff --git a/public/images/ui/settings_icon.png b/public/images/ui/settings_icon.png index 21680cce7fc..5963317affa 100644 Binary files a/public/images/ui/settings_icon.png and b/public/images/ui/settings_icon.png differ diff --git a/public/images/ui/shiny.png b/public/images/ui/shiny.png index 26d7814fb0b..362f8f48ff1 100644 Binary files a/public/images/ui/shiny.png and b/public/images/ui/shiny.png differ diff --git a/public/images/ui/shiny_1.png b/public/images/ui/shiny_1.png index 9c7a583a7f9..5154761124c 100644 Binary files a/public/images/ui/shiny_1.png and b/public/images/ui/shiny_1.png differ diff --git a/public/images/ui/shiny_icons.png b/public/images/ui/shiny_icons.png index f601d908d32..685f7b8af97 100644 Binary files a/public/images/ui/shiny_icons.png and b/public/images/ui/shiny_icons.png differ diff --git a/public/images/ui/shiny_small.png b/public/images/ui/shiny_small.png index e3d8e260573..66a8b12b876 100644 Binary files a/public/images/ui/shiny_small.png and b/public/images/ui/shiny_small.png differ diff --git a/public/images/ui/starter_container_bg.png b/public/images/ui/starter_container_bg.png index 5b60fcf551e..887616a8c87 100644 Binary files a/public/images/ui/starter_container_bg.png and b/public/images/ui/starter_container_bg.png differ diff --git a/public/images/ui/starter_select_bg.png b/public/images/ui/starter_select_bg.png index 8b11f55a963..b11cd12a5e4 100644 Binary files a/public/images/ui/starter_select_bg.png and b/public/images/ui/starter_select_bg.png differ diff --git a/public/images/ui/summary_bg.png b/public/images/ui/summary_bg.png index a86a94dd56e..ebfe87b78b2 100644 Binary files a/public/images/ui/summary_bg.png and b/public/images/ui/summary_bg.png differ diff --git a/public/images/ui/summary_moves.png b/public/images/ui/summary_moves.png index 8f6b01137c1..618d91934f2 100644 Binary files a/public/images/ui/summary_moves.png and b/public/images/ui/summary_moves.png differ diff --git a/public/images/ui/summary_moves_cursor.png b/public/images/ui/summary_moves_cursor.png index 93ce1fc0dae..5472c55d0c0 100644 Binary files a/public/images/ui/summary_moves_cursor.png and b/public/images/ui/summary_moves_cursor.png differ diff --git a/public/images/ui/summary_moves_effect.png b/public/images/ui/summary_moves_effect.png index e1b0e066732..5b1cef9c02e 100644 Binary files a/public/images/ui/summary_moves_effect.png and b/public/images/ui/summary_moves_effect.png differ diff --git a/public/images/ui/summary_moves_overlay_pp.png b/public/images/ui/summary_moves_overlay_pp.png index 9dd0a080e3d..a2bcd0bd021 100644 Binary files a/public/images/ui/summary_moves_overlay_pp.png and b/public/images/ui/summary_moves_overlay_pp.png differ diff --git a/public/images/ui/summary_moves_overlay_row.png b/public/images/ui/summary_moves_overlay_row.png index 5c975b17472..1c2ed2d3a86 100644 Binary files a/public/images/ui/summary_moves_overlay_row.png and b/public/images/ui/summary_moves_overlay_row.png differ diff --git a/public/images/ui/summary_overlay_shiny.png b/public/images/ui/summary_overlay_shiny.png index 472728cc3a3..956f9fb93f3 100644 Binary files a/public/images/ui/summary_overlay_shiny.png and b/public/images/ui/summary_overlay_shiny.png differ diff --git a/public/images/ui/summary_profile.png b/public/images/ui/summary_profile.png index 55cc70dc4d0..2c0c93af6b4 100644 Binary files a/public/images/ui/summary_profile.png and b/public/images/ui/summary_profile.png differ diff --git a/public/images/ui/summary_profile_ability.png b/public/images/ui/summary_profile_ability.png index 84c41556ab0..a803323564b 100644 Binary files a/public/images/ui/summary_profile_ability.png and b/public/images/ui/summary_profile_ability.png differ diff --git a/public/images/ui/summary_profile_passive.png b/public/images/ui/summary_profile_passive.png index 8d70a54b2a4..1ff9ad8e09a 100644 Binary files a/public/images/ui/summary_profile_passive.png and b/public/images/ui/summary_profile_passive.png differ diff --git a/public/images/ui/summary_profile_prompt_a.png b/public/images/ui/summary_profile_prompt_a.png index 146f1a862e5..f40127ec5ab 100644 Binary files a/public/images/ui/summary_profile_prompt_a.png and b/public/images/ui/summary_profile_prompt_a.png differ diff --git a/public/images/ui/summary_profile_prompt_z.png b/public/images/ui/summary_profile_prompt_z.png index ed842718914..eaa158eaddf 100644 Binary files a/public/images/ui/summary_profile_prompt_z.png and b/public/images/ui/summary_profile_prompt_z.png differ diff --git a/public/images/ui/summary_stats.png b/public/images/ui/summary_stats.png index b87d67b864a..d808be92fd1 100644 Binary files a/public/images/ui/summary_stats.png and b/public/images/ui/summary_stats.png differ diff --git a/public/images/ui/summary_stats_overlay_exp.png b/public/images/ui/summary_stats_overlay_exp.png index a5ef5093ced..126f3c71a3d 100644 Binary files a/public/images/ui/summary_stats_overlay_exp.png and b/public/images/ui/summary_stats_overlay_exp.png differ diff --git a/public/images/ui/summary_status.png b/public/images/ui/summary_status.png index 981e4dafe05..fd518c6c015 100644 Binary files a/public/images/ui/summary_status.png and b/public/images/ui/summary_status.png differ diff --git a/public/images/ui/summary_tabs_1.png b/public/images/ui/summary_tabs_1.png index c4989a86514..750c407b075 100644 Binary files a/public/images/ui/summary_tabs_1.png and b/public/images/ui/summary_tabs_1.png differ diff --git a/public/images/ui/summary_tabs_2.png b/public/images/ui/summary_tabs_2.png index 16f7c35b278..250c3ce7960 100644 Binary files a/public/images/ui/summary_tabs_2.png and b/public/images/ui/summary_tabs_2.png differ diff --git a/public/images/ui/summary_tabs_3.png b/public/images/ui/summary_tabs_3.png index b766307aaca..1af80fa5c0d 100644 Binary files a/public/images/ui/summary_tabs_3.png and b/public/images/ui/summary_tabs_3.png differ diff --git a/public/images/ui/type_bgs.png b/public/images/ui/type_bgs.png index 91d75d622ba..820dc3f5a0e 100644 Binary files a/public/images/ui/type_bgs.png and b/public/images/ui/type_bgs.png differ diff --git a/public/images/ui/type_tera.png b/public/images/ui/type_tera.png index 5c35966a6f4..f7f644cec5d 100644 Binary files a/public/images/ui/type_tera.png and b/public/images/ui/type_tera.png differ diff --git a/public/images/ui/unlink_icon.png b/public/images/ui/unlink_icon.png index f0da5f8e3ed..36723390363 100644 Binary files a/public/images/ui/unlink_icon.png and b/public/images/ui/unlink_icon.png differ diff --git a/public/images/ui/windows/window_1.png b/public/images/ui/windows/window_1.png index 16a088108ee..8349452bd1a 100644 Binary files a/public/images/ui/windows/window_1.png and b/public/images/ui/windows/window_1.png differ diff --git a/public/images/ui/windows/window_1_thin.png b/public/images/ui/windows/window_1_thin.png index 96750d47c0f..8c915f239e8 100644 Binary files a/public/images/ui/windows/window_1_thin.png and b/public/images/ui/windows/window_1_thin.png differ diff --git a/public/images/ui/windows/window_1_xthin.png b/public/images/ui/windows/window_1_xthin.png index 0c11a595ca2..edcabf7b17a 100644 Binary files a/public/images/ui/windows/window_1_xthin.png and b/public/images/ui/windows/window_1_xthin.png differ diff --git a/public/images/ui/windows/window_2.png b/public/images/ui/windows/window_2.png index 98a6de12270..ca7767d1344 100644 Binary files a/public/images/ui/windows/window_2.png and b/public/images/ui/windows/window_2.png differ diff --git a/public/images/ui/windows/window_2_thin.png b/public/images/ui/windows/window_2_thin.png index d3940f62f76..17d180bbb7b 100644 Binary files a/public/images/ui/windows/window_2_thin.png and b/public/images/ui/windows/window_2_thin.png differ diff --git a/public/images/ui/windows/window_2_xthin.png b/public/images/ui/windows/window_2_xthin.png index bbb68216776..0f6450313e3 100644 Binary files a/public/images/ui/windows/window_2_xthin.png and b/public/images/ui/windows/window_2_xthin.png differ diff --git a/public/images/ui/windows/window_3.png b/public/images/ui/windows/window_3.png index d3810b06e0c..76234f0175c 100644 Binary files a/public/images/ui/windows/window_3.png and b/public/images/ui/windows/window_3.png differ diff --git a/public/images/ui/windows/window_3_thin.png b/public/images/ui/windows/window_3_thin.png index db5e0a91266..34c6a7d21cf 100644 Binary files a/public/images/ui/windows/window_3_thin.png and b/public/images/ui/windows/window_3_thin.png differ diff --git a/public/images/ui/windows/window_3_xthin.png b/public/images/ui/windows/window_3_xthin.png index bbf7f904a98..416433f5105 100644 Binary files a/public/images/ui/windows/window_3_xthin.png and b/public/images/ui/windows/window_3_xthin.png differ diff --git a/public/images/ui/windows/window_4.png b/public/images/ui/windows/window_4.png index 5be8161ca8b..dc6095b1663 100644 Binary files a/public/images/ui/windows/window_4.png and b/public/images/ui/windows/window_4.png differ diff --git a/public/images/ui/windows/window_4_thin.png b/public/images/ui/windows/window_4_thin.png index fb8c004921a..0e60510833d 100644 Binary files a/public/images/ui/windows/window_4_thin.png and b/public/images/ui/windows/window_4_thin.png differ diff --git a/public/images/ui/windows/window_4_xthin.png b/public/images/ui/windows/window_4_xthin.png index 15bf0f8c692..bb5323a7aad 100644 Binary files a/public/images/ui/windows/window_4_xthin.png and b/public/images/ui/windows/window_4_xthin.png differ diff --git a/public/images/ui/windows/window_5.png b/public/images/ui/windows/window_5.png index 35ad9c4f22c..9e64853d3ab 100644 Binary files a/public/images/ui/windows/window_5.png and b/public/images/ui/windows/window_5.png differ diff --git a/public/images/ui/windows/window_5_thin.png b/public/images/ui/windows/window_5_thin.png index 35ad9c4f22c..9e64853d3ab 100644 Binary files a/public/images/ui/windows/window_5_thin.png and b/public/images/ui/windows/window_5_thin.png differ diff --git a/public/images/ui/windows/window_5_xthin.png b/public/images/ui/windows/window_5_xthin.png index 8c3a4e12e57..47a23a6a5d7 100644 Binary files a/public/images/ui/windows/window_5_xthin.png and b/public/images/ui/windows/window_5_xthin.png differ diff --git a/public/logo128.png b/public/logo128.png index 40f2a93715f..e6acfb9bfe2 100644 Binary files a/public/logo128.png and b/public/logo128.png differ diff --git a/public/logo512.png b/public/logo512.png index 7fdde1f6b82..6e9854d98b9 100644 Binary files a/public/logo512.png and b/public/logo512.png differ diff --git a/scripts/create-test/create-test.js b/scripts/create-test/create-test.js index d5cac5cd408..f24aac548fc 100644 --- a/scripts/create-test/create-test.js +++ b/scripts/create-test/create-test.js @@ -1,14 +1,14 @@ -/** +/* * This script creates a test boilerplate file in the appropriate * directory based on the type selected. - * @example npm run test:create + * Usage: `pnpm test:create` */ -import chalk from "chalk"; -import inquirer from "inquirer"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import chalk from "chalk"; +import inquirer from "inquirer"; //#region Constants diff --git a/scripts/create-test/test-boilerplate.ts b/scripts/create-test/test-boilerplate.ts index 1fbd3c8040a..759f3afaec0 100644 --- a/scripts/create-test/test-boilerplate.ts +++ b/scripts/create-test/test-boilerplate.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/scripts/decrypt-save.js b/scripts/decrypt-save.js index a7239a40df6..219cdb47bed 100644 --- a/scripts/decrypt-save.js +++ b/scripts/decrypt-save.js @@ -1,7 +1,8 @@ -import pkg from "crypto-js"; -const { AES, enc } = pkg; -// biome-ignore lint: This is how you import fs from node +// Usage: node decrypt-save.js [save-file] + +// biome-ignore lint/performance/noNamespaceImport: This is how you import fs from node import * as fs from "node:fs"; +import { AES, enc } from "crypto-js"; const SAVE_KEY = "x0i2O7WRiANTqPmZ"; diff --git a/src/@types/PokerogueAccountApi.ts b/src/@types/PokerogueAccountApi.ts index 68d0a5e7730..3b3e74ba41d 100644 --- a/src/@types/PokerogueAccountApi.ts +++ b/src/@types/PokerogueAccountApi.ts @@ -1,4 +1,4 @@ -import type { UserInfo } from "#app/@types/UserInfo"; +import type { UserInfo } from "#types/UserInfo"; export interface AccountInfoResponse extends UserInfo {} diff --git a/src/@types/PokerogueDailyApi.ts b/src/@types/PokerogueDailyApi.ts index 3f3d8eb61ca..838af2a2a34 100644 --- a/src/@types/PokerogueDailyApi.ts +++ b/src/@types/PokerogueDailyApi.ts @@ -1,4 +1,4 @@ -import type { ScoreboardCategory } from "#app/ui/daily-run-scoreboard"; +import type { ScoreboardCategory } from "#ui/daily-run-scoreboard"; export interface GetDailyRankingsRequest { category: ScoreboardCategory; diff --git a/src/@types/PokerogueSavedataApi.ts b/src/@types/PokerogueSavedataApi.ts index a313cd708c7..ebc80ac4ce0 100644 --- a/src/@types/PokerogueSavedataApi.ts +++ b/src/@types/PokerogueSavedataApi.ts @@ -1,4 +1,4 @@ -import type { SessionSaveData, SystemSaveData } from "#app/system/game-data"; +import type { SessionSaveData, SystemSaveData } from "#system/game-data"; export interface UpdateAllSavedataRequest { system: SystemSaveData; diff --git a/src/@types/PokerogueSystemSavedataApi.ts b/src/@types/PokerogueSystemSavedataApi.ts index 8ce160a5ec2..ded22c4c58c 100644 --- a/src/@types/PokerogueSystemSavedataApi.ts +++ b/src/@types/PokerogueSystemSavedataApi.ts @@ -1,4 +1,4 @@ -import type { SystemSaveData } from "#app/system/game-data"; +import type { SystemSaveData } from "#system/game-data"; export interface GetSystemSavedataRequest { clientSessionId: string; diff --git a/src/@types/SessionSaveMigrator.ts b/src/@types/SessionSaveMigrator.ts index c4b0ad8dda4..56518eaa8b7 100644 --- a/src/@types/SessionSaveMigrator.ts +++ b/src/@types/SessionSaveMigrator.ts @@ -1,4 +1,4 @@ -import type { SessionSaveData } from "#app/system/game-data"; +import type { SessionSaveData } from "#system/game-data"; export interface SessionSaveMigrator { version: string; diff --git a/src/@types/SystemSaveMigrator.ts b/src/@types/SystemSaveMigrator.ts index a22b5f6c93d..80fff9c7848 100644 --- a/src/@types/SystemSaveMigrator.ts +++ b/src/@types/SystemSaveMigrator.ts @@ -1,4 +1,4 @@ -import type { SystemSaveData } from "#app/system/game-data"; +import type { SystemSaveData } from "#system/game-data"; export interface SystemSaveMigrator { version: string; diff --git a/src/@types/ability-types.ts b/src/@types/ability-types.ts index 6f21a012b64..8daca8a671d 100644 --- a/src/@types/ability-types.ts +++ b/src/@types/ability-types.ts @@ -1,14 +1,14 @@ -import type { AbAttr } from "#app/data/abilities/ability"; -import type Move from "#app/data/moves/move"; -import type Pokemon from "#app/field/pokemon"; +import type { AbAttrConstructorMap } from "#abilities/ability"; import type { BattleStat } from "#enums/stat"; -import type { AbAttrConstructorMap } from "#app/data/abilities/ability"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; -// Intentionally re-export all types from the ability attributes module -export type * from "#app/data/abilities/ability"; +// intentionally re-export all types from abilities to have this be the centralized place to import ability types +export type * from "#abilities/ability"; + +// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment +import type { applyAbAttrs } from "#abilities/apply-ab-attrs"; -export type AbAttrApplyFunc = (attr: TAttr, passive: boolean, ...args: any[]) => void; -export type AbAttrSuccessFunc = (attr: TAttr, passive: boolean, ...args: any[]) => boolean; export type AbAttrCondition = (pokemon: Pokemon) => boolean; export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; @@ -25,3 +25,22 @@ export type AbAttrString = keyof AbAttrConstructorMap; export type AbAttrMap = { [K in keyof AbAttrConstructorMap]: InstanceType; }; + +/** + * Subset of ability attribute classes that may be passed to {@linkcode applyAbAttrs} method + * + * @remarks + * Our AbAttr classes violate Liskov Substitution Principle. + * + * AbAttrs that are not in this have subclasses with apply methods requiring different parameters than + * the base apply method. + * + * Such attributes may not be passed to the {@linkcode applyAbAttrs} method + */ +export type CallableAbAttrString = + | Exclude + | "PreApplyBattlerTagAbAttr"; + +export type AbAttrParamMap = { + [K in keyof AbAttrMap]: Parameters[0]; +}; diff --git a/src/@types/arena-tags.ts b/src/@types/arena-tags.ts new file mode 100644 index 00000000000..ab4339b2fef --- /dev/null +++ b/src/@types/arena-tags.ts @@ -0,0 +1,48 @@ +import type { ArenaTagTypeMap } from "#data/arena-tag"; +import type { ArenaTagType } from "#enums/arena-tag-type"; +import type { NonFunctionProperties } from "./type-helpers"; + +/** Subset of {@linkcode ArenaTagType}s that apply some negative effect to pokemon that switch in ({@link https://bulbapedia.bulbagarden.net/wiki/List_of_moves_that_cause_entry_hazards#List_of_traps | entry hazards} and Imprison. */ +export type ArenaTrapTagType = + | ArenaTagType.STICKY_WEB + | ArenaTagType.SPIKES + | ArenaTagType.TOXIC_SPIKES + | ArenaTagType.STEALTH_ROCK + | ArenaTagType.IMPRISON; + +/** Subset of {@linkcode ArenaTagType}s that are considered delayed attacks */ +export type ArenaDelayedAttackTagType = ArenaTagType.FUTURE_SIGHT | ArenaTagType.DOOM_DESIRE; + +/** Subset of {@linkcode ArenaTagType}s that create {@link https://bulbapedia.bulbagarden.net/wiki/Category:Screen-creating_moves | screens}. */ +export type ArenaScreenTagType = ArenaTagType.REFLECT | ArenaTagType.LIGHT_SCREEN | ArenaTagType.AURORA_VEIL; + +/** Subset of {@linkcode ArenaTagType}s for moves that add protection */ +export type TurnProtectArenaTagType = + | ArenaTagType.QUICK_GUARD + | ArenaTagType.WIDE_GUARD + | ArenaTagType.MAT_BLOCK + | ArenaTagType.CRAFTY_SHIELD; + +/** Subset of {@linkcode ArenaTagType}s that cannot persist across turns, and thus should not be serialized in {@linkcode SessionSaveData}. */ +export type NonSerializableArenaTagType = ArenaTagType.NONE | TurnProtectArenaTagType | ArenaTagType.ION_DELUGE; + +/** Subset of {@linkcode ArenaTagType}s that may persist across turns, and thus must be serialized in {@linkcode SessionSaveData}. */ +export type SerializableArenaTagType = Exclude; + +/** + * Type-safe representation of the serializable data of an ArenaTag + */ +export type ArenaTagTypeData = NonFunctionProperties< + ArenaTagTypeMap[keyof { + [K in keyof ArenaTagTypeMap as K extends SerializableArenaTagType ? K : never]: ArenaTagTypeMap[K]; + }] +>; + +/** Dummy, typescript-only declaration to ensure that + * {@linkcode ArenaTagTypeMap} has a map for all ArenaTagTypes. + * + * If an arena tag is missing from the map, typescript will throw an error on this statement. + * + * ⚠️ Does not actually exist at runtime, so it must not be used! + */ +declare const EnsureAllArenaTagTypesAreMapped: ArenaTagTypeMap[ArenaTagType] & never; diff --git a/src/@types/attack-move-result.ts b/src/@types/attack-move-result.ts new file mode 100644 index 00000000000..495bb1e88ff --- /dev/null +++ b/src/@types/attack-move-result.ts @@ -0,0 +1,12 @@ +import type { BattlerIndex } from "#enums/battler-index"; +import type { MoveId } from "#enums/move-id"; +import type { DamageResult } from "#types/damage-result"; + +export interface AttackMoveResult { + move: MoveId; + result: DamageResult; + damage: number; + critical: boolean; + sourceId: number; + sourceBattlerIndex: BattlerIndex; +} diff --git a/src/@types/damage-result.ts b/src/@types/damage-result.ts new file mode 100644 index 00000000000..b6eb6b82770 --- /dev/null +++ b/src/@types/damage-result.ts @@ -0,0 +1,21 @@ +import type { HitResult } from "#enums/hit-result"; + +/** Union type containing all damage-dealing {@linkcode HitResult}s. */ +export type DamageResult = + | HitResult.EFFECTIVE + | HitResult.SUPER_EFFECTIVE + | HitResult.NOT_VERY_EFFECTIVE + | HitResult.ONE_HIT_KO + | HitResult.CONFUSION + | HitResult.INDIRECT_KO + | HitResult.INDIRECT; + +/** Interface containing the results of a damage calculation for a given move. */ +export interface DamageCalculationResult { + /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ + cancelled: boolean; + /** The effectiveness of the move */ + result: HitResult; + /** The damage dealt by the move */ + damage: number; +} diff --git a/src/@types/enum-types.ts b/src/@types/enum-types.ts new file mode 100644 index 00000000000..84df0a96505 --- /dev/null +++ b/src/@types/enum-types.ts @@ -0,0 +1,18 @@ +/** Union type accepting any TS Enum or `const object`, with or without reverse mapping. */ +export type EnumOrObject = Record; + +/** + * Utility type to extract the enum values from a `const object`, + * or convert an `enum` interface produced by `typeof Enum` into the union type representing its values. + */ +export type EnumValues = E[keyof E]; + +/** + * Generic type constraint representing a TS numeric enum with reverse mappings. + * @example + * TSNumericEnum + */ +export type TSNumericEnum = number extends EnumValues ? T : never; + +/** Generic type constraint representing a non reverse-mapped TS enum or `const object`. */ +export type NormalEnum = Exclude>; diff --git a/src/@types/held-modifier-config.ts b/src/@types/held-modifier-config.ts index 5617cf2446a..46ef67bf515 100644 --- a/src/@types/held-modifier-config.ts +++ b/src/@types/held-modifier-config.ts @@ -1,7 +1,7 @@ -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; -export default interface HeldModifierConfig { +export interface HeldModifierConfig { modifier: PokemonHeldItemModifierType | PokemonHeldItemModifier; stackCount?: number; isTransferable?: boolean; diff --git a/src/@types/illusion-data.ts b/src/@types/illusion-data.ts new file mode 100644 index 00000000000..854c98c8cc9 --- /dev/null +++ b/src/@types/illusion-data.ts @@ -0,0 +1,41 @@ +import type { Gender } from "#data/gender"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import type { PokeballType } from "#enums/pokeball"; +import type { SpeciesId } from "#enums/species-id"; +import type { Variant } from "#sprites/variant"; + +/** + * Data pertaining to a Pokemon's Illusion. + */ +export interface IllusionData { + basePokemon: { + /** The actual name of the Pokemon */ + name: string; + /** The actual nickname of the Pokemon */ + nickname: string; + /** Whether the base pokemon is shiny or not */ + shiny: boolean; + /** The shiny variant of the base pokemon */ + variant: Variant; + /** Whether the fusion species of the base pokemon is shiny or not */ + fusionShiny: boolean; + /** The variant of the fusion species of the base pokemon */ + fusionVariant: Variant; + }; + /** The species of the illusion */ + species: SpeciesId; + /** The formIndex of the illusion */ + formIndex: number; + /** The gender of the illusion */ + gender: Gender; + /** The pokeball of the illusion */ + pokeball: PokeballType; + /** The fusion species of the illusion if it's a fusion */ + fusionSpecies?: PokemonSpecies; + /** The fusionFormIndex of the illusion */ + fusionFormIndex?: number; + /** The fusionGender of the illusion if it's a fusion */ + fusionGender?: Gender; + /** The level of the illusion (not used currently) */ + level?: number; +} diff --git a/src/@types/modifier-types.ts b/src/@types/modifier-types.ts index 80b92c35622..28b39d1a151 100644 --- a/src/@types/modifier-types.ts +++ b/src/@types/modifier-types.ts @@ -1,14 +1,13 @@ -/** - * Re-exports of all the types defined in the modifier module. - */ +// Intentionally re-exports `ModifierConstructorMap` from `modifier.ts` + +import type { Pokemon } from "#field/pokemon"; +import type { ModifierConstructorMap } from "#modifiers/modifier"; +import type { ModifierType, WeightedModifierType } from "#modifiers/modifier-type"; -import type Pokemon from "#app/field/pokemon"; -import type { ModifierConstructorMap } from "#app/modifier/modifier"; -import type { ModifierType, WeightedModifierType } from "#app/modifier/modifier-type"; export type ModifierTypeFunc = () => ModifierType; export type WeightedModifierTypeWeightFunc = (party: Pokemon[], rerollCount?: number) => number; -export type { ModifierConstructorMap } from "#app/modifier/modifier"; +export type { ModifierConstructorMap } from "#modifiers/modifier"; /** * Map of modifier names to their respective instance types diff --git a/src/@types/move-types.ts b/src/@types/move-types.ts index d9a06fd20ee..5f8d7e8777e 100644 --- a/src/@types/move-types.ts +++ b/src/@types/move-types.ts @@ -1,16 +1,16 @@ import type { AttackMove, - StatusMove, - SelfStatusMove, ChargingAttackMove, ChargingSelfStatusMove, - MoveAttrConstructorMap, MoveAttr, -} from "#app/data/moves/move"; + MoveAttrConstructorMap, + SelfStatusMove, + StatusMove, +} from "#moves/move"; export type MoveAttrFilter = (attr: MoveAttr) => boolean; -export type * from "#app/data/moves/move"; +export type * from "#moves/move"; /** * Map of move subclass names to their respective classes. diff --git a/src/@types/trainer-funcs.ts b/src/@types/trainer-funcs.ts index 0546dd53024..aa839cbd158 100644 --- a/src/@types/trainer-funcs.ts +++ b/src/@types/trainer-funcs.ts @@ -1,9 +1,9 @@ -import type { EnemyPokemon } from "#app/field/pokemon"; -import type { PersistentModifier } from "#app/modifier/modifier"; import type { PartyMemberStrength } from "#enums/party-member-strength"; import type { SpeciesId } from "#enums/species-id"; -import type { TrainerConfig } from "../data/trainers/trainer-config"; -import type { TrainerPartyTemplate } from "../data/trainers/TrainerPartyTemplate"; +import type { EnemyPokemon } from "#field/pokemon"; +import type { PersistentModifier } from "#modifiers/modifier"; +import type { TrainerConfig } from "#trainers/trainer-config"; +import type { TrainerPartyTemplate } from "#trainers/trainer-party-template"; export type PartyTemplateFunc = () => TrainerPartyTemplate; export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon; diff --git a/src/@types/turn-move.ts b/src/@types/turn-move.ts new file mode 100644 index 00000000000..a5a69eb3749 --- /dev/null +++ b/src/@types/turn-move.ts @@ -0,0 +1,13 @@ +import type { BattlerIndex } from "#enums/battler-index"; +import type { MoveId } from "#enums/move-id"; +import type { MoveResult } from "#enums/move-result"; +import type { MoveUseMode } from "#enums/move-use-mode"; + +/** A record of a move having been used. */ +export interface TurnMove { + move: MoveId; + targets: BattlerIndex[]; + useMode: MoveUseMode; + result?: MoveResult; + turn?: number; +} diff --git a/src/@types/type-helpers.ts b/src/@types/type-helpers.ts new file mode 100644 index 00000000000..3a5c88e3f15 --- /dev/null +++ b/src/@types/type-helpers.ts @@ -0,0 +1,77 @@ +/* + * A collection of custom utility types that aid in type checking and ensuring strict type conformity + */ + +// biome-ignore-start lint/correctness/noUnusedImports: Used in a tsdoc comment +import type { AbAttr } from "#abilities/ability"; +// biome-ignore-end lint/correctness/noUnusedImports: Used in a tsdoc comment + +import type { EnumValues } from "#types/enum-types"; + +/** + * Exactly matches the type of the argument, preventing adding additional properties. + * + * ⚠️ Should never be used with `extends`, as this will nullify the exactness of the type. + * + * As an example, used to ensure that the parameters of {@linkcode AbAttr.canApply} and {@linkcode AbAttr.getTriggerMessage} are compatible with + * the type of its {@linkcode AbAttr.apply | apply} method. + * + * @typeParam T - The type to match exactly + */ +export type Exact = { + [K in keyof T]: T[K]; +}; + +/** + * Type hint that indicates that the type is intended to be closed to a specific shape. + * Does not actually do anything special, is really just an alias for X. + */ +export type Closed = X; + +/** + * Remove `readonly` from all properties of the provided type. + * @typeParam T - The type to make mutable. + */ +export type Mutable = { + -readonly [P in keyof T]: T[P]; +}; + +/** + * Type helper to obtain the keys associated with a given value inside a `const object`. + * @typeParam O - The type of the object + * @typeParam V - The type of one of O's values + */ +export type InferKeys, V extends EnumValues> = { + [K in keyof O]: O[K] extends V ? K : never; +}[keyof O]; + +/** + * Type helper that matches any `Function` type. Equivalent to `Function`, but will not raise a warning from Biome. + */ +export type AnyFn = (...args: any[]) => any; + +/** + * Type helper to extract non-function properties from a type. + * + * @remarks + * Useful to produce a type that is roughly the same as the type of `{... obj}`, where `obj` is an instance of `T`. + * A couple of differences: + * - Private and protected properties are not included. + * - Nested properties are not recursively extracted. For this, use {@linkcode NonFunctionPropertiesRecursive} + */ +export type NonFunctionProperties = { + [K in keyof T as T[K] extends AnyFn ? never : K]: T[K]; +}; + +/** + * Type helper to extract out non-function properties from a type, recursively applying to nested properties. + */ +export type NonFunctionPropertiesRecursive = { + [K in keyof Class as Class[K] extends AnyFn ? never : K]: Class[K] extends Array + ? NonFunctionPropertiesRecursive[] + : Class[K] extends object + ? NonFunctionPropertiesRecursive + : Class[K]; +}; + +export type AbstractConstructor = abstract new (...args: any[]) => T; diff --git a/src/account.ts b/src/account.ts index 3416fa6ed5e..1bfb756b284 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,7 +1,7 @@ -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import type { UserInfo } from "#app/@types/UserInfo"; -import { bypassLogin } from "./global-vars/bypass-login"; -import { randomString } from "#app/utils/common"; +import { pokerogueApi } from "#api/pokerogue-api"; +import { bypassLogin } from "#app/global-vars/bypass-login"; +import type { UserInfo } from "#types/UserInfo"; +import { randomString } from "#utils/common"; export let loggedInUser: UserInfo | null = null; // This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b802466ee19..bb28fb0d5b6 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,26 +1,84 @@ -import Phaser from "phaser"; -import UI from "#app/ui/ui"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonSpeciesFilter } from "#app/data/pokemon-species"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import type { FixedBattleConfig } from "#app/battle"; +import { Battle } from "#app/battle"; import { - fixedInt, - getIvsFromId, - randSeedInt, - getEnumValues, - randomString, - NumberHolder, - shiftCharCodes, - formatMoney, - isNullOrUndefined, - BooleanHolder, - type Constructor, -} from "#app/utils/common"; -import { deepMergeSpriteData } from "#app/utils/data"; -import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier"; + ANTI_VARIANCE_WEIGHT_MODIFIER, + AVERAGE_ENCOUNTERS_PER_RUN_TARGET, + BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, + MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, +} from "#app/constants"; +import type { GameMode } from "#app/game-mode"; +import { getGameMode } from "#app/game-mode"; +import { timedEventManager } from "#app/global-event-manager"; +import { initGlobalScene } from "#app/global-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; +import { InputsController } from "#app/inputs-controller"; +import { LoadingScene } from "#app/loading-scene"; +import Overrides from "#app/overrides"; +import type { Phase } from "#app/phase"; +import { PhaseManager } from "#app/phase-manager"; +import { FieldSpritePipeline } from "#app/pipelines/field-sprite"; +import { InvertPostFX } from "#app/pipelines/invert"; +import { SpritePipeline } from "#app/pipelines/sprite"; +import { SceneBase } from "#app/scene-base"; +import { startingWave } from "#app/starting-wave"; +import { TimedEventManager } from "#app/timed-event-manager"; +import { UiInputs } from "#app/ui-inputs"; +import { biomeDepths, getBiomeName } from "#balance/biomes"; +import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#balance/starters"; +import { + initCommonAnims, + initMoveAnim, + loadCommonAnimAssets, + loadMoveAnimAssets, + populateAnims, +} from "#data/battle-anims"; +import { allAbilities, allMoves, allSpecies, modifierTypes } from "#data/data-lists"; +import { battleSpecDialogue } from "#data/dialogue"; +import type { SpeciesFormChangeTrigger } from "#data/form-change-triggers"; +import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger } from "#data/form-change-triggers"; +import { Gender } from "#data/gender"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies, PokemonSpeciesFilter } from "#data/pokemon-species"; +import { getTypeRgb } from "#data/type"; +import { BattleSpec } from "#enums/battle-spec"; +import { BattleStyle } from "#enums/battle-style"; +import { BattleType } from "#enums/battle-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BiomeId } from "#enums/biome-id"; +import { EaseType } from "#enums/ease-type"; +import { ExpGainsSpeed } from "#enums/exp-gains-speed"; +import { ExpNotification } from "#enums/exp-notification"; +import { FormChangeItem } from "#enums/form-change-item"; +import { GameModes } from "#enums/game-modes"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { MoneyFormat } from "#enums/money-format"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PlayerGender } from "#enums/player-gender"; +import { PokeballType } from "#enums/pokeball"; +import type { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { PokemonType } from "#enums/pokemon-type"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import type { TrainerSlot } from "#enums/trainer-slot"; +import { TrainerType } from "#enums/trainer-type"; +import { TrainerVariant } from "#enums/trainer-variant"; +import { UiTheme } from "#enums/ui-theme"; +import { NewArenaEvent } from "#events/battle-scene"; +import { Arena, ArenaBase } from "#field/arena"; +import { DamageNumberHandler } from "#field/damage-number-handler"; +import type { Pokemon } from "#field/pokemon"; +import { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; +import { PokemonSpriteSparkleHandler } from "#field/pokemon-sprite-sparkle-handler"; +import { Trainer } from "#field/trainer"; +import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "#modifiers/modifier"; import { ConsumableModifier, ConsumablePokemonModifier, @@ -38,22 +96,7 @@ import { PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, -} from "./modifier/modifier"; -import { PokeballType } from "#enums/pokeball"; -import { - initCommonAnims, - initMoveAnim, - loadCommonAnimAssets, - loadMoveAnimAssets, - populateAnims, -} from "#app/data/battle-anims"; -import type { 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 "./data/data-lists"; -import { MusicPreference } from "#app/system/settings/settings"; +} from "#modifiers/modifier"; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, @@ -61,113 +104,61 @@ import { getLuckTextTint, getPartyLuckValue, PokemonHeldItemModifierType, -} from "#app/modifier/modifier-type"; -import { getModifierType } from "./utils/modifier-utils"; -import { modifierTypes } from "./data/data-lists"; -import { getModifierPoolForType } from "./utils/modifier-utils"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import AbilityBar from "#app/ui/ability-bar"; -import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs"; -import { allAbilities } from "./data/data-lists"; -import type { FixedBattleConfig } from "#app/battle"; -import Battle from "#app/battle"; -import { BattleType } from "#enums/battle-type"; -import type { GameMode } from "#app/game-mode"; -import { getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; -import FieldSpritePipeline from "#app/pipelines/field-sprite"; -import SpritePipeline from "#app/pipelines/sprite"; -import PartyExpBar from "#app/ui/party-exp-bar"; -import type { TrainerSlot } from "./enums/trainer-slot"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import Trainer from "#app/field/trainer"; -import { TrainerVariant } from "#enums/trainer-variant"; -import type TrainerData from "#app/system/trainer-data"; -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import PokeballTray from "#app/ui/pokeball-tray"; -import InvertPostFX from "#app/pipelines/invert"; -import type { Achv } from "#app/system/achv"; -import { achvs, ModifierAchv, MoneyAchv } from "#app/system/achv"; -import type { Voucher } from "#app/system/voucher"; -import { vouchers } from "#app/system/voucher"; -import { Gender } from "#app/data/gender"; -import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; -import { addUiThemeOverrides } from "#app/ui/ui-theme"; -import type PokemonData from "#app/system/pokemon-data"; -import { Nature } from "#enums/nature"; -import type { SpeciesFormChange } from "#app/data/pokemon-forms"; -import type { SpeciesFormChangeTrigger } from "./data/pokemon-forms/form-change-triggers"; -import { pokemonFormChanges } from "#app/data/pokemon-forms"; -import { SpeciesFormChangeTimeOfDayTrigger } from "./data/pokemon-forms/form-change-triggers"; -import { SpeciesFormChangeManualTrigger } from "./data/pokemon-forms/form-change-triggers"; -import { FormChangeItem } from "#enums/form-change-item"; -import { getTypeRgb } from "#app/data/type"; -import { PokemonType } from "#enums/pokemon-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 type { Variant } from "#app/sprites/variant"; -import { variantData, clearVariantData } from "#app/sprites/variant"; -import type { Localizable } from "#app/@types/locales"; -import Overrides from "#app/overrides"; -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"; -import { BiomeId } from "#enums/biome-id"; -import type { ExpNotification } from "#enums/exp-notification"; -import { MoneyFormat } from "#enums/money-format"; -import { MoveId } from "#enums/move-id"; -import { PlayerGender } from "#enums/player-gender"; -import { SpeciesId } from "#enums/species-id"; -import { UiTheme } from "#enums/ui-theme"; -import { TimedEventManager } from "#app/timed-event-manager"; -import type { PokemonAnimType } from "#enums/pokemon-anim-type"; -import i18next from "i18next"; -import { TrainerType } from "#enums/trainer-type"; -import { battleSpecDialogue } from "#app/data/dialogue"; -import { LoadingScene } from "#app/loading-scene"; -import type { MovePhase } from "#app/phases/move-phase"; -import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { allMysteryEncounters, mysteryEncountersByBiome } from "#app/data/mystery-encounters/mystery-encounters"; +} from "#modifiers/modifier-type"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data"; +import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters"; +import type { MovePhase } from "#phases/move-phase"; +import { expSpriteKeys } from "#sprites/sprite-keys"; +import { hasExpSprite } from "#sprites/sprite-utils"; +import type { Variant } from "#sprites/variant"; +import { clearVariantData, variantData } from "#sprites/variant"; +import type { Achv } from "#system/achv"; +import { achvs, ModifierAchv, MoneyAchv } from "#system/achv"; +import { GameData } from "#system/game-data"; +import { initGameSpeed } from "#system/game-speed"; +import type { PokemonData } from "#system/pokemon-data"; +import { MusicPreference } from "#system/settings"; +import type { TrainerData } from "#system/trainer-data"; +import type { Voucher } from "#system/voucher"; +import { vouchers } from "#system/voucher"; +import { trainerConfigs } from "#trainers/trainer-config"; +import type { HeldModifierConfig } from "#types/held-modifier-config"; +import type { Localizable } from "#types/locales"; +import { AbilityBar } from "#ui/ability-bar"; +import { ArenaFlyout } from "#ui/arena-flyout"; +import { CandyBar } from "#ui/candy-bar"; +import { CharSprite } from "#ui/char-sprite"; +import { PartyExpBar } from "#ui/party-exp-bar"; +import { PokeballTray } from "#ui/pokeball-tray"; +import { PokemonInfoContainer } from "#ui/pokemon-info-container"; +import { addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { UI } from "#ui/ui"; +import { addUiThemeOverrides } from "#ui/ui-theme"; import { - ANTI_VARIANCE_WEIGHT_MODIFIER, - AVERAGE_ENCOUNTERS_PER_RUN_TARGET, - BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, - MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, -} from "./constants"; -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"; -import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { ExpGainsSpeed } from "#enums/exp-gains-speed"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; -import { StatusEffect } from "#enums/status-effect"; -import { initGlobalScene } from "#app/global-scene"; -import { expSpriteKeys } from "./sprites/sprite-keys"; -import { hasExpSprite } from "./sprites/sprite-utils"; -import { timedEventManager } from "./global-event-manager"; -import { starterColors } from "./global-vars/starter-colors"; -import { startingWave } from "./starting-wave"; -import { PhaseManager } from "./phase-manager"; + BooleanHolder, + type Constructor, + fixedInt, + formatMoney, + getIvsFromId, + isBetween, + isNullOrUndefined, + NumberHolder, + randomString, + randSeedInt, + shiftCharCodes, +} from "#utils/common"; +import { deepMergeSpriteData } from "#utils/data"; +import { getEnumValues } from "#utils/enums"; +import { getModifierPoolForType, getModifierType } from "#utils/modifier-utils"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; +import Phaser from "phaser"; +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; +import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; const DEBUG_RNG = false; -const OPP_IVS_OVERRIDE_VALIDATED: number[] = ( - Array.isArray(Overrides.OPP_IVS_OVERRIDE) ? Overrides.OPP_IVS_OVERRIDE : new Array(6).fill(Overrides.OPP_IVS_OVERRIDE) -).map(iv => (Number.isNaN(iv) || iv === null || iv > 31 ? -1 : iv)); - export interface PokeballCounts { [pb: string]: number; } @@ -179,7 +170,7 @@ export interface InfoToggle { isActive(): boolean; } -export default class BattleScene extends SceneBase { +export class BattleScene extends SceneBase { public rexUI: UIPlugin; public inputController: InputsController; public uiInputs: UiInputs; @@ -206,6 +197,7 @@ export default class BattleScene extends SceneBase { public enableMoveInfo = true; public enableRetries = false; public hideIvs = false; + // TODO: Remove all plain numbers in place of enums or `const object` equivalents for clarity /** * Determines the condition for a notification should be shown for Candy Upgrades * - 0 = 'Off' @@ -223,7 +215,7 @@ export default class BattleScene extends SceneBase { public uiTheme: UiTheme = UiTheme.DEFAULT; public windowType = 0; public experimentalSprites = false; - public musicPreference: number = MusicPreference.ALLGENS; + public musicPreference: MusicPreference = MusicPreference.ALLGENS; public moveAnimations = true; public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT; public skipSeenDialogues = false; @@ -234,33 +226,18 @@ export default class BattleScene extends SceneBase { * - 2 = Always (automatically skip animation when hatching 2 or more eggs) */ public eggSkipPreference = 0; - /** - * Defines the experience gain display mode. - * - * @remarks - * The `expParty` can have several modes: - * - `0` - Default: The normal experience gain display, nothing changed. - * - `1` - Level Up Notification: Displays the level up in the small frame instead of a message. - * - `2` - Skip: No level up frame nor message. - * - * Modes `1` and `2` are still compatible with stats display, level up, new move, etc. - * @default 0 - Uses the default normal experience gain display. + * Defines the {@linkcode ExpNotification | Experience gain display mode}. + * @defaultValue {@linkcode ExpNotification.DEFAULT} */ - public expParty: ExpNotification = 0; + public expParty: ExpNotification = ExpNotification.DEFAULT; public hpBarSpeed = 0; public fusionPaletteSwaps = true; public enableTouchControls = false; public enableVibration = false; public showBgmBar = true; - - /** - * Determines the selected battle style. - * - 0 = 'Switch' - * - 1 = 'Set' - The option to switch the active pokemon at the start of a battle will not display. - */ - public battleStyle: number = BattleStyle.SWITCH; - + /** Determines the selected battle style. */ + public battleStyle: BattleStyle = BattleStyle.SWITCH; /** * Defines whether or not to show type effectiveness hints * - true: No hints @@ -468,7 +445,7 @@ export default class BattleScene extends SceneBase { true, ); - //@ts-ignore (the defined types in the package are incromplete...) + //@ts-expect-error (the defined types in the package are incromplete...) transition.transit({ mode: "blinds", ease: "Cubic.easeInOut", @@ -800,12 +777,14 @@ export default class BattleScene extends SceneBase { // TODO: Add `undefined` to return type /** * Returns an array of PlayerPokemon of length 1 or 2 depending on if in a double battle or not. - * Does not actually check if the pokemon are on the field or not. + * @param active - (Default `false`) Whether to consider only {@linkcode Pokemon.isActive | active} on-field pokemon * @returns array of {@linkcode PlayerPokemon} */ - public getPlayerField(): PlayerPokemon[] { + public getPlayerField(active = false): PlayerPokemon[] { const party = this.getPlayerParty(); - return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1)); + return party + .slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1)) + .filter(p => !active || p.isActive()); } public getEnemyParty(): EnemyPokemon[] { @@ -836,7 +815,8 @@ export default class BattleScene extends SceneBase { /** * Returns an array of Pokemon on both sides of the battle - player first, then enemy. * Does not actually check if the pokemon are on the field or not, and always has length 4 regardless of battle type. - * @param activeOnly - Whether to consider only active pokemon; default `false` + * @param activeOnly - Whether to consider only active pokemon (as described by {@linkcode Pokemon.isActive()}); default `false`. + * If `true`, will also remove all `null` values from the array. * @returns An array of {@linkcode Pokemon}, as described above. */ public getField(activeOnly = false): Pokemon[] { @@ -849,9 +829,9 @@ export default class BattleScene extends SceneBase { } /** - * Used in doubles battles to redirect moves from one pokemon to another when one faints or is removed from the field - * @param removedPokemon {@linkcode Pokemon} the pokemon that is being removed from the field (flee, faint), moves to be redirected FROM - * @param allyPokemon {@linkcode Pokemon} the pokemon that will have the moves be redirected TO + * Attempt to redirect a move in double battles from a fainted/removed Pokemon to its ally. + * @param removedPokemon - The {@linkcode Pokemon} having been removed from the field. + * @param allyPokemon - The {@linkcode Pokemon} allied with the removed Pokemon; will have moves redirected to it */ redirectPokemonMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void { // failsafe: if not a double battle just return @@ -877,16 +857,16 @@ export default class BattleScene extends SceneBase { /** * Returns the ModifierBar of this scene, which is declared private and therefore not accessible elsewhere - * @param isEnemy Whether to return the enemy's modifier bar - * @returns {ModifierBar} + * @param isEnemy - Whether to return the enemy modifier bar instead of the player bar; default `false` + * @returns The {@linkcode ModifierBar} for the given side of the field */ - getModifierBar(isEnemy?: boolean): ModifierBar { + getModifierBar(isEnemy = false): ModifierBar { return isEnemy ? this.enemyModifierBar : this.modifierBar; } // store info toggles to be accessible by the ui - addInfoToggle(infoToggle: InfoToggle): void { - this.infoToggles.push(infoToggle); + addInfoToggle(...infoToggles: InfoToggle[]): void { + this.infoToggles.push(...infoToggles); } // return the stored info toggles; used by ui-inputs @@ -894,9 +874,19 @@ export default class BattleScene extends SceneBase { return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; } - getPokemonById(pokemonId: number): Pokemon | null { - const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return (findInParty(this.getPlayerParty()) || findInParty(this.getEnemyParty())) ?? null; + /** + * Return the {@linkcode Pokemon} associated with a given ID. + * @param pokemonId - The ID whose Pokemon will be retrieved. + * @returns The {@linkcode Pokemon} associated with the given id. + * Returns `null` if the ID is `undefined` or not present in either party. + */ + getPokemonById(pokemonId: number | undefined): Pokemon | null { + if (isNullOrUndefined(pokemonId)) { + return null; + } + + const party = (this.getPlayerParty() as Pokemon[]).concat(this.getEnemyParty()); + return party.find(p => p.id === pokemonId) ?? null; } addPlayerPokemon( @@ -924,9 +914,32 @@ export default class BattleScene extends SceneBase { nature, dataSource, ); + if (postProcess) { postProcess(pokemon); } + + if (Overrides.IVS_OVERRIDE === null) { + // do nothing + } else if (Array.isArray(Overrides.IVS_OVERRIDE)) { + if (Overrides.IVS_OVERRIDE.length !== 6) { + throw new Error("The Player IVs override must be an array of length 6 or a number!"); + } + if (Overrides.IVS_OVERRIDE.some(value => !isBetween(value, 0, 31))) { + throw new Error("All IVs in the player IV override must be between 0 and 31!"); + } + pokemon.ivs = Overrides.IVS_OVERRIDE; + } else { + if (!isBetween(Overrides.IVS_OVERRIDE, 0, 31)) { + throw new Error("The Player IV override must be a value between 0 and 31!"); + } + pokemon.ivs = new Array(6).fill(Overrides.IVS_OVERRIDE); + } + + if (Overrides.NATURE_OVERRIDE !== null) { + pokemon.nature = Overrides.NATURE_OVERRIDE; + } + pokemon.init(); return pokemon; } @@ -971,10 +984,25 @@ export default class BattleScene extends SceneBase { postProcess(pokemon); } - for (let i = 0; i < pokemon.ivs.length; i++) { - if (OPP_IVS_OVERRIDE_VALIDATED[i] > -1) { - pokemon.ivs[i] = OPP_IVS_OVERRIDE_VALIDATED[i]; + if (Overrides.ENEMY_IVS_OVERRIDE === null) { + // do nothing + } else if (Array.isArray(Overrides.ENEMY_IVS_OVERRIDE)) { + if (Overrides.ENEMY_IVS_OVERRIDE.length !== 6) { + throw new Error("The Enemy IVs override must be an array of length 6 or a number!"); } + if (Overrides.ENEMY_IVS_OVERRIDE.some(value => !isBetween(value, 0, 31))) { + throw new Error("All IVs in the enemy IV override must be between 0 and 31!"); + } + pokemon.ivs = Overrides.ENEMY_IVS_OVERRIDE; + } else { + if (!isBetween(Overrides.ENEMY_IVS_OVERRIDE, 0, 31)) { + throw new Error("The Enemy IV override must be a value between 0 and 31!"); + } + pokemon.ivs = new Array(6).fill(Overrides.ENEMY_IVS_OVERRIDE); + } + + if (Overrides.ENEMY_NATURE_OVERRIDE !== null) { + pokemon.nature = Overrides.ENEMY_NATURE_OVERRIDE; } pokemon.init(); @@ -1167,7 +1195,7 @@ export default class BattleScene extends SceneBase { this.field.remove(this.currentBattle.mysteryEncounter?.introVisuals, true); } - //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble + //@ts-expect-error - 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. @@ -1256,7 +1284,7 @@ export default class BattleScene extends SceneBase { const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); for (const p of playerField) { - applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance); + applyAbAttrs("DoubleBattleChanceAbAttr", { pokemon: p, chance: doubleChance }); } return Math.max(doubleChance.value, 1); } @@ -1434,10 +1462,12 @@ export default class BattleScene extends SceneBase { if (!waveIndex && lastBattle) { const isNewBiome = this.isNewBiome(lastBattle); + /** Whether to reset and recall pokemon */ const resetArenaState = isNewBiome || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; + for (const enemyPokemon of this.getEnemyParty()) { enemyPokemon.destroy(); } @@ -1461,7 +1491,7 @@ export default class BattleScene extends SceneBase { for (const pokemon of this.getPlayerParty()) { pokemon.resetBattleAndWaveData(); pokemon.resetTera(); - applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon); + applyAbAttrs("PostBattleInitAbAttr", { pokemon }); if ( pokemon.hasSpecies(SpeciesId.TERAPAGOS) || (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190) @@ -1812,7 +1842,7 @@ export default class BattleScene extends SceneBase { } resetSeed(waveIndex?: number): void { - const wave = waveIndex || this.currentBattle?.waveIndex || 0; + const wave = waveIndex ?? this.currentBattle?.waveIndex ?? 0; this.waveSeed = shiftCharCodes(this.seed, wave); Phaser.Math.RND.sow([this.waveSeed]); console.log("Wave Seed:", this.waveSeed, wave); @@ -2137,6 +2167,7 @@ export default class BattleScene extends SceneBase { ), ] : allSpecies.filter(s => s.isCatchable()); + // TODO: should this use `randSeedItem`? return filteredSpecies[randSeedInt(filteredSpecies.length)]; } @@ -2162,6 +2193,7 @@ export default class BattleScene extends SceneBase { } } + // TODO: should this use `randSeedItem`? return biomes[randSeedInt(biomes.length)]; } @@ -2478,6 +2510,10 @@ export default class BattleScene extends SceneBase { return 10.344; case "battle_legendary_zac_zam": //SWSH Zacian & Zamazenta Battle return 11.424; + case "battle_legendary_eternatus_p1": //SWSH Eternatus Battle + return 11.102; + case "battle_legendary_eternatus_p2": //SWSH Eternamax Eternatus Battle + return 0.0; case "battle_legendary_glas_spec": //SWSH Glastrier & Spectrier Battle return 12.503; case "battle_legendary_calyrex": //SWSH Calyrex Battle @@ -2743,7 +2779,7 @@ export default class BattleScene extends SceneBase { const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { - applyAbAttrs("BlockItemTheftAbAttr", source, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled }); } if (cancelled.value) { @@ -2783,13 +2819,13 @@ export default class BattleScene extends SceneBase { if (target.isPlayer()) { this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant); if (source && itemLost) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: source }); } return true; } this.addEnemyModifier(newItemModifier, ignoreUpdate, instant); if (source && itemLost) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: source }); } return true; } @@ -2812,7 +2848,7 @@ export default class BattleScene extends SceneBase { const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { - applyAbAttrs("BlockItemTheftAbAttr", source, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled }); } if (cancelled.value) { @@ -3237,7 +3273,7 @@ export default class BattleScene extends SceneBase { (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) && achv.validate(args) ) { - this.gameData.achvUnlocks[achv.id] = new Date().getTime(); + this.gameData.achvUnlocks[achv.id] = Date.now(); this.ui.achvBar.showAchv(achv); if (vouchers.hasOwnProperty(achv.id)) { this.validateVoucher(vouchers[achv.id]); @@ -3250,7 +3286,7 @@ export default class BattleScene extends SceneBase { validateVoucher(voucher: Voucher, args?: unknown[]): boolean { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) { - this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); + this.gameData.voucherUnlocks[voucher.id] = Date.now(); this.ui.achvBar.showAchv(voucher); this.gameData.voucherCounts[voucher.voucherType]++; return true; @@ -3681,6 +3717,7 @@ export default class BattleScene extends SceneBase { console.log("No Mystery Encounters found, falling back to Mysterious Challengers."); return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS]; } + // TODO: should this use `randSeedItem`? encounter = availableEncounters[randSeedInt(availableEncounters.length)]; // New encounter object to not dirty flags encounter = new MysteryEncounter(encounter); diff --git a/src/battle.ts b/src/battle.ts index 245705f4801..7b6a58cbaca 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -1,40 +1,37 @@ +import type { GameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene"; -import type { Command } from "#enums/command"; -import { - randomString, - getEnumValues, - NumberHolder, - randSeedInt, - shiftCharCodes, - randSeedItem, - randInt, - randSeedFloat, -} from "#app/utils/common"; -import Trainer from "./field/trainer"; -import { TrainerVariant } from "#enums/trainer-variant"; -import type { GameMode } from "./game-mode"; -import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "./modifier/modifier"; -import type { PokeballType } from "#enums/pokeball"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { SpeciesFormKey } from "#enums/species-form-key"; -import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; +import type { Command } from "#enums/command"; import type { MoveId } from "#enums/move-id"; -import { PlayerGender } from "#enums/player-gender"; -import { MusicPreference } from "#app/system/settings/settings"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import type { PokeballType } from "#enums/pokeball"; +import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; -import i18next from "#app/plugins/i18n"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import type { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { ModifierTier } from "#enums/modifier-tier"; -import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { BattleType } from "#enums/battle-type"; -import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; -import { BattlerIndex } from "#enums/battler-index"; +import { TrainerVariant } from "#enums/trainer-variant"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; +import { Trainer } from "#field/trainer"; +import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { CustomModifierSettings } from "#modifiers/modifier-type"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import i18next from "#plugins/i18n"; +import { MusicPreference } from "#system/settings"; +import { trainerConfigs } from "#trainers/trainer-config"; +import type { TurnMove } from "#types/turn-move"; +import { + NumberHolder, + randInt, + randomString, + randSeedFloat, + randSeedInt, + randSeedItem, + shiftCharCodes, +} from "#utils/common"; +import { getEnumValues } from "#utils/enums"; export interface TurnCommand { command: Command; @@ -54,7 +51,7 @@ interface TurnCommands { [key: number]: TurnCommand | null; } -export default class Battle { +export class Battle { protected gameMode: GameMode; public waveIndex: number; public battleType: BattleType; @@ -94,6 +91,12 @@ export default class Battle { /** If the current battle is a Mystery Encounter, this will always be defined */ public mysteryEncounter?: MysteryEncounter; + /** + * Tracker for whether the last run attempt failed. + * @defaultValue `false` + */ + public failedRunAway = false; + private rngCounter = 0; constructor(gameMode: GameMode, waveIndex: number, battleType: BattleType, trainer?: Trainer, double = false) { @@ -178,7 +181,7 @@ export default class Battle { ) .map(i => { const ret = i as PokemonHeldItemModifier; - //@ts-ignore - this is awful to fix/change + //@ts-expect-error - this is awful to fix/change ret.pokemonId = null; return ret; }), @@ -375,6 +378,11 @@ export default class Battle { case SpeciesId.ZACIAN: case SpeciesId.ZAMAZENTA: return "battle_legendary_zac_zam"; + case SpeciesId.ETERNATUS: + if (pokemon.getFormKey() === "eternamax") { + return "battle_legendary_eternatus_p2"; + } + return "battle_legendary_eternatus_p1"; case SpeciesId.GLASTRIER: case SpeciesId.SPECTRIER: return "battle_legendary_glas_spec"; @@ -566,369 +574,3 @@ export function getRandomTrainerFunc( return new Trainer(trainerTypes[rand], trainerGender); }; } - -export interface FixedBattleConfigs { - [key: number]: FixedBattleConfig; -} -/** - * Youngster/Lass on 5 - * Rival on 8, 55, 95, 145, 195 - * Evil team grunts on 35, 62, 64, and 112 - * Evil team admin on 66 and 114 - * Evil leader on 115, 165 - * E4 on 182, 184, 186, 188 - * Champion on 190 - */ -export const classicFixedBattles: FixedBattleConfigs = { - [ClassicFixedBossWaves.TOWN_YOUNGSTER]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => new Trainer(TrainerType.YOUNGSTER, randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT), - ), - [ClassicFixedBossWaves.RIVAL_1]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ), - [ClassicFixedBossWaves.RIVAL_2]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL_2, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], - allowLuckUpgrades: false, - }), - [ClassicFixedBossWaves.EVIL_GRUNT_1]: 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, - ), - ), - [ClassicFixedBossWaves.RIVAL_3]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL_3, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], - allowLuckUpgrades: false, - }), - [ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) - .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, - ), - ), - [ClassicFixedBossWaves.EVIL_GRUNT_3]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) - .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, - ), - ), - [ClassicFixedBossWaves.EVIL_ADMIN_1]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_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.COLRESS], - [TrainerType.XEROSIC, TrainerType.BRYONY], - TrainerType.FABA, - TrainerType.PLUMERIA, - TrainerType.OLEANA, - [TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI], - ], - true, - ), - ), - [ClassicFixedBossWaves.RIVAL_4]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL_4, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], - allowLuckUpgrades: false, - }), - [ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) - .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, - ), - ), - [ClassicFixedBossWaves.EVIL_ADMIN_2]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_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.COLRESS], - [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(ClassicFixedBossWaves.EVIL_GRUNT_1) - .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, - }), - [ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL_5, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ], - allowLuckUpgrades: false, - }), - [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) - .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, - }), - [ClassicFixedBossWaves.ELITE_FOUR_1]: 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, - ]), - ), - [ClassicFixedBossWaves.ELITE_FOUR_2]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) - .setGetTrainerFunc( - getRandomTrainerFunc([ - TrainerType.BRUNO, - TrainerType.KOGA, - TrainerType.PHOEBE, - TrainerType.BERTHA, - TrainerType.MARSHAL, - TrainerType.SIEBOLD, - TrainerType.OLIVIA, - TrainerType.NESSA_ELITE, - TrainerType.POPPY, - TrainerType.AMARYS, - ]), - ), - [ClassicFixedBossWaves.ELITE_FOUR_3]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) - .setGetTrainerFunc( - getRandomTrainerFunc([ - TrainerType.AGATHA, - TrainerType.BRUNO, - TrainerType.GLACIA, - TrainerType.FLINT, - TrainerType.GRIMSLEY, - TrainerType.WIKSTROM, - TrainerType.ACEROLA, - [TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE], - TrainerType.LARRY_ELITE, - TrainerType.LACEY, - ]), - ), - [ClassicFixedBossWaves.ELITE_FOUR_4]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) - .setGetTrainerFunc( - getRandomTrainerFunc([ - TrainerType.LANCE, - TrainerType.KAREN, - TrainerType.DRAKE, - TrainerType.LUCIAN, - TrainerType.CAITLIN, - TrainerType.DRASNA, - TrainerType.KAHILI, - TrainerType.RAIHAN_ELITE, - TrainerType.HASSEL, - TrainerType.DRAYTON, - ]), - ), - [ClassicFixedBossWaves.CHAMPION]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) - .setGetTrainerFunc( - getRandomTrainerFunc([ - TrainerType.BLUE, - [TrainerType.RED, TrainerType.LANCE_CHAMPION], - [TrainerType.STEVEN, TrainerType.WALLACE], - TrainerType.CYNTHIA, - [TrainerType.ALDER, TrainerType.IRIS], - TrainerType.DIANTHA, - [TrainerType.KUKUI, TrainerType.HAU], - [TrainerType.LEON, TrainerType.MUSTARD], - [TrainerType.GEETA, TrainerType.NEMONA], - TrainerType.KIERAN, - ]), - ), - [ClassicFixedBossWaves.RIVAL_6]: new FixedBattleConfig() - .setBattleType(BattleType.TRAINER) - .setGetTrainerFunc( - () => - new Trainer( - TrainerType.RIVAL_6, - globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, - ), - ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ModifierTier.GREAT, - ModifierTier.GREAT, - ], - allowLuckUpgrades: false, - }), -}; diff --git a/src/configs/inputs/cfg_keyboard_qwerty.ts b/src/configs/inputs/cfg_keyboard_qwerty.ts index 4f0353971e0..bca4f828d34 100644 --- a/src/configs/inputs/cfg_keyboard_qwerty.ts +++ b/src/configs/inputs/cfg_keyboard_qwerty.ts @@ -1,5 +1,5 @@ import { Button } from "#enums/buttons"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { SettingKeyboard } from "#system/settings-keyboard"; const cfg_keyboard_qwerty = { padID: "default", diff --git a/src/configs/inputs/pad_dualshock.ts b/src/configs/inputs/pad_dualshock.ts index 51af1b2defd..100baa5efad 100644 --- a/src/configs/inputs/pad_dualshock.ts +++ b/src/configs/inputs/pad_dualshock.ts @@ -1,5 +1,5 @@ -import { SettingGamepad } from "../../system/settings/settings-gamepad"; import { Button } from "#enums/buttons"; +import { SettingGamepad } from "#system/settings-gamepad"; /** * Dualshock mapping diff --git a/src/configs/inputs/pad_generic.ts b/src/configs/inputs/pad_generic.ts index e47b7ce1ace..fc70f55fc7a 100644 --- a/src/configs/inputs/pad_generic.ts +++ b/src/configs/inputs/pad_generic.ts @@ -1,5 +1,5 @@ -import { SettingGamepad } from "../../system/settings/settings-gamepad"; import { Button } from "#enums/buttons"; +import { SettingGamepad } from "#system/settings-gamepad"; /** * Generic pad mapping diff --git a/src/configs/inputs/pad_procon.ts b/src/configs/inputs/pad_procon.ts index 61558c7365e..5667db01a85 100644 --- a/src/configs/inputs/pad_procon.ts +++ b/src/configs/inputs/pad_procon.ts @@ -1,5 +1,5 @@ -import { SettingGamepad } from "#app/system/settings/settings-gamepad"; import { Button } from "#enums/buttons"; +import { SettingGamepad } from "#system/settings-gamepad"; /** * Nintendo Pro Controller mapping diff --git a/src/configs/inputs/pad_unlicensedSNES.ts b/src/configs/inputs/pad_unlicensedSNES.ts index d0c4f41c8f5..7d5dc055507 100644 --- a/src/configs/inputs/pad_unlicensedSNES.ts +++ b/src/configs/inputs/pad_unlicensedSNES.ts @@ -1,5 +1,5 @@ -import { SettingGamepad } from "../../system/settings/settings-gamepad"; import { Button } from "#enums/buttons"; +import { SettingGamepad } from "#system/settings-gamepad"; /** * 081f-e401 - UnlicensedSNES diff --git a/src/configs/inputs/pad_xbox360.ts b/src/configs/inputs/pad_xbox360.ts index 60cbd9ab181..7c719e77446 100644 --- a/src/configs/inputs/pad_xbox360.ts +++ b/src/configs/inputs/pad_xbox360.ts @@ -1,5 +1,5 @@ -import { SettingGamepad } from "../../system/settings/settings-gamepad"; import { Button } from "#enums/buttons"; +import { SettingGamepad } from "#system/settings-gamepad"; /** * Generic pad mapping diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 70195d6a152..30f6f6fb8b2 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1,86 +1,79 @@ -import { MoveResult } from "#enums/move-result"; -import { HitResult } from "#enums/hit-result"; -import { - BooleanHolder, - NumberHolder, - toDmgValue, - isNullOrUndefined, - randSeedItem, - randSeedInt, - randSeedFloat, - coerceArray, -} from "#app/utils/common"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { GroundedTag } from "#app/data/battler-tags"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { - getNonVolatileStatusEffects, - getStatusEffectDescriptor, - getStatusEffectHealText, -} from "#app/data/status-effect"; -import { Gender } from "#app/data/gender"; -import { applyMoveAttrs } from "../moves/apply-attrs"; -import { allMoves } from "../data-lists"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { TerrainType } from "#app/data/terrain"; -import { pokemonFormChanges } from "../pokemon-forms"; -import { - SpeciesFormChangeWeatherTrigger, - SpeciesFormChangeAbilityTrigger, -} from "../pokemon-forms/form-change-triggers"; -import i18next from "i18next"; -import { Command } from "#enums/command"; -import { BerryModifierType } from "#app/modifier/modifier-type"; -import { getPokeballName } from "#app/data/pokeball"; -import { BattleType } from "#enums/battle-type"; -import { globalScene } from "#app/global-scene"; -import { allAbilities } from "#app/data/data-lists"; +/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ +import type { BattleScene } from "#app/battle-scene"; +import type { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-change-triggers"; +/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */ -// Enum imports -import { Stat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#enums/stat"; -import { PokemonType } from "#enums/pokemon-type"; -import { PokemonAnimType } from "#enums/pokemon-anim-type"; -import { StatusEffect } from "#enums/status-effect"; -import { WeatherType } from "#enums/weather-type"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import type { ArenaTrapTag, SuppressAbilitiesTag } from "#data/arena-tag"; +import type { BattlerTag } from "#data/battler-tags"; +import { GroundedTag } from "#data/battler-tags"; +import { getBerryEffectFunc } from "#data/berry"; +import { allAbilities, allMoves } from "#data/data-lists"; +import { SpeciesFormChangeAbilityTrigger, SpeciesFormChangeWeatherTrigger } from "#data/form-change-triggers"; +import { Gender } from "#data/gender"; +import { getPokeballName } from "#data/pokeball"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#data/status-effect"; +import { TerrainType } from "#data/terrain"; +import type { Weather } from "#data/weather"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { SwitchType } from "#enums/switch-type"; +import type { BerryType } from "#enums/berry-type"; +import { Command } from "#enums/command"; +import { HitResult } from "#enums/hit-result"; +import { MoveCategory } from "#enums/MoveCategory"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; -import { MoveCategory } from "#enums/MoveCategory"; import { CommonAnim } from "#enums/move-anims-common"; -import { getBerryEffectFunc } from "#app/data/berry"; -import { BerryUsedEvent } from "#app/events/battle-scene"; -import { noAbilityTypeOverrideMoves } from "#app/data/moves/invalid-moves"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { MoveUseMode } from "#enums/move-use-mode"; - -// Type imports -import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; import type { BattleStat, EffectiveStat } from "#enums/stat"; -import type { BerryType } from "#enums/berry-type"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "../moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import type { Weather } from "#app/data/weather"; -import type { BattlerTag } from "#app/data/battler-tags"; +import { BATTLE_STATS, EFFECTIVE_STATS, getStatKey, Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { SwitchType } from "#enums/switch-type"; +import { WeatherType } from "#enums/weather-type"; +import { BerryUsedEvent } from "#events/battle-scene"; +import type { EnemyPokemon, Pokemon } from "#field/pokemon"; +import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; +import { BerryModifierType } from "#modifiers/modifier-type"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import { noAbilityTypeOverrideMoves } from "#moves/invalid-moves"; +import type { Move } from "#moves/move"; +import type { PokemonMove } from "#moves/pokemon-move"; +import type { StatStageChangePhase } from "#phases/stat-stage-change-phase"; import type { AbAttrCondition, + AbAttrMap, + AbAttrString, + PokemonAttackCondition, PokemonDefendCondition, PokemonStatStageChangeCondition, - PokemonAttackCondition, - AbAttrString, - AbAttrMap, -} from "#app/@types/ability-types"; -import type { BattlerIndex } from "#enums/battler-index"; -import type Move from "#app/data/moves/move"; -import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; -import type { Constructor } from "#app/utils/common"; -import type { Localizable } from "#app/@types/locales"; -import { applyAbAttrs } from "./apply-ab-attrs"; +} from "#types/ability-types"; +import type { Localizable } from "#types/locales"; +import type { Closed, Exact } from "#types/type-helpers"; +import type { Constructor } from "#utils/common"; +import { + BooleanHolder, + coerceArray, + isNullOrUndefined, + NumberHolder, + randSeedFloat, + randSeedInt, + randSeedItem, + toDmgValue, +} from "#utils/common"; +import i18next from "i18next"; export class Ability implements Localizable { public id: AbilityId; @@ -135,7 +128,8 @@ export class Ability implements Localizable { if (!targetAttr) { return []; } - return this.attrs.filter((a): a is AbAttrMap[T] => a instanceof targetAttr); + // TODO: figure out how to remove the `as AbAttrMap[T][]` cast + return this.attrs.filter((a): a is AbAttrMap[T] => a instanceof targetAttr) as AbAttrMap[T][]; } /** @@ -151,78 +145,185 @@ export class Ability implements Localizable { return this.attrs.some(attr => attr instanceof targetAttr); } - attr>(AttrType: T, ...args: ConstructorParameters): Ability { + /** + * Create a new {@linkcode AbAttr} instance and add it to this {@linkcode Ability}. + * @param attrType - The constructor of the {@linkcode AbAttr} to create. + * @param args - The arguments needed to instantiate the given class. + * @returns `this` + */ + attr>(AttrType: T, ...args: ConstructorParameters): this { const attr = new AttrType(...args); this.attrs.push(attr); return this; } + /** + * Create a new {@linkcode AbAttr} instance with the given condition and add it to this {@linkcode Ability}. + * Checked before all other conditions, and is unique to the individual {@linkcode AbAttr} being created. + * @param condition - The {@linkcode AbAttrCondition} to add. + * @param attrType - The constructor of the {@linkcode AbAttr} to create. + * @param args - The arguments needed to instantiate the given class. + * @returns `this` + */ conditionalAttr>( condition: AbAttrCondition, - AttrType: T, + attrType: T, ...args: ConstructorParameters - ): Ability { - const attr = new AttrType(...args); + ): this { + const attr = new attrType(...args); attr.addCondition(condition); this.attrs.push(attr); return this; } - bypassFaint(): Ability { + /** + * Make this ability trigger even if the user faints. + * @returns `this` + * @remarks + * This is also required for abilities to trigger when revived via Reviver Seed. + */ + bypassFaint(): this { this.isBypassFaint = true; return this; } - ignorable(): Ability { + /** + * Make this ability ignorable by effects like {@linkcode MoveId.SUNSTEEL_STRIKE | Sunsteel Strike} or {@linkcode AbilityId.MOLD_BREAKER | Mold Breaker}. + * @returns `this` + */ + ignorable(): this { this.isIgnorable = true; return this; } - unsuppressable(): Ability { + /** + * Make this ability unsuppressable by effects like {@linkcode MoveId.GASTRO_ACID | Gastro Acid} or {@linkcode AbilityId.NEUTRALIZING_GAS | Neutralizing Gas}. + * @returns `this` + */ + unsuppressable(): this { this.isSuppressable = false; return this; } - uncopiable(): Ability { + /** + * Make this ability uncopiable by effects like {@linkcode MoveId.ROLE_PLAY | Role Play} or {@linkcode AbilityId.TRACE | Trace}. + * @returns `this` + */ + uncopiable(): this { this.isCopiable = false; return this; } - unreplaceable(): Ability { + /** + * Make this ability unreplaceable by effects like {@linkcode MoveId.SIMPLE_BEAM | Simple Beam} or {@linkcode MoveId.ENTRAINMENT | Entrainment}. + * @returns `this` + */ + unreplaceable(): this { this.isReplaceable = false; return this; } - condition(condition: AbAttrCondition): Ability { + /** + * Add a condition for this ability to be applied. + * Applies to **all** attributes of the given ability. + * @param condition - The {@linkcode AbAttrCondition} to add + * @returns `this` + * @see {@linkcode AbAttr.canApply} for setting conditions per attribute type + * @see {@linkcode conditionalAttr} for setting individual conditions per attribute instance + * @todo Review if this is necessary anymore - this is used extremely sparingly + */ + condition(condition: AbAttrCondition): this { this.conditions.push(condition); return this; } + /** + * Mark an ability as partially implemented. + * Partial abilities are expected to have some of their core functionality implemented, but may lack + * certain notable features or interactions with other moves or abilities. + * @returns `this` + */ partial(): this { this.nameAppend += " (P)"; return this; } + /** + * Mark an ability as unimplemented. + * Unimplemented abilities are ones which have _none_ of their basic functionality enabled. + * @returns `this` + */ unimplemented(): this { this.nameAppend += " (N)"; return this; } /** - * Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case. - * @returns the ability + * Mark an ability as having one or more edge cases. + * It may lack certain niche interactions with other moves/abilities, but still functions + * as intended in most cases. + * Does not show up in game and is solely for internal dev use. + * + * When using this, make sure to **document the edge case** (or else this becomes pointless). + * @returns `this` */ edgeCase(): this { return this; } } +/** + * Base set of parameters passed to every ability attribute's {@linkcode AbAttr.apply | apply} method. + * + * Extended by sub-classes to contain additional parameters pertaining to the ability type(s) being triggered. + */ +export interface AbAttrBaseParams { + /** The pokemon that has the ability being applied */ + readonly pokemon: Pokemon; + + /** + * Whether the ability's effects are being simulated (for instance, during AI damage calculations). + * + * @remarks + * Used to prevent message flyouts and other effects from being triggered. + * @defaultValue `false` + */ + readonly simulated?: boolean; + + /** + * (For callers of {@linkcode applyAbAttrs}): If provided, **only** apply ability attributes of the passive (true) or active (false). + * + * This should almost always be left undefined, as otherwise it will *only* apply attributes of *either* the pokemon's passive (true) or + * non-passive (false) ability. In almost all cases, you want to apply attributes that are from either. + * + * (For implementations of {@linkcode AbAttr}): This will *never* be undefined, and will be `true` if the ability being applied + * is the pokemon's passive, and `false` otherwise. + */ + passive?: boolean; +} + +export interface AbAttrParamsWithCancel extends AbAttrBaseParams { + /** Whether the ability application results in the interaction being cancelled */ + readonly cancelled: BooleanHolder; +} + +/** + * Abstract class for all ability attributes. + * + * Each {@linkcode Ability} may have any number of individual attributes, each functioning independently from one another. + */ export abstract class AbAttr { - public showAbility: boolean; - private extraCondition: AbAttrCondition; + /** + * Whether to show this ability as a flyout when applying its effects. + * Should be kept in parity with mainline where possible. + * @defaultValue `true` + */ + public showAbility = true; + /** The additional condition associated with this AbAttr, if any. */ + private extraCondition?: AbAttrCondition; /** * Return whether this attribute is of the given type. @@ -250,25 +351,43 @@ export abstract class AbAttr { } /** - * Applies ability effects without checking conditions - * @param _pokemon - The pokemon to apply this ability to - * @param _passive - Whether or not the ability is a passive - * @param _simulated - Whether the call is simulated - * @param _args - Extra args passed to the function. Handled by child classes. - * @see {@linkcode canApply} + * Apply this attribute's effects without checking conditions. + * + * @remarks + * **Never call this method directly!** \ + * Use {@linkcode applyAbAttrs} instead. */ - apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void {} + apply(_params: AbAttrBaseParams): void {} - getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + /** + * Return the trigger message to show when this attribute is executed. + * @param _params - The parameters passed to this attribute's {@linkcode apply} function; must match type exactly + * @param _abilityName - The name of the current ability. + * @privateRemarks + * If more fields are provided than needed, any excess can be discarded using destructuring. + * @todo Remove `null` from signature in lieu of using an empty string + */ + getTriggerMessage(_params: Exact[0]>, _abilityName: string): string | null { return null; } + /** + * Check whether this attribute can have its effects successfully applied. + * Applies to **all** instances of the given attribute. + * @param _params - The parameters passed to this attribute's {@linkcode apply} function; must match type exactly + * @privateRemarks + * If more fields are provided than needed, any excess can be discarded using destructuring. + */ + canApply(_params: Exact[0]>): boolean { + return true; + } + + /** + * Return the additional condition associated with this particular AbAttr instance, if any. + * @returns The extra condition for this {@linkcode AbAttr}, or `null` if none exist + * @todo Make this use `undefined` instead of `null` + * @todo Prevent this from being overridden by sub-classes + */ getCondition(): AbAttrCondition | null { return this.extraCondition || null; } @@ -277,42 +396,22 @@ export abstract class AbAttr { this.extraCondition = condition; return this; } - - /** - * Returns a boolean describing whether the ability can be applied under current conditions - * @param _pokemon - The pokemon to apply this ability to - * @param _passive - Whether or not the ability is a passive - * @param _simulated - Whether the call is simulated - * @param _args - Extra args passed to the function. Handled by child classes. - * @returns `true` if the ability can be applied, `false` otherwise - * @see {@linkcode apply} - */ - canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - return true; - } } export class BlockRecoilDamageAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } +} - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { - return i18next.t("abilityTriggers:blockRecoilDamage", { - pokemonName: getPokemonNameWithAffix(pokemon), - abilityName: abilityName, - }); - } +export interface DoubleBattleChanceAbAttrParams extends AbAttrBaseParams { + /** Holder for the chance of a double battle that may be modified by the ability */ + chance: NumberHolder; } /** @@ -321,34 +420,23 @@ export class BlockRecoilDamageAttr extends AbAttr { * @see {@linkcode apply} */ export class DoubleBattleChanceAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } /** - * Increases the chance of a double battle occurring - * @param args [0] {@linkcode NumberHolder} for double battle chance + * Increase the chance of a double battle occurring, storing the result in `chance` */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const doubleBattleChance = args[0] as NumberHolder; - // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt - // A double battle will initiate if the generated number is 0 - doubleBattleChance.value = doubleBattleChance.value / 4; + override apply({ chance }: DoubleBattleChanceAbAttrParams): void { + // This is divided by 4 as the chance is generated as a number from 0 to chance.value using Utils.randSeedInt + // A double battle will initiate if the generated number is 0. + chance.value /= 4; } } export class PostBattleInitAbAttr extends AbAttr { - canApplyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args?: any[]): boolean { - return true; - } - - applyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args?: any[]): void {} + private declare readonly _: never; } export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { @@ -360,12 +448,12 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { this.formFunc = formFunc; } - override canApplyPostBattleInit(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: never[]): boolean { + override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex && !simulated; } - override applyPostBattleInit(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } @@ -381,13 +469,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { this.stages = stages; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { @@ -407,6 +489,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { * Clears a specified weather whenever this attribute is called. */ export class ClearWeatherAbAttr extends AbAttr { + // TODO: evaluate why this is a field and constructor parameter even though it is never checked private weather: WeatherType[]; /** @@ -418,17 +501,14 @@ export class ClearWeatherAbAttr extends AbAttr { this.weather = weather; } - public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + /** + * @param _params - No parameters are used for this attribute. + */ + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetWeather(WeatherType.NONE); } - public override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE, pokemon); } @@ -439,6 +519,7 @@ export class ClearWeatherAbAttr extends AbAttr { * Clears a specified terrain whenever this attribute is called. */ export class ClearTerrainAbAttr extends AbAttr { + // TODO: evaluate why this is a field and constructor parameter even though it is never checked private terrain: TerrainType[]; /** @@ -450,17 +531,11 @@ export class ClearTerrainAbAttr extends AbAttr { this.terrain = terrain; } - public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(TerrainType.NONE); } - public override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + public override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(TerrainType.NONE, true, pokemon); } @@ -469,58 +544,50 @@ export class ClearTerrainAbAttr extends AbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; -export class PreDefendAbAttr extends AbAttr { - canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { - return true; - } +/** + * Shared interface for AbAttrs that interact with a move that is being used by or against the user. + * + * Often extended by other interfaces to add more parameters. + * Used, e.g. by {@linkcode PreDefendAbAttr} and {@linkcode PostAttackAbAttr} + */ +export interface AugmentMoveInteractionAbAttrParams extends AbAttrBaseParams { + /** The move used by (or against, for defend attributes) the pokemon with the ability */ + move: Move; + /** The pokemon on the other side of the interaction */ + opponent: Pokemon; +} - applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - _args: any[], - ): void {} +/** + * Shared interface for parameters of several {@linkcode PreDefendAbAttr} ability attributes that modify damage. + */ +export interface PreDefendModifyDamageAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holder for the amount of damage that will be dealt by a move */ + damage: NumberHolder; +} + +/** + * Class for abilities that apply effects before the defending Pokemon takes damage. + * + * ⚠️ This attribute must not be called via `applyAbAttrs` as its subclasses violate the Liskov Substitution Principle. + */ +export abstract class PreDefendAbAttr extends AbAttr { + private declare readonly _: never; } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { + override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean { return ( pokemon.isFullHp() && // Checks if pokemon has wonder_guard (which forces 1hp) pokemon.getMaxHp() > 1 && // Damage >= hp - (args[0] as NumberHolder).value >= pokemon.hp + damage.value >= pokemon.hp && + // Cannot apply if the pokemon already has sturdy from some other source + !pokemon.getTag(BattlerTagType.STURDY) ); } - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PreDefendModifyDamageAbAttrParams): void { if (!simulated) { pokemon.addTag(BattlerTagType.STURDY, 1); } @@ -528,17 +595,11 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { + getTriggerMessage({ pokemon }: AbAttrBaseParams, abilityName: string) { return i18next.t("abilityTriggers:blockItemTheft", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -546,23 +607,22 @@ export class BlockItemTheftAbAttr extends AbAttr { } } +export interface StabBoostAbAttrParams extends AbAttrBaseParams { + /** Holds the resolved STAB multiplier after ability application */ + multiplier: NumberHolder; +} + export class StabBoostAbAttr extends AbAttr { constructor() { super(false); } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return (args[0] as NumberHolder).value > 1; + override canApply({ multiplier }: StabBoostAbAttrParams): boolean { + return multiplier.value > 1; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value += 0.5; + override apply({ multiplier }: StabBoostAbAttrParams): void { + multiplier.value += 0.5; } } @@ -577,28 +637,12 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { this.damageMultiplier = damageMultiplier; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PreDefendModifyDamageAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.damageMultiplier); + override apply({ damage }: PreDefendModifyDamageAbAttrParams): void { + damage.value = toDmgValue(damage.value * this.damageMultiplier); } } @@ -615,20 +659,9 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { } /** - * Handles the damage reduction - * @param args - * - `[0]` {@linkcode NumberHolder} - The damage being dealt + * Apply the damage reduction multiplier to the damage value. */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - const damage = args[0] as NumberHolder; + override apply({ damage }: PreDefendModifyDamageAbAttrParams): void { damage.value = toDmgValue(damage.value * this.damageMultiplier); } } @@ -639,9 +672,18 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip } } +/** + * Shared interface used by several {@linkcode PreDefendAbAttr} abilities that influence the computed type effectiveness + */ +export interface TypeMultiplierAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the type multiplier of an attack. In the case of an immunity, this value will be set to `0`. */ + typeMultiplier: NumberHolder; + /** Its particular meaning depends on the ability attribute, though usually means that the "no effect" message should not be played */ + cancelled: BooleanHolder; +} + /** * Determines whether a Pokemon is immune to a move because of an ability. - * @extends PreDefendAbAttr * @see {@linkcode applyPreDefend} * @see {@linkcode getCondition} */ @@ -649,7 +691,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { private immuneType: PokemonType | null; private condition: AbAttrCondition | null; - // TODO: `immuneType` shouldn't be able to be `null` + // TODO: Change `NonSuperEffectiveImmunityAbAttr` to not pass `null` as immune type constructor(immuneType: PokemonType | null, condition?: AbAttrCondition) { super(true); @@ -657,15 +699,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { this.condition = condition ?? null; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean { return ( ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) && attacker !== pokemon && @@ -673,26 +707,8 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { ); } - /** - * Applies immunity if this ability grants immunity to the type of the given move. - * @param _pokemon {@linkcode Pokemon} The defending Pokemon. - * @param _passive - Whether the ability is passive. - * @param _attacker {@linkcode Pokemon} The attacking Pokemon. - * @param _move {@linkcode Move} The attacking move. - * @param _cancelled {@linkcode BooleanHolder} - A holder for a boolean value indicating if the move was cancelled. - * @param args [0] {@linkcode NumberHolder} gets set to 0 if move is immuned by an ability. - * @param args [1] - Whether the move is simulated. - */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ typeMultiplier }: TypeMultiplierAbAttrParams): void { + typeMultiplier.value = 0; } getImmuneType(): PokemonType | null { @@ -710,39 +726,14 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { super(immuneType, condition); } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { + override canApply(params: TypeMultiplierAbAttrParams): boolean { + const { move } = params; return ( move.category !== MoveCategory.STATUS && !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") && - super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args) + super.canApply(params) ); } - - /** - * Applies immunity if the move used is not a status move. - * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type - * Example: Levitate - */ - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - // this is a hacky way to fix the Levitate/Thousand Arrows interaction, but it works for now... - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } } export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { @@ -751,28 +742,9 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + super.apply(params); + const { pokemon, cancelled, simulated, passive } = params; if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -801,28 +773,9 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + const { cancelled, simulated, pokemon } = params; + super.apply(params); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { globalScene.phaseManager.unshiftNew( @@ -847,28 +800,9 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + const { cancelled, simulated, pokemon } = params; + super.apply(params); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); @@ -881,36 +815,16 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const modifierValue = - args.length > 0 - ? (args[0] as NumberHolder).value - : pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move); - return move.is("AttackMove") && modifierValue < 2; + override canApply({ move, typeMultiplier }: TypeMultiplierAbAttrParams): boolean { + return move.is("AttackMove") && typeMultiplier.value < 2; } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ typeMultiplier, cancelled }: TypeMultiplierAbAttrParams): void { cancelled.value = true; // Suppresses "No Effect" message - (args[0] as NumberHolder).value = 0; + typeMultiplier.value = 0; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: TypeMultiplierAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:nonSuperEffectiveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -924,16 +838,10 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { * @extends PreDefendAbAttr */ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const typeMultiplier = args[0]; + /** + * Allow application if the pokemon with the ability is at full hp and the mvoe is not fixed damage + */ + override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean { return ( typeMultiplier instanceof NumberHolder && !move?.hasAttr("FixedDamageAttr") && @@ -943,70 +851,27 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { } /** - * Reduces a type multiplier to 0.5 if the source is at full HP. - * @param pokemon {@linkcode Pokemon} the Pokemon with this ability - * @param _passive n/a - * @param _simulated n/a (this doesn't change game state) - * @param _attacker n/a - * @param _move {@linkcode Move} the move being used on the source - * @param _cancelled n/a - * @param args `[0]` a container for the move's current type effectiveness multiplier + * Reduce the type multiplier to 0.5 if the source is at full HP. */ - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): void { - const typeMultiplier = args[0]; + override apply({ typeMultiplier, pokemon }: TypeMultiplierAbAttrParams): void { typeMultiplier.value = 0.5; pokemon.turnData.moveEffectiveness = 0.5; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: TypeMultiplierAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:fullHpResistType", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); } } -export class PostDefendAbAttr extends AbAttr { - canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - return true; - } - - applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): void {} +export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds whether the pokemon is immune to the move being used */ + cancelled: BooleanHolder; } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean { return ( !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) && move.getPriority(attacker) > 0 && @@ -1014,41 +879,17 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { ); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: FieldPriorityMoveImmunityAbAttrParams): void { cancelled.value = true; } } -export class PostStatStageChangeAbAttr extends AbAttr { - canApplyPostStatStageChange( - _pokemon: Pokemon, - _simulated: boolean, - _statsChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): boolean { - return true; - } - - applyPostStatStageChange( - _pokemon: Pokemon, - _simulated: boolean, - _statsChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): void {} +export interface MoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds whether the standard "no effect" message (due to a type-based immunity) should be suppressed */ + cancelled: BooleanHolder; } - +// TODO: Consider examining whether this move immunity ability attribute +// can be merged with the MoveTypeMultiplierAbAttr in some way. export class MoveImmunityAbAttr extends PreDefendAbAttr { private immuneCondition: PreDefendAbAttrCondition; @@ -1058,70 +899,41 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: MoveImmunityAbAttrParams): boolean { + // TODO: Investigate whether this method should be checking against `cancelled`, specifically + // if not checking this results in multiple flyouts showing when multiple abilities block the move. return this.immuneCondition(pokemon, attacker, move); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: MoveImmunityAbAttrParams): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: MoveImmunityAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); } } +export interface PreDefendModifyAccAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the accuracy of the move after the ability is applied */ + accuracy: NumberHolder; +} + /** * Reduces the accuracy of status moves used against the Pokémon with this ability to 50%. * Used by Wonder Skin. - * - * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { constructor() { super(false); } - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const moveAccuracy = args[0] as NumberHolder; - return move.category === MoveCategory.STATUS && moveAccuracy.value >= 50; + override canApply({ move, accuracy }: PreDefendModifyAccAbAttrParams): boolean { + return move.category === MoveCategory.STATUS && accuracy.value >= 50; } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - const moveAccuracy = args[0] as NumberHolder; - moveAccuracy.value = 50; + override apply({ accuracy }: PreDefendModifyAccAbAttrParams): void { + accuracy.value = 50; } } @@ -1135,52 +947,44 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return !simulated && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApply(params: MoveImmunityAbAttrParams): boolean { + // TODO: Evaluate whether it makes sense to check against simulated here. + // We likely want to check 'simulated' when the apply method enqueues the phase + return !params.simulated && super.canApply(params); } - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: MoveImmunityAbAttrParams): void { + super.apply(params); + // TODO: We probably should not unshift the phase if this is simulated globalScene.phaseManager.unshiftNew( "StatStageChangePhase", - pokemon.getBattlerIndex(), + params.pokemon.getBattlerIndex(), true, [this.stat], this.stages, ); } } + /** - * Class for abilities that make drain moves deal damage to user instead of healing them. - * @extends PostDefendAbAttr - * @see {@linkcode applyPostDefend} + * Shared parameters for ability attributes that apply an effect after move was used by or against the the user. */ +export interface PostMoveInteractionAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Stores the hit result of the move used in the interaction */ + readonly hitResult: HitResult; +} + +export class PostDefendAbAttr extends AbAttr { + private declare readonly _: never; + override canApply(_params: PostMoveInteractionAbAttrParams): boolean { + return true; + } + override apply(_params: PostMoveInteractionAbAttrParams): void {} +} + +/** Class for abilities that make drain moves deal damage to user instead of healing them. */ export class ReverseDrainAbAttr extends PostDefendAbAttr { - override canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move }: PostMoveInteractionAbAttrParams): boolean { return move.hasAttr("HitHealAttr"); } @@ -1188,22 +992,8 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * Determines if a damage and draining move was used to check if this ability should stop the healing. * 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 attacker {@linkcode Pokemon} that is attacking this Pokemon - * @param _move {@linkcode PokemonMove} that is being used - * @param _hitResult N/A - * @param _args N/A */ - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) }), @@ -1235,27 +1025,11 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (simulated) { return; } @@ -1307,15 +1081,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): 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; @@ -1324,15 +1090,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1347,42 +1105,27 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; - private tagType: ArenaTagType; + private arenaTagType: ArenaTagType; constructor(condition: PokemonDefendCondition, tagType: ArenaTagType) { super(true); this.condition = condition; - this.tagType = tagType; + this.arenaTagType = tagType; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - const tag = globalScene.arena.getTag(this.tagType) as ArenaTrapTag; + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { + const tag = globalScene.arena.getTag(this.arenaTagType) as ArenaTrapTag; return ( - this.condition(pokemon, attacker, move) && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) + this.condition(pokemon, attacker, move) && + (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers) ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.addTag( - this.tagType, + this.arenaTagType, 0, undefined, pokemon.id, @@ -1402,27 +1145,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, move }: PostMoveInteractionAbAttrParams): void { if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); globalScene.phaseManager.queueMessage( @@ -1438,34 +1165,24 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { private type: PokemonType; - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ + opponent: attacker, + move, + pokemon, + hitResult, + simulated, + }: PostMoveInteractionAbAttrParams): boolean { this.type = attacker.getMoveType(move); const pokemonTypes = pokemon.getTypes(true); return hitResult < HitResult.NO_EFFECT && (simulated || pokemonTypes.length !== 1 || pokemonTypes[0] !== this.type); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): void { const type = attacker.getMoveType(move); pokemon.summonData.types = [type]; } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1483,27 +1200,11 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - override canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ hitResult }: PostMoveInteractionAbAttrParams): boolean { return hitResult < HitResult.NO_EFFECT && globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -1511,7 +1212,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { } export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { - public chance: number; + private chance: number; private effects: StatusEffect[]; constructor(chance: number, ...effects: StatusEffect[]) { @@ -1521,15 +1222,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, move, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; return ( @@ -1540,15 +1233,8 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { + // TODO: Probably want to check against simulated here const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; attacker.trySetStatus(effect, true, pokemon); @@ -1560,31 +1246,9 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - override canApplyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { - return ( - !(attacker.hasAbility(AbilityId.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) && - super.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args) - ); - } - - override applyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - args: any[], - ): void { - super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const attacker = params.opponent; + return !(attacker.isOfType(PokemonType.GRASS) || attacker.hasAbility(AbilityId.OVERCOAT)) && super.canApply(params); } } @@ -1601,15 +1265,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && pokemon.randBattleSeedInt(100) < this.chance && @@ -1617,22 +1273,23 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, move }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); } } } -export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { +/** + * Set stat stages when the user gets hit by a critical hit + * + * @privateremarks + * It is the responsibility of the caller to ensure that this ability attribute is only applied + * when the user has been hit by a critical hit; such an event is not checked here. + * + * @sealed + */ +export class PostReceiveCritStatStageChangeAbAttr extends AbAttr { private stat: BattleStat; private stages: number; @@ -1643,15 +1300,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1662,12 +1311,6 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { ); } } - - override getCondition(): AbAttrCondition { - return (pokemon: Pokemon) => - pokemon.turnData.attacksReceived.length !== 0 && - pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; - } } export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { @@ -1679,15 +1322,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( !simulated && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && @@ -1695,20 +1330,12 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent: attacker }: PostMoveInteractionAbAttrParams): void { attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1731,37 +1358,21 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.getTag(BattlerTagType.PERISH_SONG) ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); } } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName, @@ -1780,15 +1391,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return ( !(this.condition && !this.condition(pokemon, attacker, move)) && !globalScene.arena.weather?.isImmutable() && @@ -1796,15 +1399,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -1812,30 +1407,14 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { } export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && attacker.getAbility().isSwappable ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { const tempAbility = attacker.getAbility(); attacker.setTempAbility(pokemon.getAbility()); @@ -1843,7 +1422,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } } - override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); @@ -1858,15 +1437,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && attacker.getAbility().isSuppressable && @@ -1874,21 +1445,13 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.setTempAbility(allAbilities[this.ability]); } } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendAbilityGive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1907,15 +1470,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && @@ -1923,15 +1478,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, move, pokemon }: PostMoveInteractionAbAttrParams): void { + // TODO: investigate why this is setting properties if (!simulated) { this.attacker = attacker; this.move = move; @@ -1940,6 +1488,25 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { } } +export interface PostStatStageChangeAbAttrParams extends AbAttrBaseParams { + /** The stats that were changed */ + stats: BattleStat[]; + /** The amount of stages that the stats changed by */ + stages: number; + /** Whether the source of the stat stages were from the user's own move */ + selfTarget: boolean; +} + +export class PostStatStageChangeAbAttr extends AbAttr { + private declare readonly _: never; + + override canApply(_params: Closed) { + return true; + } + + override apply(_params: Closed) {} +} + export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChangeAbAttr { private condition: PokemonStatStageChangeCondition; private statsToChange: BattleStat[]; @@ -1953,25 +1520,14 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang this.stages = stages; } - override canApplyPostStatStageChange( - pokemon: Pokemon, - _simulated: boolean, - statStagesChanged: BattleStat[], - stagesChanged: number, - selfTarget: boolean, - _args: any[], - ): boolean { - return this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget; + override canApply({ pokemon, stats, stages, selfTarget }: PostStatStageChangeAbAttrParams): boolean { + return this.condition(pokemon, stats, stages) && !selfTarget; } - override applyPostStatStageChange( - pokemon: Pokemon, - simulated: boolean, - _statStagesChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): void { + /** + * Add additional stat changes when one of the pokemon's own stats change + */ + override apply({ simulated, pokemon }: PostStatStageChangeAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1984,32 +1540,19 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang } } -export class PreAttackAbAttr extends AbAttr { - canApplyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): boolean { - return true; - } +export abstract class PreAttackAbAttr extends AbAttr { + private declare readonly _: never; +} - applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): void {} +export interface ModifyMoveEffectChanceAbAttrParams extends AbAttrBaseParams { + /** The move being used by the attacker */ + move: Move; + /** Holds the additional effect chance. Must be between `0` and `1` */ + chance: NumberHolder; } /** * Modifies moves additional effects with multipliers, ie. Sheer Force, Serene Grace. - * @extends AbAttr - * @see {@linkcode apply} */ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { private chanceMultiplier: number; @@ -2019,100 +1562,73 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { this.chanceMultiplier = chanceMultiplier; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + override canApply({ chance, move }: ModifyMoveEffectChanceAbAttrParams): boolean { const exceptMoves = [MoveId.ORDER_UP, MoveId.ELECTRO_SHOT]; - return !((args[0] as NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); + return !(chance.value <= 0 || exceptMoves.includes(move.id)); } - /** - * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. - * [1]: {@linkcode MoveId } Move used by the ability user. - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.chanceMultiplier; - (args[0] as NumberHolder).value = Math.min((args[0] as NumberHolder).value, 100); + override apply({ chance }: ModifyMoveEffectChanceAbAttrParams): void { + chance.value *= this.chanceMultiplier; + chance.value = Math.min(chance.value, 100); } } /** * Sets incoming moves additional effect chance to zero, ignoring all effects from moves. ie. Shield Dust. - * @extends PreDefendAbAttr - * @see {@linkcode applyPreDefend} */ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { constructor(showAbility = false) { super(showAbility); } - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return (args[0] as NumberHolder).value > 0; + override canApply({ chance }: ModifyMoveEffectChanceAbAttrParams): boolean { + return chance.value > 0; } - /** - * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. - */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ chance }: ModifyMoveEffectChanceAbAttrParams): void { + chance.value = 0; } } -export class VariableMovePowerAbAttr extends PreAttackAbAttr { - override canApplyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - _args: any[], - ): boolean { - return true; - } +export interface FieldPreventExplosiveMovesAbAttrParams extends AbAttrBaseParams { + /** Holds whether the explosive move should be prevented*/ + cancelled: BooleanHolder; } export class FieldPreventExplosiveMovesAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + // TODO: investigate whether we need to check against `cancelled` in a `canApply` method + override apply({ cancelled }: FieldPreventExplosiveMovesAbAttrParams): void { cancelled.value = true; } } +export interface FieldMultiplyStatAbAttrParams extends AbAttrBaseParams { + /** The kind of stat that is being checked for modification */ + stat: Stat; + /** Holds the value of the stat after multipliers */ + statVal: NumberHolder; + /** The target of the stat multiplier */ + target: Pokemon; + /** Holds whether another multiplier has already been applied to the stat. + * + * @remarks + * Intended to be used to prevent the multiplier from stacking + * with other instances of the ability */ + hasApplied: BooleanHolder; +} + /** * Multiplies a Stat if the checked Pokemon lacks this ability. * If this ability cannot stack, a BooleanHolder can be used to prevent this from stacking. - * @see {@link applyFieldStatMultiplierAbAttrs} - * @see {@link applyFieldStat} - * @see {@link BooleanHolder} */ export class FieldMultiplyStatAbAttr extends AbAttr { private stat: Stat; private multiplier: number; + /** + * Whether this ability can stack with others of the same type for this stat. + * @defaultValue `false` + * @todo Remove due to being literally useless - the ruin abilities are hardcoded to never stack in game + */ private canStack: boolean; constructor(stat: Stat, multiplier: number, canStack = false) { @@ -2123,49 +1639,32 @@ export class FieldMultiplyStatAbAttr extends AbAttr { this.canStack = canStack; } - canApplyFieldStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: Stat, - _statValue: NumberHolder, - checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - _args: any[], - ): boolean { + canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean { return ( this.canStack || (!hasApplied.value && this.stat === stat && - checkedPokemon.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat)) + target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat)) ); } /** - * applyFieldStat: Tries to multiply a Pokemon's Stat - * @param _pokemon {@linkcode Pokemon} the Pokemon using this ability - * @param _passive {@linkcode boolean} unused - * @param _stat {@linkcode Stat} the type of the checked stat - * @param statValue {@linkcode NumberHolder} the value of the checked stat - * @param _checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting - * @param hasApplied {@linkcode BooleanHolder} whether or not another multiplier has been applied to this stat - * @param _args {any[]} unused + * Atttempt to multiply a Pokemon's Stat. */ - applyFieldStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: Stat, - statValue: NumberHolder, - _checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - _args: any[], - ): void { - statValue.value *= this.multiplier; + apply({ statVal, hasApplied }: FieldMultiplyStatAbAttrParams): void { + statVal.value *= this.multiplier; hasApplied.value = true; } } +export interface MoveTypeChangeAbAttrParams extends AugmentMoveInteractionAbAttrParams { + // TODO: Replace the number holder with a holder for the type. + /** Holds the type of the move, which may change after ability application */ + moveType: NumberHolder; + /** Holds the power of the move, which may change after ability application */ + power: NumberHolder; +} + export class MoveTypeChangeAbAttr extends PreAttackAbAttr { constructor( private newType: PokemonType, @@ -2176,113 +1675,69 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { } /** - * Determine if the move type change attribute can be applied + * Determine if the move type change attribute can be applied. * * Can be applied if: * - The ability's condition is met, e.g. pixilate only boosts normal moves, * - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode MoveId.MULTI_ATTACK} - * - The user is not terastallized and using tera blast - * - The user is not a terastallized terapagos with tera stellar using tera starstorm - * @param pokemon - The pokemon that has the move type changing ability and is using the attacking move - * @param _passive - Unused - * @param _simulated - Unused - * @param _defender - The pokemon being attacked (unused) - * @param move - The move being used - * @param _args - args[0] holds the type that the move is changed to, args[1] holds the multiplier - * @returns whether the move type change attribute can be applied + * - The user is not Terastallized and using Tera Blast + * - The user is not a Terastallized Terapagos using Stellar-type Tera Starstorm */ - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: [NumberHolder?, NumberHolder?, ...any], - ): boolean { + override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean { return ( - (!this.condition || this.condition(pokemon, _defender, move)) && + (!this.condition || this.condition(pokemon, target, move)) && !noAbilityTypeOverrideMoves.has(move.id) && - (!pokemon.isTerastallized || - (move.id !== MoveId.TERA_BLAST && - (move.id !== MoveId.TERA_STARSTORM || - pokemon.getTeraType() !== PokemonType.STELLAR || - !pokemon.hasSpecies(SpeciesId.TERAPAGOS)))) + !( + pokemon.isTerastallized && + (move.id === MoveId.TERA_BLAST || + (move.id === MoveId.TERA_STARSTORM && + pokemon.getTeraType() === PokemonType.STELLAR && + pokemon.hasSpecies(SpeciesId.TERAPAGOS))) + ) ); } - /** - * @param _pokemon - The pokemon that has the move type changing ability and is using the attacking move - * @param _passive - Unused - * @param _simulated - Unused - * @param _defender - The pokemon being attacked (unused) - * @param _move - The move being used - * @param args - args[0] holds the type that the move is changed to, args[1] holds the multiplier - */ - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: [NumberHolder?, NumberHolder?, ...any], - ): void { - if (args[0] && args[0] instanceof NumberHolder) { - args[0].value = this.newType; - } - if (args[1] && args[1] instanceof NumberHolder) { - args[1].value *= this.powerMultiplier; - } + override apply({ moveType, power }: MoveTypeChangeAbAttrParams): void { + moveType.value = this.newType; + power.value *= this.powerMultiplier; } } -/** Ability attribute for changing a pokemon's type before using a move */ +/** + * Attribute to change the user's type to that of the move currently being executed. + * Used by {@linkcode AbilityId.PROTEAN} and {@linkcode AbilityId.LIBERO}. + */ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { - private moveType: PokemonType; - + private moveType: PokemonType = PokemonType.UNKNOWN; constructor() { super(true); } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { + override canApply({ move, pokemon }: AugmentMoveInteractionAbAttrParams): boolean { if ( - !pokemon.isTerastallized && - move.id !== MoveId.STRUGGLE && - /** + pokemon.isTerastallized || + move.id === MoveId.STRUGGLE || + /* * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute - * @see {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves} + * See: https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves */ - !move.findAttr( - attr => - attr.is("RandomMovesetMoveAttr") || - attr.is("RandomMoveAttr") || - attr.is("NaturePowerAttr") || - attr.is("CopyMoveAttr"), - ) + move.hasAttr("CallMoveAttr") || + move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr` ) { - const moveType = pokemon.getMoveType(move); - if (pokemon.getTypes().some(t => t !== moveType)) { - this.moveType = moveType; - return true; - } + return false; } - return false; + + // Skip changing type if we're already of the given type as-is + const moveType = pokemon.getMoveType(move); + if (pokemon.getTypes().every(t => t === moveType)) { + return false; + } + + this.moveType = moveType; + return true; } - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _defender: Pokemon, - move: Move, - _args: any[], - ): void { + override apply({ simulated, pokemon, move }: AugmentMoveInteractionAbAttrParams): void { const moveType = pokemon.getMoveType(move); if (!simulated) { @@ -2292,7 +1747,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AugmentMoveInteractionAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveType: i18next.t(`pokemonInfo:Type.${PokemonType[this.moveType]}`), @@ -2301,50 +1756,39 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } /** - * Class for abilities that convert single-strike moves to two-strike moves (i.e. Parental Bond). - * @param damageMultiplier the damage multiplier for the second strike, relative to the first. + * Parameters for abilities that modify the hit count and damage of a move + */ +export interface AddSecondStrikeAbAttrParams extends Omit { + /** Holder for the number of hits. May be modified by ability application */ + hitCount?: NumberHolder; + /** Holder for the damage multiplier _of the current hit_ */ + multiplier?: NumberHolder; +} + +/** + * Class for abilities that add additional strikes to single-target moves. + * Used by {@linkcode Moves.PARENTAL_BOND | Parental Bond}. */ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { - private damageMultiplier: number; - - constructor(damageMultiplier: number) { + /** + * @param damageMultiplier - The damage multiplier for the second strike, relative to the first + */ + constructor(private damageMultiplier: number) { super(false); - - this.damageMultiplier = damageMultiplier; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { + /** + * Return whether the move can be multi-strike enhanced. + */ + override canApply({ pokemon, move }: AddSecondStrikeAbAttrParams): boolean { return move.canBeMultiStrikeEnhanced(pokemon, true); } /** - * If conditions are met, this doubles the move's hit count (via args[1]) - * or multiplies the damage of secondary strikes (via args[2]) - * @param pokemon the {@linkcode Pokemon} using the move - * @param _passive n/a - * @param _defender n/a - * @param _move the {@linkcode Move} used by the ability source - * @param args Additional arguments: - * - `[0]` the number of strikes this move currently has ({@linkcode NumberHolder}) - * - `[1]` the damage multiplier for the current strike ({@linkcode NumberHolder}) + * Add one to the move's hit count, and, if the pokemon has only one hit left, sets the damage multiplier + * to the damage multiplier of this ability. */ - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - const hitCount = args[0] as NumberHolder; - const multiplier = args[1] as NumberHolder; + override apply({ hitCount, multiplier, pokemon }: AddSecondStrikeAbAttrParams): void { if (hitCount?.value) { hitCount.value += 1; } @@ -2355,6 +1799,16 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { } } +/** + * Common interface for parameters used by abilities that modify damage/power of a move before an attack + */ +export interface PreAttackModifyDamageAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** + * The amount of damage dealt by the move. May be modified by ability application. + */ + damage: NumberHolder; +} + /** * Class for abilities that boost the damage of moves * For abilities that boost the base power of moves, see VariableMovePowerAbAttr @@ -2371,38 +1825,37 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { this.condition = condition; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { - return this.condition(pokemon, defender, move); + override canApply({ pokemon, opponent: target, move }: PreAttackModifyDamageAbAttrParams): boolean { + return this.condition(pokemon, target, move); } /** - * - * @param _pokemon the attacker pokemon - * @param _passive N/A - * @param _defender the target pokemon - * @param _move the move used by the attacker pokemon - * @param args Utils.NumberHolder as damage + * Adjust the power by the damage multiplier. */ - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - const power = args[0] as NumberHolder; + override apply({ damage: power }: PreAttackModifyDamageAbAttrParams): void { power.value = toDmgValue(power.value * this.damageMultiplier); } } +export interface PreAttackModifyPowerAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the base power of the move, which may be modified after ability application */ + power: NumberHolder; +} + +/* +This base class *is* allowed to be invoked directly by `applyAbAttrs`. +As such, we require that all subclasses have compatible `apply` parameters. +To do this, we use the `Closed` type. This ensures that any subclass of `VariableMovePowerAbAttr` +may not modify the type of apply's parameter to an interface that introduces new fields +or changes the type of existing fields. +*/ +export abstract class VariableMovePowerAbAttr extends PreAttackAbAttr { + override canApply(_params: Closed): boolean { + return true; + } + override apply(_params: Closed): void {} +} + export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { private condition: PokemonAttackCondition; private powerMultiplier: number; @@ -2413,26 +1866,12 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.powerMultiplier = powerMultiplier; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { - return this.condition(pokemon, defender, move); + override canApply({ pokemon, opponent, move }: PreAttackModifyPowerAbAttrParams): boolean { + return this.condition(pokemon, opponent, move); } - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.powerMultiplier; + override apply({ power }: PreAttackModifyPowerAbAttrParams): void { + power.value *= this.powerMultiplier; } } @@ -2455,48 +1894,31 @@ export class LowHpMoveTypePowerBoostAbAttr extends MoveTypePowerBoostAbAttr { /** * Abilities which cause a variable amount of power increase. - * @extends VariableMovePowerAbAttr - * @see {@link applyPreAttack} */ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { private mult: (user: Pokemon, target: Pokemon, move: Move) => number; /** - * @param mult A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier. - * @param {boolean} showAbility Whether to show the ability when it activates. + * @param mult - A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier. + * @param showAbility - Whether to show the ability when it activates. */ constructor(mult: (user: Pokemon, target: Pokemon, move: Move) => number, showAbility = true) { super(showAbility); this.mult = mult; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - _args: any[], - ): boolean { - return this.mult(pokemon, defender, move) !== 1; + override canApply({ pokemon, opponent, move }: PreAttackModifyPowerAbAttrParams): boolean { + return this.mult(pokemon, opponent, move) !== 1; } - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - args: any[], - ): void { - const multiplier = this.mult(pokemon, defender, move); - (args[0] as NumberHolder).value *= multiplier; + override apply({ pokemon, opponent, move, power }: PreAttackModifyPowerAbAttrParams): void { + const multiplier = this.mult(pokemon, opponent, move); + power.value *= multiplier; } } /** * Boosts the power of a Pokémon's move under certain conditions. - * @extends AbAttr */ export class FieldMovePowerBoostAbAttr extends AbAttr { // TODO: Refactor this class? It extends from base AbAttr but has preAttack methods and gets called directly instead of going through applyAbAttrsInternal @@ -2513,34 +1935,19 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - canApplyPreAttack( - _pokemon: Pokemon | null, - _passive: boolean | null, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): boolean { + canApply(_params: PreAttackModifyPowerAbAttrParams): boolean { return true; // logic for this attr is handled in move.ts instead of normally } - applyPreAttack( - pokemon: Pokemon | null, - _passive: boolean | null, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - args: any[], - ): void { - if (this.condition(pokemon, defender, move)) { - (args[0] as NumberHolder).value *= this.powerMultiplier; + apply({ pokemon, opponent, move, power }: PreAttackModifyPowerAbAttrParams): void { + if (this.condition(pokemon, opponent, move)) { + power.value *= this.powerMultiplier; } } } /** * Boosts the power of a specific type of move. - * @extends FieldMovePowerBoostAbAttr */ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostAbAttr { /** @@ -2578,9 +1985,25 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr } } +export interface StatMultiplierAbAttrParams extends AbAttrBaseParams { + /** The move being used by the user in the interaction*/ + move: Move; + /** The stat to determine modification for*/ + stat: BattleStat; + /** Holds the value of the stat, which may change after ability application. */ + statVal: NumberHolder; +} + export class StatMultiplierAbAttr extends AbAttr { + private declare readonly _: never; private stat: BattleStat; private multiplier: number; + /** + * Function determining if the stat multiplier is able to be applied to the move. + * + * @remarks + * Currently only used by Hustle. + */ private condition: PokemonAttackCondition | null; constructor(stat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { @@ -2591,77 +2014,26 @@ export class StatMultiplierAbAttr extends AbAttr { this.condition = condition ?? null; } - canApplyStatStage( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _statValue: NumberHolder, - args: any[], - ): boolean { - const move = args[0] as Move; + override canApply({ pokemon, move, stat }: StatMultiplierAbAttrParams): boolean { return stat === this.stat && (!this.condition || this.condition(pokemon, null, move)); } - applyStatStage( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - statValue: NumberHolder, - _args: any[], - ): void { - statValue.value *= this.multiplier; + override apply({ statVal }: StatMultiplierAbAttrParams): void { + statVal.value *= this.multiplier; } } -export class PostAttackAbAttr extends AbAttr { - private attackCondition: PokemonAttackCondition; - - /** The default attackCondition requires that the selected move is a damaging move */ - constructor( - attackCondition: PokemonAttackCondition = (_user, _target, move) => move.category !== MoveCategory.STATUS, - showAbility = true, - ) { - super(showAbility); - - this.attackCondition = attackCondition; - } - +export interface AllyStatMultiplierAbAttrParams extends StatMultiplierAbAttrParams { /** - * By default, this method checks that the move used is a damaging attack before - * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} - * for an example of an effect that does not require a damaging move. + * Whether abilities are being ignored during the interaction (e.g. due to a Mold-Breaker like effect). + * + * Note that some abilities that provide stat multipliers to allies apply their boosts regardless of this flag. */ - canApplyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. - // If attackRequired is false, we always defer to the secondary requirements. - return this.attackCondition(pokemon, defender, move); - } - - applyPostAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): void {} + ignoreAbility: boolean; } /** * Multiplies a Stat from an ally pokemon's ability. - * @see {@link applyAllyStatMultiplierAbAttrs} - * @see {@link applyAllyStat} */ export class AllyStatMultiplierAbAttr extends AbAttr { private stat: BattleStat; @@ -2683,65 +2055,29 @@ export class AllyStatMultiplierAbAttr extends AbAttr { /** * Multiply a Pokemon's Stat due to an Ally's ability. - * @param _pokemon - The ally {@linkcode Pokemon} with the ability (unused) - * @param passive - unused - * @param _simulated - Whether the ability is being simulated (unused) - * @param _stat - The type of the checked {@linkcode Stat} (unused) - * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param _checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) - * @param _ignoreAbility - Whether the ability should be ignored if possible - * @param _args - unused - * @returns `true` if this changed the checked stat, `false` otherwise. */ - applyAllyStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - statValue: NumberHolder, - _checkedPokemon: Pokemon, - _ignoreAbility: boolean, - _args: any[], - ) { - statValue.value *= this.multiplier; + apply({ statVal }: AllyStatMultiplierAbAttrParams) { + statVal.value *= this.multiplier; } /** - * Check if this ability can apply to the checked stat. - * @param _pokemon - The ally {@linkcode Pokemon} with the ability (unused) - * @param passive - unused - * @param _simulated - Whether the ability is being simulated (unused) - * @param stat - The type of the checked {@linkcode Stat} - * @param _statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param _checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) - * @param ignoreAbility - Whether the ability should be ignored if possible - * @param _args - unused - * @returns `true` if this can apply to the checked stat, `false` otherwise. + * @returns Whether the ability with this attribute can apply to the checked stat */ - canApplyAllyStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _statValue: NumberHolder, - _checkedPokemon: Pokemon, - ignoreAbility: boolean, - _args: any[], - ): boolean { + canApply({ stat, ignoreAbility }: AllyStatMultiplierAbAttrParams): boolean { return stat === this.stat && !(ignoreAbility && this.ignorable); } } /** - * Takes effect whenever a move succesfully executes, such as gorilla tactics' move-locking. + * Takes effect whenever the user's move succesfully executes, such as gorilla tactics' move-locking. * (More specifically, whenever a move is pushed to the move history) */ export class ExecutedMoveAbAttr extends AbAttr { - canApplyExecutedMove(_pokemon: Pokemon, _simulated: boolean): boolean { + canApply(_params: Closed): boolean { return true; } - applyExecutedMove(_pokemon: Pokemon, _simulated: boolean): void {} + apply(_params: Closed): void {} } /** @@ -2753,17 +2089,51 @@ export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr { super(showAbility); } - override canApplyExecutedMove(pokemon: Pokemon, _simulated: boolean): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { + // TODO: Consider whether checking against simulated makes sense here return !pokemon.getTag(BattlerTagType.GORILLA_TACTICS); } - override applyExecutedMove(pokemon: Pokemon, simulated: boolean): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.addTag(BattlerTagType.GORILLA_TACTICS); } } } +/* +Subclasses that override the `canApply` and `apply` are not allowed to change the type of their parameters. +This is enforced via the `Closed` type. +*/ +/** + * Base class for abilities that apply some effect after the user's move successfully executes. + */ +export abstract class PostAttackAbAttr extends AbAttr { + private attackCondition: PokemonAttackCondition; + + /** The default `attackCondition` requires that the selected move is a damaging move */ + constructor( + attackCondition: PokemonAttackCondition = (_user, _target, move) => move.category !== MoveCategory.STATUS, + showAbility = true, + ) { + super(showAbility); + + this.attackCondition = attackCondition; + } + + /** + * By default, this method checks that the move used is a damaging attack before + * applying the effect of any inherited class. + * This can be changed by providing a different {@linkcode attackCondition} to the constructor. + * @see {@linkcode ConfusionOnStatusEffectAbAttr} for an example of an effect that does not require a damaging move. + */ + override canApply({ pokemon, opponent, move }: Closed): boolean { + return this.attackCondition(pokemon, opponent, move); + } + + override apply(_params: Closed): void {} +} + export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { private stealCondition: PokemonAttackCondition | null; private stolenItem?: PokemonHeldItemModifier; @@ -2774,22 +2144,18 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stealCondition = stealCondition ?? null; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { simulated, pokemon, opponent, move, hitResult } = params; + // TODO: Revisit the hitResult check here. + // The PostAttackAbAttr should should only be invoked in cases where the move successfully connected, + // calling `super.canApply` already checks that the move was a damage move and not a status move. if ( - super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && + super.canApply(params) && !simulated && hitResult < HitResult.NO_EFFECT && - (!this.stealCondition || this.stealCondition(pokemon, defender, move)) + (!this.stealCondition || this.stealCondition(pokemon, opponent, move)) ) { - const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (heldItems.length) { // Ensure that the stolen item in testing is the same as when the effect is applied this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; @@ -2802,16 +2168,8 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { return false; } - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { - const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); + override apply({ opponent, pokemon }: PostMoveInteractionAbAttrParams): void { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -2819,7 +2177,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postAttackStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - defenderName: defender.name, + defenderName: opponent.name, stolenItemType: this.stolenItem.type.name, }), ); @@ -2848,45 +2206,30 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { simulated, pokemon, move, opponent } = params; if ( - super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && + super.canApply(params) && (simulated || - (!attacker.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && - pokemon !== attacker && + (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && + pokemon !== opponent && (!this.contactRequired || - move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) && pokemon.randBattleSeedInt(100) < this.chance && !pokemon.status)) ) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - return simulated || attacker.canSetStatus(effect, true, false, pokemon); + return simulated || opponent.canSetStatus(effect, true, false, pokemon); } return false; } - applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + apply({ pokemon, opponent }: PostMoveInteractionAbAttrParams): void { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - attacker.trySetStatus(effect, true, pokemon); + opponent.trySetStatus(effect, true, pokemon); } } @@ -2913,40 +2256,25 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { pokemon, move, opponent } = params; /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ return ( - super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && - !attacker.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && - pokemon !== attacker && + super.canApply(params) && + !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && + pokemon !== opponent && (!this.contactRequired || - move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && - pokemon.randBattleSeedInt(100) < this.chance(attacker, pokemon, move) && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) && + pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) && !pokemon.status ); } - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon, simulated, opponent }: PostMoveInteractionAbAttrParams): void { if (!simulated) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - attacker.addTag(effect); + opponent.addTag(effect); } } } @@ -2961,17 +2289,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { - if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { - const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); + override canApply({ simulated, pokemon, opponent, move, hitResult }: PostMoveInteractionAbAttrParams): boolean { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, opponent, move))) { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (heldItems.length) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { @@ -2982,16 +2302,8 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { return false; } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { - const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); + override apply({ pokemon, opponent }: PostMoveInteractionAbAttrParams): void { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -2999,7 +2311,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - attackerName: attacker.name, + attackerName: opponent.name, stolenItemType: this.stolenItem.type.name, }), ); @@ -3015,38 +2327,29 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { } } +/** + * Shared parameters used for abilities that apply an effect after the user is inflicted with a status condition. + */ +export interface PostSetStatusAbAttrParams extends AbAttrBaseParams { + /** The pokemon that set the status condition, or `undefined` if not set by a pokemon */ + sourcePokemon?: Pokemon; + /** The status effect that was set */ + effect: StatusEffect; +} + +/* +Subclasses that override the `canApply` and `apply` methods of `PostSetStatusAbAttr` are not allowed to change the +type of their parameters. This is enforced via the Closed type. +*/ /** * Base class for defining all {@linkcode Ability} Attributes after a status effect has been set. - * @see {@linkcode applyPostSetStatus()}. */ export class PostSetStatusAbAttr extends AbAttr { - canApplyPostSetStatus( - _pokemon: Pokemon, - _sourcePokemon: Pokemon | null = null, - _passive: boolean, - _effect: StatusEffect, - _simulated: boolean, - _rgs: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - /** - * Does nothing after a status condition is set. - * @param _pokemon {@linkcode Pokemon} that status condition was set on. - * @param _sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon. - * @param _passive Whether this ability is a passive. - * @param _effect {@linkcode StatusEffect} that was set. - * @param _args Set of unique arguments needed by this attribute. - */ - applyPostSetStatus( - _pokemon: Pokemon, - _sourcePokemon: Pokemon | null = null, - _passive: boolean, - _effect: StatusEffect, - _simulated: boolean, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -3055,14 +2358,14 @@ export class PostSetStatusAbAttr extends AbAttr { * ability attribute. For Synchronize ability. */ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { - override canApplyPostSetStatus( - _pokemon: Pokemon, - sourcePokemon: (Pokemon | null) | undefined, - _passive: boolean, - effect: StatusEffect, - _simulated: boolean, - _args: any[], - ): boolean { + /** + * @returns Whether the status effect that was set is one of the synchronizable statuses: + * - {@linkcode StatusEffect.BURN | Burn} + * - {@linkcode StatusEffect.PARALYSIS | Paralysis} + * - {@linkcode StatusEffect.POISON | Poison} + * - {@linkcode StatusEffect.TOXIC | Toxic} + */ + override canApply({ sourcePokemon, effect }: PostSetStatusAbAttrParams): boolean { /** Synchronizable statuses */ const syncStatuses = new Set([ StatusEffect.BURN, @@ -3078,32 +2381,25 @@ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { /** * If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status * was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`. - * @param pokemon {@linkcode Pokemon} that status condition was set on. - * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon. - * @param _passive Whether this ability is a passive. - * @param effect {@linkcode StatusEffect} that was set. - * @param _args Set of unique arguments needed by this attribute. */ - override applyPostSetStatus( - pokemon: Pokemon, - sourcePokemon: Pokemon | null = null, - _passive: boolean, - effect: StatusEffect, - simulated: boolean, - _args: any[], - ): void { + override apply({ simulated, effect, sourcePokemon, pokemon }: PostSetStatusAbAttrParams): void { if (!simulated && sourcePokemon) { sourcePokemon.trySetStatus(effect, true, pokemon); } } } +/** + * Base class for abilities that apply an effect after the user knocks out an opponent in battle. + * + * Not to be confused with {@linkcode PostKnockOutAbAttr}, which applies after any pokemon is knocked out in battle. + */ export class PostVictoryAbAttr extends AbAttr { - canApplyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { @@ -3117,7 +2413,7 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { this.stages = stages; } - override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); @@ -3134,36 +2430,37 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { this.formFunc = formFunc; } - override canApplyPostVictory(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex; } - override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } } -export class PostKnockOutAbAttr extends AbAttr { - canApplyPostKnockOut( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): boolean { +/** + * Shared parameters used for abilities that apply an effect after a Pokemon (other than the user) is knocked out. + */ +export interface PostKnockOutAbAttrParams extends AbAttrBaseParams { + /** The Pokemon that was knocked out */ + victim: Pokemon; +} + +/** + * Base class for ability attributes that apply after a Pokemon (other than the user) is knocked out, including indirectly. + * + * Not to be confused with {@linkcode PostVictoryAbAttr}, which applies after the user directly knocks out an opponent. + */ +export abstract class PostKnockOutAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostKnockOut( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): void {} + apply(_params: Closed): void {} } export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { @@ -3177,13 +2474,7 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { this.stages = stages; } - override applyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PostKnockOutAbAttrParams): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); @@ -3192,35 +2483,29 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { } export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { - override canApplyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - knockedOut: Pokemon, - _args: any[], - ): boolean { - return pokemon.isPlayer() === knockedOut.isPlayer() && knockedOut.getAbility().isCopiable; + override canApply({ pokemon, victim }: PostKnockOutAbAttrParams): boolean { + return pokemon.isPlayer() === victim.isPlayer() && victim.getAbility().isCopiable; } - override applyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - knockedOut: Pokemon, - _args: any[], - ): void { + override apply({ pokemon, simulated, victim }: PostKnockOutAbAttrParams): void { if (!simulated) { - pokemon.setTempAbility(knockedOut.getAbility()); + pokemon.setTempAbility(victim.getAbility()); globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:copyFaintedAllyAbility", { - pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), - abilityName: allAbilities[knockedOut.getAbility().id].name, + pokemonNameWithAffix: getPokemonNameWithAffix(victim), + abilityName: allAbilities[victim.getAbility().id].name, }), ); } } } +export interface IgnoreOpponentStatStagesAbAttrParams extends AbAttrBaseParams { + /** The stat to check for ignorability */ + stat: BattleStat; + /** Holds whether the stat is ignored by the ability */ + ignored: BooleanHolder; +} /** * Ability attribute for ignoring the opponent's stat changes * @param stats the stats that should be ignored @@ -3234,45 +2519,35 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { this.stats = stats ?? BATTLE_STATS; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.stats.includes(args[0]); + /** + * @returns Whether `stat` is one of the stats ignored by the ability + */ + override canApply({ stat }: IgnoreOpponentStatStagesAbAttrParams): boolean { + return this.stats.includes(stat); } /** - * Modifies a BooleanHolder and returns the result to see if a stat is ignored or not - * @param _pokemon n/a - * @param _passive n/a - * @param _simulated n/a - * @param _cancelled n/a - * @param args A BooleanHolder that represents whether or not to ignore a stat's stat changes + * Sets the ignored holder to true. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[1] as BooleanHolder).value = true; + override apply({ ignored }: IgnoreOpponentStatStagesAbAttrParams): void { + ignored.value = true; } } +/** + * Abilities with this attribute prevent the user from being affected by Intimidate. + * @sealed + */ export class IntimidateImmunityAbAttr extends AbAttr { constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AbAttrParamsWithCancel, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:intimidateImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -3292,13 +2567,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated, cancelled }: AbAttrParamsWithCancel): void { if (!simulated) { globalScene.phaseManager.pushNew( "StatStageChangePhase", @@ -3316,7 +2585,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { * Base class for defining all {@linkcode Ability} Attributes post summon * @see {@linkcode applyPostSummon()} */ -export class PostSummonAbAttr extends AbAttr { +export abstract class PostSummonAbAttr extends AbAttr { /** Should the ability activate when gained in battle? This will almost always be true */ private activateOnGain: boolean; @@ -3332,23 +2601,20 @@ export class PostSummonAbAttr extends AbAttr { return this.activateOnGain; } - canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } /** * Applies ability post summon (after switching in) - * @param _pokemon {@linkcode Pokemon} with this ability - * @param _passive Whether this ability is a passive - * @param _args Set of unique arguments needed by this attribute */ - applyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Base class for ability attributes which remove an effect on summon */ -export class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {} +export abstract class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {} /** * Removes specified arena tags when a Pokemon is summoned. @@ -3365,11 +2631,11 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.tags.some(tag => this.arenaTags.includes(tag.tagType)); } - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { for (const arenaTag of this.arenaTags) { globalScene.arena.removeTag(arenaTag); @@ -3396,7 +2662,7 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr { this.quiet = quiet; } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + public override apply({ pokemon, simulated }: AbAttrBaseParams): void { this.sourceId = pokemon.id; if (!simulated) { globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet); @@ -3413,7 +2679,7 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.messageFunc(pokemon)); } @@ -3430,7 +2696,7 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.message); } @@ -3448,11 +2714,11 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.canAddTag(this.tagType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -3475,11 +2741,11 @@ export class PostSummonRemoveBattlerTagAbAttr extends PostSummonRemoveEffectAbAt this.immuneTags = immuneTags; } - public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + public override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.immuneTags.some(tagType => !!pokemon.getTag(tagType)); } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply({ pokemon }: AbAttrBaseParams): void { this.immuneTags.forEach(tagType => pokemon.removeTag(tagType)); } } @@ -3499,7 +2765,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (simulated) { return; } @@ -3514,27 +2780,29 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.stats, this.stages, ); - } else { - for (const opponent of pokemon.getOpponents()) { - const cancelled = new BooleanHolder(false); - if (this.intimidate) { - applyAbAttrs("IntimidateImmunityAbAttr", opponent, cancelled, simulated); - applyAbAttrs("PostIntimidateStatStageChangeAbAttr", opponent, cancelled, simulated); + return; + } - if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { - cancelled.value = true; - } - } - if (!cancelled.value) { - globalScene.phaseManager.unshiftNew( - "StatStageChangePhase", - opponent.getBattlerIndex(), - false, - this.stats, - this.stages, - ); + for (const opponent of pokemon.getOpponents()) { + const cancelled = new BooleanHolder(false); + if (this.intimidate) { + const params: AbAttrParamsWithCancel = { pokemon: opponent, cancelled, simulated }; + applyAbAttrs("IntimidateImmunityAbAttr", params); + applyAbAttrs("PostIntimidateStatStageChangeAbAttr", params); + + if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { + cancelled.value = true; } } + if (!cancelled.value) { + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + opponent.getBattlerIndex(), + false, + this.stats, + this.stages, + ); + } } } } @@ -3550,11 +2818,11 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { globalScene.phaseManager.unshiftNew( @@ -3581,11 +2849,11 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { * @returns if the move was successful */ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { for (const s of BATTLE_STATS) { @@ -3605,8 +2873,6 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { * Download raises either the Attack stat or Special Attack stat by one stage depending on the foe's currently lowest defensive stat: * it will raise Attack if the foe's current Defense is lower than its current Special Defense stat; * otherwise, it will raise Special Attack. - * @extends PostSummonAbAttr - * @see {applyPostSummon} */ export class DownloadAbAttr extends PostSummonAbAttr { private enemyDef: number; @@ -3614,7 +2880,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { private enemyCountTally: number; private stats: BattleStat[]; - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -3632,11 +2898,8 @@ export class DownloadAbAttr extends PostSummonAbAttr { /** * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account * vitamins and items, so it needs to use the Stat and the stat alone. - * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. - * @param {boolean} _passive N/A - * @param {any[]} _args N/A */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (this.enemyDef < this.enemySpDef) { this.stats = [Stat.ATK]; } else { @@ -3658,7 +2921,7 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { const weatherReplaceable = this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || @@ -3667,7 +2930,7 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -3683,11 +2946,11 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -3709,12 +2972,13 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { this.immuneEffects = immuneEffects; } - public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + public override canApply({ pokemon }: AbAttrBaseParams): boolean { const status = pokemon.status?.effect; return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status)); } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply({ pokemon }: AbAttrBaseParams): void { + // TODO: should probably check against simulated... const status = pokemon.status?.effect; if (!isNullOrUndefined(status)) { this.statusHealed = status; @@ -3723,9 +2987,9 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { } } - public override getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + public override getTriggerMessage({ pokemon }: AbAttrBaseParams): string | null { if (this.statusHealed) { - return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(_pokemon)); + return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(pokemon)); } return null; } @@ -3740,11 +3004,11 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { this.formFunc = formFunc; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -3756,7 +3020,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -3782,7 +3046,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return true; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.setTempAbility(this.target!.getAbility()); setAbilityRevealed(this.target!); @@ -3790,7 +3054,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }, _abilityName: string): string { return i18next.t("abilityTriggers:trace", { pokemonName: getPokemonNameWithAffix(pokemon), targetName: getPokemonNameWithAffix(this.target), @@ -3814,31 +3078,28 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt this.statusEffect = statusEffect; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); return party.filter(p => p.isAllowedInBattle()).length > 0; } /** * Removes supplied status effect from the user's field when user of the ability is summoned. - * - * @param pokemon - The Pokémon that triggered the ability. - * @param _passive - n/a - * @param _args - n/a */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); - if (!simulated) { - for (const pokemon of allowedParty) { - if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - globalScene.phaseManager.queueMessage( - getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), - ); - pokemon.resetStatus(false); - pokemon.updateInfo(); - } + for (const pokemon of allowedParty) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), + ); + pokemon.resetStatus(false); + pokemon.updateInfo(); } } } @@ -3846,7 +3107,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (!globalScene.currentBattle.double) { return false; } @@ -3855,9 +3116,12 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return !(isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const ally = pokemon.getAlly(); - if (!simulated && !isNullOrUndefined(ally)) { + if (!isNullOrUndefined(ally)) { for (const s of BATTLE_STATS) { pokemon.setStatStage(s, ally.getStatStage(s)); } @@ -3865,7 +3129,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AbAttrBaseParams, _abilityName: string): string { return i18next.t("abilityTriggers:costar", { pokemonName: getPokemonNameWithAffix(pokemon), allyName: getPokemonNameWithAffix(pokemon.getAlly()), @@ -3877,60 +3141,40 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { * Attribute used by {@linkcode AbilityId.IMPOSTER} to transform into a random opposing pokemon on entry. */ export class PostSummonTransformAbAttr extends PostSummonAbAttr { + private targetIndex: BattlerIndex = BattlerIndex.ATTACKER; constructor() { super(true, false); } - private getTarget(targets: Pokemon[]): Pokemon { - let target: Pokemon = targets[0]; - if (targets.length > 1) { - globalScene.executeWithSeedOffset(() => { - // in a double battle, if one of the opposing pokemon is fused the other one will be chosen - // if both are fused, then Imposter will fail below - if (targets[0].fusionSpecies) { - target = targets[1]; - return; - } - if (targets[1].fusionSpecies) { - target = targets[0]; - return; - } - target = randSeedItem(targets); - }, globalScene.currentBattle.waveIndex); - } else { - target = targets[0]; + /** + * Return the correct opponent for Imposter to copy, barring enemies with fusions, substitutes and illusions. + * @param user - The {@linkcode Pokemon} with this ability. + * @returns The {@linkcode Pokemon} to transform into, or `undefined` if none are eligible. + * @remarks + * This sets the private `targetIndex` field to the target's {@linkcode BattlerIndex} on success. + */ + private getTarget(user: Pokemon): Pokemon | undefined { + // As opposed to the mainline behavior of "always copy the opposite slot", + // PKR Imposter instead attempts to copy a random eligible opposing Pokemon meeting Transform's criteria. + // If none are eligible to copy, it will not activate. + const targets = user.getOpponents().filter(opp => user.canTransformInto(opp)); + if (targets.length === 0) { + return undefined; } - target = target!; - - return target; + const mon = targets[user.randBattleSeedInt(targets.length)]; + this.targetIndex = mon.getBattlerIndex(); + return mon; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { - const targets = pokemon.getOpponents(); - const target = this.getTarget(targets); + override canApply({ pokemon }: AbAttrBaseParams): boolean { + const target = this.getTarget(pokemon); - if (target.summonData.illusion) { - return false; - } - - if (simulated || !targets.length) { - return simulated; - } - - // transforming from or into fusion pokemon causes various problems (including crashes and save corruption) - return !(this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies); + return !!target; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { - const target = this.getTarget(pokemon.getOpponents()); - - globalScene.phaseManager.unshiftNew( - "PokemonTransformPhase", - pokemon.getBattlerIndex(), - target.getBattlerIndex(), - true, - ); + override apply({ pokemon }: AbAttrBaseParams): void { + globalScene.phaseManager.unshiftNew("PokemonTransformPhase", pokemon.getBattlerIndex(), this.targetIndex, true); } } @@ -3940,17 +3184,14 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { * @extends PostSummonAbAttr */ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} - * @param {Pokemon} _pokemon the Pokemon with this ability - * @param _passive n/a - * @param _args n/a */ - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChangesToNormal(); } @@ -3973,28 +3214,20 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { /** * Determine if the pokemon has a forme change that is triggered by the weather - * - * @param pokemon - The pokemon with the forme change ability - * @param _passive - unused - * @param _simulated - unused - * @param _args - unused */ - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !!pokemonFormChanges[pokemon.species.speciesId]?.some( fc => fc.findTrigger(SpeciesFormChangeWeatherTrigger) && fc.canChange(pokemon), ); } /** - * Trigger the pokemon's forme change by invoking - * {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} - * - * @param pokemon - The Pokemon with this ability - * @param _passive - unused - * @param simulated - unused - * @param _args - unused + * Calls the {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} for both + * {@linkcode SpeciesFormChangeWeatherTrigger} and + * {@linkcode SpeciesFormChangeRevertWeatherFormTrigger} if it + * is the specific Pokemon and ability */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger); } @@ -4004,7 +3237,7 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { /** * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Commander_(Ability) | Commander}. * When the source of an ability with this attribute detects a Dondozo as their active ally, the source "jumps - * into the Dondozo's mouth," sharply boosting the Dondozo's stats, cancelling the source's moves, and + * into the Dondozo's mouth", sharply boosting the Dondozo's stats, cancelling the source's moves, and * causing attacks that target the source to always miss. */ export class CommanderAbAttr extends AbAttr { @@ -4012,7 +3245,7 @@ export class CommanderAbAttr extends AbAttr { super(true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { // If the ally Dondozo is fainted or was previously "commanded" by // another Pokemon, this effect cannot apply. @@ -4026,7 +3259,7 @@ export class CommanderAbAttr extends AbAttr { ); } - override apply(pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: null, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { // Lapse the source's semi-invulnerable tags (to avoid visual inconsistencies) pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); @@ -4040,28 +3273,34 @@ export class CommanderAbAttr extends AbAttr { } } -export class PreSwitchOutAbAttr extends AbAttr { +/** + * Base class for ability attributes that apply their effect when their user switches out. + */ +export abstract class PreSwitchOutAbAttr extends AbAttr { constructor(showAbility = true) { super(showAbility); } - canApplyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } +/** + * Resets all status effects on the user when it switches out. + */ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { constructor() { super(false); } - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !isNullOrUndefined(pokemon.status); } - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.resetStatus(); pokemon.updateInfo(); @@ -4073,13 +3312,8 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { - /** - * @param pokemon The {@linkcode Pokemon} with the ability - * @param _passive N/A - * @param _args N/A - * @returns {boolean} Returns true if the weather clears, otherwise false. - */ - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override apply({ pokemon, simulated }: AbAttrBaseParams): boolean { + // TODO: Evaluate why this is returning a boolean rather than relay const weatherType = globalScene.arena.weather?.weatherType; let turnOffWeather = false; @@ -4134,11 +3368,11 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isFullHp(); } - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { const healAmount = toDmgValue(pokemon.getMaxHp() * 0.33); pokemon.heal(healAmount); @@ -4149,7 +3383,6 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { /** * Attribute for form changes that occur on switching out - * @extends PreSwitchOutAbAttr * @see {@linkcode applyPreSwitchOut} */ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { @@ -4161,36 +3394,36 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { this.formFunc = formFunc; } - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } /** * On switch out, trigger the form change to the one defined in the ability - * @param pokemon The pokemon switching out and changing form {@linkcode Pokemon} - * @param _passive N/A - * @param _args N/A */ - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } } +/** + * Base class for ability attributes that apply their effect just before the user leaves the field + */ export class PreLeaveFieldAbAttr extends AbAttr { - canApplyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { - override canApplyPreLeaveField(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const weatherType = globalScene.arena.weather?.weatherType; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. switch (weatherType) { @@ -4231,12 +3464,7 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { return false; } - /** - * @param _pokemon The {@linkcode Pokemon} with the ability - * @param _passive N/A - * @param _args N/A - */ - override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE); } @@ -4245,47 +3473,49 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { /** * Updates the active {@linkcode SuppressAbilitiesTag} when a pokemon with {@linkcode AbilityId.NEUTRALIZING_GAS} leaves the field + * + * @sealed */ export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFieldAbAttr { constructor() { super(false); } - public override canApplyPreLeaveField( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _args: any[], - ): boolean { + public override canApply(_params: AbAttrBaseParams): boolean { return !!globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS); } - public override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply(_params: AbAttrBaseParams): void { const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; suppressTag.onSourceLeave(globalScene.arena); } } -export class PreStatStageChangeAbAttr extends AbAttr { - canApplyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { +export interface PreStatStageChangeAbAttrParams extends AbAttrBaseParams { + /** The stat being affected by the stat stage change */ + stat: BattleStat; + /** The amount of stages to change by (negative if the stat is being decreased) */ + stages: number; + /** + * The source of the stat stage drop. May be omitted if the source of the stat drop is the user itself. + * + * @remarks + * Currently, only used by {@linkcode ReflectStatStageChangeAbAttr} in order to reflect the stat stage change + */ + source?: Pokemon; + /** Holder that will be set to true if the stat stage change should be cancelled due to the ability */ + cancelled: BooleanHolder; +} + +/** + * Base class for ability attributes that apply their effect before a stat stage change. + */ +export abstract class PreStatStageChangeAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -4296,30 +3526,22 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { /** {@linkcode BattleStat} to reflect */ private reflectedStat?: BattleStat; + override canApply({ source, cancelled }: PreStatStageChangeAbAttrParams): boolean { + return !!source && !cancelled.value; + } + /** * Apply the {@linkcode ReflectStatStageChangeAbAttr} to an interaction - * @param _pokemon The user pokemon - * @param _passive N/A - * @param simulated `true` if the ability is being simulated by the AI - * @param stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode BooleanHolder} that will be set to true due to reflection - * @param args */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: any[], - ): void { - const attacker: Pokemon = args[0]; - const stages = args[1]; + override apply({ source, cancelled, stat, simulated, stages }: PreStatStageChangeAbAttrParams): void { + if (!source) { + return; + } this.reflectedStat = stat; if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", - attacker.getBattlerIndex(), + source.getBattlerIndex(), false, [stat], stages, @@ -4333,7 +3555,7 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: PreStatStageChangeAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -4355,38 +3577,18 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } - override canApplyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ stat }: PreStatStageChangeAbAttrParams): boolean { return isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; } /** * Apply the {@linkcode ProtectedStatAbAttr} to an interaction - * @param _pokemon - * @param _passive - * @param simulated - * @param _stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode BooleanHolder} that will be set to true if the stat is protected - * @param _args */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreStatStageChangeAbAttrParams): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PreStatStageChangeAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -4395,85 +3597,60 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { } } +export interface ConfusionOnStatusEffectAbAttrParams extends AbAttrBaseParams { + /** The status effect that was applied */ + effect: StatusEffect; + /** The move that applied the status effect */ + move: Move; + /** The opponent that was inflicted with the status effect */ + opponent: Pokemon; +} + /** * This attribute applies confusion to the target whenever the user * directly poisons them with a move, e.g. Poison Puppeteer. * Called in {@linkcode StatusEffectAttr}. - * @extends PostAttackAbAttr - * @see {@linkcode applyPostAttack} */ -export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { +export class ConfusionOnStatusEffectAbAttr extends AbAttr { /** List of effects to apply confusion after */ - private effects: StatusEffect[]; + private effects: ReadonlySet; constructor(...effects: StatusEffect[]) { - /** This effect does not require a damaging move */ - super((_user, _target, _move) => true); - this.effects = effects; + super(); + this.effects = new Set(effects); } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { - return ( - super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && - this.effects.indexOf(args[0]) > -1 && - !defender.isFainted() && - defender.canAddTag(BattlerTagType.CONFUSED) - ); + /** + * @returns Whether the ability can apply confusion to the opponent + */ + override canApply({ opponent, effect }: ConfusionOnStatusEffectAbAttrParams): boolean { + return this.effects.has(effect) && !opponent.isFainted() && opponent.canAddTag(BattlerTagType.CONFUSED); } /** * Applies confusion to the target pokemon. - * @param pokemon {@link Pokemon} attacking - * @param _passive N/A - * @param defender {@link Pokemon} defending - * @param move {@link Move} used to apply status effect and confusion - * @param _hitResult N/A - * @param _args [0] {@linkcode StatusEffect} applied by move */ - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent, simulated, pokemon, move }: ConfusionOnStatusEffectAbAttrParams): void { if (!simulated) { - defender.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, defender.id); + opponent.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, opponent.id); } } } +export interface PreSetStatusAbAttrParams extends AbAttrBaseParams { + /** The status effect being applied */ + effect: StatusEffect; + /** Holds whether the status effect is prevented by the ability */ + cancelled: BooleanHolder; +} + export class PreSetStatusAbAttr extends AbAttr { /** Return whether the ability attribute can be applied */ - canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _effect: StatusEffect | undefined, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _effect: StatusEffect | undefined, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -4481,7 +3658,6 @@ export class PreSetStatusAbAttr extends AbAttr { */ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { protected immuneEffects: StatusEffect[]; - private lastEffect: StatusEffect; /** * @param immuneEffects - The status effects to which the Pokémon is immune. @@ -4492,44 +3668,23 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { this.immuneEffects = immuneEffects; } - override canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ effect }: PreSetStatusAbAttrParams): boolean { return (effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || this.immuneEffects.includes(effect); } /** * Applies immunity to supplied status effects. - * - * @param _pokemon - The Pokémon to which the status is being applied. - * @param _passive - n/a - * @param effect - The status effect being applied. - * @param cancelled - A holder for a boolean value indicating if the status application was cancelled. - * @param _args - n/a */ - override applyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreSetStatusAbAttrParams): void { cancelled.value = true; - this.lastEffect = effect; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon, effect }: PreSetStatusAbAttrParams, abilityName: string): string { return this.immuneEffects.length ? i18next.t("abilityTriggers:statusEffectImmunityWithName", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statusEffectName: getStatusEffectDescriptor(this.lastEffect), + statusEffectName: getStatusEffectDescriptor(effect), }) : i18next.t("abilityTriggers:statusEffectImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), @@ -4538,63 +3693,98 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { } } +// NOTE: There is a good amount of overlapping code between this +// and PreSetStatusEffectImmunity. However, we need these classes to be distinct +// as this one's apply method requires additional parameters +// TODO: Find away to avoid the code duplication without sacrificing +// the subclass split /** * Provides immunity to status effects to the user. - * @extends PreSetStatusEffectImmunityAbAttr */ export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} -/** - * Provides immunity to status effects to the user's field. - * @extends PreSetStatusEffectImmunityAbAttr - */ -export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} +export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBaseParams { + /** The status effect being applied */ + effect: StatusEffect; + /** Holds whether the status effect is prevented by the ability */ + cancelled: BooleanHolder; + /** The target of the status effect */ + target: Pokemon; + // TODO: It may be the case that callers are passing `null` in the case that the pokemon setting the status is the same as the target. + // Evaluate this and update the tsdoc accordingly. + /** The source of the status effect, or null if it is not coming from a pokemon */ + source: Pokemon | null; +} /** - * Conditionally provides immunity to status effects to the user's field. + * Provides immunity to status effects to the user's field. + */ +export class UserFieldStatusEffectImmunityAbAttr extends AbAttr { + protected immuneEffects: StatusEffect[]; + constructor(...immuneEffects: StatusEffect[]) { + super(); + + this.immuneEffects = immuneEffects; + } + + override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean { + return ( + (!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || + this.immuneEffects.includes(effect) + ); + } + + /** + * Set the `cancelled` value to true, indicating that the status effect is prevented. + */ + override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void { + cancelled.value = true; + } +} + +/** + * Conditionally provides immunity to status effects for the user's field. * * Used by {@linkcode AbilityId.FLOWER_VEIL | Flower Veil}. - * @extends UserFieldStatusEffectImmunityAbAttr - * */ export class ConditionalUserFieldStatusEffectImmunityAbAttr extends UserFieldStatusEffectImmunityAbAttr { /** * The condition for the field immunity to be applied. - * @param target The target of the status effect - * @param source The source of the status effect + * @param target - The target of the status effect + * @param source - The source of the status effect */ - protected condition: (target: Pokemon, source: Pokemon | null) => boolean; - - /** - * Evaluate the condition to determine if the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} can be applied. - * @param _pokemon The pokemon with the ability - * @param _passive unused - * @param _simulated Whether the ability is being simulated - * @param effect The status effect being applied - * @param cancelled Holds whether the status effect was cancelled by a prior effect - * @param args `Args[0]` is the target of the status effect, `Args[1]` is the source. - * @returns Whether the ability can be applied to cancel the status effect. - */ - override canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - cancelled: BooleanHolder, - args: [Pokemon, Pokemon | null, ...any], - ): boolean { - return ( - ((!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || - this.immuneEffects.includes(effect)) && - this.condition(args[0], args[1]) - ); - } + private condition: (target: Pokemon, source: Pokemon | null) => boolean; constructor(condition: (target: Pokemon, source: Pokemon | null) => boolean, ...immuneEffects: StatusEffect[]) { super(...immuneEffects); this.condition = condition; } + + /** + * Evaluate the condition to determine if the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} can be applied. + * @returns Whether the ability can be applied to cancel the status effect. + */ + override canApply(params: UserFieldStatusEffectImmunityAbAttrParams): boolean { + return this.condition(params.target, params.source) && super.canApply(params); + } + + /** + * Set the `cancelled` value to true, indicating that the status effect is prevented. + */ + override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void { + cancelled.value = true; + } +} + +export interface ConditionalUserFieldProtectStatAbAttrParams extends AbAttrBaseParams { + /** The stat being affected by the stat stage change */ + stat: BattleStat; + /** Holds whether the stat stage change is prevented by the ability */ + cancelled: BooleanHolder; + // TODO: consider making this required and not inherit from PreStatStageChangeAbAttr + /** The target of the stat stage change */ + target?: Pokemon; } /** @@ -4615,24 +3805,9 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA } /** - * Determine whether the {@linkcode ConditionalUserFieldProtectStatAbAttr} can be applied. - * @param _pokemon The pokemon with the ability - * @param _passive unused - * @param _simulated Unused - * @param stat The stat being affected - * @param cancelled Holds whether the stat change was already prevented. - * @param args Args[0] is the target pokemon of the stat change. - * @returns `true` if the ability can be applied + * @returns Whether the ability can be used to cancel the stat stage change. */ - override canApplyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: [Pokemon, ...any], - ): boolean { - const target = args[0]; + override canApply({ stat, cancelled, target }: ConditionalUserFieldProtectStatAbAttrParams): boolean { if (!target) { return false; } @@ -4645,53 +3820,37 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA /** * Apply the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} to an interaction - * @param _pokemon The pokemon the stat change is affecting (unused) - * @param _passive unused - * @param _simulated unused - * @param stat The stat being affected - * @param cancelled Will be set to true if the stat change is prevented - * @param _args unused */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: ConditionalUserFieldProtectStatAbAttrParams): void { cancelled.value = true; } } -export class PreApplyBattlerTagAbAttr extends AbAttr { - canApplyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { - return true; - } - - applyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - _cancelled: BooleanHolder, - _args: any[], - ): void {} +export interface PreApplyBattlerTagAbAttrParams extends AbAttrBaseParams { + /** The tag being applied */ + tag: BattlerTag; + /** Holds whether the tag is prevented by the ability */ + cancelled: BooleanHolder; } /** - * Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets. + * Base class for ability attributes that apply their effect before a BattlerTag {@linkcode BattlerTag} is applied. + * + * ⚠️ Subclasses violate Liskov Substitution Principle, so this class must not be provided to {@linkcode applyAbAttrs} */ -export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { +export abstract class PreApplyBattlerTagAbAttr extends AbAttr { + canApply(_params: PreApplyBattlerTagAbAttrParams): boolean { + return true; + } + + apply(_params: PreApplyBattlerTagAbAttrParams): void {} +} + +// Intentionally not exported because this shouldn't be able to be passed to `applyAbAttrs`. It only exists so that +// PreApplyBattlerTagImmunityAbAttr and UserFieldPreApplyBattlerTagImmunityAbAttr can avoid code duplication +// while preserving type safety. (Since the UserField version require an additional parameter, target, in its apply methods) +abstract class BaseBattlerTagImmunityAbAttr

extends PreApplyBattlerTagAbAttr { protected immuneTagTypes: BattlerTagType[]; - protected battlerTag: BattlerTag; constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) { super(true); @@ -4699,75 +3858,57 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { this.immuneTagTypes = coerceArray(immuneTagTypes); } - override canApplyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - _args: any[], - ): boolean { - this.battlerTag = tag; - + override canApply({ cancelled, tag }: P): boolean { return !cancelled.value && this.immuneTagTypes.includes(tag.tagType); } - override applyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: P): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon, tag }: P, abilityName: string): string { return i18next.t("abilityTriggers:battlerTagImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - battlerTagName: this.battlerTag.getDescriptor(), + battlerTagName: tag.getDescriptor(), }); } } +// TODO: The battler tag ability attributes are in dire need of improvement +// It is unclear why there is a `PreApplyBattlerTagImmunityAbAttr` class that isn't used, +// and then why there's a BattlerTagImmunityAbAttr class as well. + +/** + * Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets. + * + * This does not check whether the tag is already applied; that check should happen in the caller. + */ +export class PreApplyBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr {} + /** * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user. - * @extends PreApplyBattlerTagImmunityAbAttr */ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} +export interface UserFieldBattlerTagImmunityAbAttrParams extends PreApplyBattlerTagAbAttrParams { + /** The pokemon that the battler tag is being applied to */ + target: Pokemon; +} /** * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user's field. - * @extends PreApplyBattlerTagImmunityAbAttr */ -export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} +export class UserFieldBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr {} export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattlerTagImmunityAbAttr { private condition: (target: Pokemon) => boolean; /** * Determine whether the {@linkcode ConditionalUserFieldBattlerTagImmunityAbAttr} can be applied by passing the target pokemon to the condition. - * @param pokemon The pokemon owning the ability - * @param passive unused - * @param simulated whether the ability is being simulated (unused) - * @param tag The {@linkcode BattlerTag} being applied - * @param cancelled Holds whether the tag was previously cancelled (unused) - * @param args Args[0] is the target that the tag is attempting to be applied to * @returns Whether the ability can be used to cancel the battler tag */ - override canApplyPreApplyBattlerTag( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - args: [Pokemon, ...any], - ): boolean { - return ( - super.canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args) && this.condition(args[0]) - ); + override canApply(params: UserFieldBattlerTagImmunityAbAttrParams): boolean { + return super.canApply(params) && this.condition(params.target); } constructor(condition: (target: Pokemon) => boolean, immuneTagTypes: BattlerTagType | BattlerTagType[]) { @@ -4777,6 +3918,13 @@ export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattl } } +export interface BlockCritAbAttrParams extends AbAttrBaseParams { + /** + * Holds a boolean that will be set to `true` if the user's ability prevents the attack from being a critical hit + */ + readonly blockCrit: BooleanHolder; +} + export class BlockCritAbAttr extends AbAttr { constructor() { super(false); @@ -4784,19 +3932,17 @@ export class BlockCritAbAttr extends AbAttr { /** * Apply the block crit ability by setting the value in the provided boolean holder to `true`. - * @param args - `[0]`: A {@linkcode BooleanHolder} containing whether the attack is prevented from critting. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: [BooleanHolder], - ): void { - args[0].value = true; + override apply({ blockCrit }: BlockCritAbAttrParams): void { + blockCrit.value = true; } } +export interface BonusCritAbAttrParams extends AbAttrBaseParams { + /** Holds the crit stage that may be modified by ability application */ + critStage: NumberHolder; +} + export class BonusCritAbAttr extends AbAttr { constructor() { super(false); @@ -4804,24 +3950,17 @@ export class BonusCritAbAttr extends AbAttr { /** * Apply the bonus crit ability by increasing the value in the provided number holder by 1 - * - * @param _pokemon The pokemon with the BonusCrit ability (unused) - * @param _passive Unused - * @param _simulated Unused - * @param _cancelled Unused - * @param args Args[0] is a number holder containing the crit stage. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: [NumberHolder, ...any], - ): void { - (args[0] as NumberHolder).value += 1; + override apply({ critStage }: BonusCritAbAttrParams): void { + critStage.value += 1; } } +export interface MultCritAbAttrParams extends AbAttrBaseParams { + /** The critical hit multiplier that may be modified by ability application */ + critMult: NumberHolder; +} + export class MultCritAbAttr extends AbAttr { public multAmount: number; @@ -4831,27 +3970,26 @@ export class MultCritAbAttr extends AbAttr { this.multAmount = multAmount; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - const critMult = args[0] as NumberHolder; + override canApply({ critMult }: MultCritAbAttrParams): boolean { return critMult.value > 1; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const critMult = args[0] as NumberHolder; + override apply({ critMult }: MultCritAbAttrParams): void { critMult.value *= this.multAmount; } } +export interface ConditionalCritAbAttrParams extends AbAttrBaseParams { + /** Holds a boolean that will be set to true if the attack is guaranteed to crit */ + target: Pokemon; + /** The move being used */ + move: Move; + /** Holds whether the attack will critically hit */ + isCritical: BooleanHolder; +} + /** * Guarantees a critical hit according to the given condition, except if target prevents critical hits. ie. Merciless - * @extends AbAttr - * @see {@linkcode apply} */ export class ConditionalCritAbAttr extends AbAttr { private condition: PokemonAttackCondition; @@ -4862,26 +4000,12 @@ export class ConditionalCritAbAttr extends AbAttr { this.condition = condition; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - const target = args[1] as Pokemon; - const move = args[2] as Move; - return this.condition(pokemon, target, move); + override canApply({ isCritical, pokemon, target, move }: ConditionalCritAbAttrParams): boolean { + return !isCritical.value && this.condition(pokemon, target, move); } - /** - * @param _pokemon {@linkcode Pokemon} user. - * @param args [0] {@linkcode BooleanHolder} If true critical hit is guaranteed. - * [1] {@linkcode Pokemon} Target. - * [2] {@linkcode Move} used by ability user. - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as BooleanHolder).value = true; + override apply({ isCritical }: ConditionalCritAbAttrParams): void { + isCritical.value = true; } } @@ -4890,13 +4014,7 @@ export class BlockNonDirectDamageAbAttr extends AbAttr { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } @@ -4908,7 +4026,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { private effects: StatusEffect[]; /** - * @param {StatusEffect[]} effects The status effect(s) that will be blocked from damaging the ability pokemon + * @param effects - The status effect(s) that will be blocked from damaging the ability pokemon */ constructor(...effects: StatusEffect[]) { super(false); @@ -4916,51 +4034,42 @@ export class BlockStatusDamageAbAttr extends AbAttr { this.effects = effects; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrParamsWithCancel): boolean { return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect); } - /** - * @param {Pokemon} _pokemon The pokemon with the ability - * @param {boolean} _passive N/A - * @param {BooleanHolder} cancelled Whether to cancel the status damage - * @param {any[]} _args N/A - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } export class BlockOneHitKOAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ChangeMovePriorityAbAttrParams extends AbAttrBaseParams { + /** The move being used */ + move: Move; + /** The priority of the move being used */ + priority: NumberHolder; +} + /** * This governs abilities that alter the priority of moves * Abilities: Prankster, Gale Wings, Triage, Mycelium Might, Stall * Note - Quick Claw has a separate and distinct implementation outside of priority + * + * @sealed */ export class ChangeMovePriorityAbAttr extends AbAttr { private moveFunc: (pokemon: Pokemon, move: Move) => boolean; private changeAmount: number; /** - * @param {(pokemon, move) => boolean} moveFunc applies priority-change to moves within a provided category - * @param {number} changeAmount the amount of priority added or subtracted + * @param moveFunc - applies priority-change to moves that meet the condition + * @param changeAmount - The amount of priority added or subtracted */ constructor(moveFunc: (pokemon: Pokemon, move: Move) => boolean, changeAmount: number) { super(false); @@ -4969,46 +4078,39 @@ export class ChangeMovePriorityAbAttr extends AbAttr { this.changeAmount = changeAmount; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.moveFunc(pokemon, args[0] as Move); + override canApply({ pokemon, move }: ChangeMovePriorityAbAttrParams): boolean { + return this.moveFunc(pokemon, move); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[1] as NumberHolder).value += this.changeAmount; + override apply({ priority }: ChangeMovePriorityAbAttrParams): void { + priority.value += this.changeAmount; } } -export class IgnoreContactAbAttr extends AbAttr {} +export class IgnoreContactAbAttr extends AbAttr { + private declare readonly _: never; +} -export class PreWeatherEffectAbAttr extends AbAttr { - canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { +/** + * Shared interface for attributes that respond to a weather. + */ +export interface PreWeatherEffectAbAttrParams extends AbAttrParamsWithCancel { + /** The weather effect for the interaction. `null` is treated as no weather */ + weather: Weather | null; +} + +export abstract class PreWeatherEffectAbAttr extends AbAttr { + override canApply(_params: Closed): boolean { return true; } - applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + override apply(_params: Closed): void {} } -export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr {} +/** + * Base class for abilities that apply an effect before a weather effect is applied. + */ +export abstract class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr {} export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { private weatherTypes: WeatherType[]; @@ -5019,57 +4121,36 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - override canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: Weather, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { - return !this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1; + override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean { + if (!weather) { + return false; + } + const weatherType = weather.weatherType; + return !this.weatherTypes.length || this.weatherTypes.includes(weatherType); } - override applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreWeatherEffectAbAttrParams): void { cancelled.value = true; } } export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { - public affectsImmutable: boolean; + public readonly affectsImmutable: boolean; - constructor(affectsImmutable?: boolean) { + constructor(affectsImmutable = false) { super(true); - this.affectsImmutable = !!affectsImmutable; + this.affectsImmutable = affectsImmutable; } - override canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: Weather, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean { + if (!weather) { + return false; + } return this.affectsImmutable || weather.isImmutable(); } - override applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreWeatherEffectAbAttrParams): void { cancelled.value = true; } } @@ -5187,12 +4268,18 @@ function getOncePerBattleCondition(ability: AbilityId): AbAttrCondition { }; } +/** + * @sealed + */ export class ForewarnAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { + if (!simulated) { + return; + } let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -5220,23 +4307,26 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } } - if (!simulated) { - globalScene.phaseManager.queueMessage( - i18next.t("abilityTriggers:forewarn", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - moveName: maxMove, - }), - ); - } + + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:forewarn", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + moveName: maxMove, + }), + ); } } +/** + * Ability attribute that reveals the abilities of all opposing Pokémon when the Pokémon with this ability is summoned. + * @sealed + */ export class FriskAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { for (const opponent of pokemon.getOpponents()) { globalScene.phaseManager.queueMessage( @@ -5252,30 +4342,27 @@ export class FriskAbAttr extends PostSummonAbAttr { } } -export class PostWeatherChangeAbAttr extends AbAttr { - canApplyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): boolean { +export interface PostWeatherChangeAbAttrParams extends AbAttrBaseParams { + /** The kind of the weather that was just changed to */ + weather: WeatherType; +} + +/** + * Base class for ability attributes that apply their effect after a weather change. + */ +export abstract class PostWeatherChangeAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** * Triggers weather-based form change when weather changes. * Used by Forecast and Flower Gift. - * @extends PostWeatherChangeAbAttr + * + * @sealed */ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { private ability: AbilityId; @@ -5288,13 +4375,7 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { this.formRevertingWeathers = formRevertingWeathers; } - override canApplyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const isCastformWithForecast = pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST; const isCherrimWithFlowerGift = @@ -5306,23 +4387,15 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} - * @param _pokemon - The Pokemon with this ability - * @param _passive - unused - * @param simulated - unused - * @param _weather - unused - * @param _args - unused */ - override applyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void { + override apply({ simulated }: AbAttrBaseParams): void { if (simulated) { return; } + // TODO: investigate why this is not using the weatherType parameter + // and is instead reading the weather from the global scene + const weatherType = globalScene.arena.weather?.weatherType; if (weatherType && this.formRevertingWeathers.includes(weatherType)) { @@ -5333,6 +4406,10 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { } } +/** + * Add a battler tag to the pokemon when the weather changes. + * @sealed + */ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr { private tagType: BattlerTagType; private turnCount: number; @@ -5346,29 +4423,18 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - override canApplyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: WeatherType, - _args: any[], - ): boolean { - return !!this.weatherTypes.find(w => weather === w) && pokemon.canAddTag(this.tagType); + override canApply({ weather, pokemon }: PostWeatherChangeAbAttrParams): boolean { + return this.weatherTypes.includes(weather) && pokemon.canAddTag(this.tagType); } - override applyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostWeatherChangeAbAttrParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } } } +export type PostWeatherLapseAbAttrParams = Omit; export class PostWeatherLapseAbAttr extends AbAttr { protected weatherTypes: WeatherType[]; @@ -5378,23 +4444,11 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - canApplyPostWeatherLapse( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostWeatherLapse( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): void {} + apply(_params: Closed): void {} getCondition(): AbAttrCondition { return getWeatherCondition(...this.weatherTypes); @@ -5410,23 +4464,11 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - override canApplyPostWeatherLapse( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + override canApply({ pokemon }: PostWeatherLapseAbAttrParams): boolean { return !pokemon.isFullHp(); } - override applyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - _weather: Weather, - _args: any[], - ): void { + override apply({ pokemon, passive, simulated }: PostWeatherLapseAbAttrParams): void { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { globalScene.phaseManager.unshiftNew( @@ -5452,23 +4494,11 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - override canApplyPostWeatherLapse( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + override canApply({ pokemon }: PostWeatherLapseAbAttrParams): boolean { return !pokemon.hasAbilityWithAttr("BlockNonDirectDamageAbAttr"); } - override applyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - _weather: Weather, - _args: any[], - ): void { + override apply({ simulated, pokemon, passive }: PostWeatherLapseAbAttrParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.queueMessage( @@ -5484,24 +4514,17 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { } } +export interface PostTerrainChangeAbAttrParams extends AbAttrBaseParams { + /** The terrain type that is being changed to */ + terrain: TerrainType; +} + export class PostTerrainChangeAbAttr extends AbAttr { - canApplyPostTerrainChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostTerrainChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): void {} + apply(_params: Closed): void {} } export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr { @@ -5517,23 +4540,11 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - override canApplyPostTerrainChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - terrain: TerrainType, - _args: any[], - ): boolean { + override canApply({ pokemon, terrain }: PostTerrainChangeAbAttrParams): boolean { return !!this.terrainTypes.find(t => t === terrain) && pokemon.canAddTag(this.tagType); } - override applyPostTerrainChange( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PostTerrainChangeAbAttrParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -5548,21 +4559,23 @@ function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { } export class PostTurnAbAttr extends AbAttr { - canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * This attribute will heal 1/8th HP if the ability pokemon has the correct status. + * + * @sealed */ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { private effects: StatusEffect[]; /** - * @param {StatusEffect[]} effects The status effect(s) that will qualify healing the ability pokemon + * @param effects - The status effect(s) that will qualify healing the ability pokemon */ constructor(...effects: StatusEffect[]) { super(false); @@ -5570,16 +4583,11 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { this.effects = effects; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !isNullOrUndefined(pokemon.status) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); } - /** - * @param {Pokemon} pokemon The pokemon with the ability that will receive the healing - * @param {Boolean} passive N/A - * @param {any[]} _args N/A - */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, passive, pokemon }: AbAttrBaseParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -5594,8 +4602,10 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { } /** - * After the turn ends, resets the status of either the ability holder or their ally - * @param allyTarget Whether to target ally, defaults to false (self-target) + * After the turn ends, resets the status of either the user or their ally. + * @param allyTarget Whether to target the user's ally; default `false` (self-target) + * + * @sealed */ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { private allyTarget: boolean; @@ -5606,7 +4616,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { @@ -5617,7 +4627,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { return !!effect && effect !== StatusEffect.FAINT; } - override applyPostTurn(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated && this.target?.status) { globalScene.phaseManager.queueMessage( getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target)), @@ -5647,7 +4657,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { super(); } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { // Ensure we have at least 1 recoverable berry (at least 1 berry in berriesEaten is not capped) const cappedBerries = new Set( globalScene @@ -5667,7 +4677,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { return this.procChance(pokemon) >= pass; } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { this.createEatenBerry(pokemon); } @@ -5715,35 +4725,20 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { /** * Attribute to track and re-trigger last turn's berries at the end of the `BerryPhase`. + * Must only be used by Cud Chew! Do _not_ reuse this attribute for anything else * Used by {@linkcode AbilityId.CUD_CHEW}. + * @sealed */ -export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { +export class CudChewConsumeBerryAbAttr extends AbAttr { /** * @returns `true` if the pokemon ate anything last turn */ - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - // force ability popup for ability triggers on normal turns. - // Still not used if ability doesn't proc - this.showAbility = true; + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !!pokemon.summonData.berriesEatenLast.length; } - /** - * Cause this {@linkcode Pokemon} to regurgitate and eat all berries inside its `berriesEatenLast` array. - * Triggers a berry use animation, but does *not* count for other berry or item-related abilities. - * @param pokemon - The {@linkcode Pokemon} having a bad tummy ache - * @param _passive - N/A - * @param _simulated - N/A - * @param _cancelled - N/A - * @param _args - N/A - */ - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + override apply({ pokemon }: AbAttrBaseParams): void { + // TODO: Consider respecting the `simulated` flag globalScene.phaseManager.unshiftNew( "CommonAnimPhase", pokemon.getBattlerIndex(), @@ -5760,27 +4755,27 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { } // uncomment to make cheek pouch work with cud chew - // applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false)); + // applyAbAttrs("HealFromBerryUseAbAttr", {pokemon}); } +} - /** - * @returns always `true` as we always want to move berries into summon data - */ - override canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - this.showAbility = false; // don't show popup for turn end berry moving (should ideally be hidden) - return true; +/** + * Consume a berry at the end of the turn if the pokemon has one. + * + * Must be used in conjunction with {@linkcode CudChewConsumeBerryAbAttr}, and is + * only used by {@linkcode AbilityId.CUD_CHEW}. + */ +export class CudChewRecordBerryAbAttr extends PostTurnAbAttr { + constructor() { + super(false); } /** * Move this {@linkcode Pokemon}'s `berriesEaten` array from `PokemonTurnData` * into `PokemonSummonData` on turn end. * Both arrays are cleared on switch. - * @param pokemon - The {@linkcode Pokemon} having a nice snack - * @param _passive - N/A - * @param _simulated - N/A - * @param _args - N/A */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.summonData.berriesEatenLast = pokemon.turnData.berriesEaten; } } @@ -5794,16 +4789,14 @@ export class MoodyAbAttr extends PostTurnAbAttr { } /** * Randomly increases one stat stage by 2 and decreases a different stat stage by 1 - * @param {Pokemon} pokemon Pokemon that has this ability - * @param _passive N/A - * @param simulated true if applying in a simulated call. - * @param _args N/A - * * Any stat stages at +6 or -6 are excluded from being increased or decreased, respectively * If the pokemon already has all stat stages raised to 6, it will only decrease one stat stage by 1 * If the pokemon already has all stat stages lowered to -6, it will only increase one stat stage by 2 */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const canRaise = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) < 6); let canLower = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) > -6); @@ -5821,26 +4814,28 @@ export class MoodyAbAttr extends PostTurnAbAttr { } } +/** @sealed */ export class SpeedBoostAbAttr extends PostTurnAbAttr { constructor() { super(true); } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean { + // todo: Consider moving the `simulated` check to the `apply` method return simulated || (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway); } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.SPD], 1); } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isFullHp(); } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon, passive }: AbAttrBaseParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -5857,6 +4852,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { } } +/** @sealed */ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { private formFunc: (p: Pokemon) => number; @@ -5866,11 +4862,11 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { this.formFunc = formFunc; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -5879,9 +4875,10 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { /** * Attribute used for abilities (Bad Dreams) that damages the opponents for being asleep + * @sealed */ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon .getOpponents() .some( @@ -5891,26 +4888,26 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { !opp.switchOutStatus, ); } - /** - * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) - * @param pokemon {@linkcode Pokemon} with this ability - * @param _passive N/A - * @param simulated `true` if applying in a simulated call. - * @param _args N/A - */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + + /** Deal damage to all sleeping, on-field opponents equal to 1/8 of their max hp (min 1). */ + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } + for (const opp of pokemon.getOpponents()) { - if ( - (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && - !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") && - !opp.switchOutStatus - ) { - if (!simulated) { - opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); - globalScene.phaseManager.queueMessage( - i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }), - ); - } + if ((opp.status?.effect !== StatusEffect.SLEEP && !opp.hasAbility(AbilityId.COMATOSE)) || opp.switchOutStatus) { + continue; + } + + const cancelled = new BooleanHolder(false); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, simulated, cancelled }); + + if (!cancelled.value) { + opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }), + ); } } } @@ -5918,20 +4915,18 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { /** * Grabs the last failed Pokeball used - * @extends PostTurnAbAttr - * @see {@linkcode applyPostTurn} */ + * @sealed + * @see {@linkcode applyPostTurn} + */ export class FetchBallAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean { return !simulated && !isNullOrUndefined(globalScene.currentBattle.lastUsedPokeball) && !!pokemon.isPlayer; } /** * Adds the last used Pokeball back into the player's inventory - * @param pokemon {@linkcode Pokemon} with this ability - * @param _passive N/A - * @param _args N/A */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { const lastUsed = globalScene.currentBattle.lastUsedPokeball; globalScene.pokeballCounts[lastUsed!]++; globalScene.currentBattle.lastUsedPokeball = null; @@ -5944,7 +4939,9 @@ export class FetchBallAbAttr extends PostTurnAbAttr { } } -export class PostBiomeChangeAbAttr extends AbAttr {} +export class PostBiomeChangeAbAttr extends AbAttr { + private declare readonly _: never; +} export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { private weatherType: WeatherType; @@ -5955,23 +4952,18 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return (globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType); } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } } } +/** @sealed */ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { private terrainType: TerrainType; @@ -5981,47 +4973,35 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } } } +export interface PostMoveUsedAbAttrParams extends AbAttrBaseParams { + /** The move that was used */ + move: PokemonMove; + /** The source of the move */ + source: Pokemon; + /** The targets of the move */ + targets: BattlerIndex[]; +} + /** * Triggers just after a move is used either by the opponent or the player - * @extends AbAttr */ export class PostMoveUsedAbAttr extends AbAttr { - canApplyPostMoveUsed( - _pokemon: Pokemon, - _move: PokemonMove, - _source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostMoveUsed( - _pokemon: Pokemon, - _move: PokemonMove, - _source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -6029,14 +5009,7 @@ export class PostMoveUsedAbAttr extends AbAttr { * @extends PostMoveUsedAbAttr */ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { - override canApplyPostMoveUsed( - dancer: Pokemon, - _move: PokemonMove, - source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): boolean { + override canApply({ source, pokemon }: PostMoveUsedAbAttrParams): boolean { // List of tags that prevent the Dancer from replicating the move const forbiddenTags = [ BattlerTagType.FLYING, @@ -6046,40 +5019,28 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { ]; // The move to replicate cannot come from the Dancer return ( - source.getBattlerIndex() !== dancer.getBattlerIndex() && - !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) + source.getBattlerIndex() !== pokemon.getBattlerIndex() && + !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) ); } /** * Resolves the Dancer ability by replicating the move used by the source of the dance * either on the source itself or on the target of the dance - * @param dancer {@linkcode Pokemon} with Dancer ability - * @param move {@linkcode PokemonMove} Dancing move used by the source - * @param source {@linkcode Pokemon} that used the dancing move - * @param targets {@linkcode BattlerIndex}Targets of the dancing move - * @param _args N/A */ - override applyPostMoveUsed( - dancer: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated: boolean, - _args: any[], - ): void { + override apply({ source, pokemon, move, targets, simulated }: PostMoveUsedAbAttrParams): void { if (!simulated) { - dancer.turnData.extraTurns++; + pokemon.turnData.extraTurns++; // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove().is("AttackMove") || move.getMove().is("StatusMove")) { - const target = this.getTarget(dancer, source, targets); - globalScene.phaseManager.unshiftNew("MovePhase", dancer, target, move, MoveUseMode.INDIRECT); + const target = this.getTarget(pokemon, source, targets); + globalScene.phaseManager.unshiftNew("MovePhase", pokemon, target, move, MoveUseMode.INDIRECT); } else if (move.getMove().is("SelfStatusMove")) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself globalScene.phaseManager.unshiftNew( "MovePhase", - dancer, - [dancer.getBattlerIndex()], + pokemon, + [pokemon.getBattlerIndex()], move, MoveUseMode.INDIRECT, ); @@ -6090,9 +5051,9 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { /** * Get the correct targets of Dancer ability * - * @param dancer {@linkcode Pokemon} Pokemon with Dancer ability - * @param source {@linkcode Pokemon} Source of the dancing move - * @param targets {@linkcode BattlerIndex} Targets of the dancing move + * @param dancer - Pokemon with Dancer ability + * @param source - Source of the dancing move + * @param targets - Targets of the dancing move */ getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]): BattlerIndex[] { if (dancer.isPlayer()) { @@ -6107,16 +5068,15 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @extends AbAttr */ export class PostItemLostAbAttr extends AbAttr { - canApplyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Applies a Battler Tag to the Pokemon after it loses or consumes an item - * @extends PostItemLostAbAttr */ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { private tagType: BattlerTagType; @@ -6125,7 +5085,7 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { this.tagType = tagType; } - override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean { return !pokemon.getTag(this.tagType) && !simulated; } @@ -6134,11 +5094,15 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { * @param pokemon {@linkcode Pokemon} with this ability * @param _args N/A */ - override applyPostItemLost(pokemon: Pokemon, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.addTag(this.tagType); } } +export interface StatStageChangeMultiplierAbAttrParams extends AbAttrBaseParams { + /** Holder for the stages after applying the ability. */ + numStages: NumberHolder; +} export class StatStageChangeMultiplierAbAttr extends AbAttr { private multiplier: number; @@ -6148,32 +5112,27 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.multiplier; + override apply({ numStages }: StatStageChangeMultiplierAbAttrParams): void { + numStages.value *= this.multiplier; } } +export interface StatStageChangeCopyAbAttrParams extends AbAttrBaseParams { + /** The stats to change */ + stats: BattleStat[]; + /** The number of stages that were changed by the original */ + numStages: number; +} + export class StatStageChangeCopyAbAttr extends AbAttr { - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ pokemon, stats, numStages, simulated }: StatStageChangeCopyAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", pokemon.getBattlerIndex(), true, - args[0] as BattleStat[], - args[1] as number, + stats, + numStages, true, false, false, @@ -6183,21 +5142,21 @@ export class StatStageChangeCopyAbAttr extends AbAttr { } export class BypassBurnDamageReductionAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ReduceBurnDamageAbAttrParams extends AbAttrBaseParams { + /** Holds the damage done by the burn */ + burnDamage: NumberHolder; +} + /** * Causes Pokemon to take reduced damage from the {@linkcode StatusEffect.BURN | Burn} status * @param multiplier Multiplied with the damage taken @@ -6209,31 +5168,20 @@ export class ReduceBurnDamageAbAttr extends AbAttr { /** * Applies the damage reduction - * @param _pokemon N/A - * @param _passive N/A - * @param _cancelled N/A - * @param args `[0]` {@linkcode NumberHolder} The damage value being modified */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.multiplier); + override apply({ burnDamage }: ReduceBurnDamageAbAttrParams): void { + burnDamage.value = toDmgValue(burnDamage.value * this.multiplier); } } +export interface DoubleBerryEffectAbAttrParams extends AbAttrBaseParams { + /** The value of the berry effect that will be doubled by the ability's application */ + effectValue: NumberHolder; +} + export class DoubleBerryEffectAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= 2; + override apply({ effectValue }: DoubleBerryEffectAbAttrParams): void { + effectValue.value *= 2; } } @@ -6244,12 +5192,8 @@ export class DoubleBerryEffectAbAttr extends AbAttr { export class PreventBerryUseAbAttr extends AbAttr { /** * Prevent use of opposing berries. - * @param _pokemon - Unused - * @param _passive - Unused - * @param _simulated - Unused - * @param cancelled - {@linkcode BooleanHolder} containing whether to block berry use */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, cancelled: BooleanHolder): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } @@ -6257,7 +5201,6 @@ export class PreventBerryUseAbAttr extends AbAttr { /** * A Pokemon with this ability heals by a percentage of their maximum hp after eating a berry * @param healPercent - Percent of Max HP to heal - * @see {@linkcode apply()} for implementation */ export class HealFromBerryUseAbAttr extends AbAttr { /** Percent of Max HP to heal */ @@ -6270,7 +5213,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ..._args: [BooleanHolder, any[]]): void { + override apply({ simulated, passive, pokemon }: AbAttrBaseParams): void { if (simulated) { return; } @@ -6289,15 +5232,14 @@ export class HealFromBerryUseAbAttr extends AbAttr { } } +export interface RunSuccessAbAttrParams extends AbAttrBaseParams { + /** Holder for the likelihood that the pokemon will flee */ + chance: NumberHolder; +} + export class RunSuccessAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 256; + override apply({ chance }: RunSuccessAbAttrParams): void { + chance.value = 256; } } @@ -6317,50 +5259,33 @@ export class CheckTrappedAbAttr extends AbAttr { this.arenaTrapCondition = condition; } - canApplyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): boolean { + override canApply(_params: Closed): boolean { return true; } - applyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): void {} + override apply(_params: Closed): void {} +} + +export interface CheckTrappedAbAttrParams extends AbAttrBaseParams { + /** The pokemon to attempt to trap */ + opponent: Pokemon; + /** Holds whether the other Pokemon will be trapped or not */ + trapped: BooleanHolder; } /** * Determines whether a Pokemon is blocked from switching/running away * because of a trapping ability or move. - * @extends CheckTrappedAbAttr - * @see {@linkcode applyCheckTrapped} */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { - override canApplyCheckTrapped( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - otherPokemon: Pokemon, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean { return ( - this.arenaTrapCondition(pokemon, otherPokemon) && + this.arenaTrapCondition(pokemon, opponent) && !( - otherPokemon.getTypes(true).includes(PokemonType.GHOST) || - (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && - otherPokemon.getTypes().includes(PokemonType.GHOST)) + opponent.getTypes(true).includes(PokemonType.GHOST) || + (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST)) ) && - !otherPokemon.hasAbility(AbilityId.RUN_AWAY) + !opponent.hasAbility(AbilityId.RUN_AWAY) ); } @@ -6370,24 +5295,12 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * If the enemy has the ability Run Away, it is not trapped. * If the user has Magnet Pull and the enemy is not a Steel type, it is not trapped. * If the user has Arena Trap and the enemy is not grounded, it is not trapped. - * @param _pokemon The {@link Pokemon} with this {@link AbAttr} - * @param _passive N/A - * @param trapped {@link BooleanHolder} indicating whether the other Pokemon is trapped or not - * @param _otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability - * @param _args N/A */ - override applyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): void { + override apply({ trapped }: CheckTrappedAbAttrParams): void { trapped.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: CheckTrappedAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:arenaTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6395,50 +5308,52 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } } +export interface MaxMultiHitAbAttrParams extends AbAttrBaseParams { + /** The number of hits that the move will do */ + hits: NumberHolder; +} + export class MaxMultiHitAbAttr extends AbAttr { constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ hits }: MaxMultiHitAbAttrParams): void { + hits.value = 0; } } -export class PostBattleAbAttr extends AbAttr { +export interface PostBattleAbAttrParams extends AbAttrBaseParams { + /** Whether the battle that just ended was a victory */ + victory: boolean; +} + +export abstract class PostBattleAbAttr extends AbAttr { + private declare readonly _: never; constructor(showAbility = true) { super(showAbility); } - canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } export class PostBattleLootAbAttr extends PostBattleAbAttr { private randItem?: PokemonHeldItemModifier; - override canApplyPostBattle(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply({ simulated, victory, pokemon }: PostBattleAbAttrParams): boolean { const postBattleLoot = globalScene.currentBattle.postBattleLoot; - if (!simulated && postBattleLoot.length && args[0]) { + if (!simulated && postBattleLoot.length && victory) { this.randItem = randSeedItem(postBattleLoot); return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1); } return false; } - /** - * @param _args - `[0]`: boolean for if the battle ended in a victory - */ - override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: PostBattleAbAttrParams): void { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!this.randItem) { this.randItem = randSeedItem(postBattleLoot); @@ -6457,73 +5372,68 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } } -export class PostFaintAbAttr extends AbAttr { - canApplyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { +/** + * Shared parameters for ability attributes that trigger after the user faints. + */ +export interface PostFaintAbAttrParams extends AbAttrBaseParams { + /** The pokemon that caused the user to faint, or `undefined` if not caused by a Pokemon */ + readonly attacker?: Pokemon; + /** The move that caused the user to faint, or `undefined` if not caused by a move */ + readonly move?: Move; + /** The result of the hit that caused the user to faint */ + // TODO: Do we need this? It's unused by all classes + readonly hitResult?: HitResult; +} + +export abstract class PostFaintAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void {} + apply(_params: Closed): void {} } /** * Used for weather suppressing abilities to trigger weather-based form changes upon being fainted. * Used by Cloud Nine and Air Lock. - * @extends PostFaintAbAttr + * @sealed */ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAttr { - override canApplyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { + override canApply(_params: PostFaintAbAttrParams): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} * when the user of the ability faints - * @param {Pokemon} _pokemon the fainted Pokemon - * @param _passive n/a - * @param _attacker n/a - * @param _move n/a - * @param _hitResult n/a - * @param _args n/a */ - override applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated }: PostFaintAbAttrParams): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChanges(); } } } +export class PostFaintFormChangeAbAttr extends PostFaintAbAttr { + private formFunc: (p: Pokemon) => number; + + constructor(formFunc: (p: Pokemon) => number) { + super(true); + + this.formFunc = formFunc; + } + + override canApply({ pokemon }: AbAttrBaseParams): boolean { + return this.formFunc(pokemon) !== pokemon.formIndex; + } + + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (!simulated) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); + } + } +} + export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { private damageRatio: number; @@ -6533,42 +5443,42 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - override canApplyPostFaint( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { - const diedToDirectDamage = - move !== undefined && - attacker !== undefined && - move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }); - const cancelled = new BooleanHolder(false); - globalScene.getField(true).map(p => applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled, simulated)); - return !(!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")); - } - - override applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void { - if (!simulated) { - attacker!.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { - result: HitResult.INDIRECT, - }); - attacker!.turnData.damageTaken += toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); + override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean { + if ( + move === undefined || + attacker === undefined || + !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) + ) { + return false; } + + const cancelled = new BooleanHolder(false); + // TODO: This should be in speed order + globalScene + .getField(true) + .forEach(p => applyAbAttrs("FieldPreventExplosiveMovesAbAttr", { pokemon: p, cancelled, simulated })); + + if (cancelled.value) { + return false; + } + + // Confirmed: Aftermath does not activate or show text vs Magic Guard killers + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: attacker, cancelled }); + return !cancelled.value; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override apply({ simulated, attacker }: PostFaintAbAttrParams): void { + if (!attacker || simulated) { + return; + } + + attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { + result: HitResult.INDIRECT, + }); + attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); + } + + getTriggerMessage({ pokemon }: PostFaintAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postFaintContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6577,27 +5487,33 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { } /** - * Attribute used for abilities (Innards Out) that damage the opponent based on how much HP the last attack used to knock out the owner of the ability. + * Attribute used for abilities that damage opponents causing the user to faint + * equal to the amount of damage the last attack inflicted. + * + * Used for {@linkcode Abilities.INNARDS_OUT}. + * @sealed */ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { - override applyPostFaint( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void { - //If the mon didn't die to indirect damage - if (move !== undefined && attacker !== undefined && !simulated) { - const damage = pokemon.turnData.attacksReceived[0].damage; - attacker.damageAndUpdate(damage, { result: HitResult.INDIRECT }); - attacker.turnData.damageTaken += damage; + override apply({ simulated, pokemon, move, attacker }: PostFaintAbAttrParams): void { + // return early if the user died to indirect damage, target has magic guard or was KO'd by an ally + if (!move || !attacker || simulated || attacker.getAlly() === pokemon) { + return; } + + const cancelled = new BooleanHolder(false); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: attacker, cancelled }); + if (cancelled.value) { + return; + } + + const damage = pokemon.turnData.attacksReceived[0].damage; + attacker.damageAndUpdate(damage, { result: HitResult.INDIRECT }); + attacker.turnData.damageTaken += damage; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + // Oddly, Innards Out still shows a flyout if the effect was blocked due to Magic Guard... + // TODO: Verify on cart + override getTriggerMessage({ pokemon }: PostFaintAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postFaintHpDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6605,45 +5521,41 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } } -/** - * Redirects a move to the pokemon with this ability if it meets the conditions - */ -export class RedirectMoveAbAttr extends AbAttr { - /** - * @param pokemon - The Pokemon with the redirection ability - * @param args - The args passed to the `AbAttr`: - * - `[0]` - The id of the {@linkcode Move} used - * - `[1]` - The target's battler index (before redirection) - * - `[2]` - The Pokemon that used the move being redirected - */ +export interface RedirectMoveAbAttrParams extends AbAttrBaseParams { + /** The id of the move being redirected */ + moveId: MoveId; + /** The target's battler index before redirection */ + targetIndex: NumberHolder; + /** The Pokemon that used the move being redirected */ + sourcePokemon: Pokemon; +} - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - if (!this.canRedirect(args[0] as MoveId, args[2] as Pokemon)) { +/** + * Base class for abilities that redirect moves to the pokemon with this ability. + */ +export abstract class RedirectMoveAbAttr extends AbAttr { + override canApply({ pokemon, moveId, targetIndex, sourcePokemon }: RedirectMoveAbAttrParams): boolean { + if (!this.canRedirect(moveId, sourcePokemon)) { return false; } - const target = args[1] as NumberHolder; const newTarget = pokemon.getBattlerIndex(); - return target.value !== newTarget; + return targetIndex.value !== newTarget; } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const target = args[1] as NumberHolder; + override apply({ pokemon, targetIndex }: RedirectMoveAbAttrParams): void { const newTarget = pokemon.getBattlerIndex(); - target.value = newTarget; + targetIndex.value = newTarget; } - canRedirect(moveId: MoveId, _user: Pokemon): boolean { + protected canRedirect(moveId: MoveId, _user: Pokemon): boolean { const move = allMoves[moveId]; return !![MoveTarget.NEAR_OTHER, MoveTarget.OTHER].find(t => move.moveTarget === t); } } +/** + * @sealed + */ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { public type: PokemonType; @@ -6652,17 +5564,27 @@ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { this.type = type; } - canRedirect(moveId: MoveId, user: Pokemon): boolean { + protected override canRedirect(moveId: MoveId, user: Pokemon): boolean { return super.canRedirect(moveId, user) && user.getMoveType(allMoves[moveId]) === this.type; } } -export class BlockRedirectAbAttr extends AbAttr {} +export class BlockRedirectAbAttr extends AbAttr { + private declare readonly _: never; +} + +export interface ReduceStatusEffectDurationAbAttrParams extends AbAttrBaseParams { + /** The status effect in question */ + statusEffect: StatusEffect; + /** Holds the number of turns until the status is healed, which may be modified by ability application. */ + duration: NumberHolder; +} /** * Used by Early Bird, makes the pokemon wake up faster * @param statusEffect - The {@linkcode StatusEffect} to check for * @see {@linkcode apply} + * @sealed */ export class ReduceStatusEffectDurationAbAttr extends AbAttr { private statusEffect: StatusEffect; @@ -6673,8 +5595,8 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { this.statusEffect = statusEffect; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return args[1] instanceof NumberHolder && args[0] === this.statusEffect; + override canApply({ statusEffect }: ReduceStatusEffectDurationAbAttrParams): boolean { + return statusEffect === this.statusEffect; } /** @@ -6683,21 +5605,24 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { * - `[0]` - The {@linkcode StatusEffect} of the Pokemon * - `[1]` - The number of turns remaining until the status is healed */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - args[1].value -= 1; + override apply({ duration }: ReduceStatusEffectDurationAbAttrParams): void { + duration.value -= 1; } } -export class FlinchEffectAbAttr extends AbAttr { +/** + * Base class for abilities that apply an effect when the user is flinched. + */ +export abstract class FlinchEffectAbAttr extends AbAttr { constructor() { super(true); } + + canApply(_params: Closed): boolean { + return true; + } + + apply(_params: Closed): void {} } export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { @@ -6711,13 +5636,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { this.stages = stages; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -6730,44 +5649,47 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { } } -export class IncreasePpAbAttr extends AbAttr {} +export class IncreasePpAbAttr extends AbAttr { + private declare readonly _: never; +} +/** @sealed */ export class ForceSwitchOutImmunityAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ReduceBerryUseThresholdAbAttrParams extends AbAttrBaseParams { + /** Holds the hp ratio for the berry to proc, which may be modified by ability application */ + hpRatioReq: NumberHolder; +} + +/** @sealed */ export class ReduceBerryUseThresholdAbAttr extends AbAttr { constructor() { super(false); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + override canApply({ pokemon, hpRatioReq }: ReduceBerryUseThresholdAbAttrParams): boolean { const hpRatio = pokemon.getHpRatio(); - return args[0].value < hpRatio; + return hpRatioReq.value < hpRatio; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - args[0].value *= 2; + override apply({ hpRatioReq }: ReduceBerryUseThresholdAbAttrParams): void { + hpRatioReq.value *= 2; } } +export interface WeightMultiplierAbAttrParams extends AbAttrBaseParams { + /** The weight of the Pokemon, which may be modified by ability application */ + weight: NumberHolder; +} + /** * Ability attribute used for abilites that change the ability owner's weight * Used for Heavy Metal (doubling weight) and Light Metal (halving weight) + * @sealed */ export class WeightMultiplierAbAttr extends AbAttr { private multiplier: number; @@ -6778,33 +5700,34 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.multiplier; + override apply({ weight }: WeightMultiplierAbAttrParams): void { + weight.value *= this.multiplier; } } +export interface SyncEncounterNatureAbAttrParams extends AbAttrBaseParams { + /** The Pokemon whose nature is being synced */ + target: Pokemon; +} + +/** @sealed */ export class SyncEncounterNatureAbAttr extends AbAttr { constructor() { super(false); } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as Pokemon).setNature(pokemon.getNature()); + override apply({ target, pokemon }: SyncEncounterNatureAbAttrParams): void { + target.setNature(pokemon.getNature()); } } +export interface MoveAbilityBypassAbAttrParams extends AbAttrBaseParams { + /** The move being used */ + move: Move; + /** Holds whether the move's ability should be ignored */ + cancelled: BooleanHolder; +} + export class MoveAbilityBypassAbAttr extends AbAttr { private moveIgnoreFunc: (pokemon: Pokemon, move: Move) => boolean; @@ -6814,49 +5737,49 @@ export class MoveAbilityBypassAbAttr extends AbAttr { this.moveIgnoreFunc = moveIgnoreFunc || ((_pokemon, _move) => true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.moveIgnoreFunc(pokemon, args[0] as Move); + override canApply({ pokemon, move }: MoveAbilityBypassAbAttrParams): boolean { + return this.moveIgnoreFunc(pokemon, move); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: MoveAbilityBypassAbAttrParams): void { cancelled.value = true; } } -export class AlwaysHitAbAttr extends AbAttr {} +export class AlwaysHitAbAttr extends AbAttr { + private declare readonly _: never; +} /** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */ -export class IgnoreProtectOnContactAbAttr extends AbAttr {} +export class IgnoreProtectOnContactAbAttr extends AbAttr { + private declare readonly _: never; +} + +export interface InfiltratorAbAttrParams extends AbAttrBaseParams { + /** Holds a flag indicating that infiltrator's bypass is active */ + bypassed: BooleanHolder; +} /** * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Infiltrator_(Ability) | Infiltrator}. * Allows the source's moves to bypass the effects of opposing Light Screen, Reflect, Aurora Veil, Safeguard, Mist, and Substitute. + * @sealed */ export class InfiltratorAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return args[0] instanceof BooleanHolder; + /** @returns Whether bypassed has not yet been set */ + override canApply({ bypassed }: InfiltratorAbAttrParams): boolean { + return !bypassed.value; } /** * Sets a flag to bypass screens, Substitute, Safeguard, and Mist - * @param _pokemon n/a - * @param _passive n/a - * @param _simulated n/a - * @param _cancelled n/a - * @param args `[0]` a {@linkcode BooleanHolder | BooleanHolder} containing the flag */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: null, args: any[]): void { - const bypassed = args[0]; + override apply({ bypassed }: InfiltratorAbAttrParams): void { bypassed.value = true; } } @@ -6865,21 +5788,40 @@ export class InfiltratorAbAttr extends AbAttr { * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Magic_Bounce_(ability) | Magic Bounce}. * Allows the source to bounce back {@linkcode MoveFlags.REFLECTABLE | Reflectable} * moves as if the user had used {@linkcode MoveId.MAGIC_COAT | Magic Coat}. + * @sealed + * @todo Make reflection a part of this ability's effects */ -export class ReflectStatusMoveAbAttr extends AbAttr {} +export class ReflectStatusMoveAbAttr extends AbAttr { + private declare readonly _: never; +} +// TODO: Make these ability attributes be flags instead of dummy attributes +/** @sealed */ export class NoTransformAbilityAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } } +/** @sealed */ export class NoFusionAbilityAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } } +export interface IgnoreTypeImmunityAbAttrParams extends AbAttrBaseParams { + /** The type of the move being used */ + readonly moveType: PokemonType; + /** The type being checked for */ + readonly defenderType: PokemonType; + /** Holds whether the type immunity should be bypassed */ + cancelled: BooleanHolder; +} + +/** @sealed */ export class IgnoreTypeImmunityAbAttr extends AbAttr { private defenderType: PokemonType; private allowedMoveTypes: PokemonType[]; @@ -6890,23 +5832,25 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { this.allowedMoveTypes = allowedMoveTypes; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType); + override canApply({ moveType, defenderType }: IgnoreTypeImmunityAbAttrParams): boolean { + return this.defenderType === defenderType && this.allowedMoveTypes.includes(moveType); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: IgnoreTypeImmunityAbAttrParams): void { cancelled.value = true; } } +export interface IgnoreTypeStatusEffectImmunityAbAttrParams extends AbAttrParamsWithCancel { + /** The status effect being applied */ + readonly statusEffect: StatusEffect; + /** Holds whether the type immunity should be bypassed */ + readonly defenderType: PokemonType; +} + /** * Ignores the type immunity to Status Effects of the defender if the defender is of a certain type + * @sealed */ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { private statusEffect: StatusEffect[]; @@ -6919,17 +5863,11 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { this.defenderType = defenderType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType); + override canApply({ statusEffect, defenderType, cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): boolean { + return !cancelled.value && this.statusEffect.includes(statusEffect) && this.defenderType.includes(defenderType); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): void { cancelled.value = true; } } @@ -6938,65 +5876,43 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { * Gives money to the user after the battle. * * @extends PostBattleAbAttr - * @see {@linkcode applyPostBattle} */ export class MoneyAbAttr extends PostBattleAbAttr { - override canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { - return !simulated && args[0]; + override canApply({ simulated, victory }: PostBattleAbAttrParams): boolean { + // TODO: Consider moving the simulated check to the apply method + return !simulated && victory; } - /** - * @param _pokemon {@linkcode Pokemon} that is the user of this ability. - * @param _passive N/A - * @param _args - `[0]`: boolean for if the battle ended in a victory - */ - override applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply(_params: PostBattleAbAttrParams): void { globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); } } +// TODO: Consider removing this class and just using the PostSummonStatStageChangeAbAttr with a conditionalAttr +// that checks for the presence of the tag. /** * Applies a stat change after a Pokémon is summoned, * conditioned on the presence of a specific arena tag. - * - * @extends PostSummonStatStageChangeAbAttr + * @sealed */ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageChangeAbAttr { - /** - * The type of arena tag that conditions the stat change. - * @private - */ - private tagType: ArenaTagType; + /** The type of arena tag that conditions the stat change. */ + private arenaTagType: ArenaTagType; /** * Creates an instance of PostSummonStatStageChangeOnArenaAbAttr. * Initializes the stat change to increase Attack by 1 stage if the specified arena tag is present. * - * @param {ArenaTagType} tagType - The type of arena tag to check for. + * @param tagType - The type of arena tag to check for. */ constructor(tagType: ArenaTagType) { super([Stat.ATK], 1, true, false); - this.tagType = tagType; + this.arenaTagType = tagType; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - return ( - (globalScene.arena.getTagOnSide(this.tagType, side) ?? false) && - super.canApplyPostSummon(pokemon, passive, simulated, args) - ); - } - - /** - * Applies the post-summon stat change if the specified arena tag is present on pokemon's side. - * This is used in Wind Rider ability. - * - * @param {Pokemon} pokemon - The Pokémon being summoned. - * @param {boolean} passive - Whether the effect is passive. - * @param {any[]} args - Additional arguments. - */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - super.applyPostSummon(pokemon, passive, simulated, args); + override canApply(params: AbAttrBaseParams): boolean { + const side = params.pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + return (globalScene.arena.getTagOnSide(this.arenaTagType, side) ?? false) && super.canApply(params); } } @@ -7005,7 +5921,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC * This is used in the Disguise and Ice Face abilities. * * Does not apply to a user's substitute - * @extends ReceivedMoveDamageMultiplierAbAttr + * @sealed */ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; @@ -7028,40 +5944,18 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { this.triggerMessageFunc = triggerMessageFunc; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { - return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + override canApply({ pokemon, opponent, move }: PreDefendModifyDamageAbAttrParams): boolean { + // TODO: Investigate whether the substitute check can be removed, as it should be accounted for in the move effect phase + return this.condition(pokemon, opponent, move) && !move.hitsSubstitute(opponent, pokemon); } /** * 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 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. */ - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ pokemon, simulated, damage }: PreDefendModifyDamageAbAttrParams): void { if (!simulated) { - (args[0] as NumberHolder).value = this.multiplier; + damage.value = this.multiplier; pokemon.removeTag(this.tagType); if (this.recoilDamageFunc) { pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), { @@ -7075,12 +5969,9 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { /** * Gets the message triggered when the Pokémon avoids damage using the form-changing ability. - * @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 { + override getTriggerMessage({ pokemon }: PreDefendModifyDamageAbAttrParams, abilityName: string): string { return this.triggerMessageFunc(pokemon, abilityName); } } @@ -7091,23 +5982,22 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @see {@linkcode applyPreSummon()} */ export class PreSummonAbAttr extends AbAttr { - applyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): void {} + private declare readonly _: never; + apply(_params: Closed): void {} - canApplyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } } +/** @sealed */ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { /** * Apply a new illusion when summoning Zoroark if the illusion is available * * @param pokemon - The Pokémon with the Illusion ability. - * @param _passive - N/A - * @param _args - N/A - * @returns Whether the illusion was applied. */ - override applyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( p => p.isAllowedInBattle(), ); @@ -7115,7 +6005,8 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { pokemon.setIllusion(lastPokemon); } - override canApplyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { + /** @returns Whether the illusion can be applied. */ + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (pokemon.hasTrainer()) { const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( p => p.isAllowedInBattle(), @@ -7137,53 +6028,28 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } } +/** @sealed */ export class IllusionBreakAbAttr extends AbAttr { - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + private declare readonly _: never; + // TODO: Consider adding a `canApply` method that checks if the pokemon has an active illusion + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } } +/** @sealed */ export class PostDefendIllusionBreakAbAttr extends PostDefendAbAttr { /** * Destroy the illusion upon taking damage - * - * @param pokemon - The Pokémon with the Illusion ability. - * @param _passive - unused - * @param _attacker - The attacking Pokémon. - * @param _move - The move being used. - * @param _hitResult - The type of hitResult the pokemon got - * @param _args - unused * @returns - Whether the illusion was destroyed. */ - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon }: PostMoveInteractionAbAttrParams): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ pokemon, hitResult }: PostMoveInteractionAbAttrParams): boolean { const breakIllusion: HitResult[] = [ HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, @@ -7203,121 +6069,108 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { * @param _args - Unused * @returns - Whether the illusion was applied. */ - override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: PostBattleAbAttrParams): void { pokemon.breakIllusion(); } } +export interface BypassSpeedChanceAbAttrParams extends AbAttrBaseParams { + /** Holds whether the speed check is bypassed after ability application */ + bypass: BooleanHolder; +} + /** * If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection). - * - * @extends AbAttr + * @sealed */ export class BypassSpeedChanceAbAttr extends AbAttr { public chance: number; /** - * @param {number} chance probability of ability being active. + * @param chance - Probability of the ability activating */ constructor(chance: number) { super(true); this.chance = chance; } - override canApply(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { - const bypassSpeed = args[0] as BooleanHolder; + override canApply({ bypass, simulated, pokemon }: BypassSpeedChanceAbAttrParams): boolean { + // TODO: Consider whether we can move the simulated check to the `apply` method + // May be difficult as we likely do not want to modify the randBattleSeed const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; return ( - !simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove + !simulated && !bypass.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove ); } /** * bypass move order in their priority bracket when pokemon choose damaging move - * @param {Pokemon} _pokemon {@linkcode Pokemon} the Pokemon applying this ability - * @param {boolean} _passive N/A - * @param {BooleanHolder} _cancelled N/A - * @param {any[]} args [0] {@linkcode BooleanHolder} set to true when the ability activated */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const bypassSpeed = args[0] as BooleanHolder; - bypassSpeed.value = true; + override apply({ bypass }: BypassSpeedChanceAbAttrParams): void { + bypass.value = true; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: BypassSpeedChanceAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) }); } } +export interface PreventBypassSpeedChanceAbAttrParams extends AbAttrBaseParams { + /** Holds whether the speed check is bypassed after ability application */ + bypass: BooleanHolder; + /** Holds whether the Pokemon can check held items for Quick Claw's effects */ + canCheckHeldItems: BooleanHolder; +} + /** * This attribute checks if a Pokemon's move meets a provided condition to determine if the Pokemon can use Quick Claw * It was created because Pokemon with the ability Mycelium Might cannot access Quick Claw's benefits when using status moves. + * @sealed */ export class PreventBypassSpeedChanceAbAttr extends AbAttr { private condition: (pokemon: Pokemon, move: Move) => boolean; /** - * @param {function} condition - checks if a move meets certain conditions + * @param condition - checks if a move meets certain conditions */ constructor(condition: (pokemon: Pokemon, move: Move) => boolean) { super(true); this.condition = condition; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: PreventBypassSpeedChanceAbAttrParams): boolean { + // TODO: Consider having these be passed as parameters instead of being retrieved here const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; return isCommandFight && this.condition(pokemon, move!); } - /** - * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment - * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const bypassSpeed = args[0] as BooleanHolder; - const canCheckHeldItems = args[1] as BooleanHolder; - bypassSpeed.value = false; + override apply({ bypass, canCheckHeldItems }: PreventBypassSpeedChanceAbAttrParams): void { + bypass.value = false; canCheckHeldItems.value = false; } } +// Also consider making this a postTerrainChange attribute instead of a post-summon attribute /** * This applies a terrain-based type change to the Pokemon. * Used by Mimicry. + * @sealed */ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isTerastallized; } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon }: AbAttrBaseParams): void { const currentTerrain = globalScene.arena.getTerrainType(); const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain); if (typeChange.length !== 0) { @@ -7359,18 +6212,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { return typeChange; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return globalScene.arena.getTerrainType() !== TerrainType.NONE && this.canApply(pokemon, passive, simulated, args); - } - - /** - * Checks if the Pokemon should change types if summoned into an active terrain - */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { - this.apply(pokemon, passive, simulated, new BooleanHolder(false), []); - } - - override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]) { + override getTriggerMessage({ pokemon }: AbAttrBaseParams, _abilityName: string) { const currentTerrain = globalScene.arena.getTerrainType(); const pokemonNameWithAffix = getPokemonNameWithAffix(pokemon); if (currentTerrain === TerrainType.NONE) { @@ -7493,7 +6335,7 @@ class ForceSwitchOutHelper { if (player) { const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", opponent, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: opponent, cancelled: blockedByAbility }); return !blockedByAbility.value; } @@ -7531,7 +6373,7 @@ class ForceSwitchOutHelper { */ public getFailedText(target: Pokemon): string | null { const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: target, cancelled: blockedByAbility }); return blockedByAbility.value ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) : null; @@ -7556,30 +6398,21 @@ function calculateShellBellRecovery(pokemon: Pokemon): number { return 0; } +export interface PostDamageAbAttrParams extends AbAttrBaseParams { + /** The pokemon that caused the damage; omitted if the damage was not from a pokemon */ + source?: Pokemon; + /** The amount of damage that was dealt */ + readonly damage: number; +} /** * Triggers after the Pokemon takes any damage - * @extends AbAttr */ export class PostDamageAbAttr extends AbAttr { - public canApplyPostDamage( - _pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): boolean { + override canApply(_params: PostDamageAbAttrParams): boolean { return true; } - public applyPostDamage( - _pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): void {} + override apply(_params: PostDamageAbAttrParams): void {} } /** @@ -7589,8 +6422,8 @@ export class PostDamageAbAttr extends AbAttr { * * Used by Wimp Out and Emergency Exit * - * @extends PostDamageAbAttr * @see {@linkcode applyPostDamage} + * @sealed */ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { private helper: ForceSwitchOutHelper = new ForceSwitchOutHelper(SwitchType.SWITCH); @@ -7602,14 +6435,7 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { } // TODO: Refactor to use more early returns - public override canApplyPostDamage( - pokemon: Pokemon, - damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - source?: Pokemon, - ): boolean { + public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean { const moveHistory = pokemon.getMoveHistory(); // Will not activate when the Pokémon's HP is lowered by cutting its own HP const fordbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT]; @@ -7667,22 +6493,9 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { * Applies the switch-out logic after the Pokémon takes damage. * Checks various conditions based on the moves used by the Pokémon, the opponents' moves, and * the Pokémon's health after damage to determine whether the switch-out should occur. - * - * @param pokemon The Pokémon that took damage. - * @param _damage N/A - * @param _passive N/A - * @param _simulated Whether the ability is being simulated. - * @param _args N/A - * @param _source N/A */ - public override applyPostDamage( - pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): void { + public override apply({ pokemon }: PostDamageAbAttrParams): void { + // TODO: Consider respecting the `simulated` flag here this.helper.switchOutLogic(pokemon); } } @@ -7726,7 +6539,7 @@ const AbilityAttrs = Object.freeze({ PostDefendContactApplyStatusEffectAbAttr, EffectSporeAbAttr, PostDefendContactApplyTagChanceAbAttr, - PostDefendCritStatStageChangeAbAttr, + PostReceiveCritStatStageChangeAbAttr, PostDefendContactDamageAbAttr, PostDefendPerishSongAbAttr, PostDefendWeatherChangeAbAttr, @@ -7843,7 +6656,8 @@ const AbilityAttrs = Object.freeze({ PostTurnStatusHealAbAttr, PostTurnResetStatusAbAttr, PostTurnRestoreBerryAbAttr, - RepeatBerryNextTurnAbAttr, + CudChewConsumeBerryAbAttr, + CudChewRecordBerryAbAttr, MoodyAbAttr, SpeedBoostAbAttr, PostTurnHealAbAttr, @@ -8194,7 +7008,7 @@ export function initAbilities() { new Ability(AbilityId.GLUTTONY, 4) .attr(ReduceBerryUseThresholdAbAttr), new Ability(AbilityId.ANGER_POINT, 4) - .attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6), + .attr(PostReceiveCritStatStageChangeAbAttr, Stat.ATK, 12), new Ability(AbilityId.UNBURDEN, 4) .attr(PostItemLostApplyBattlerTagAbAttr, BattlerTagType.UNBURDEN) .bypassFaint() // Allows reviver seed to activate Unburden @@ -8409,7 +7223,8 @@ export function initAbilities() { .bypassFaint(), new Ability(AbilityId.IMPOSTER, 5) .attr(PostSummonTransformAbAttr) - .uncopiable(), + .uncopiable() + .edgeCase(), // Should copy rage fist hit count, etc (see Transform edge case for full list) new Ability(AbilityId.INFILTRATOR, 5) .attr(InfiltratorAbAttr) .partial(), // does not bypass Mist @@ -8485,8 +7300,10 @@ export function initAbilities() { new Ability(AbilityId.CHEEK_POUCH, 6) .attr(HealFromBerryUseAbAttr, 1 / 3), new Ability(AbilityId.PROTEAN, 6) - .attr(PokemonTypeChangeAbAttr), - //.condition((p) => !p.summonData.abilitiesApplied.includes(AbilityId.PROTEAN)), //Gen 9 Implementation + .attr(PokemonTypeChangeAbAttr) + // .condition((p) => !p.summonData.abilitiesApplied.includes(Abilities.PROTEAN)) //Gen 9 Implementation + // TODO: needs testing on interaction with weather blockage + .edgeCase(), new Ability(AbilityId.FUR_COAT, 6) .attr(ReceivedMoveDamageMultiplierAbAttr, (_target, _user, move) => move.category === MoveCategory.PHYSICAL, 0.5) .ignorable(), @@ -8571,11 +7388,14 @@ export function initAbilities() { new Ability(AbilityId.MERCILESS, 7) .attr(ConditionalCritAbAttr, (_user, target, _move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), new Ability(AbilityId.SHIELDS_DOWN, 7, -1) - .attr(PostBattleInitFormChangeAbAttr, () => 0) + // Change into Meteor Form on switch-in or turn end if HP >= 50%, + // or Core Form if HP <= 50%. + .attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) - .conditionalAttr(p => p.formIndex !== 7, StatusEffectImmunityAbAttr) - .conditionalAttr(p => p.formIndex !== 7, BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) + // All variants of Meteor Form are immune to status effects & Yawn + .conditionalAttr(p => p.formIndex < 7, StatusEffectImmunityAbAttr) + .conditionalAttr(p => p.formIndex < 7, BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .attr(NoFusionAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .uncopiable() @@ -8627,6 +7447,7 @@ export function initAbilities() { (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon) => toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) + .attr(PostFaintFormChangeAbAttr, () => 0) .uncopiable() .unreplaceable() .unsuppressable() @@ -8635,18 +7456,19 @@ export function initAbilities() { new Ability(AbilityId.BATTLE_BOND, 7) .attr(PostVictoryFormChangeAbAttr, () => 2) .attr(PostBattleInitFormChangeAbAttr, () => 1) + .attr(PostFaintFormChangeAbAttr, () => 1) .attr(NoFusionAbilityAbAttr) .uncopiable() .unreplaceable() .unsuppressable() .bypassFaint(), new Ability(AbilityId.POWER_CONSTRUCT, 7) - .conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostBattleInitFormChangeAbAttr, () => 2) - .conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostBattleInitFormChangeAbAttr, () => 3) - .conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2) - .conditionalAttr(pokemon => pokemon.formIndex === 2 || pokemon.formIndex === 4, PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2) - .conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "10-complete" ? 5 : 3) - .conditionalAttr(pokemon => pokemon.formIndex === 3 || pokemon.formIndex === 5, PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "10-complete" ? 5 : 3) + // Change to 10% complete or 50% complete on switchout/turn end if at <50% HP; + // revert to 10% PC or 50% PC before a new battle starts + .conditionalAttr(p => p.formIndex === 4 || p.formIndex === 5, PostBattleInitFormChangeAbAttr, p => p.formIndex - 2) + .conditionalAttr(p => p.getHpRatio() <= 0.5 && (p.formIndex === 2 || p.formIndex === 3), PostSummonFormChangeAbAttr, p => p.formIndex + 2) + .conditionalAttr(p => p.getHpRatio() <= 0.5 && (p.formIndex === 2 || p.formIndex === 3), PostTurnFormChangeAbAttr, p => p.formIndex + 2) + .conditionalAttr(p => p.formIndex === 4 || p.formIndex === 5, PostFaintFormChangeAbAttr, p => p.formIndex - 2) .attr(NoFusionAbilityAbAttr) .uncopiable() .unreplaceable() @@ -8737,8 +7559,10 @@ export function initAbilities() { new Ability(AbilityId.DAUNTLESS_SHIELD, 8) .attr(PostSummonStatStageChangeAbAttr, [ Stat.DEF ], 1, true), new Ability(AbilityId.LIBERO, 8) - .attr(PokemonTypeChangeAbAttr), - //.condition((p) => !p.summonData.abilitiesApplied.includes(AbilityId.LIBERO)), //Gen 9 Implementation + .attr(PokemonTypeChangeAbAttr) + //.condition((p) => !p.summonData.abilitiesApplied.includes(Abilities.LIBERO)), //Gen 9 Implementation + // TODO: needs testing on interaction with weather blockage + .edgeCase(), new Ability(AbilityId.BALL_FETCH, 8) .attr(FetchBallAbAttr) .condition(getOncePerBattleCondition(AbilityId.BALL_FETCH)), @@ -8960,7 +7784,8 @@ export function initAbilities() { new Ability(AbilityId.OPPORTUNIST, 9) .attr(StatStageChangeCopyAbAttr), new Ability(AbilityId.CUD_CHEW, 9) - .attr(RepeatBerryNextTurnAbAttr), + .attr(CudChewConsumeBerryAbAttr) + .attr(CudChewRecordBerryAbAttr), new Ability(AbilityId.SHARPNESS, 9) .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), new Ability(AbilityId.SUPREME_OVERLORD, 9) diff --git a/src/data/abilities/apply-ab-attrs.ts b/src/data/abilities/apply-ab-attrs.ts index fdbd2652698..58f63c5924a 100644 --- a/src/data/abilities/apply-ab-attrs.ts +++ b/src/data/abilities/apply-ab-attrs.ts @@ -1,99 +1,50 @@ -import type { AbAttrApplyFunc, AbAttrMap, AbAttrString, AbAttrSuccessFunc } from "#app/@types/ability-types"; -import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; -import type { BooleanHolder, NumberHolder } from "#app/utils/common"; -import type { BattlerIndex } from "#enums/battler-index"; -import type { HitResult } from "#enums/hit-result"; -import type { BattleStat, Stat } from "#enums/stat"; -import type { StatusEffect } from "#enums/status-effect"; -import type { WeatherType } from "#enums/weather-type"; -import type { BattlerTag } from "../battler-tags"; -import type Move from "../moves/move"; -import type { PokemonMove } from "../moves/pokemon-move"; -import type { TerrainType } from "../terrain"; -import type { Weather } from "../weather"; -import type { - PostBattleInitAbAttr, - PreDefendAbAttr, - PostDefendAbAttr, - PostMoveUsedAbAttr, - StatMultiplierAbAttr, - AllyStatMultiplierAbAttr, - PostSetStatusAbAttr, - PostDamageAbAttr, - FieldMultiplyStatAbAttr, - PreAttackAbAttr, - ExecutedMoveAbAttr, - PostAttackAbAttr, - PostKnockOutAbAttr, - PostVictoryAbAttr, - PostSummonAbAttr, - PreSummonAbAttr, - PreSwitchOutAbAttr, - PreLeaveFieldAbAttr, - PreStatStageChangeAbAttr, - PostStatStageChangeAbAttr, - PreSetStatusAbAttr, - PreApplyBattlerTagAbAttr, - PreWeatherEffectAbAttr, - PreWeatherDamageAbAttr, - PostTurnAbAttr, - PostWeatherChangeAbAttr, - PostWeatherLapseAbAttr, - PostTerrainChangeAbAttr, - CheckTrappedAbAttr, - PostBattleAbAttr, - PostFaintAbAttr, - PostItemLostAbAttr, -} from "./ability"; +import type { AbAttrBaseParams, AbAttrParamMap, AbAttrString, CallableAbAttrString } from "#types/ability-types"; function applySingleAbAttrs( - pokemon: Pokemon, - passive: boolean, attrType: T, - applyFunc: AbAttrApplyFunc, - successFunc: AbAttrSuccessFunc, - args: any[], + params: AbAttrParamMap[T], gainedMidTurn = false, - simulated = false, messages: string[] = [], ) { - if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) { + const { simulated = false, passive = false, pokemon } = params; + if (!pokemon.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) { return; } const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - if ( - gainedMidTurn && - ability.getAttrs(attrType).some(attr => { - attr.is("PostSummonAbAttr") && !attr.shouldActivateOnGain(); - }) - ) { + const attrs = ability.getAttrs(attrType); + if (gainedMidTurn && attrs.some(attr => attr.is("PostSummonAbAttr") && !attr.shouldActivateOnGain())) { return; } - for (const attr of ability.getAttrs(attrType)) { + for (const attr of attrs) { const condition = attr.getCondition(); - let abShown = false; - if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) { + // We require an `as any` cast to suppress an error about the `params` type not being assignable to + // the type of the argument expected by `attr.canApply()`. This is OK, because we know that + // `attr` is an instance of the `attrType` class provided to the method, and typescript _will_ check + // that the `params` object has the correct properties for that class at the callsites. + if ((condition && !condition(pokemon)) || !attr.canApply(params as any)) { continue; } - globalScene.phaseManager.setPhaseQueueSplice(); + let abShown = false; if (attr.showAbility && !simulated) { globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true); abShown = true; } - const message = attr.getTriggerMessage(pokemon, ability.name, args); + + const message = attr.getTriggerMessage(params as any, ability.name); if (message) { if (!simulated) { globalScene.phaseManager.queueMessage(message); } + // TODO: Should messages be added to the array if they aren't actually shown? messages.push(message); } - - applyFunc(attr, passive); + // The `as any` cast here uses the same reasoning as above. + attr.apply(params as any); if (abShown) { globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false); @@ -102,731 +53,63 @@ function applySingleAbAttrs( if (!simulated) { pokemon.waveData.abilitiesApplied.add(ability.id); } - - globalScene.phaseManager.clearPhaseQueueSplice(); } } -function applyAbAttrsInternal( +function applyAbAttrsInternal( attrType: T, - pokemon: Pokemon | null, - applyFunc: AbAttrApplyFunc, - successFunc: AbAttrSuccessFunc, - args: any[], - simulated = false, + params: AbAttrParamMap[T], messages: string[] = [], gainedMidTurn = false, ) { - for (const passive of [false, true]) { - if (pokemon) { - applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages); - globalScene.phaseManager.clearPhaseQueueSplice(); - } + // If the pokemon is not defined, no ability attributes to be applied. + // TODO: Evaluate whether this check is even necessary anymore + if (!params.pokemon) { + return; } + if (params.passive !== undefined) { + applySingleAbAttrs(attrType, params, gainedMidTurn, messages); + return; + } + for (const passive of [false, true]) { + params.passive = passive; + applySingleAbAttrs(attrType, params, gainedMidTurn, messages); + globalScene.phaseManager.clearPhaseQueueSplice(); + } + // We need to restore passive to its original state in the case that it was undefined on entry + // this is necessary in case this method is called with an object that is reused. + params.passive = undefined; } -export function applyAbAttrs( +/** + * @param attrType - The type of the ability attribute to apply. (note: may not be any attribute that extends PostSummonAbAttr) + * @param params - The parameters to pass to the ability attribute's apply method + * @param messages - An optional array to which ability trigger messges will be added + */ +export function applyAbAttrs( attrType: T, - pokemon: Pokemon, - cancelled: BooleanHolder | null, - simulated = false, - ...args: any[] + params: AbAttrParamMap[T], + messages?: string[], ): void { - applyAbAttrsInternal( - attrType, - pokemon, - // @ts-expect-error: TODO: fix the error on `cancelled` - (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), - (attr, passive) => attr.canApply(pokemon, passive, simulated, args), - args, - simulated, - ); + applyAbAttrsInternal(attrType, params, messages); } // TODO: Improve the type signatures of the following methods / refactor the apply methods -export function applyPostBattleInitAbAttrs( - attrType: AbAttrMap[K] extends PostBattleInitAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostBattleInitAbAttr).applyPostBattleInit(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostBattleInitAbAttr).canApplyPostBattleInit(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreDefendAbAttrs( - attrType: AbAttrMap[K] extends PreDefendAbAttr ? K : never, - pokemon: Pokemon, - attacker: Pokemon, - move: Move | null, - cancelled: BooleanHolder | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreDefendAbAttr).applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), - (attr, passive) => - (attr as PreDefendAbAttr).canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), - args, - simulated, - ); -} - -export function applyPostDefendAbAttrs( - attrType: AbAttrMap[K] extends PostDefendAbAttr ? K : never, - pokemon: Pokemon, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostDefendAbAttr).applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - (attr, passive) => - (attr as PostDefendAbAttr).canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostMoveUsedAbAttrs( - attrType: AbAttrMap[K] extends PostMoveUsedAbAttr ? K : never, - pokemon: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => (attr as PostMoveUsedAbAttr).applyPostMoveUsed(pokemon, move, source, targets, simulated, args), - (attr, _passive) => - (attr as PostMoveUsedAbAttr).canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args), - args, - simulated, - ); -} - -export function applyStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends StatMultiplierAbAttr ? K : never, - pokemon: Pokemon, - stat: BattleStat, - statValue: NumberHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as StatMultiplierAbAttr).applyStatStage(pokemon, passive, simulated, stat, statValue, args), - (attr, passive) => - (attr as StatMultiplierAbAttr).canApplyStatStage(pokemon, passive, simulated, stat, statValue, args), - args, - ); -} - -/** - * Applies an ally's Stat multiplier attribute - * @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being - * @param pokemon - The {@linkcode Pokemon} with the ability - * @param stat - The type of the checked {@linkcode Stat} - * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param checkedPokemon - The {@linkcode Pokemon} with the checked stat - * @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move. - * @param args - unused - */ -export function applyAllyStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends AllyStatMultiplierAbAttr ? K : never, - pokemon: Pokemon, - stat: BattleStat, - statValue: NumberHolder, - simulated = false, - checkedPokemon: Pokemon, - ignoreAbility: boolean, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as AllyStatMultiplierAbAttr).applyAllyStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - ignoreAbility, - args, - ), - (attr, passive) => - (attr as AllyStatMultiplierAbAttr).canApplyAllyStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - ignoreAbility, - args, - ), - args, - simulated, - ); -} - -export function applyPostSetStatusAbAttrs( - attrType: AbAttrMap[K] extends PostSetStatusAbAttr ? K : never, - pokemon: Pokemon, - effect: StatusEffect, - sourcePokemon?: Pokemon | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostSetStatusAbAttr).applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), - (attr, passive) => - (attr as PostSetStatusAbAttr).canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), - args, - simulated, - ); -} - -export function applyPostDamageAbAttrs( - attrType: AbAttrMap[K] extends PostDamageAbAttr ? K : never, - pokemon: Pokemon, - damage: number, - _passive: boolean, - simulated = false, - args: any[], - source?: Pokemon, -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostDamageAbAttr).applyPostDamage(pokemon, damage, passive, simulated, args, source), - (attr, passive) => (attr as PostDamageAbAttr).canApplyPostDamage(pokemon, damage, passive, simulated, args, source), - args, - ); -} -/** - * Applies a field Stat multiplier attribute - * @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being - * @param pokemon {@linkcode Pokemon} the Pokemon applying this ability - * @param stat {@linkcode Stat} the type of the checked stat - * @param statValue {@linkcode NumberHolder} the value of the checked stat - * @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat - * @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat - * @param args unused - */ - -export function applyFieldStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends FieldMultiplyStatAbAttr ? K : never, - pokemon: Pokemon, - stat: Stat, - statValue: NumberHolder, - checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as FieldMultiplyStatAbAttr).applyFieldStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - hasApplied, - args, - ), - (attr, passive) => - (attr as FieldMultiplyStatAbAttr).canApplyFieldStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - hasApplied, - args, - ), - args, - ); -} - -export function applyPreAttackAbAttrs( - attrType: AbAttrMap[K] extends PreAttackAbAttr ? K : never, - pokemon: Pokemon, - defender: Pokemon | null, - move: Move, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreAttackAbAttr).applyPreAttack(pokemon, passive, simulated, defender, move, args), - (attr, passive) => (attr as PreAttackAbAttr).canApplyPreAttack(pokemon, passive, simulated, defender, move, args), - args, - simulated, - ); -} - -export function applyExecutedMoveAbAttrs( - attrType: AbAttrMap[K] extends ExecutedMoveAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - attr => (attr as ExecutedMoveAbAttr).applyExecutedMove(pokemon, simulated), - attr => (attr as ExecutedMoveAbAttr).canApplyExecutedMove(pokemon, simulated), - args, - simulated, - ); -} - -export function applyPostAttackAbAttrs( - attrType: AbAttrMap[K] extends PostAttackAbAttr ? K : never, - pokemon: Pokemon, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostAttackAbAttr).applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - (attr, passive) => - (attr as PostAttackAbAttr).canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostKnockOutAbAttrs( - attrType: AbAttrMap[K] extends PostKnockOutAbAttr ? K : never, - pokemon: Pokemon, - knockedOut: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostKnockOutAbAttr).applyPostKnockOut(pokemon, passive, simulated, knockedOut, args), - (attr, passive) => (attr as PostKnockOutAbAttr).canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args), - args, - simulated, - ); -} - -export function applyPostVictoryAbAttrs( - attrType: AbAttrMap[K] extends PostVictoryAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostVictoryAbAttr).applyPostVictory(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostVictoryAbAttr).canApplyPostVictory(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostSummonAbAttrs( - attrType: AbAttrMap[K] extends PostSummonAbAttr ? K : never, - pokemon: Pokemon, - passive = false, - simulated = false, - ...args: any[] -): void { - applySingleAbAttrs( - pokemon, - passive, - attrType, - (attr, passive) => (attr as PostSummonAbAttr).applyPostSummon(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostSummonAbAttr).canApplyPostSummon(pokemon, passive, simulated, args), - args, - false, - simulated, - ); -} - -export function applyPreSummonAbAttrs( - attrType: AbAttrMap[K] extends PreSummonAbAttr ? K : never, - pokemon: Pokemon, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreSummonAbAttr).applyPreSummon(pokemon, passive, args), - (attr, passive) => (attr as PreSummonAbAttr).canApplyPreSummon(pokemon, passive, args), - args, - ); -} - -export function applyPreSwitchOutAbAttrs( - attrType: AbAttrMap[K] extends PreSwitchOutAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreSwitchOutAbAttr).applyPreSwitchOut(pokemon, passive, simulated, args), - (attr, passive) => (attr as PreSwitchOutAbAttr).canApplyPreSwitchOut(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreLeaveFieldAbAttrs( - attrType: AbAttrMap[K] extends PreLeaveFieldAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreLeaveFieldAbAttr).applyPreLeaveField(pokemon, passive, simulated, args), - (attr, passive) => (attr as PreLeaveFieldAbAttr).canApplyPreLeaveField(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreStatStageChangeAbAttrs( - attrType: AbAttrMap[K] extends PreStatStageChangeAbAttr ? K : never, - pokemon: Pokemon | null, - stat: BattleStat, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreStatStageChangeAbAttr).applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), - (attr, passive) => - (attr as PreStatStageChangeAbAttr).canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), - args, - simulated, - ); -} - -export function applyPostStatStageChangeAbAttrs( - attrType: AbAttrMap[K] extends PostStatStageChangeAbAttr ? K : never, - pokemon: Pokemon, - stats: BattleStat[], - stages: number, - selfTarget: boolean, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => - (attr as PostStatStageChangeAbAttr).applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), - (attr, _passive) => - (attr as PostStatStageChangeAbAttr).canApplyPostStatStageChange( - pokemon, - simulated, - stats, - stages, - selfTarget, - args, - ), - args, - simulated, - ); -} - -export function applyPreSetStatusAbAttrs( - attrType: AbAttrMap[K] extends PreSetStatusAbAttr ? K : never, - pokemon: Pokemon, - effect: StatusEffect | undefined, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreSetStatusAbAttr).applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), - (attr, passive) => - (attr as PreSetStatusAbAttr).canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), - args, - simulated, - ); -} - -export function applyPreApplyBattlerTagAbAttrs( - attrType: AbAttrMap[K] extends PreApplyBattlerTagAbAttr ? K : never, - pokemon: Pokemon, - tag: BattlerTag, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreApplyBattlerTagAbAttr).applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), - (attr, passive) => - (attr as PreApplyBattlerTagAbAttr).canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), - args, - simulated, - ); -} - -export function applyPreWeatherEffectAbAttrs( - attrType: AbAttrMap[K] extends PreWeatherEffectAbAttr ? K : never, - pokemon: Pokemon, - weather: Weather | null, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreWeatherDamageAbAttr).applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), - (attr, passive) => - (attr as PreWeatherDamageAbAttr).canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), - args, - simulated, - ); -} - -export function applyPostTurnAbAttrs( - attrType: AbAttrMap[K] extends PostTurnAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostTurnAbAttr).applyPostTurn(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostTurnAbAttr).canApplyPostTurn(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostWeatherChangeAbAttrs( - attrType: AbAttrMap[K] extends PostWeatherChangeAbAttr ? K : never, - pokemon: Pokemon, - weather: WeatherType, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostWeatherChangeAbAttr).applyPostWeatherChange(pokemon, passive, simulated, weather, args), - (attr, passive) => - (attr as PostWeatherChangeAbAttr).canApplyPostWeatherChange(pokemon, passive, simulated, weather, args), - args, - simulated, - ); -} - -export function applyPostWeatherLapseAbAttrs( - attrType: AbAttrMap[K] extends PostWeatherLapseAbAttr ? K : never, - pokemon: Pokemon, - weather: Weather | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostWeatherLapseAbAttr).applyPostWeatherLapse(pokemon, passive, simulated, weather, args), - (attr, passive) => - (attr as PostWeatherLapseAbAttr).canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args), - args, - simulated, - ); -} - -export function applyPostTerrainChangeAbAttrs( - attrType: AbAttrMap[K] extends PostTerrainChangeAbAttr ? K : never, - pokemon: Pokemon, - terrain: TerrainType, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostTerrainChangeAbAttr).applyPostTerrainChange(pokemon, passive, simulated, terrain, args), - (attr, passive) => - (attr as PostTerrainChangeAbAttr).canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args), - args, - simulated, - ); -} - -export function applyCheckTrappedAbAttrs( - attrType: AbAttrMap[K] extends CheckTrappedAbAttr ? K : never, - pokemon: Pokemon, - trapped: BooleanHolder, - otherPokemon: Pokemon, - messages: string[], - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as CheckTrappedAbAttr).applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - (attr, passive) => - (attr as CheckTrappedAbAttr).canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - args, - simulated, - messages, - ); -} - -export function applyPostBattleAbAttrs( - attrType: AbAttrMap[K] extends PostBattleAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostBattleAbAttr).applyPostBattle(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostBattleAbAttr).canApplyPostBattle(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostFaintAbAttrs( - attrType: AbAttrMap[K] extends PostFaintAbAttr ? K : never, - pokemon: Pokemon, - attacker?: Pokemon, - move?: Move, - hitResult?: HitResult, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostFaintAbAttr).applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), - (attr, passive) => - (attr as PostFaintAbAttr).canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostItemLostAbAttrs( - attrType: AbAttrMap[K] extends PostItemLostAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => (attr as PostItemLostAbAttr).applyPostItemLost(pokemon, simulated, args), - (attr, _passive) => (attr as PostItemLostAbAttr).canApplyPostItemLost(pokemon, simulated, args), - args, - ); -} - /** * Applies abilities when they become active mid-turn (ability switch) * * Ignores passives as they don't change and shouldn't be reapplied when main abilities change */ -export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs( - pokemon, - passive, - "PostSummonAbAttr", - (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), - (attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args), - args, - true, - simulated, - ); +export function applyOnGainAbAttrs(params: AbAttrBaseParams): void { + applySingleAbAttrs("PostSummonAbAttr", params, true); } + /** * Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather) */ -export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs( - pokemon, - passive, - "PreLeaveFieldAbAttr", - (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]), - (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]), - args, - true, - simulated, - ); +export function applyOnLoseAbAttrs(params: AbAttrBaseParams): void { + applySingleAbAttrs("PreLeaveFieldAbAttr", params, true); - applySingleAbAttrs( - pokemon, - passive, - "IllusionBreakAbAttr", - (attr, passive) => attr.apply(pokemon, passive, simulated, null, args), - (attr, passive) => attr.canApply(pokemon, passive, simulated, args), - args, - true, - simulated, - ); + applySingleAbAttrs("IllusionBreakAbAttr", params, true); } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 494a0438b18..2d49e7011af 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1,35 +1,106 @@ +import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type { Arena } from "#app/field/arena"; -import { PokemonType } from "#enums/pokemon-type"; -import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils/common"; -import { allMoves } from "./data-lists"; -import { MoveTarget } from "#enums/MoveTarget"; -import { MoveCategory } from "#enums/MoveCategory"; import { getPokemonNameWithAffix } from "#app/messages"; -import type Pokemon from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; -import { StatusEffect } from "#enums/status-effect"; -import type { BattlerIndex } from "#enums/battler-index"; -import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "./abilities/apply-ab-attrs"; -import { Stat } from "#enums/stat"; -import { CommonBattleAnim } from "#app/data/battle-anims"; -import { CommonAnim } from "#enums/move-anims-common"; -import i18next from "i18next"; +import { CommonBattleAnim } from "#data/battle-anims"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { MoveId } from "#enums/move-id"; import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import type { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { HitResult } from "#enums/hit-result"; +import { MoveCategory } from "#enums/MoveCategory"; +import { MoveTarget } from "#enums/MoveTarget"; +import { CommonAnim } from "#enums/move-anims-common"; +import { MoveId } from "#enums/move-id"; import { MoveUseMode } from "#enums/move-use-mode"; +import { PokemonType } from "#enums/pokemon-type"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import type { Arena } from "#field/arena"; +import type { Pokemon } from "#field/pokemon"; +import type { + ArenaDelayedAttackTagType, + ArenaScreenTagType, + ArenaTagTypeData, + ArenaTrapTagType, + SerializableArenaTagType, +} from "#types/arena-tags"; +import type { Mutable, NonFunctionProperties } from "#types/type-helpers"; +import { BooleanHolder, isNullOrUndefined, NumberHolder, toDmgValue } from "#utils/common"; +import i18next from "i18next"; -export abstract class ArenaTag { - constructor( - public tagType: ArenaTagType, - public turnCount: number, - public sourceMove?: MoveId, - public sourceId?: number, - public side: ArenaTagSide = ArenaTagSide.BOTH, - ) {} +/* +ArenaTags are are meant for effects that are tied to the arena (as opposed to a specific pokemon). +Examples include (but are not limited to) +- Cross-turn effects that persist even if the user/target switches out, such as Wish, Future Sight, and Happy Hour +- Effects that are applied to a specific side of the field, such as Crafty Shield, Reflect, and Spikes +- Field-Effects, like Gravity and Trick Room + +Any arena tag that persists across turns *must* extend from `SerializableArenaTag` in the class definition signature. + +Serializable ArenaTags have strict rules for their fields. +These rules ensure that only the data necessary to reconstruct the tag is serialized, and that the +session loader is able to deserialize saved tags correctly. + +If the data is static (i.e. it is always the same for all instances of the class, such as the +type that is weakened by Mud Sport/Water Sport), then it must not be defined as a field, and must +instead be defined as a getter. +A static property is also acceptable, though static properties are less ergonomic with inheritance. + +If the data is mutable (i.e. it can change over the course of the tag's lifetime), then it *must* +be defined as a field, and it must be set in the `loadTag` method. +Such fields cannot be marked as `private/protected`, as if they were, typescript would omit them from +types that are based off of the class, namely, `ArenaTagTypeData`. It is preferrable to trade the +type-safety of private/protected fields for the type safety when deserializing arena tags from save data. + +For data that is mutable only within a turn (e.g. SuppressAbilitiesTag's beingRemoved field), +where it does not make sense to be serialized, the field should use ES2020's private field syntax (a `#` prepended to the field name). +If the field should be accessible outside of the class, then a public getter should be used. +*/ + +/** Interface containing the serializable fields of ArenaTagData. */ +interface BaseArenaTag { + /** + * The tag's remaining duration. Setting to any number `<=0` will make the tag's duration effectively infinite. + */ + turnCount: number; + /** + * The {@linkcode MoveId} that created this tag, or `undefined` if not set by a move. + */ + sourceMove?: MoveId; + /** + * The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created the tag, or `undefined` if not set by a Pokemon. + * @todo Implement handling for `ArenaTag`s created by non-pokemon sources (most tags will throw errors without a source) + */ + // Note: Intentionally not using `?`, as the property should always exist, but just be undefined if not present. + sourceId: number | undefined; + /** + * The {@linkcode ArenaTagSide | side of the field} that this arena tag affects. + * @defaultValue `ArenaTagSide.BOTH` + */ + side: ArenaTagSide; +} + +/** + * An {@linkcode ArenaTag} represents a semi-persistent effect affecting a given _side_ of the field. + * Unlike {@linkcode BattlerTag}s (which are tied to individual {@linkcode Pokemon}), `ArenaTag`s function independently of + * the Pokemon currently on-field, only cleared on arena reset or through their respective {@linkcode ArenaTag.lapse | lapse} methods. + */ +export abstract class ArenaTag implements BaseArenaTag { + /** The type of the arena tag */ + public abstract readonly tagType: ArenaTagType; + public turnCount: number; + public sourceMove?: MoveId; + public sourceId: number | undefined; + public side: ArenaTagSide; + + constructor(turnCount: number, sourceMove?: MoveId, sourceId?: number, side: ArenaTagSide = ArenaTagSide.BOTH) { + this.turnCount = turnCount; + this.sourceMove = sourceMove; + this.sourceId = sourceId; + this.side = side; + } apply(_arena: Arena, _simulated: boolean, ..._args: unknown[]): boolean { return true; @@ -50,8 +121,17 @@ export abstract class ArenaTag { onOverlap(_arena: Arena, _source: Pokemon | null): void {} + /** + * Trigger this {@linkcode ArenaTag}'s effect, reducing its duration as applicable. + * Will ignore durations of all tags with durations `<=0`. + * @param _arena - The {@linkcode Arena} at the moment the tag is being lapsed. + * Unused by default but can be used by sub-classes. + * @returns `true` if this tag should be kept; `false` if it should be removed. + */ lapse(_arena: Arena): boolean { - return this.turnCount < 1 || !!--this.turnCount; + // TODO: Rather than treating negative duration tags as being indefinite, + // make all duration based classes inherit from their own sub-class + return this.turnCount < 1 || --this.turnCount > 0; } getMoveName(): string | null { @@ -61,9 +141,9 @@ export abstract class ArenaTag { /** * When given a arena tag or json representing one, load the data for it. * This is meant to be inherited from by any arena tag with custom attributes - * @param {ArenaTag | any} source An arena tag + * @param source - The {@linkcode BaseArenaTag} being loaded */ - loadTag(source: ArenaTag | any): void { + loadTag(source: BaseArenaTag): void { this.turnCount = source.turnCount; this.sourceMove = source.sourceMove; this.sourceId = source.sourceId; @@ -72,10 +152,11 @@ export abstract class ArenaTag { /** * Helper function that retrieves the source Pokemon - * @returns The source {@linkcode Pokemon} or `null` if none is found + * @returns - The source {@linkcode Pokemon} for this tag. + * Returns `null` if `this.sourceId` is `undefined` */ public getSourcePokemon(): Pokemon | null { - return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + return globalScene.getPokemonById(this.sourceId); } /** @@ -95,31 +176,42 @@ export abstract class ArenaTag { } } +/** + * Abstract class for arena tags that can persist across turns. + */ +export abstract class SerializableArenaTag extends ArenaTag { + abstract readonly tagType: SerializableArenaTagType; +} + /** * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Mist_(move) Mist}. * Prevents Pokémon on the opposing side from lowering the stats of the Pokémon in the Mist. */ -export class MistTag extends ArenaTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.MIST, turnCount, MoveId.MIST, sourceId, side); +export class MistTag extends SerializableArenaTag { + readonly tagType = ArenaTagType.MIST; + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.MIST, sourceId, side); } onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - if (this.sourceId) { - const source = globalScene.getPokemonById(this.sourceId); - - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:mistOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); - } else if (!quiet) { - console.warn("Failed to get source for MistTag onAdd"); - } + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for MistTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:mistOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } /** @@ -137,7 +229,7 @@ export class MistTag extends ArenaTag { if (attacker) { const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, simulated: false, bypassed }); if (bypassed.value) { return false; } @@ -155,33 +247,11 @@ export class MistTag extends ArenaTag { /** * Reduces the damage of specific move categories in the arena. - * @extends ArenaTag */ -export class WeakenMoveScreenTag extends ArenaTag { - protected weakenedCategories: MoveCategory[]; - - /** - * Creates a new instance of the WeakenMoveScreenTag class. - * - * @param tagType - The type of the arena tag. - * @param turnCount - The number of turns the tag is active. - * @param sourceMove - The move that created the tag. - * @param sourceId - The ID of the source of the tag. - * @param side - The side (player or enemy) the tag affects. - * @param weakenedCategories - The categories of moves that are weakened by this tag. - */ - constructor( - tagType: ArenaTagType, - turnCount: number, - sourceMove: MoveId, - sourceId: number, - side: ArenaTagSide, - weakenedCategories: MoveCategory[], - ) { - super(tagType, turnCount, sourceMove, sourceId, side); - - this.weakenedCategories = weakenedCategories; - } +export abstract class WeakenMoveScreenTag extends SerializableArenaTag { + public abstract readonly tagType: ArenaScreenTagType; + // Getter to avoid unnecessary serialization and prevent modification + protected abstract get weakenedCategories(): MoveCategory[]; /** * Applies the weakening effect to the move. @@ -202,7 +272,7 @@ export class WeakenMoveScreenTag extends ArenaTag { ): boolean { if (this.weakenedCategories.includes(moveCategory)) { const bypassed = new BooleanHolder(false); - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed }); if (bypassed.value) { return false; } @@ -218,8 +288,13 @@ export class WeakenMoveScreenTag extends ArenaTag { * Used by {@linkcode MoveId.REFLECT} */ class ReflectTag extends WeakenMoveScreenTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.REFLECT, turnCount, MoveId.REFLECT, sourceId, side, [MoveCategory.PHYSICAL]); + public readonly tagType = ArenaTagType.REFLECT; + protected get weakenedCategories(): [MoveCategory.PHYSICAL] { + return [MoveCategory.PHYSICAL]; + } + + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.REFLECT, sourceId, side); } onAdd(_arena: Arena, quiet = false): void { @@ -238,8 +313,12 @@ class ReflectTag extends WeakenMoveScreenTag { * Used by {@linkcode MoveId.LIGHT_SCREEN} */ class LightScreenTag extends WeakenMoveScreenTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.LIGHT_SCREEN, turnCount, MoveId.LIGHT_SCREEN, sourceId, side, [MoveCategory.SPECIAL]); + public readonly tagType = ArenaTagType.LIGHT_SCREEN; + protected get weakenedCategories(): [MoveCategory.SPECIAL] { + return [MoveCategory.SPECIAL]; + } + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.LIGHT_SCREEN, sourceId, side); } onAdd(_arena: Arena, quiet = false): void { @@ -258,11 +337,13 @@ class LightScreenTag extends WeakenMoveScreenTag { * Used by {@linkcode MoveId.AURORA_VEIL} */ class AuroraVeilTag extends WeakenMoveScreenTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.AURORA_VEIL, turnCount, MoveId.AURORA_VEIL, sourceId, side, [ - MoveCategory.SPECIAL, - MoveCategory.PHYSICAL, - ]); + public readonly tagType = ArenaTagType.AURORA_VEIL; + protected get weakenedCategories(): [MoveCategory.PHYSICAL, MoveCategory.SPECIAL] { + return [MoveCategory.PHYSICAL, MoveCategory.SPECIAL]; + } + + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.AURORA_VEIL, sourceId, side); } onAdd(_arena: Arena, quiet = false): void { @@ -282,21 +363,23 @@ type ProtectConditionFunc = (arena: Arena, moveId: MoveId) => boolean; * Class to implement conditional team protection * applies protection based on the attributes of incoming moves */ -export class ConditionalProtectTag extends ArenaTag { +export abstract class ConditionalProtectTag extends ArenaTag { /** The condition function to determine which moves are negated */ protected protectConditionFunc: ProtectConditionFunc; - /** Does this apply to all moves, including those that ignore other forms of protection? */ + /** + * Whether this protection effect should apply to _all_ moves, including ones that ignore other forms of protection. + * @defaultValue `false` + */ protected ignoresBypass: boolean; constructor( - tagType: ArenaTagType, sourceMove: MoveId, - sourceId: number, + sourceId: number | undefined, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass = false, ) { - super(tagType, 1, sourceMove, sourceId, side); + super(1, sourceMove, sourceId, side); this.protectConditionFunc = condition; this.ignoresBypass = ignoresBypass; @@ -382,8 +465,9 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => { * Condition: The incoming move has increased priority. */ class QuickGuardTag extends ConditionalProtectTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.QUICK_GUARD, MoveId.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc); + public readonly tagType = ArenaTagType.QUICK_GUARD; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc); } } @@ -413,8 +497,9 @@ const WideGuardConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean = * can be an ally or enemy. */ class WideGuardTag extends ConditionalProtectTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.WIDE_GUARD, MoveId.WIDE_GUARD, sourceId, side, WideGuardConditionFunc); + public readonly tagType = ArenaTagType.WIDE_GUARD; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.WIDE_GUARD, sourceId, side, WideGuardConditionFunc); } } @@ -435,23 +520,24 @@ const MatBlockConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean => * Condition: The incoming move is a Physical or Special attack move. */ class MatBlockTag extends ConditionalProtectTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.MAT_BLOCK, MoveId.MAT_BLOCK, sourceId, side, MatBlockConditionFunc); + public readonly tagType = ArenaTagType.MAT_BLOCK; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.MAT_BLOCK, sourceId, side, MatBlockConditionFunc); } onAdd(_arena: Arena) { - if (this.sourceId) { - const source = globalScene.getPokemonById(this.sourceId); - if (source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:matBlockOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); - } else { - console.warn("Failed to get source for MatBlockTag onAdd"); - } + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for Mat Block message; id: ${this.sourceId}`); + return; } + + super.onAdd(_arena); + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:matBlockOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } } @@ -479,8 +565,9 @@ const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => { * not target all Pokemon or sides of the field. */ class CraftyShieldTag extends ConditionalProtectTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.CRAFTY_SHIELD, MoveId.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true); + public readonly tagType = ArenaTagType.CRAFTY_SHIELD; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true); } } @@ -488,17 +575,8 @@ class CraftyShieldTag extends ConditionalProtectTag { * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Lucky_Chant_(move) Lucky Chant}. * Prevents critical hits against the tag's side. */ -export class NoCritTag extends ArenaTag { - /** - * Constructor method for the NoCritTag class - * @param turnCount `number` the number of turns this effect lasts - * @param sourceMove {@linkcode MoveId} the move that created this effect - * @param sourceId `number` the ID of the {@linkcode Pokemon} that created this effect - * @param side {@linkcode ArenaTagSide} the side to which this effect belongs - */ - constructor(turnCount: number, sourceMove: MoveId, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side); - } +export class NoCritTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.NO_CRIT; /** Queues a message upon adding this effect to the field */ onAdd(_arena: Arena): void { @@ -511,7 +589,12 @@ export class NoCritTag extends ArenaTag { /** Queues a message upon removing this effect from the field */ onRemove(_arena: Arena): void { - const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct? + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for NoCritTag on remove message; id: ${this.sourceId}`); + return; + } + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:noCritOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), @@ -522,62 +605,61 @@ export class NoCritTag extends ArenaTag { } /** - * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) Wish}. + * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) | Wish}. * Heals the Pokémon in the user's position the turn after Wish is used. */ -class WishTag extends ArenaTag { - private battlerIndex: BattlerIndex; - private triggerMessage: string; - private healHp: number; +class WishTag extends SerializableArenaTag { + // The following fields are meant to be inwardly mutable, but outwardly immutable. + readonly battlerIndex: BattlerIndex; + readonly healHp: number; + readonly sourceName: string; + // End inwardly mutable fields - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.WISH, turnCount, MoveId.WISH, sourceId, side); + public readonly tagType = ArenaTagType.WISH; + + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.WISH, sourceId, side); } onAdd(_arena: Arena): void { - if (this.sourceId) { - const user = globalScene.getPokemonById(this.sourceId); - if (user) { - this.battlerIndex = user.getBattlerIndex(); - this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(user), - }); - this.healHp = toDmgValue(user.getMaxHp() / 2); - } else { - console.warn("Failed to get source for WishTag onAdd"); - } + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for WishTag on add message; id: ${this.sourceId}`); + return; } + + (this as Mutable).sourceName = getPokemonNameWithAffix(source); + (this as Mutable).healHp = toDmgValue(source.getMaxHp() / 2); + (this as Mutable).battlerIndex = source.getBattlerIndex(); } onRemove(_arena: Arena): void { const target = globalScene.getField()[this.battlerIndex]; if (target?.isActive(true)) { - globalScene.phaseManager.queueMessage(this.triggerMessage); + globalScene.phaseManager.queueMessage( + // TODO: Rename key as it triggers on activation + i18next.t("arenaTag:wishTagOnAdd", { + pokemonNameWithAffix: this.sourceName, + }), + ); globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), this.healHp, null, true, false); } } + + override loadTag(source: NonFunctionProperties): void { + super.loadTag(source); + (this as Mutable).battlerIndex = source.battlerIndex; + (this as Mutable).healHp = source.healHp; + (this as Mutable).sourceName = source.sourceName; + } } /** * Abstract class to implement weakened moves of a specific type. */ -export class WeakenMoveTypeTag extends ArenaTag { - private weakenedType: PokemonType; - - /** - * Creates a new instance of the WeakenMoveTypeTag class. - * - * @param tagType - The type of the arena tag. - * @param turnCount - The number of turns the tag is active. - * @param type - The type being weakened from this tag. - * @param sourceMove - The move that created the tag. - * @param sourceId - The ID of the source of the tag. - */ - constructor(tagType: ArenaTagType, turnCount: number, type: PokemonType, sourceMove: MoveId, sourceId: number) { - super(tagType, turnCount, sourceMove, sourceId); - - this.weakenedType = type; - } +export abstract class WeakenMoveTypeTag extends SerializableArenaTag { + abstract readonly tagType: ArenaTagType.MUD_SPORT | ArenaTagType.WATER_SPORT; + abstract get weakenedType(): PokemonType; /** * Reduces an attack's power by 0.33x if it matches this tag's weakened type. @@ -601,8 +683,12 @@ export class WeakenMoveTypeTag extends ArenaTag { * Weakens Electric type moves for a set amount of turns, usually 5. */ class MudSportTag extends WeakenMoveTypeTag { - constructor(turnCount: number, sourceId: number) { - super(ArenaTagType.MUD_SPORT, turnCount, PokemonType.ELECTRIC, MoveId.MUD_SPORT, sourceId); + public readonly tagType = ArenaTagType.MUD_SPORT; + override get weakenedType(): PokemonType.ELECTRIC { + return PokemonType.ELECTRIC; + } + constructor(turnCount: number, sourceId?: number) { + super(turnCount, MoveId.MUD_SPORT, sourceId); } onAdd(_arena: Arena): void { @@ -619,8 +705,12 @@ class MudSportTag extends WeakenMoveTypeTag { * Weakens Fire type moves for a set amount of turns, usually 5. */ class WaterSportTag extends WeakenMoveTypeTag { - constructor(turnCount: number, sourceId: number) { - super(ArenaTagType.WATER_SPORT, turnCount, PokemonType.FIRE, MoveId.WATER_SPORT, sourceId); + public readonly tagType = ArenaTagType.WATER_SPORT; + override get weakenedType(): PokemonType.FIRE { + return PokemonType.FIRE; + } + constructor(turnCount: number, sourceId?: number) { + super(turnCount, MoveId.WATER_SPORT, sourceId); } onAdd(_arena: Arena): void { @@ -638,8 +728,9 @@ class WaterSportTag extends WeakenMoveTypeTag { * Converts Normal-type moves to Electric type for the rest of the turn. */ export class IonDelugeTag extends ArenaTag { + public readonly tagType = ArenaTagType.ION_DELUGE; constructor(sourceMove?: MoveId) { - super(ArenaTagType.ION_DELUGE, 1, sourceMove); + super(1, sourceMove); } /** Queues an on-add message */ @@ -668,7 +759,8 @@ export class IonDelugeTag extends ArenaTag { /** * Abstract class to implement arena traps. */ -export class ArenaTrapTag extends ArenaTag { +export abstract class ArenaTrapTag extends SerializableArenaTag { + abstract readonly tagType: ArenaTrapTagType; public layers: number; public maxLayers: number; @@ -681,8 +773,8 @@ export class ArenaTrapTag extends ArenaTag { * @param side - The side (player or enemy) the tag affects. * @param maxLayers - The maximum amount of layers this tag can have. */ - constructor(tagType: ArenaTagType, sourceMove: MoveId, sourceId: number, side: ArenaTagSide, maxLayers: number) { - super(tagType, 0, sourceMove, sourceId, side); + constructor(sourceMove: MoveId, sourceId: number | undefined, side: ArenaTagSide, maxLayers: number) { + super(0, sourceMove, sourceId, side); this.layers = 1; this.maxLayers = maxLayers; @@ -721,7 +813,7 @@ export class ArenaTrapTag extends ArenaTag { : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2); } - loadTag(source: any): void { + loadTag(source: NonFunctionProperties): void { super.loadTag(source); this.layers = source.layers; this.maxLayers = source.maxLayers; @@ -734,22 +826,31 @@ export class ArenaTrapTag extends ArenaTag { * in damage for 1, 2, or 3 layers of Spikes respectively if they are summoned into this trap. */ class SpikesTag extends ArenaTrapTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.SPIKES, MoveId.SPIKES, sourceId, side, 3); + public readonly tagType = ArenaTagType.SPIKES; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.SPIKES, sourceId, side, 3); } onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:spikesOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:spikesOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { @@ -758,7 +859,7 @@ class SpikesTag extends ArenaTrapTag { } const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (simulated || cancelled.value) { return !cancelled.value; } @@ -784,29 +885,38 @@ class SpikesTag extends ArenaTrapTag { * Pokémon summoned into this trap remove it entirely. */ class ToxicSpikesTag extends ArenaTrapTag { - private neutralized: boolean; + #neutralized: boolean; + public readonly tagType = ArenaTagType.TOXIC_SPIKES; - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.TOXIC_SPIKES, MoveId.TOXIC_SPIKES, sourceId, side, 2); - this.neutralized = false; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.TOXIC_SPIKES, sourceId, side, 2); + this.#neutralized = false; } onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:toxicSpikesOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + if (quiet) { + // We assume `quiet=true` means "just add the bloody tag no questions asked" + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ToxicSpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:toxicSpikesOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } onRemove(arena: Arena): void { - if (!this.neutralized) { + if (!this.#neutralized) { super.onRemove(arena); } } @@ -817,7 +927,7 @@ class ToxicSpikesTag extends ArenaTrapTag { return true; } if (pokemon.isOfType(PokemonType.POISON)) { - this.neutralized = true; + this.#neutralized = true; if (globalScene.arena.removeTag(this.tagType)) { globalScene.phaseManager.queueMessage( i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { @@ -851,61 +961,25 @@ class ToxicSpikesTag extends ArenaTrapTag { } } -/** - * Arena Tag class for delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}. - * Delays the attack's effect by a set amount of turns, usually 3 (including the turn the move is used), - * and deals damage after the turn count is reached. - */ -export class DelayedAttackTag extends ArenaTag { - public targetIndex: BattlerIndex; - - constructor( - tagType: ArenaTagType, - sourceMove: MoveId | undefined, - sourceId: number, - targetIndex: BattlerIndex, - side: ArenaTagSide = ArenaTagSide.BOTH, - ) { - super(tagType, 3, sourceMove, sourceId, side); - - this.targetIndex = targetIndex; - this.side = side; - } - - lapse(arena: Arena): boolean { - const ret = super.lapse(arena); - - if (!ret) { - // TODO: This should not add to move history (for Spite) - globalScene.phaseManager.unshiftNew( - "MoveEffectPhase", - this.sourceId!, - [this.targetIndex], - allMoves[this.sourceMove!], - MoveUseMode.FOLLOW_UP, - ); // TODO: are those bangs correct? - } - - return ret; - } - - onRemove(_arena: Arena): void {} -} - /** * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Stealth_Rock_(move) Stealth Rock}. * Applies up to 1 layer of Stealth Rocks, dealing percentage-based damage to any Pokémon * who is summoned into the trap, based on the Rock type's type effectiveness. */ class StealthRockTag extends ArenaTrapTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.STEALTH_ROCK, MoveId.STEALTH_ROCK, sourceId, side, 1); + public readonly tagType = ArenaTagType.STEALTH_ROCK; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.STEALTH_ROCK, sourceId, side, 1); } onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + if (quiet) { + return; + } + + const source = this.getSourcePokemon(); if (!quiet && source) { globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stealthRockOnAdd", { @@ -946,7 +1020,7 @@ class StealthRockTag extends ArenaTrapTag { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (cancelled.value) { return false; } @@ -983,27 +1057,42 @@ class StealthRockTag extends ArenaTrapTag { * to any Pokémon who is summoned into this trap. */ class StickyWebTag extends ArenaTrapTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.STICKY_WEB, MoveId.STICKY_WEB, sourceId, side, 1); + public readonly tagType = ArenaTagType.STICKY_WEB; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.STICKY_WEB, sourceId, side, 1); } onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:stickyWebOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:stickyWebOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { if (pokemon.isGrounded()) { const cancelled = new BooleanHolder(false); - applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled); + applyAbAttrs("ProtectStatAbAttr", { + pokemon, + cancelled, + stat: Stat.SPD, + stages: -1, + }); if (simulated) { return !cancelled.value; @@ -1037,14 +1126,57 @@ class StickyWebTag extends ArenaTrapTag { } } +/** + * Arena Tag class for delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}. + * Delays the attack's effect by a set amount of turns, usually 3 (including the turn the move is used), + * and deals damage after the turn count is reached. + */ +export class DelayedAttackTag extends SerializableArenaTag { + public targetIndex: BattlerIndex; + public readonly tagType: ArenaDelayedAttackTagType; + + constructor( + tagType: ArenaTagType.DOOM_DESIRE | ArenaTagType.FUTURE_SIGHT, + sourceMove: MoveId | undefined, + sourceId: number | undefined, + targetIndex: BattlerIndex, + side: ArenaTagSide = ArenaTagSide.BOTH, + ) { + super(3, sourceMove, sourceId, side); + this.tagType = tagType; + this.targetIndex = targetIndex; + this.side = side; + } + + lapse(arena: Arena): boolean { + const ret = super.lapse(arena); + + if (!ret) { + // TODO: This should not add to move history (for Spite) + globalScene.phaseManager.unshiftNew( + "MoveEffectPhase", + this.sourceId!, + [this.targetIndex], + allMoves[this.sourceMove!], + MoveUseMode.FOLLOW_UP, + ); // TODO: are those bangs correct? + } + + return ret; + } + + onRemove(_arena: Arena): void {} +} + /** * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Trick_Room_(move) Trick Room}. * Reverses the Speed stats for all Pokémon on the field as long as this arena tag is up, * also reversing the turn order for all Pokémon on the field as well. */ -export class TrickRoomTag extends ArenaTag { - constructor(turnCount: number, sourceId: number) { - super(ArenaTagType.TRICK_ROOM, turnCount, MoveId.TRICK_ROOM, sourceId); +export class TrickRoomTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.TRICK_ROOM; + constructor(turnCount: number, sourceId?: number) { + super(turnCount, MoveId.TRICK_ROOM, sourceId); } /** @@ -1061,14 +1193,19 @@ export class TrickRoomTag extends ArenaTag { } onAdd(_arena: Arena): void { - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:trickRoomOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); + super.onAdd(_arena); + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for TrickRoomTag on add message; id: ${this.sourceId}`); + return; } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:trickRoomOnAdd", { + moveName: this.getMoveName(), + }), + ); } onRemove(_arena: Arena): void { @@ -1081,9 +1218,10 @@ export class TrickRoomTag extends ArenaTag { * Grounds all Pokémon on the field, including Flying-types and those with * {@linkcode AbilityId.LEVITATE} for the duration of the arena tag, usually 5 turns. */ -export class GravityTag extends ArenaTag { - constructor(turnCount: number) { - super(ArenaTagType.GRAVITY, turnCount, MoveId.GRAVITY); +export class GravityTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.GRAVITY; + constructor(turnCount: number, sourceId?: number) { + super(turnCount, MoveId.GRAVITY, sourceId); } onAdd(_arena: Arena): void { @@ -1109,12 +1247,20 @@ export class GravityTag extends ArenaTag { * Doubles the Speed of the Pokémon who created this arena tag, as well as all allied Pokémon. * Applies this arena tag for 4 turns (including the turn the move was used). */ -class TailwindTag extends ArenaTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.TAILWIND, turnCount, MoveId.TAILWIND, sourceId, side); +class TailwindTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.TAILWIND; + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.TAILWIND, sourceId, side); } onAdd(_arena: Arena, quiet = false): void { + const source = this.getSourcePokemon(); + if (!source) { + return; + } + + super.onAdd(_arena, quiet); + if (!quiet) { globalScene.phaseManager.queueMessage( i18next.t( @@ -1123,15 +1269,14 @@ class TailwindTag extends ArenaTag { ); } - const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! - const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? []; - const phaseManager = globalScene.phaseManager; + const field = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); - for (const pokemon of party) { + for (const pokemon of field) { // Apply the CHARGED tag to party members with the WIND_POWER ability + // TODO: This should not be handled here if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { pokemon.addTag(BattlerTagType.CHARGED); - phaseManager.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -1142,9 +1287,16 @@ class TailwindTag extends ArenaTag { // Raise attack by one stage if party member has WIND_RIDER ability // TODO: Ability displays should be handled by the ability if (pokemon.hasAbility(AbilityId.WIND_RIDER)) { - phaseManager.queueAbilityDisplay(pokemon, false, true); - phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true); - phaseManager.queueAbilityDisplay(pokemon, false, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, true); + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.ATK], + 1, + true, + ); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, false); } } } @@ -1164,9 +1316,10 @@ class TailwindTag extends ArenaTag { * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Happy_Hour_(move) Happy Hour}. * Doubles the prize money from trainers and money moves like {@linkcode MoveId.PAY_DAY} and {@linkcode MoveId.MAKE_IT_RAIN}. */ -class HappyHourTag extends ArenaTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.HAPPY_HOUR, turnCount, MoveId.HAPPY_HOUR, sourceId, side); +class HappyHourTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.HAPPY_HOUR; + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.HAPPY_HOUR, sourceId, side); } onAdd(_arena: Arena): void { @@ -1179,8 +1332,9 @@ class HappyHourTag extends ArenaTag { } class SafeguardTag extends ArenaTag { - constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.SAFEGUARD, turnCount, MoveId.SAFEGUARD, sourceId, side); + public readonly tagType = ArenaTagType.SAFEGUARD; + constructor(turnCount: number, sourceId: number | undefined, side: ArenaTagSide) { + super(turnCount, MoveId.SAFEGUARD, sourceId, side); } onAdd(_arena: Arena): void { @@ -1201,39 +1355,44 @@ class SafeguardTag extends ArenaTag { } class NoneTag extends ArenaTag { + public readonly tagType = ArenaTagType.NONE; constructor() { - super(ArenaTagType.NONE, 0); + super(0); } } + /** * This arena tag facilitates the application of the move Imprison * Imprison remains in effect as long as the source Pokemon is active and present on the field. * Imprison will apply to any opposing Pokemon that switch onto the field as well. */ class ImprisonTag extends ArenaTrapTag { - constructor(sourceId: number, side: ArenaTagSide) { - super(ArenaTagType.IMPRISON, MoveId.IMPRISON, sourceId, side, 1); + public readonly tagType = ArenaTagType.IMPRISON; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(MoveId.IMPRISON, sourceId, side, 1); } /** - * This function applies the effects of Imprison to the opposing Pokemon already present on the field. - * @param arena + * Apply the effects of Imprison to all opposing on-field Pokemon. */ override onAdd() { const source = this.getSourcePokemon(); - if (source) { - const party = this.getAffectedPokemon(); - party?.forEach((p: Pokemon) => { - if (p.isAllowedInBattle()) { - p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId); - } - }); - globalScene.phaseManager.queueMessage( - i18next.t("battlerTags:imprisonOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); + if (!source) { + return; } + + const party = this.getAffectedPokemon(); + party.forEach(p => { + if (p.isAllowedInBattle()) { + p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId); + } + }); + + globalScene.phaseManager.queueMessage( + i18next.t("battlerTags:imprisonOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } /** @@ -1243,7 +1402,7 @@ class ImprisonTag extends ArenaTrapTag { */ override lapse(): boolean { const source = this.getSourcePokemon(); - return source ? source.isActive(true) : false; + return !!source?.isActive(true); } /** @@ -1265,7 +1424,7 @@ class ImprisonTag extends ArenaTrapTag { */ override onRemove(): void { const party = this.getAffectedPokemon(); - party?.forEach((p: Pokemon) => { + party.forEach(p => { p.removeTag(BattlerTagType.IMPRISON); }); } @@ -1278,9 +1437,10 @@ class ImprisonTag extends ArenaTrapTag { * 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, MoveId.FIRE_PLEDGE, sourceId, side); +class FireGrassPledgeTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.FIRE_GRASS_PLEDGE; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(4, MoveId.FIRE_PLEDGE, sourceId, side); } override onAdd(_arena: Arena): void { @@ -1326,9 +1486,10 @@ class FireGrassPledgeTag extends ArenaTag { * 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, MoveId.WATER_PLEDGE, sourceId, side); +class WaterFirePledgeTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.WATER_FIRE_PLEDGE; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(4, MoveId.WATER_PLEDGE, sourceId, side); } override onAdd(_arena: Arena): void { @@ -1360,9 +1521,10 @@ class WaterFirePledgeTag extends ArenaTag { * 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, MoveId.GRASS_PLEDGE, sourceId, side); +class GrassWaterPledgeTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.GRASS_WATER_PLEDGE; + constructor(sourceId: number | undefined, side: ArenaTagSide) { + super(4, MoveId.GRASS_PLEDGE, sourceId, side); } override onAdd(_arena: Arena): void { @@ -1382,9 +1544,10 @@ class GrassWaterPledgeTag extends ArenaTag { * If a Pokémon that's on the field when Fairy Lock is used goes on to faint later in the same turn, * the Pokémon that replaces it will still be unable to switch out in the following turn. */ -export class FairyLockTag extends ArenaTag { - constructor(turnCount: number, sourceId: number) { - super(ArenaTagType.FAIRY_LOCK, turnCount, MoveId.FAIRY_LOCK, sourceId); +export class FairyLockTag extends SerializableArenaTag { + public readonly tagType = ArenaTagType.FAIRY_LOCK; + constructor(turnCount: number, sourceId?: number) { + super(turnCount, MoveId.FAIRY_LOCK, sourceId); } onAdd(_arena: Arena): void { @@ -1398,15 +1561,29 @@ export class FairyLockTag extends ArenaTag { * Keeps track of the number of pokemon on the field with Neutralizing Gas - If it drops to zero, the effect is ended and abilities are reactivated * * Additionally ends onLose abilities when it is activated + * @sealed */ -export class SuppressAbilitiesTag extends ArenaTag { - private sourceCount: number; - private beingRemoved: boolean; +export class SuppressAbilitiesTag extends SerializableArenaTag { + // Source count is allowed to be inwardly mutable, but outwardly immutable + public readonly sourceCount: number; + public readonly tagType = ArenaTagType.NEUTRALIZING_GAS; + // Private field prevents field from appearing during serialization + /** Whether the tag is in the process of being removed */ + #beingRemoved: boolean; + /** Whether the tag is in the process of being removed */ + public get beingRemoved(): boolean { + return this.#beingRemoved; + } - constructor(sourceId: number) { - super(ArenaTagType.NEUTRALIZING_GAS, 0, undefined, sourceId); + constructor(sourceId?: number) { + super(0, undefined, sourceId); this.sourceCount = 1; - this.beingRemoved = false; + this.#beingRemoved = false; + } + + public override loadTag(source: NonFunctionProperties): void { + super.loadTag(source); + (this as Mutable).sourceCount = source.sourceCount; } public override onAdd(_arena: Arena): void { @@ -1416,19 +1593,23 @@ export class SuppressAbilitiesTag extends ArenaTag { for (const fieldPokemon of globalScene.getField(true)) { if (fieldPokemon && fieldPokemon.id !== pokemon.id) { - [true, false].forEach(passive => applyOnLoseAbAttrs(fieldPokemon, passive)); + // TODO: investigate whether we can just remove the foreach and call `applyAbAttrs` directly, providing + // the appropriate attributes (preLEaveField and IllusionBreak) + [true, false].forEach(passive => { + applyOnLoseAbAttrs({ pokemon: fieldPokemon, passive }); + }); } } } } public override onOverlap(_arena: Arena, source: Pokemon | null): void { - this.sourceCount++; + (this as Mutable).sourceCount++; this.playActivationMessage(source); } public onSourceLeave(arena: Arena): void { - this.sourceCount--; + (this as Mutable).sourceCount--; if (this.sourceCount <= 0) { arena.removeTag(ArenaTagType.NEUTRALIZING_GAS); } else if (this.sourceCount === 1) { @@ -1438,12 +1619,15 @@ export class SuppressAbilitiesTag extends ArenaTag { const setter = globalScene .getField() .filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0]; - applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr")); + applyOnGainAbAttrs({ + pokemon: setter, + passive: setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"), + }); } } public override onRemove(_arena: Arena, quiet = false) { - this.beingRemoved = true; + this.#beingRemoved = true; if (!quiet) { globalScene.phaseManager.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove")); } @@ -1451,7 +1635,9 @@ export class SuppressAbilitiesTag extends ArenaTag { for (const pokemon of globalScene.getField(true)) { // There is only one pokemon with this attr on the field on removal, so its abilities are already active if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) { - [true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive)); + [true, false].forEach(passive => { + applyOnGainAbAttrs({ pokemon, passive }); + }); } } } @@ -1460,10 +1646,6 @@ export class SuppressAbilitiesTag extends ArenaTag { return this.sourceCount > 1; } - public isBeingRemoved() { - return this.beingRemoved; - } - private playActivationMessage(pokemon: Pokemon | null) { if (pokemon) { globalScene.phaseManager.queueMessage( @@ -1480,7 +1662,7 @@ export function getArenaTag( tagType: ArenaTagType, turnCount: number, sourceMove: MoveId | undefined, - sourceId: number, + sourceId: number | undefined, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH, ): ArenaTag | null { @@ -1496,7 +1678,7 @@ export function getArenaTag( case ArenaTagType.CRAFTY_SHIELD: return new CraftyShieldTag(sourceId, side); case ArenaTagType.NO_CRIT: - return new NoCritTag(turnCount, sourceMove!, sourceId, side); // TODO: is this bang correct? + return new NoCritTag(turnCount, sourceMove, sourceId, side); case ArenaTagType.MUD_SPORT: return new MudSportTag(turnCount, sourceId); case ArenaTagType.WATER_SPORT: @@ -1509,7 +1691,10 @@ export function getArenaTag( return new ToxicSpikesTag(sourceId, side); case ArenaTagType.FUTURE_SIGHT: case ArenaTagType.DOOM_DESIRE: - return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex!, side); // TODO:questionable bang + if (isNullOrUndefined(targetIndex)) { + return null; // If missing target index, no tag is created + } + return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex, side); case ArenaTagType.WISH: return new WishTag(turnCount, sourceId, side); case ArenaTagType.STEALTH_ROCK: @@ -1519,7 +1704,7 @@ export function getArenaTag( case ArenaTagType.TRICK_ROOM: return new TrickRoomTag(turnCount, sourceId); case ArenaTagType.GRAVITY: - return new GravityTag(turnCount); + return new GravityTag(turnCount, sourceId); case ArenaTagType.REFLECT: return new ReflectTag(turnCount, sourceId, side); case ArenaTagType.LIGHT_SCREEN: @@ -1551,10 +1736,10 @@ export function getArenaTag( /** * When given a battler tag or json representing one, creates an actual ArenaTag object with the same data. - * @param {ArenaTag | any} source An arena tag - * @return {ArenaTag} The valid arena tag + * @param source - An arena tag + * @returns The valid arena tag */ -export function loadArenaTag(source: ArenaTag | any): ArenaTag { +export function loadArenaTag(source: (ArenaTag | ArenaTagTypeData) & { targetIndex?: BattlerIndex }): ArenaTag { const tag = getArenaTag( source.tagType, @@ -1567,3 +1752,37 @@ export function loadArenaTag(source: ArenaTag | any): ArenaTag { tag.loadTag(source); return tag; } + +export type ArenaTagTypeMap = { + [ArenaTagType.MUD_SPORT]: MudSportTag; + [ArenaTagType.WATER_SPORT]: WaterSportTag; + [ArenaTagType.ION_DELUGE]: IonDelugeTag; + [ArenaTagType.SPIKES]: SpikesTag; + [ArenaTagType.MIST]: MistTag; + [ArenaTagType.QUICK_GUARD]: QuickGuardTag; + [ArenaTagType.WIDE_GUARD]: WideGuardTag; + [ArenaTagType.MAT_BLOCK]: MatBlockTag; + [ArenaTagType.CRAFTY_SHIELD]: CraftyShieldTag; + [ArenaTagType.NO_CRIT]: NoCritTag; + [ArenaTagType.TOXIC_SPIKES]: ToxicSpikesTag; + [ArenaTagType.FUTURE_SIGHT]: DelayedAttackTag; + [ArenaTagType.DOOM_DESIRE]: DelayedAttackTag; + [ArenaTagType.WISH]: WishTag; + [ArenaTagType.STEALTH_ROCK]: StealthRockTag; + [ArenaTagType.STICKY_WEB]: StickyWebTag; + [ArenaTagType.TRICK_ROOM]: TrickRoomTag; + [ArenaTagType.GRAVITY]: GravityTag; + [ArenaTagType.REFLECT]: ReflectTag; + [ArenaTagType.LIGHT_SCREEN]: LightScreenTag; + [ArenaTagType.AURORA_VEIL]: AuroraVeilTag; + [ArenaTagType.TAILWIND]: TailwindTag; + [ArenaTagType.HAPPY_HOUR]: HappyHourTag; + [ArenaTagType.SAFEGUARD]: SafeguardTag; + [ArenaTagType.IMPRISON]: ImprisonTag; + [ArenaTagType.FIRE_GRASS_PLEDGE]: FireGrassPledgeTag; + [ArenaTagType.WATER_FIRE_PLEDGE]: WaterFirePledgeTag; + [ArenaTagType.GRASS_WATER_PLEDGE]: GrassWaterPledgeTag; + [ArenaTagType.FAIRY_LOCK]: FairyLockTag; + [ArenaTagType.NEUTRALIZING_GAS]: SuppressAbilitiesTag; + [ArenaTagType.NONE]: NoneTag; +}; diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index 713ab9637ab..32195b90e43 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -1,13 +1,13 @@ -import { PokemonType } from "#enums/pokemon-type"; -import { randSeedInt, getEnumValues } from "#app/utils/common"; -import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import i18next from "i18next"; +import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; import { BiomeId } from "#enums/biome-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { TimeOfDay } from "#enums/time-of-day"; import { TrainerType } from "#enums/trainer-type"; -// import beautify from "json-beautify"; +import { randSeedInt } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import i18next from "i18next"; export function getBiomeName(biome: BiomeId | -1) { if (biome === -1) { @@ -126,7 +126,7 @@ export const biomePokemonPools: BiomePokemonPools = { [BiomeId.TOWN]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.CATERPIE ], 7: [ SpeciesId.METAPOD ]}, + { 1: [ SpeciesId.CATERPIE ], 7: [ SpeciesId.METAPOD ] }, SpeciesId.SENTRET, SpeciesId.LEDYBA, SpeciesId.HOPPIP, @@ -134,12 +134,12 @@ export const biomePokemonPools: BiomePokemonPools = { SpeciesId.STARLY, SpeciesId.PIDOVE, SpeciesId.COTTONEE, - { 1: [ SpeciesId.SCATTERBUG ], 9: [ SpeciesId.SPEWPA ]}, + { 1: [ SpeciesId.SCATTERBUG ], 9: [ SpeciesId.SPEWPA ] }, SpeciesId.YUNGOOS, SpeciesId.SKWOVET ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.CATERPIE ], 7: [ SpeciesId.METAPOD ]}, + { 1: [ SpeciesId.CATERPIE ], 7: [ SpeciesId.METAPOD ] }, SpeciesId.SENTRET, SpeciesId.HOPPIP, SpeciesId.SUNKERN, @@ -147,12 +147,12 @@ export const biomePokemonPools: BiomePokemonPools = { SpeciesId.STARLY, SpeciesId.PIDOVE, SpeciesId.COTTONEE, - { 1: [ SpeciesId.SCATTERBUG ], 9: [ SpeciesId.SPEWPA ]}, + { 1: [ SpeciesId.SCATTERBUG ], 9: [ SpeciesId.SPEWPA ] }, SpeciesId.YUNGOOS, SpeciesId.SKWOVET ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.WEEDLE ], 7: [ SpeciesId.KAKUNA ]}, SpeciesId.POOCHYENA, SpeciesId.PATRAT, SpeciesId.PURRLOIN, SpeciesId.BLIPBUG ], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.WEEDLE ], 7: [ SpeciesId.KAKUNA ]}, SpeciesId.HOOTHOOT, SpeciesId.SPINARAK, SpeciesId.POOCHYENA, SpeciesId.CASCOON, SpeciesId.PATRAT, SpeciesId.PURRLOIN, SpeciesId.BLIPBUG ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.WEEDLE ], 7: [ SpeciesId.KAKUNA ] }, SpeciesId.POOCHYENA, SpeciesId.PATRAT, SpeciesId.PURRLOIN, SpeciesId.BLIPBUG ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.WEEDLE ], 7: [ SpeciesId.KAKUNA ] }, SpeciesId.HOOTHOOT, SpeciesId.SPINARAK, SpeciesId.POOCHYENA, SpeciesId.CASCOON, SpeciesId.PATRAT, SpeciesId.PURRLOIN, SpeciesId.BLIPBUG ], [TimeOfDay.ALL]: [ SpeciesId.PIDGEY, SpeciesId.RATTATA, SpeciesId.SPEAROW, SpeciesId.ZIGZAGOON, SpeciesId.WURMPLE, SpeciesId.TAILLOW, SpeciesId.BIDOOF, SpeciesId.LILLIPUP, SpeciesId.FLETCHLING, SpeciesId.WOOLOO, SpeciesId.LECHONK ] }, [BiomePoolTier.UNCOMMON]: { @@ -162,56 +162,56 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.EKANS, SpeciesId.ODDISH, SpeciesId.PARAS, SpeciesId.VENONAT, SpeciesId.MEOWTH, SpeciesId.SEEDOT, SpeciesId.SHROOMISH, SpeciesId.KRICKETOT, SpeciesId.VENIPEDE ], [TimeOfDay.ALL]: [ SpeciesId.NINCADA, SpeciesId.WHISMUR, SpeciesId.FIDOUGH ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.TANDEMAUS ], [TimeOfDay.DAY]: [ SpeciesId.TANDEMAUS ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ABRA, SpeciesId.SURSKIT, SpeciesId.ROOKIDEE ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.EEVEE, SpeciesId.RALTS ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.TANDEMAUS ], [TimeOfDay.DAY]: [ SpeciesId.TANDEMAUS ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ABRA, SpeciesId.SURSKIT, SpeciesId.ROOKIDEE ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.EEVEE, SpeciesId.RALTS ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.PLAINS]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.SENTRET ], 15: [ SpeciesId.FURRET ]}, { 1: [ SpeciesId.YUNGOOS ], 30: [ SpeciesId.GUMSHOOS ]}, { 1: [ SpeciesId.SKWOVET ], 24: [ SpeciesId.GREEDENT ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.SENTRET ], 15: [ SpeciesId.FURRET ]}, { 1: [ SpeciesId.YUNGOOS ], 30: [ SpeciesId.GUMSHOOS ]}, { 1: [ SpeciesId.SKWOVET ], 24: [ SpeciesId.GREEDENT ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.MEOWTH ], 28: [ SpeciesId.PERSIAN ]}, { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.ZUBAT ], 22: [ SpeciesId.GOLBAT ]}, { 1: [ SpeciesId.MEOWTH ], 28: [ SpeciesId.PERSIAN ]}, { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.ZIGZAGOON ], 20: [ SpeciesId.LINOONE ]}, { 1: [ SpeciesId.BIDOOF ], 15: [ SpeciesId.BIBAREL ]}, { 1: [ SpeciesId.LECHONK ], 18: [ SpeciesId.OINKOLOGNE ]}] + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.SENTRET ], 15: [ SpeciesId.FURRET ] }, { 1: [ SpeciesId.YUNGOOS ], 20: [ SpeciesId.GUMSHOOS ] }, { 1: [ SpeciesId.SKWOVET ], 24: [ SpeciesId.GREEDENT ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.SENTRET ], 15: [ SpeciesId.FURRET ] }, { 1: [ SpeciesId.YUNGOOS ], 20: [ SpeciesId.GUMSHOOS ] }, { 1: [ SpeciesId.SKWOVET ], 24: [ SpeciesId.GREEDENT ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.MEOWTH ], 28: [ SpeciesId.PERSIAN ] }, { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.ZUBAT ], 22: [ SpeciesId.GOLBAT ] }, { 1: [ SpeciesId.MEOWTH ], 28: [ SpeciesId.PERSIAN ] }, { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.ZIGZAGOON ], 20: [ SpeciesId.LINOONE ] }, { 1: [ SpeciesId.BIDOOF ], 15: [ SpeciesId.BIBAREL ] }, { 1: [ SpeciesId.LECHONK ], 18: [ SpeciesId.OINKOLOGNE ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.DODUO ], 31: [ SpeciesId.DODRIO ]}, - { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ]}, - { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ]}, - { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ]}, - { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ]} + { 1: [ SpeciesId.DODUO ], 31: [ SpeciesId.DODRIO ] }, + { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ] }, + { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ] }, + { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ] }, + { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ] } ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.DODUO ], 31: [ SpeciesId.DODRIO ]}, - { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ]}, - { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ]}, - { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ]}, - { 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ]}, - { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ]} + { 1: [ SpeciesId.DODUO ], 31: [ SpeciesId.DODRIO ] }, + { 1: [ SpeciesId.POOCHYENA ], 18: [ SpeciesId.MIGHTYENA ] }, + { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ] }, + { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ] }, + { 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ] }, + { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ] } ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.PIDGEY ], 18: [ SpeciesId.PIDGEOTTO ], 36: [ SpeciesId.PIDGEOT ]}, - { 1: [ SpeciesId.SPEAROW ], 20: [ SpeciesId.FEAROW ]}, + { 1: [ SpeciesId.PIDGEY ], 18: [ SpeciesId.PIDGEOTTO ], 36: [ SpeciesId.PIDGEOT ] }, + { 1: [ SpeciesId.SPEAROW ], 20: [ SpeciesId.FEAROW ] }, SpeciesId.PIKACHU, - { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ]} + { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ] } ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.PALDEA_TAUROS ], [TimeOfDay.DAY]: [ SpeciesId.PALDEA_TAUROS ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.ABRA ], 16: [ SpeciesId.KADABRA ]}, { 1: [ SpeciesId.BUNEARY ], 20: [ SpeciesId.LOPUNNY ]}, { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ]}] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.ABRA ], 16: [ SpeciesId.KADABRA ] }, { 1: [ SpeciesId.BUNEARY ], 20: [ SpeciesId.LOPUNNY ] }, { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FARFETCHD, SpeciesId.LICKITUNG, SpeciesId.CHANSEY, SpeciesId.EEVEE, SpeciesId.SNORLAX, { 1: [ SpeciesId.DUNSPARCE ], 62: [ SpeciesId.DUDUNSPARCE ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.LATIAS, SpeciesId.LATIOS ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FARFETCHD, SpeciesId.LICKITUNG, SpeciesId.CHANSEY, SpeciesId.EEVEE, SpeciesId.SNORLAX, { 1: [ SpeciesId.DUNSPARCE ], 62: [ SpeciesId.DUDUNSPARCE ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.LATIAS, SpeciesId.LATIOS ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.DODRIO, SpeciesId.FURRET, SpeciesId.GUMSHOOS, SpeciesId.GREEDENT ], [TimeOfDay.DAY]: [ SpeciesId.DODRIO, SpeciesId.FURRET, SpeciesId.GUMSHOOS, SpeciesId.GREEDENT ], @@ -226,22 +226,22 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FARFETCHD, SpeciesId.SNORLAX, SpeciesId.LICKILICKY, SpeciesId.DUDUNSPARCE ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LATIAS, SpeciesId.LATIOS ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LATIAS, SpeciesId.LATIOS ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.GRASS]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.HOPPIP ], 18: [ SpeciesId.SKIPLOOM ]}, SpeciesId.SUNKERN, SpeciesId.COTTONEE, SpeciesId.PETILIL ], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.HOPPIP ], 18: [ SpeciesId.SKIPLOOM ]}, SpeciesId.SUNKERN, SpeciesId.COTTONEE, SpeciesId.PETILIL ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ]}, { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ]}, { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.HOPPIP ], 18: [ SpeciesId.SKIPLOOM ] }, SpeciesId.SUNKERN, SpeciesId.COTTONEE, SpeciesId.PETILIL ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.HOPPIP ], 18: [ SpeciesId.SKIPLOOM ] }, SpeciesId.SUNKERN, SpeciesId.COTTONEE, SpeciesId.PETILIL ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ] }, { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ] }, { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ] } ], [TimeOfDay.ALL]: [] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ]}, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ]}, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ] }, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ] }, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] } ], [TimeOfDay.ALL]: [] }, [BiomePoolTier.RARE]: { @@ -249,28 +249,28 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BULBASAUR ], 16: [ SpeciesId.IVYSAUR ], 32: [ SpeciesId.VENUSAUR ]}, SpeciesId.GROWLITHE, { 1: [ SpeciesId.TURTWIG ], 18: [ SpeciesId.GROTLE ], 32: [ SpeciesId.TORTERRA ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BULBASAUR ], 16: [ SpeciesId.IVYSAUR ], 32: [ SpeciesId.VENUSAUR ] }, SpeciesId.GROWLITHE, { 1: [ SpeciesId.TURTWIG ], 18: [ SpeciesId.GROTLE ], 32: [ SpeciesId.TORTERRA ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUDOWOODO ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VIRIZION ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.JUMPLUFF, SpeciesId.SUNFLORA, SpeciesId.WHIMSICOTT ], [TimeOfDay.DAY]: [ SpeciesId.JUMPLUFF, SpeciesId.SUNFLORA, SpeciesId.WHIMSICOTT ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VENUSAUR, SpeciesId.SUDOWOODO, SpeciesId.TORTERRA ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VIRIZION ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUDOWOODO ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VIRIZION ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.JUMPLUFF, SpeciesId.SUNFLORA, SpeciesId.WHIMSICOTT ], [TimeOfDay.DAY]: [ SpeciesId.JUMPLUFF, SpeciesId.SUNFLORA, SpeciesId.WHIMSICOTT ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VENUSAUR, SpeciesId.SUDOWOODO, SpeciesId.TORTERRA ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VIRIZION ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.TALL_GRASS]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.BOUNSWEET ], 18: [ SpeciesId.STEENEE ], 58: [ SpeciesId.TSAREENA ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.NIDORAN_F ], 16: [ SpeciesId.NIDORINA ]}, { 1: [ SpeciesId.NIDORAN_M ], 16: [ SpeciesId.NIDORINO ]}, { 1: [ SpeciesId.BOUNSWEET ], 18: [ SpeciesId.STEENEE ], 58: [ SpeciesId.TSAREENA ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.ODDISH ], 21: [ SpeciesId.GLOOM ]}, { 1: [ SpeciesId.KRICKETOT ], 10: [ SpeciesId.KRICKETUNE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.ODDISH ], 21: [ SpeciesId.GLOOM ]}, { 1: [ SpeciesId.KRICKETOT ], 10: [ SpeciesId.KRICKETUNE ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.NINCADA ], 20: [ SpeciesId.NINJASK ]}, { 1: [ SpeciesId.FOMANTIS ], 44: [ SpeciesId.LURANTIS ]}, { 1: [ SpeciesId.NYMBLE ], 24: [ SpeciesId.LOKIX ]}] + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.BOUNSWEET ], 18: [ SpeciesId.STEENEE ], 58: [ SpeciesId.TSAREENA ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.NIDORAN_F ], 16: [ SpeciesId.NIDORINA ] }, { 1: [ SpeciesId.NIDORAN_M ], 16: [ SpeciesId.NIDORINO ] }, { 1: [ SpeciesId.BOUNSWEET ], 18: [ SpeciesId.STEENEE ], 58: [ SpeciesId.TSAREENA ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.ODDISH ], 21: [ SpeciesId.GLOOM ] }, { 1: [ SpeciesId.KRICKETOT ], 10: [ SpeciesId.KRICKETUNE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.ODDISH ], 21: [ SpeciesId.GLOOM ] }, { 1: [ SpeciesId.KRICKETOT ], 10: [ SpeciesId.KRICKETUNE ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.NINCADA ], 20: [ SpeciesId.NINJASK ] }, { 1: [ SpeciesId.FOMANTIS ], 34: [ SpeciesId.LURANTIS ] }, { 1: [ SpeciesId.NYMBLE ], 24: [ SpeciesId.LOKIX ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.PARAS ], 24: [ SpeciesId.PARASECT ]}, { 1: [ SpeciesId.VENONAT ], 31: [ SpeciesId.VENOMOTH ]}, { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ]}], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.PARAS ], 24: [ SpeciesId.PARASECT ] }, { 1: [ SpeciesId.VENONAT ], 31: [ SpeciesId.VENOMOTH ] }, { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ] } ], [TimeOfDay.ALL]: [ SpeciesId.VULPIX ] }, [BiomePoolTier.RARE]: { @@ -278,10 +278,10 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.PINSIR, { 1: [ SpeciesId.CHIKORITA ], 16: [ SpeciesId.BAYLEEF ], 32: [ SpeciesId.MEGANIUM ]}, { 1: [ SpeciesId.GIRAFARIG ], 62: [ SpeciesId.FARIGIRAF ]}, SpeciesId.ZANGOOSE, SpeciesId.KECLEON, SpeciesId.TROPIUS ] + [TimeOfDay.ALL]: [ SpeciesId.PINSIR, { 1: [ SpeciesId.CHIKORITA ], 16: [ SpeciesId.BAYLEEF ], 32: [ SpeciesId.MEGANIUM ] }, { 1: [ SpeciesId.GIRAFARIG ], 62: [ SpeciesId.FARIGIRAF ] }, SpeciesId.ZANGOOSE, SpeciesId.KECLEON, SpeciesId.TROPIUS ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SCYTHER, SpeciesId.SHEDINJA, SpeciesId.ROTOM ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SCYTHER, SpeciesId.SHEDINJA, SpeciesId.ROTOM ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.TSAREENA ], [TimeOfDay.DAY]: [ SpeciesId.NIDOQUEEN, SpeciesId.NIDOKING, SpeciesId.TSAREENA ], @@ -289,87 +289,87 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.VILEPLUME, SpeciesId.KRICKETUNE ], [TimeOfDay.ALL]: [ SpeciesId.NINJASK, SpeciesId.ZANGOOSE, SpeciesId.KECLEON, SpeciesId.LURANTIS, SpeciesId.LOKIX ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.BELLOSSOM ], [TimeOfDay.DAY]: [ SpeciesId.BELLOSSOM ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PINSIR, SpeciesId.MEGANIUM, SpeciesId.FARIGIRAF ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.BELLOSSOM ], [TimeOfDay.DAY]: [ SpeciesId.BELLOSSOM ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PINSIR, SpeciesId.MEGANIUM, SpeciesId.FARIGIRAF ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.METROPOLIS]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.YAMPER ], 25: [ SpeciesId.BOLTUND ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.YAMPER ], 25: [ SpeciesId.BOLTUND ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.HOUNDOUR ], 24: [ SpeciesId.HOUNDOOM ]}, { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.RATTATA ], 20: [ SpeciesId.RATICATE ]}, { 1: [ SpeciesId.ZIGZAGOON ], 20: [ SpeciesId.LINOONE ]}, { 1: [ SpeciesId.LILLIPUP ], 16: [ SpeciesId.HERDIER ], 32: [ SpeciesId.STOUTLAND ]}] + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.YAMPER ], 25: [ SpeciesId.BOLTUND ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.YAMPER ], 25: [ SpeciesId.BOLTUND ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.HOUNDOUR ], 24: [ SpeciesId.HOUNDOOM ] }, { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.RATTATA ], 20: [ SpeciesId.RATICATE ] }, { 1: [ SpeciesId.ZIGZAGOON ], 20: [ SpeciesId.LINOONE ] }, { 1: [ SpeciesId.LILLIPUP ], 16: [ SpeciesId.HERDIER ], 32: [ SpeciesId.STOUTLAND ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}, SpeciesId.INDEEDEE ], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}, SpeciesId.INDEEDEE ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.ESPURR ], 25: [ SpeciesId.MEOWSTIC ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.ESPURR ], 25: [ SpeciesId.MEOWSTIC ]}], - [TimeOfDay.ALL]: [ SpeciesId.PIKACHU, { 1: [ SpeciesId.GLAMEOW ], 38: [ SpeciesId.PURUGLY ]}, SpeciesId.FURFROU, { 1: [ SpeciesId.FIDOUGH ], 26: [ SpeciesId.DACHSBUN ]}, SpeciesId.SQUAWKABILLY ] + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] }, SpeciesId.INDEEDEE ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] }, SpeciesId.INDEEDEE ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.ESPURR ], 25: [ SpeciesId.MEOWSTIC ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.ESPURR ], 25: [ SpeciesId.MEOWSTIC ] } ], + [TimeOfDay.ALL]: [ SpeciesId.PIKACHU, { 1: [ SpeciesId.GLAMEOW ], 38: [ SpeciesId.PURUGLY ] }, SpeciesId.FURFROU, { 1: [ SpeciesId.FIDOUGH ], 26: [ SpeciesId.DACHSBUN ] }, SpeciesId.SQUAWKABILLY ] }, [BiomePoolTier.RARE]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.TANDEMAUS ], 25: [ SpeciesId.MAUSHOLD ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.TANDEMAUS ], 25: [ SpeciesId.MAUSHOLD ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.TANDEMAUS ], 25: [ SpeciesId.MAUSHOLD ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.TANDEMAUS ], 25: [ SpeciesId.MAUSHOLD ] } ], [TimeOfDay.DUSK]: [ SpeciesId.MORPEKO ], [TimeOfDay.NIGHT]: [ SpeciesId.MORPEKO ], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.VAROOM ], 40: [ SpeciesId.REVAVROOM ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.VAROOM ], 40: [ SpeciesId.REVAVROOM ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.EEVEE, SpeciesId.SMEARGLE ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CASTFORM ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.BOLTUND ], [TimeOfDay.DAY]: [ SpeciesId.BOLTUND ], [TimeOfDay.DUSK]: [ SpeciesId.MEOWSTIC ], [TimeOfDay.NIGHT]: [ SpeciesId.MEOWSTIC ], [TimeOfDay.ALL]: [ SpeciesId.STOUTLAND, SpeciesId.FURFROU, SpeciesId.DACHSBUN ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.MAUSHOLD ], [TimeOfDay.DAY]: [ SpeciesId.MAUSHOLD ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CASTFORM, SpeciesId.REVAVROOM ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.EEVEE, SpeciesId.SMEARGLE ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CASTFORM ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.BOLTUND ], [TimeOfDay.DAY]: [ SpeciesId.BOLTUND ], [TimeOfDay.DUSK]: [ SpeciesId.MEOWSTIC ], [TimeOfDay.NIGHT]: [ SpeciesId.MEOWSTIC ], [TimeOfDay.ALL]: [ SpeciesId.STOUTLAND, SpeciesId.FURFROU, SpeciesId.DACHSBUN ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.MAUSHOLD ], [TimeOfDay.DAY]: [ SpeciesId.MAUSHOLD ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CASTFORM, SpeciesId.REVAVROOM ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.FOREST]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [ SpeciesId.BUTTERFREE, - { 1: [ SpeciesId.BELLSPROUT ], 21: [ SpeciesId.WEEPINBELL ]}, - { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ]}, + { 1: [ SpeciesId.BELLSPROUT ], 21: [ SpeciesId.WEEPINBELL ] }, + { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ] }, SpeciesId.PETILIL, - { 1: [ SpeciesId.DEERLING ], 34: [ SpeciesId.SAWSBUCK ]}, + { 1: [ SpeciesId.DEERLING ], 34: [ SpeciesId.SAWSBUCK ] }, SpeciesId.VIVILLON ], [TimeOfDay.DAY]: [ SpeciesId.BUTTERFREE, - { 1: [ SpeciesId.BELLSPROUT ], 21: [ SpeciesId.WEEPINBELL ]}, + { 1: [ SpeciesId.BELLSPROUT ], 21: [ SpeciesId.WEEPINBELL ] }, SpeciesId.BEAUTIFLY, - { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ]}, + { 1: [ SpeciesId.COMBEE ], 21: [ SpeciesId.VESPIQUEN ] }, SpeciesId.PETILIL, - { 1: [ SpeciesId.DEERLING ], 34: [ SpeciesId.SAWSBUCK ]}, + { 1: [ SpeciesId.DEERLING ], 34: [ SpeciesId.SAWSBUCK ] }, SpeciesId.VIVILLON ], [TimeOfDay.DUSK]: [ SpeciesId.BEEDRILL, - { 1: [ SpeciesId.PINECO ], 31: [ SpeciesId.FORRETRESS ]}, - { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ]}, - { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ]}, - { 1: [ SpeciesId.VENIPEDE ], 22: [ SpeciesId.WHIRLIPEDE ], 30: [ SpeciesId.SCOLIPEDE ]} + { 1: [ SpeciesId.PINECO ], 31: [ SpeciesId.FORRETRESS ] }, + { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ] }, + { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ] }, + { 1: [ SpeciesId.VENIPEDE ], 22: [ SpeciesId.WHIRLIPEDE ], 30: [ SpeciesId.SCOLIPEDE ] } ], [TimeOfDay.NIGHT]: [ SpeciesId.BEEDRILL, - { 1: [ SpeciesId.VENONAT ], 31: [ SpeciesId.VENOMOTH ]}, - { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ]}, - { 1: [ SpeciesId.PINECO ], 31: [ SpeciesId.FORRETRESS ]}, + { 1: [ SpeciesId.VENONAT ], 31: [ SpeciesId.VENOMOTH ] }, + { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ] }, + { 1: [ SpeciesId.PINECO ], 31: [ SpeciesId.FORRETRESS ] }, SpeciesId.DUSTOX, - { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ]}, - { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ]}, - { 1: [ SpeciesId.VENIPEDE ], 22: [ SpeciesId.WHIRLIPEDE ], 30: [ SpeciesId.SCOLIPEDE ]} + { 1: [ SpeciesId.SEEDOT ], 14: [ SpeciesId.NUZLEAF ] }, + { 1: [ SpeciesId.SHROOMISH ], 23: [ SpeciesId.BRELOOM ] }, + { 1: [ SpeciesId.VENIPEDE ], 22: [ SpeciesId.WHIRLIPEDE ], 30: [ SpeciesId.SCOLIPEDE ] } ], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.TAROUNTULA ], 15: [ SpeciesId.SPIDOPS ]}, { 1: [ SpeciesId.NYMBLE ], 24: [ SpeciesId.LOKIX ]}, { 1: [ SpeciesId.SHROODLE ], 28: [ SpeciesId.GRAFAIAI ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.TAROUNTULA ], 15: [ SpeciesId.SPIDOPS ] }, { 1: [ SpeciesId.NYMBLE ], 24: [ SpeciesId.LOKIX ] }, { 1: [ SpeciesId.SHROODLE ], 28: [ SpeciesId.GRAFAIAI ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [ SpeciesId.ROSELIA, SpeciesId.MOTHIM, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ]}], - [TimeOfDay.DAY]: [ SpeciesId.ROSELIA, SpeciesId.MOTHIM, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ]}, { 1: [ SpeciesId.DOTTLER ], 30: [ SpeciesId.ORBEETLE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.HOOTHOOT ], 20: [ SpeciesId.NOCTOWL ]}, { 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ]}, { 1: [ SpeciesId.DOTTLER ], 30: [ SpeciesId.ORBEETLE ]}], + [TimeOfDay.DAWN]: [ SpeciesId.ROSELIA, SpeciesId.MOTHIM, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ] } ], + [TimeOfDay.DAY]: [ SpeciesId.ROSELIA, SpeciesId.MOTHIM, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ] }, { 1: [ SpeciesId.DOTTLER ], 30: [ SpeciesId.ORBEETLE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.HOOTHOOT ], 20: [ SpeciesId.NOCTOWL ] }, { 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ] }, { 1: [ SpeciesId.DOTTLER ], 30: [ SpeciesId.ORBEETLE ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ]}, - { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, - { 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ]}, - { 1: [ SpeciesId.PANSAGE ], 30: [ SpeciesId.SIMISAGE ]} + { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ] }, + { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, + { 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ] }, + { 1: [ SpeciesId.PANSAGE ], 30: [ SpeciesId.SIMISAGE ] } ] }, [BiomePoolTier.RARE]: { @@ -379,18 +379,18 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.SCYTHER ], [TimeOfDay.ALL]: [ SpeciesId.HERACROSS, - { 1: [ SpeciesId.TREECKO ], 16: [ SpeciesId.GROVYLE ], 36: [ SpeciesId.SCEPTILE ]}, + { 1: [ SpeciesId.TREECKO ], 16: [ SpeciesId.GROVYLE ], 36: [ SpeciesId.SCEPTILE ] }, SpeciesId.TROPIUS, SpeciesId.KARRABLAST, SpeciesId.SHELMET, - { 1: [ SpeciesId.CHESPIN ], 16: [ SpeciesId.QUILLADIN ], 36: [ SpeciesId.CHESNAUGHT ]}, - { 1: [ SpeciesId.ROWLET ], 17: [ SpeciesId.DARTRIX ], 34: [ SpeciesId.DECIDUEYE ]}, + { 1: [ SpeciesId.CHESPIN ], 16: [ SpeciesId.QUILLADIN ], 36: [ SpeciesId.CHESNAUGHT ] }, + { 1: [ SpeciesId.ROWLET ], 17: [ SpeciesId.DARTRIX ], 34: [ SpeciesId.DECIDUEYE ] }, SpeciesId.SQUAWKABILLY, - { 1: [ SpeciesId.TOEDSCOOL ], 30: [ SpeciesId.TOEDSCRUEL ]} + { 1: [ SpeciesId.TOEDSCOOL ], 30: [ SpeciesId.TOEDSCRUEL ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.BLOODMOON_URSALUNA ], [TimeOfDay.ALL]: [ SpeciesId.DURANT ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KARTANA, SpeciesId.WO_CHIEN ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.BLOODMOON_URSALUNA ], [TimeOfDay.ALL]: [ SpeciesId.DURANT ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KARTANA, SpeciesId.WO_CHIEN ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.VICTREEBEL, SpeciesId.MOTHIM, SpeciesId.VESPIQUEN, SpeciesId.LILLIGANT, SpeciesId.SAWSBUCK ], [TimeOfDay.DAY]: [ SpeciesId.VICTREEBEL, SpeciesId.BEAUTIFLY, SpeciesId.MOTHIM, SpeciesId.VESPIQUEN, SpeciesId.LILLIGANT, SpeciesId.SAWSBUCK ], @@ -405,29 +405,29 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.LYCANROC, SpeciesId.BLOODMOON_URSALUNA ], [TimeOfDay.ALL]: [ SpeciesId.HERACROSS, SpeciesId.SCEPTILE, SpeciesId.ESCAVALIER, SpeciesId.ACCELGOR, SpeciesId.DURANT, SpeciesId.CHESNAUGHT, SpeciesId.DECIDUEYE, SpeciesId.TOEDSCRUEL ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KARTANA, SpeciesId.WO_CHIEN ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CALYREX ]} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KARTANA, SpeciesId.WO_CHIEN ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CALYREX ] } }, [BiomeId.SEA]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ]}, { 1: [ SpeciesId.WINGULL ], 25: [ SpeciesId.PELIPPER ]}, SpeciesId.CRAMORANT, { 1: [ SpeciesId.FINIZEN ], 38: [ SpeciesId.PALAFIN ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ]}, { 1: [ SpeciesId.WINGULL ], 25: [ SpeciesId.PELIPPER ]}, SpeciesId.CRAMORANT, { 1: [ SpeciesId.FINIZEN ], 38: [ SpeciesId.PALAFIN ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.INKAY ], 30: [ SpeciesId.MALAMAR ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.FINNEON ], 31: [ SpeciesId.LUMINEON ]}, { 1: [ SpeciesId.INKAY ], 30: [ SpeciesId.MALAMAR ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.TENTACOOL ], 30: [ SpeciesId.TENTACRUEL ]}, { 1: [ SpeciesId.MAGIKARP ], 20: [ SpeciesId.GYARADOS ]}, { 1: [ SpeciesId.BUIZEL ], 26: [ SpeciesId.FLOATZEL ]}] + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ] }, { 1: [ SpeciesId.WINGULL ], 25: [ SpeciesId.PELIPPER ] }, SpeciesId.CRAMORANT, { 1: [ SpeciesId.FINIZEN ], 38: [ SpeciesId.PALAFIN ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ] }, { 1: [ SpeciesId.WINGULL ], 25: [ SpeciesId.PELIPPER ] }, SpeciesId.CRAMORANT, { 1: [ SpeciesId.FINIZEN ], 38: [ SpeciesId.PALAFIN ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.INKAY ], 30: [ SpeciesId.MALAMAR ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.FINNEON ], 31: [ SpeciesId.LUMINEON ] }, { 1: [ SpeciesId.INKAY ], 30: [ SpeciesId.MALAMAR ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.TENTACOOL ], 30: [ SpeciesId.TENTACRUEL ] }, { 1: [ SpeciesId.MAGIKARP ], 20: [ SpeciesId.GYARADOS ] }, { 1: [ SpeciesId.BUIZEL ], 26: [ SpeciesId.FLOATZEL ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ]}, SpeciesId.SHELLDER, { 1: [ SpeciesId.CARVANHA ], 30: [ SpeciesId.SHARPEDO ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ]}, SpeciesId.SHELLDER, { 1: [ SpeciesId.CHINCHOU ], 27: [ SpeciesId.LANTURN ]}, { 1: [ SpeciesId.CARVANHA ], 30: [ SpeciesId.SHARPEDO ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ] }, SpeciesId.SHELLDER, { 1: [ SpeciesId.CARVANHA ], 30: [ SpeciesId.SHARPEDO ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ] }, SpeciesId.SHELLDER, { 1: [ SpeciesId.CHINCHOU ], 27: [ SpeciesId.LANTURN ] }, { 1: [ SpeciesId.CARVANHA ], 30: [ SpeciesId.SHARPEDO ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.POLIWAG ], 25: [ SpeciesId.POLIWHIRL ]}, - { 1: [ SpeciesId.HORSEA ], 32: [ SpeciesId.SEADRA ]}, - { 1: [ SpeciesId.GOLDEEN ], 33: [ SpeciesId.SEAKING ]}, - { 1: [ SpeciesId.WAILMER ], 40: [ SpeciesId.WAILORD ]}, - { 1: [ SpeciesId.PANPOUR ], 30: [ SpeciesId.SIMIPOUR ]}, - { 1: [ SpeciesId.WATTREL ], 25: [ SpeciesId.KILOWATTREL ]} + { 1: [ SpeciesId.POLIWAG ], 25: [ SpeciesId.POLIWHIRL ] }, + { 1: [ SpeciesId.HORSEA ], 32: [ SpeciesId.SEADRA ] }, + { 1: [ SpeciesId.GOLDEEN ], 33: [ SpeciesId.SEAKING ] }, + { 1: [ SpeciesId.WAILMER ], 40: [ SpeciesId.WAILORD ] }, + { 1: [ SpeciesId.PANPOUR ], 30: [ SpeciesId.SIMIPOUR ] }, + { 1: [ SpeciesId.WATTREL ], 25: [ SpeciesId.KILOWATTREL ] } ] }, [BiomePoolTier.RARE]: { @@ -435,10 +435,10 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.LAPRAS, { 1: [ SpeciesId.PIPLUP ], 16: [ SpeciesId.PRINPLUP ], 36: [ SpeciesId.EMPOLEON ]}, { 1: [ SpeciesId.POPPLIO ], 17: [ SpeciesId.BRIONNE ], 34: [ SpeciesId.PRIMARINA ]}] + [TimeOfDay.ALL]: [ SpeciesId.LAPRAS, { 1: [ SpeciesId.PIPLUP ], 16: [ SpeciesId.PRINPLUP ], 36: [ SpeciesId.EMPOLEON ] }, { 1: [ SpeciesId.POPPLIO ], 17: [ SpeciesId.BRIONNE ], 34: [ SpeciesId.PRIMARINA ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KINGDRA, SpeciesId.ROTOM, { 1: [ SpeciesId.TIRTOUGA ], 37: [ SpeciesId.CARRACOSTA ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KINGDRA, SpeciesId.ROTOM, { 1: [ SpeciesId.TIRTOUGA ], 37: [ SpeciesId.CARRACOSTA ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.PELIPPER, SpeciesId.CRAMORANT, SpeciesId.PALAFIN ], [TimeOfDay.DAY]: [ SpeciesId.PELIPPER, SpeciesId.CRAMORANT, SpeciesId.PALAFIN ], @@ -446,34 +446,34 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.SHARPEDO, SpeciesId.LUMINEON, SpeciesId.MALAMAR ], [TimeOfDay.ALL]: [ SpeciesId.TENTACRUEL, SpeciesId.FLOATZEL, SpeciesId.SIMIPOUR, SpeciesId.KILOWATTREL ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KINGDRA, SpeciesId.EMPOLEON, SpeciesId.PRIMARINA ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LUGIA ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KINGDRA, SpeciesId.EMPOLEON, SpeciesId.PRIMARINA ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LUGIA ] } }, [BiomeId.SWAMP]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ]}, { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ]}, { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ]}, { 1: [ SpeciesId.PALDEA_WOOPER ], 20: [ SpeciesId.CLODSIRE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ]}, { 1: [ SpeciesId.PALDEA_WOOPER ], 20: [ SpeciesId.CLODSIRE ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ] }, { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ] }, { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ] }, { 1: [ SpeciesId.PALDEA_WOOPER ], 20: [ SpeciesId.CLODSIRE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ] }, { 1: [ SpeciesId.PALDEA_WOOPER ], 20: [ SpeciesId.CLODSIRE ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.POLIWAG ], 25: [ SpeciesId.POLIWHIRL ]}, - { 1: [ SpeciesId.GULPIN ], 26: [ SpeciesId.SWALOT ]}, - { 1: [ SpeciesId.SHELLOS ], 30: [ SpeciesId.GASTRODON ]}, - { 1: [ SpeciesId.TYMPOLE ], 25: [ SpeciesId.PALPITOAD ], 36: [ SpeciesId.SEISMITOAD ]} + { 1: [ SpeciesId.POLIWAG ], 25: [ SpeciesId.POLIWHIRL ] }, + { 1: [ SpeciesId.GULPIN ], 26: [ SpeciesId.SWALOT ] }, + { 1: [ SpeciesId.SHELLOS ], 30: [ SpeciesId.GASTRODON ] }, + { 1: [ SpeciesId.TYMPOLE ], 25: [ SpeciesId.PALPITOAD ], 36: [ SpeciesId.SEISMITOAD ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.EKANS ], 22: [ SpeciesId.ARBOK ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.PSYDUCK ], 33: [ SpeciesId.GOLDUCK ]}, - { 1: [ SpeciesId.BARBOACH ], 30: [ SpeciesId.WHISCASH ]}, - { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ]}, + { 1: [ SpeciesId.PSYDUCK ], 33: [ SpeciesId.GOLDUCK ] }, + { 1: [ SpeciesId.BARBOACH ], 30: [ SpeciesId.WHISCASH ] }, + { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ] }, SpeciesId.STUNFISK, - { 1: [ SpeciesId.MAREANIE ], 38: [ SpeciesId.TOXAPEX ]} + { 1: [ SpeciesId.MAREANIE ], 38: [ SpeciesId.TOXAPEX ] } ] }, [BiomePoolTier.RARE]: { @@ -481,16 +481,16 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.TOTODILE ], 18: [ SpeciesId.CROCONAW ], 30: [ SpeciesId.FERALIGATR ]}, { 1: [ SpeciesId.MUDKIP ], 16: [ SpeciesId.MARSHTOMP ], 36: [ SpeciesId.SWAMPERT ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.TOTODILE ], 18: [ SpeciesId.CROCONAW ], 30: [ SpeciesId.FERALIGATR ] }, { 1: [ SpeciesId.MUDKIP ], 16: [ SpeciesId.MARSHTOMP ], 36: [ SpeciesId.SWAMPERT ] } ] }, [BiomePoolTier.SUPER_RARE]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.GALAR_SLOWPOKE ], 40: [ SpeciesId.GALAR_SLOWBRO ]}, { 1: [ SpeciesId.HISUI_SLIGGOO ], 80: [ SpeciesId.HISUI_GOODRA ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.GALAR_SLOWPOKE ], 40: [ SpeciesId.GALAR_SLOWBRO ]}, { 1: [ SpeciesId.HISUI_SLIGGOO ], 80: [ SpeciesId.HISUI_GOODRA ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.GALAR_SLOWPOKE ], 40: [ SpeciesId.GALAR_SLOWBRO ] }, { 1: [ SpeciesId.HISUI_SLIGGOO ], 80: [ SpeciesId.HISUI_GOODRA ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.GALAR_SLOWPOKE ], 40: [ SpeciesId.GALAR_SLOWBRO ] }, { 1: [ SpeciesId.HISUI_SLIGGOO ], 80: [ SpeciesId.HISUI_GOODRA ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.POLITOED, SpeciesId.GALAR_STUNFISK ] }, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AZELF, SpeciesId.POIPOLE ]}, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AZELF, { 1: [ SpeciesId.POIPOLE ], 60: [ SpeciesId.NAGANADEL ] } ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.QUAGSIRE, SpeciesId.LUDICOLO ], [TimeOfDay.DAY]: [ SpeciesId.QUAGSIRE, SpeciesId.LUDICOLO ], @@ -505,22 +505,22 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FERALIGATR, SpeciesId.POLITOED, SpeciesId.SWAMPERT, SpeciesId.GALAR_STUNFISK ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AZELF, SpeciesId.NAGANADEL ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AZELF, { 1: [ SpeciesId.POIPOLE ], 60: [ SpeciesId.NAGANADEL ] } ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.BEACH]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.STARYU ], 30: [ SpeciesId.STARMIE ] } ], [TimeOfDay.DUSK]: [ SpeciesId.SHELLDER ], [TimeOfDay.NIGHT]: [ SpeciesId.SHELLDER ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.KRABBY ], 28: [ SpeciesId.KINGLER ]}, - { 1: [ SpeciesId.CORPHISH ], 30: [ SpeciesId.CRAWDAUNT ]}, - { 1: [ SpeciesId.DWEBBLE ], 34: [ SpeciesId.CRUSTLE ]}, - { 1: [ SpeciesId.BINACLE ], 39: [ SpeciesId.BARBARACLE ]}, - { 1: [ SpeciesId.MAREANIE ], 38: [ SpeciesId.TOXAPEX ]}, - { 1: [ SpeciesId.WIGLETT ], 26: [ SpeciesId.WUGTRIO ]} + { 1: [ SpeciesId.KRABBY ], 28: [ SpeciesId.KINGLER ] }, + { 1: [ SpeciesId.CORPHISH ], 30: [ SpeciesId.CRAWDAUNT ] }, + { 1: [ SpeciesId.DWEBBLE ], 34: [ SpeciesId.CRUSTLE ] }, + { 1: [ SpeciesId.BINACLE ], 39: [ SpeciesId.BARBARACLE ] }, + { 1: [ SpeciesId.MAREANIE ], 38: [ SpeciesId.TOXAPEX ] }, + { 1: [ SpeciesId.WIGLETT ], 26: [ SpeciesId.WUGTRIO ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -528,11 +528,11 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ]}, { 1: [ SpeciesId.CLAUNCHER ], 37: [ SpeciesId.CLAWITZER ]}, { 1: [ SpeciesId.SANDYGAST ], 42: [ SpeciesId.PALOSSAND ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ] }, { 1: [ SpeciesId.CLAUNCHER ], 37: [ SpeciesId.CLAWITZER ] }, { 1: [ SpeciesId.SANDYGAST ], 42: [ SpeciesId.PALOSSAND ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.QUAXLY ], 16: [ SpeciesId.QUAXWELL ], 36: [ SpeciesId.QUAQUAVAL ]}, SpeciesId.TATSUGIRI ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.TIRTOUGA ], 37: [ SpeciesId.CARRACOSTA ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRESSELIA, SpeciesId.KELDEO, SpeciesId.TAPU_FINI ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.QUAXLY ], 16: [ SpeciesId.QUAXWELL ], 36: [ SpeciesId.QUAQUAVAL ] }, SpeciesId.TATSUGIRI ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.TIRTOUGA ], 37: [ SpeciesId.CARRACOSTA ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRESSELIA, SpeciesId.KELDEO, SpeciesId.TAPU_FINI ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.STARMIE ], [TimeOfDay.DAY]: [ SpeciesId.STARMIE ], @@ -540,29 +540,29 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.CLOYSTER ], [TimeOfDay.ALL]: [ SpeciesId.KINGLER, SpeciesId.CRAWDAUNT, SpeciesId.WORMADAM, SpeciesId.CRUSTLE, SpeciesId.BARBARACLE, SpeciesId.CLAWITZER, SpeciesId.TOXAPEX, SpeciesId.PALOSSAND ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CARRACOSTA, SpeciesId.QUAQUAVAL ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRESSELIA, SpeciesId.KELDEO, SpeciesId.TAPU_FINI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CARRACOSTA, SpeciesId.QUAQUAVAL ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRESSELIA, SpeciesId.KELDEO, SpeciesId.TAPU_FINI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.LAKE]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ]}, { 1: [ SpeciesId.DUCKLETT ], 35: [ SpeciesId.SWANNA ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ]}, { 1: [ SpeciesId.DUCKLETT ], 35: [ SpeciesId.SWANNA ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ] }, { 1: [ SpeciesId.DUCKLETT ], 35: [ SpeciesId.SWANNA ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.LOTAD ], 14: [ SpeciesId.LOMBRE ] }, { 1: [ SpeciesId.DUCKLETT ], 35: [ SpeciesId.SWANNA ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.PSYDUCK ], 33: [ SpeciesId.GOLDUCK ]}, - { 1: [ SpeciesId.GOLDEEN ], 33: [ SpeciesId.SEAKING ]}, - { 1: [ SpeciesId.MAGIKARP ], 20: [ SpeciesId.GYARADOS ]}, - { 1: [ SpeciesId.CHEWTLE ], 22: [ SpeciesId.DREDNAW ]} + { 1: [ SpeciesId.PSYDUCK ], 33: [ SpeciesId.GOLDUCK ] }, + { 1: [ SpeciesId.GOLDEEN ], 33: [ SpeciesId.SEAKING ] }, + { 1: [ SpeciesId.MAGIKARP ], 20: [ SpeciesId.GYARADOS ] }, + { 1: [ SpeciesId.CHEWTLE ], 22: [ SpeciesId.DREDNAW ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.DEWPIDER ], 22: [ SpeciesId.ARAQUANID ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.DEWPIDER ], 22: [ SpeciesId.ARAQUANID ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.DEWPIDER ], 22: [ SpeciesId.ARAQUANID ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.DEWPIDER ], 22: [ SpeciesId.ARAQUANID ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ]}, { 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ]}, { 1: [ SpeciesId.SURSKIT ], 22: [ SpeciesId.MASQUERAIN ]}, SpeciesId.WISHIWASHI, SpeciesId.FLAMIGO ] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SLOWPOKE ], 37: [ SpeciesId.SLOWBRO ] }, { 1: [ SpeciesId.WOOPER ], 20: [ SpeciesId.QUAGSIRE ] }, { 1: [ SpeciesId.SURSKIT ], 22: [ SpeciesId.MASQUERAIN ] }, SpeciesId.WISHIWASHI, SpeciesId.FLAMIGO ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], @@ -570,14 +570,14 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.SQUIRTLE ], 16: [ SpeciesId.WARTORTLE ], 36: [ SpeciesId.BLASTOISE ]}, - { 1: [ SpeciesId.OSHAWOTT ], 17: [ SpeciesId.DEWOTT ], 36: [ SpeciesId.SAMUROTT ]}, - { 1: [ SpeciesId.FROAKIE ], 16: [ SpeciesId.FROGADIER ], 36: [ SpeciesId.GRENINJA ]}, - { 1: [ SpeciesId.SOBBLE ], 16: [ SpeciesId.DRIZZILE ], 35: [ SpeciesId.INTELEON ]} + { 1: [ SpeciesId.SQUIRTLE ], 16: [ SpeciesId.WARTORTLE ], 36: [ SpeciesId.BLASTOISE ] }, + { 1: [ SpeciesId.OSHAWOTT ], 17: [ SpeciesId.DEWOTT ], 36: [ SpeciesId.SAMUROTT ] }, + { 1: [ SpeciesId.FROAKIE ], 16: [ SpeciesId.FROGADIER ], 36: [ SpeciesId.GRENINJA ] }, + { 1: [ SpeciesId.SOBBLE ], 16: [ SpeciesId.DRIZZILE ], 35: [ SpeciesId.INTELEON ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VAPOREON, SpeciesId.SLOWKING ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUICUNE, SpeciesId.MESPRIT ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.VAPOREON, SpeciesId.SLOWKING ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUICUNE, SpeciesId.MESPRIT ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.SWANNA, SpeciesId.ARAQUANID ], [TimeOfDay.DAY]: [ SpeciesId.SWANNA, SpeciesId.ARAQUANID ], @@ -585,9 +585,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.AZUMARILL ], [TimeOfDay.ALL]: [ SpeciesId.GOLDUCK, SpeciesId.SLOWBRO, SpeciesId.SEAKING, SpeciesId.GYARADOS, SpeciesId.MASQUERAIN, SpeciesId.WISHIWASHI, SpeciesId.DREDNAW ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLASTOISE, SpeciesId.VAPOREON, SpeciesId.SLOWKING, SpeciesId.SAMUROTT, SpeciesId.GRENINJA, SpeciesId.INTELEON ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUICUNE, SpeciesId.MESPRIT ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLASTOISE, SpeciesId.VAPOREON, SpeciesId.SLOWKING, SpeciesId.SAMUROTT, SpeciesId.GRENINJA, SpeciesId.INTELEON ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SUICUNE, SpeciesId.MESPRIT ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.SEABED]: { [BiomePoolTier.COMMON]: { @@ -596,12 +596,12 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.CHINCHOU ], 27: [ SpeciesId.LANTURN ]}, + { 1: [ SpeciesId.CHINCHOU ], 27: [ SpeciesId.LANTURN ] }, SpeciesId.REMORAID, SpeciesId.CLAMPERL, SpeciesId.BASCULIN, - { 1: [ SpeciesId.FRILLISH ], 40: [ SpeciesId.JELLICENT ]}, - { 1: [ SpeciesId.ARROKUDA ], 26: [ SpeciesId.BARRASKEWDA ]}, + { 1: [ SpeciesId.FRILLISH ], 40: [ SpeciesId.JELLICENT ] }, + { 1: [ SpeciesId.ARROKUDA ], 26: [ SpeciesId.BARRASKEWDA ] }, SpeciesId.VELUZA ] }, @@ -611,12 +611,12 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.TENTACOOL ], 30: [ SpeciesId.TENTACRUEL ]}, + { 1: [ SpeciesId.TENTACOOL ], 30: [ SpeciesId.TENTACRUEL ] }, SpeciesId.SHELLDER, - { 1: [ SpeciesId.WAILMER ], 40: [ SpeciesId.WAILORD ]}, + { 1: [ SpeciesId.WAILMER ], 40: [ SpeciesId.WAILORD ] }, SpeciesId.LUVDISC, - { 1: [ SpeciesId.SHELLOS ], 30: [ SpeciesId.GASTRODON ]}, - { 1: [ SpeciesId.SKRELP ], 48: [ SpeciesId.DRAGALGE ]}, + { 1: [ SpeciesId.SHELLOS ], 30: [ SpeciesId.GASTRODON ] }, + { 1: [ SpeciesId.SKRELP ], 48: [ SpeciesId.DRAGALGE ] }, SpeciesId.PINCURCHIN, SpeciesId.DONDOZO ] @@ -626,7 +626,7 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.QWILFISH, SpeciesId.CORSOLA, SpeciesId.OCTILLERY, { 1: [ SpeciesId.MANTYKE ], 52: [ SpeciesId.MANTINE ]}, SpeciesId.ALOMOMOLA, { 1: [ SpeciesId.TYNAMO ], 39: [ SpeciesId.EELEKTRIK ]}, SpeciesId.DHELMISE ] + [TimeOfDay.ALL]: [ SpeciesId.QWILFISH, SpeciesId.CORSOLA, SpeciesId.OCTILLERY, { 1: [ SpeciesId.MANTYKE ], 52: [ SpeciesId.MANTINE ] }, SpeciesId.ALOMOMOLA, { 1: [ SpeciesId.TYNAMO ], 39: [ SpeciesId.EELEKTRIK ] }, SpeciesId.DHELMISE ] }, [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], @@ -634,16 +634,16 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.OMANYTE ], 40: [ SpeciesId.OMASTAR ]}, - { 1: [ SpeciesId.KABUTO ], 40: [ SpeciesId.KABUTOPS ]}, + { 1: [ SpeciesId.OMANYTE ], 40: [ SpeciesId.OMASTAR ] }, + { 1: [ SpeciesId.KABUTO ], 40: [ SpeciesId.KABUTOPS ] }, SpeciesId.RELICANTH, SpeciesId.PYUKUMUKU, - { 1: [ SpeciesId.GALAR_CORSOLA ], 38: [ SpeciesId.CURSOLA ]}, + { 1: [ SpeciesId.GALAR_CORSOLA ], 38: [ SpeciesId.CURSOLA ] }, SpeciesId.ARCTOVISH, SpeciesId.HISUI_QWILFISH ] }, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FEEBAS, SpeciesId.NIHILEGO ]}, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FEEBAS, SpeciesId.NIHILEGO ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -658,56 +658,56 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.OMASTAR, SpeciesId.KABUTOPS, SpeciesId.RELICANTH, SpeciesId.EELEKTROSS, SpeciesId.PYUKUMUKU, SpeciesId.DHELMISE, SpeciesId.CURSOLA, SpeciesId.ARCTOVISH, SpeciesId.BASCULEGION, SpeciesId.OVERQWIL ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MILOTIC, SpeciesId.NIHILEGO ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KYOGRE ]} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MILOTIC, SpeciesId.NIHILEGO ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KYOGRE ] } }, [BiomeId.MOUNTAIN]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.TAILLOW ], 22: [ SpeciesId.SWELLOW ]}, - { 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ]}, - { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ]}, - { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ]}, - { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ]} + { 1: [ SpeciesId.TAILLOW ], 22: [ SpeciesId.SWELLOW ] }, + { 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ] }, + { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ] }, + { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ] }, + { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ] } ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.TAILLOW ], 22: [ SpeciesId.SWELLOW ]}, - { 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ]}, - { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ]}, - { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ]}, - { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ]} + { 1: [ SpeciesId.TAILLOW ], 22: [ SpeciesId.SWELLOW ] }, + { 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ] }, + { 1: [ SpeciesId.STARLY ], 14: [ SpeciesId.STARAVIA ], 34: [ SpeciesId.STARAPTOR ] }, + { 1: [ SpeciesId.PIDOVE ], 21: [ SpeciesId.TRANQUILL ], 32: [ SpeciesId.UNFEZANT ] }, + { 1: [ SpeciesId.FLETCHLING ], 17: [ SpeciesId.FLETCHINDER ], 35: [ SpeciesId.TALONFLAME ] } ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ]}, { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ]}, { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.PIDGEY ], 18: [ SpeciesId.PIDGEOTTO ], 36: [ SpeciesId.PIDGEOT ]}, { 1: [ SpeciesId.SPEAROW ], 20: [ SpeciesId.FEAROW ]}, { 1: [ SpeciesId.SKIDDO ], 32: [ SpeciesId.GOGOAT ]}] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ] }, { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ] }, { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.PIDGEY ], 18: [ SpeciesId.PIDGEOTTO ], 36: [ SpeciesId.PIDGEOT ] }, { 1: [ SpeciesId.SPEAROW ], 20: [ SpeciesId.FEAROW ] }, { 1: [ SpeciesId.SKIDDO ], 32: [ SpeciesId.GOGOAT ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, - { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ]}, - { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}, - { 1: [ SpeciesId.RUFFLET ], 54: [ SpeciesId.BRAVIARY ]}, - { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ]}, - { 1: [ SpeciesId.FLITTLE ], 35: [ SpeciesId.ESPATHRA ]}, + { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, + { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ] }, + { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] }, + { 1: [ SpeciesId.RUFFLET ], 54: [ SpeciesId.BRAVIARY ] }, + { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ] }, + { 1: [ SpeciesId.FLITTLE ], 35: [ SpeciesId.ESPATHRA ] }, SpeciesId.BOMBIRDIER ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, - { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ]}, - { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}, - { 1: [ SpeciesId.RUFFLET ], 54: [ SpeciesId.BRAVIARY ]}, - { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ]}, - { 1: [ SpeciesId.FLITTLE ], 35: [ SpeciesId.ESPATHRA ]}, + { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, + { 1: [ SpeciesId.ARON ], 32: [ SpeciesId.LAIRON ], 42: [ SpeciesId.AGGRON ] }, + { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] }, + { 1: [ SpeciesId.RUFFLET ], 54: [ SpeciesId.BRAVIARY ] }, + { 1: [ SpeciesId.ROOKIDEE ], 18: [ SpeciesId.CORVISQUIRE ], 38: [ SpeciesId.CORVIKNIGHT ] }, + { 1: [ SpeciesId.FLITTLE ], 35: [ SpeciesId.ESPATHRA ] }, SpeciesId.BOMBIRDIER ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.VULLABY ], 54: [ SpeciesId.MANDIBUZZ ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.VULLABY ], 54: [ SpeciesId.MANDIBUZZ ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.VULLABY ], 54: [ SpeciesId.MANDIBUZZ ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.VULLABY ], 54: [ SpeciesId.MANDIBUZZ ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ]}, - { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ]}, - { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ]}, - { 1: [ SpeciesId.SLUGMA ], 38: [ SpeciesId.MAGCARGO ]}, - { 1: [ SpeciesId.NACLI ], 24: [ SpeciesId.NACLSTACK ], 38: [ SpeciesId.GARGANACL ]} + { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ] }, + { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ] }, + { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ] }, + { 1: [ SpeciesId.SLUGMA ], 38: [ SpeciesId.MAGCARGO ] }, + { 1: [ SpeciesId.NACLI ], 24: [ SpeciesId.NACLSTACK ], 38: [ SpeciesId.GARGANACL ] } ] }, [BiomePoolTier.RARE]: { @@ -715,7 +715,7 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.MURKROW ], - [TimeOfDay.ALL]: [ SpeciesId.SKARMORY, { 1: [ SpeciesId.TORCHIC ], 16: [ SpeciesId.COMBUSKEN ], 36: [ SpeciesId.BLAZIKEN ]}, { 1: [ SpeciesId.SPOINK ], 32: [ SpeciesId.GRUMPIG ]}, SpeciesId.HAWLUCHA, SpeciesId.KLAWF ] + [TimeOfDay.ALL]: [ SpeciesId.SKARMORY, { 1: [ SpeciesId.TORCHIC ], 16: [ SpeciesId.COMBUSKEN ], 36: [ SpeciesId.BLAZIKEN ] }, { 1: [ SpeciesId.SPOINK ], 32: [ SpeciesId.GRUMPIG ] }, SpeciesId.HAWLUCHA, SpeciesId.KLAWF ] }, [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], @@ -723,16 +723,16 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ]}, - { 1: [ SpeciesId.CRANIDOS ], 30: [ SpeciesId.RAMPARDOS ]}, - { 1: [ SpeciesId.SHIELDON ], 30: [ SpeciesId.BASTIODON ]}, - { 1: [ SpeciesId.GIBLE ], 24: [ SpeciesId.GABITE ], 48: [ SpeciesId.GARCHOMP ]}, + { 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ] }, + { 1: [ SpeciesId.CRANIDOS ], 30: [ SpeciesId.RAMPARDOS ] }, + { 1: [ SpeciesId.SHIELDON ], 30: [ SpeciesId.BASTIODON ] }, + { 1: [ SpeciesId.GIBLE ], 24: [ SpeciesId.GABITE ], 48: [ SpeciesId.GARCHOMP ] }, SpeciesId.ROTOM, SpeciesId.ARCHEOPS, - { 1: [ SpeciesId.AXEW ], 38: [ SpeciesId.FRAXURE ]} + { 1: [ SpeciesId.AXEW ], 38: [ SpeciesId.FRAXURE ] } ] }, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TORNADUS, SpeciesId.TING_LU, SpeciesId.OGERPON ]}, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TORNADUS, SpeciesId.TING_LU, SpeciesId.OGERPON ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.SWELLOW, SpeciesId.ALTARIA, SpeciesId.STARAPTOR, SpeciesId.UNFEZANT, SpeciesId.BRAVIARY, SpeciesId.TALONFLAME, SpeciesId.CORVIKNIGHT, SpeciesId.ESPATHRA ], [TimeOfDay.DAY]: [ SpeciesId.SWELLOW, SpeciesId.ALTARIA, SpeciesId.STARAPTOR, SpeciesId.UNFEZANT, SpeciesId.BRAVIARY, SpeciesId.TALONFLAME, SpeciesId.CORVIKNIGHT, SpeciesId.ESPATHRA ], @@ -740,39 +740,39 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.MANDIBUZZ ], [TimeOfDay.ALL]: [ SpeciesId.PIDGEOT, SpeciesId.FEAROW, SpeciesId.SKARMORY, SpeciesId.AGGRON, SpeciesId.GOGOAT, SpeciesId.GARGANACL ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.HISUI_BRAVIARY ], [TimeOfDay.DAY]: [ SpeciesId.HISUI_BRAVIARY ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLAZIKEN, SpeciesId.RAMPARDOS, SpeciesId.BASTIODON, SpeciesId.HAWLUCHA ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.TORNADUS, SpeciesId.TING_LU, SpeciesId.OGERPON ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HO_OH ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.HISUI_BRAVIARY ], [TimeOfDay.DAY]: [ SpeciesId.HISUI_BRAVIARY ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLAZIKEN, SpeciesId.RAMPARDOS, SpeciesId.BASTIODON, SpeciesId.HAWLUCHA ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.TORNADUS, SpeciesId.TING_LU, SpeciesId.OGERPON ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HO_OH ] } }, [BiomeId.BADLANDS]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.PHANPY ], 25: [ SpeciesId.DONPHAN ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.PHANPY ], 25: [ SpeciesId.DONPHAN ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.PHANPY ], 25: [ SpeciesId.DONPHAN ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.PHANPY ], 25: [ SpeciesId.DONPHAN ] } ], [TimeOfDay.DUSK]: [], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ]}], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.DIGLETT ], 26: [ SpeciesId.DUGTRIO ]}, - { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ]}, - { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, - { 1: [ SpeciesId.DRILBUR ], 31: [ SpeciesId.EXCADRILL ]}, - { 1: [ SpeciesId.MUDBRAY ], 30: [ SpeciesId.MUDSDALE ]} + { 1: [ SpeciesId.DIGLETT ], 26: [ SpeciesId.DUGTRIO ] }, + { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ] }, + { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, + { 1: [ SpeciesId.DRILBUR ], 31: [ SpeciesId.EXCADRILL ] }, + { 1: [ SpeciesId.MUDBRAY ], 30: [ SpeciesId.MUDSDALE ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.SIZZLIPEDE ], 28: [ SpeciesId.CENTISKORCH ]}, { 1: [ SpeciesId.CAPSAKID ], 30: [ SpeciesId.SCOVILLAIN ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.SIZZLIPEDE ], 28: [ SpeciesId.CENTISKORCH ]}, { 1: [ SpeciesId.CAPSAKID ], 30: [ SpeciesId.SCOVILLAIN ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.SIZZLIPEDE ], 28: [ SpeciesId.CENTISKORCH ] }, { 1: [ SpeciesId.CAPSAKID ], 30: [ SpeciesId.SCOVILLAIN ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.SIZZLIPEDE ], 28: [ SpeciesId.CENTISKORCH ] }, { 1: [ SpeciesId.CAPSAKID ], 30: [ SpeciesId.SCOVILLAIN ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.SANDSHREW ], 22: [ SpeciesId.SANDSLASH ]}, - { 1: [ SpeciesId.NUMEL ], 33: [ SpeciesId.CAMERUPT ]}, - { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}, - { 1: [ SpeciesId.CUFANT ], 34: [ SpeciesId.COPPERAJAH ]} + { 1: [ SpeciesId.SANDSHREW ], 22: [ SpeciesId.SANDSLASH ] }, + { 1: [ SpeciesId.NUMEL ], 33: [ SpeciesId.CAMERUPT ] }, + { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] }, + { 1: [ SpeciesId.CUFANT ], 34: [ SpeciesId.COPPERAJAH ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ONIX, SpeciesId.GLIGAR, { 1: [ SpeciesId.POLTCHAGEIST ], 30: [ SpeciesId.SINISTCHA ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LANDORUS, SpeciesId.OKIDOGI ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ONIX, SpeciesId.GLIGAR, { 1: [ SpeciesId.POLTCHAGEIST ], 30: [ SpeciesId.SINISTCHA ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LANDORUS, SpeciesId.OKIDOGI ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.DONPHAN, SpeciesId.CENTISKORCH, SpeciesId.SCOVILLAIN ], [TimeOfDay.DAY]: [ SpeciesId.DONPHAN, SpeciesId.CENTISKORCH, SpeciesId.SCOVILLAIN ], @@ -780,9 +780,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.MAROWAK ], [TimeOfDay.ALL]: [ SpeciesId.DUGTRIO, SpeciesId.GOLEM, SpeciesId.RHYPERIOR, SpeciesId.GLISCOR, SpeciesId.EXCADRILL, SpeciesId.MUDSDALE, SpeciesId.COPPERAJAH ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.STEELIX, SpeciesId.SINISTCHA ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LANDORUS, SpeciesId.OKIDOGI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GROUDON ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.STEELIX, SpeciesId.SINISTCHA ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.LANDORUS, SpeciesId.OKIDOGI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GROUDON ] } }, [BiomeId.CAVE]: { [BiomePoolTier.COMMON]: { @@ -791,27 +791,27 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.ZUBAT ], 22: [ SpeciesId.GOLBAT ]}, - { 1: [ SpeciesId.PARAS ], 24: [ SpeciesId.PARASECT ]}, - { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, - { 1: [ SpeciesId.WHISMUR ], 20: [ SpeciesId.LOUDRED ], 40: [ SpeciesId.EXPLOUD ]}, - { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ]}, - { 1: [ SpeciesId.WOOBAT ], 20: [ SpeciesId.SWOOBAT ]}, - { 1: [ SpeciesId.BUNNELBY ], 20: [ SpeciesId.DIGGERSBY ]}, - { 1: [ SpeciesId.NACLI ], 24: [ SpeciesId.NACLSTACK ], 38: [ SpeciesId.GARGANACL ]} + { 1: [ SpeciesId.ZUBAT ], 22: [ SpeciesId.GOLBAT ] }, + { 1: [ SpeciesId.PARAS ], 24: [ SpeciesId.PARASECT ] }, + { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, + { 1: [ SpeciesId.WHISMUR ], 20: [ SpeciesId.LOUDRED ], 40: [ SpeciesId.EXPLOUD ] }, + { 1: [ SpeciesId.ROGGENROLA ], 25: [ SpeciesId.BOLDORE ] }, + { 1: [ SpeciesId.WOOBAT ], 20: [ SpeciesId.SWOOBAT ] }, + { 1: [ SpeciesId.BUNNELBY ], 20: [ SpeciesId.DIGGERSBY ] }, + { 1: [ SpeciesId.NACLI ], 24: [ SpeciesId.NACLSTACK ], 38: [ SpeciesId.GARGANACL ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.ROCKRUFF ], 25: [ SpeciesId.LYCANROC ] } ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ]}, - { 1: [ SpeciesId.MAKUHITA ], 24: [ SpeciesId.HARIYAMA ]}, + { 1: [ SpeciesId.GEODUDE ], 25: [ SpeciesId.GRAVELER ] }, + { 1: [ SpeciesId.MAKUHITA ], 24: [ SpeciesId.HARIYAMA ] }, SpeciesId.NOSEPASS, - { 1: [ SpeciesId.NOIBAT ], 48: [ SpeciesId.NOIVERN ]}, - { 1: [ SpeciesId.WIMPOD ], 30: [ SpeciesId.GOLISOPOD ]} + { 1: [ SpeciesId.NOIBAT ], 48: [ SpeciesId.NOIVERN ] }, + { 1: [ SpeciesId.WIMPOD ], 30: [ SpeciesId.GOLISOPOD ] } ] }, [BiomePoolTier.RARE]: { @@ -819,10 +819,10 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.ONIX, { 1: [ SpeciesId.FERROSEED ], 40: [ SpeciesId.FERROTHORN ]}, SpeciesId.CARBINK, { 1: [ SpeciesId.GLIMMET ], 35: [ SpeciesId.GLIMMORA ]}] + [TimeOfDay.ALL]: [ SpeciesId.ONIX, { 1: [ SpeciesId.FERROSEED ], 40: [ SpeciesId.FERROTHORN ] }, SpeciesId.CARBINK, { 1: [ SpeciesId.GLIMMET ], 35: [ SpeciesId.GLIMMORA ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHUCKLE ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UXIE ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHUCKLE ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UXIE ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -830,34 +830,34 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PARASECT, SpeciesId.ONIX, SpeciesId.CROBAT, SpeciesId.URSARING, SpeciesId.EXPLOUD, SpeciesId.PROBOPASS, SpeciesId.GIGALITH, SpeciesId.SWOOBAT, SpeciesId.DIGGERSBY, SpeciesId.NOIVERN, SpeciesId.GOLISOPOD, SpeciesId.GARGANACL ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.LYCANROC ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHUCKLE, SpeciesId.FERROTHORN, SpeciesId.GLIMMORA ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UXIE ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERAPAGOS ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.LYCANROC ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHUCKLE, SpeciesId.FERROTHORN, SpeciesId.GLIMMORA ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UXIE ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERAPAGOS ] } }, [BiomeId.DESERT]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [ SpeciesId.TRAPINCH, { 1: [ SpeciesId.HIPPOPOTAS ], 34: [ SpeciesId.HIPPOWDON ]}, { 1: [ SpeciesId.RELLOR ], 29: [ SpeciesId.RABSCA ]}], - [TimeOfDay.DAY]: [ SpeciesId.TRAPINCH, { 1: [ SpeciesId.HIPPOPOTAS ], 34: [ SpeciesId.HIPPOWDON ]}, { 1: [ SpeciesId.RELLOR ], 29: [ SpeciesId.RABSCA ]}], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.CACNEA ], 32: [ SpeciesId.CACTURNE ]}, { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.CACNEA ], 32: [ SpeciesId.CACTURNE ]}, { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.SANDSHREW ], 22: [ SpeciesId.SANDSLASH ]}, { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ]}, { 1: [ SpeciesId.SILICOBRA ], 36: [ SpeciesId.SANDACONDA ]}] + [TimeOfDay.DAWN]: [ SpeciesId.TRAPINCH, { 1: [ SpeciesId.HIPPOPOTAS ], 34: [ SpeciesId.HIPPOWDON ] }, { 1: [ SpeciesId.RELLOR ], 29: [ SpeciesId.RABSCA ] } ], + [TimeOfDay.DAY]: [ SpeciesId.TRAPINCH, { 1: [ SpeciesId.HIPPOPOTAS ], 34: [ SpeciesId.HIPPOWDON ] }, { 1: [ SpeciesId.RELLOR ], 29: [ SpeciesId.RABSCA ] } ], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.CACNEA ], 32: [ SpeciesId.CACTURNE ] }, { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.CACNEA ], 32: [ SpeciesId.CACTURNE ] }, { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SANDSHREW ], 22: [ SpeciesId.SANDSLASH ] }, { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ] }, { 1: [ SpeciesId.SILICOBRA ], 36: [ SpeciesId.SANDACONDA ] } ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ]}, SpeciesId.HELIOPTILE ], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ]}, SpeciesId.HELIOPTILE ], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ] }, SpeciesId.HELIOPTILE ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.SANDILE ], 29: [ SpeciesId.KROKOROK ], 40: [ SpeciesId.KROOKODILE ] }, SpeciesId.HELIOPTILE ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.MARACTUS, { 1: [ SpeciesId.BRAMBLIN ], 30: [ SpeciesId.BRAMBLEGHAST ]}, SpeciesId.ORTHWORM ] + [TimeOfDay.ALL]: [ SpeciesId.MARACTUS, { 1: [ SpeciesId.BRAMBLIN ], 30: [ SpeciesId.BRAMBLEGHAST ] }, SpeciesId.ORTHWORM ] }, [BiomePoolTier.RARE]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.DARUMAKA ], 35: [ SpeciesId.DARMANITAN ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.DARUMAKA ], 35: [ SpeciesId.DARMANITAN ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.LILEEP ], 40: [ SpeciesId.CRADILY ]}, { 1: [ SpeciesId.ANORITH ], 40: [ SpeciesId.ARMALDO ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIROCK, SpeciesId.TAPU_BULU, SpeciesId.PHEROMOSA ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.LILEEP ], 40: [ SpeciesId.CRADILY ] }, { 1: [ SpeciesId.ANORITH ], 40: [ SpeciesId.ARMALDO ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIROCK, SpeciesId.TAPU_BULU, SpeciesId.PHEROMOSA ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.HIPPOWDON, SpeciesId.HELIOLISK, SpeciesId.RABSCA ], [TimeOfDay.DAY]: [ SpeciesId.HIPPOWDON, SpeciesId.HELIOLISK, SpeciesId.RABSCA ], @@ -865,9 +865,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.CACTURNE, SpeciesId.KROOKODILE ], [TimeOfDay.ALL]: [ SpeciesId.SANDSLASH, SpeciesId.DRAPION, SpeciesId.DARMANITAN, SpeciesId.MARACTUS, SpeciesId.SANDACONDA, SpeciesId.BRAMBLEGHAST ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRADILY, SpeciesId.ARMALDO ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIROCK, SpeciesId.TAPU_BULU, SpeciesId.PHEROMOSA ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CRADILY, SpeciesId.ARMALDO ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIROCK, SpeciesId.TAPU_BULU, SpeciesId.PHEROMOSA ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.ICE_CAVE]: { [BiomePoolTier.COMMON]: { @@ -876,14 +876,14 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.SEEL ], 34: [ SpeciesId.DEWGONG ]}, - { 1: [ SpeciesId.SWINUB ], 33: [ SpeciesId.PILOSWINE ]}, - { 1: [ SpeciesId.SNOVER ], 40: [ SpeciesId.ABOMASNOW ]}, - { 1: [ SpeciesId.VANILLITE ], 35: [ SpeciesId.VANILLISH ], 47: [ SpeciesId.VANILLUXE ]}, - { 1: [ SpeciesId.CUBCHOO ], 37: [ SpeciesId.BEARTIC ]}, - { 1: [ SpeciesId.BERGMITE ], 37: [ SpeciesId.AVALUGG ]}, + { 1: [ SpeciesId.SEEL ], 34: [ SpeciesId.DEWGONG ] }, + { 1: [ SpeciesId.SWINUB ], 33: [ SpeciesId.PILOSWINE ] }, + { 1: [ SpeciesId.SNOVER ], 40: [ SpeciesId.ABOMASNOW ] }, + { 1: [ SpeciesId.VANILLITE ], 35: [ SpeciesId.VANILLISH ], 47: [ SpeciesId.VANILLUXE ] }, + { 1: [ SpeciesId.CUBCHOO ], 37: [ SpeciesId.BEARTIC ] }, + { 1: [ SpeciesId.BERGMITE ], 37: [ SpeciesId.AVALUGG ] }, SpeciesId.CRABRAWLER, - { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ]} + { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -893,15 +893,15 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SNEASEL, - { 1: [ SpeciesId.SNORUNT ], 42: [ SpeciesId.GLALIE ]}, - { 1: [ SpeciesId.SPHEAL ], 32: [ SpeciesId.SEALEO ], 44: [ SpeciesId.WALREIN ]}, + { 1: [ SpeciesId.SNORUNT ], 42: [ SpeciesId.GLALIE ] }, + { 1: [ SpeciesId.SPHEAL ], 32: [ SpeciesId.SEALEO ], 44: [ SpeciesId.WALREIN ] }, SpeciesId.EISCUE, - { 1: [ SpeciesId.CETODDLE ], 30: [ SpeciesId.CETITAN ]} + { 1: [ SpeciesId.CETODDLE ], 30: [ SpeciesId.CETITAN ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JYNX, SpeciesId.LAPRAS, SpeciesId.FROSLASS, SpeciesId.CRYOGONAL ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DELIBIRD, SpeciesId.ROTOM, { 1: [ SpeciesId.AMAURA ], 59: [ SpeciesId.AURORUS ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARTICUNO, SpeciesId.REGICE ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JYNX, SpeciesId.LAPRAS, SpeciesId.FROSLASS, SpeciesId.CRYOGONAL ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DELIBIRD, SpeciesId.ROTOM, { 1: [ SpeciesId.AMAURA ], 39: [ SpeciesId.AURORUS ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARTICUNO, SpeciesId.REGICE ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -909,46 +909,46 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DEWGONG, SpeciesId.GLALIE, SpeciesId.WALREIN, SpeciesId.WEAVILE, SpeciesId.MAMOSWINE, SpeciesId.FROSLASS, SpeciesId.VANILLUXE, SpeciesId.BEARTIC, SpeciesId.CRYOGONAL, SpeciesId.AVALUGG, SpeciesId.CRABOMINABLE, SpeciesId.CETITAN ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JYNX, SpeciesId.LAPRAS, SpeciesId.GLACEON, SpeciesId.AURORUS ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARTICUNO, SpeciesId.REGICE, SpeciesId.ROTOM ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KYUREM ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JYNX, SpeciesId.LAPRAS, SpeciesId.GLACEON, SpeciesId.AURORUS ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARTICUNO, SpeciesId.REGICE, SpeciesId.ROTOM ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KYUREM ] } }, [BiomeId.MEADOW]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.LEDYBA ], 18: [ SpeciesId.LEDIAN ]}, SpeciesId.ROSELIA, SpeciesId.COTTONEE, SpeciesId.MINCCINO ], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.LEDYBA ], 18: [ SpeciesId.LEDIAN ] }, SpeciesId.ROSELIA, SpeciesId.COTTONEE, SpeciesId.MINCCINO ], [TimeOfDay.DAY]: [ SpeciesId.ROSELIA, SpeciesId.COTTONEE, SpeciesId.MINCCINO ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.BLITZLE ], 27: [ SpeciesId.ZEBSTRIKA ]}, - { 1: [ SpeciesId.FLABEBE ], 19: [ SpeciesId.FLOETTE ]}, - { 1: [ SpeciesId.CUTIEFLY ], 25: [ SpeciesId.RIBOMBEE ]}, - { 1: [ SpeciesId.GOSSIFLEUR ], 20: [ SpeciesId.ELDEGOSS ]}, - { 1: [ SpeciesId.WOOLOO ], 24: [ SpeciesId.DUBWOOL ]} + { 1: [ SpeciesId.BLITZLE ], 27: [ SpeciesId.ZEBSTRIKA ] }, + { 1: [ SpeciesId.FLABEBE ], 19: [ SpeciesId.FLOETTE ] }, + { 1: [ SpeciesId.CUTIEFLY ], 25: [ SpeciesId.RIBOMBEE ] }, + { 1: [ SpeciesId.GOSSIFLEUR ], 20: [ SpeciesId.ELDEGOSS ] }, + { 1: [ SpeciesId.WOOLOO ], 24: [ SpeciesId.DUBWOOL ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ]}, - { 1: [ SpeciesId.SNUBBULL ], 23: [ SpeciesId.GRANBULL ]}, - { 1: [ SpeciesId.SKITTY ], 30: [ SpeciesId.DELCATTY ]}, + { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ] }, + { 1: [ SpeciesId.SNUBBULL ], 23: [ SpeciesId.GRANBULL ] }, + { 1: [ SpeciesId.SKITTY ], 30: [ SpeciesId.DELCATTY ] }, SpeciesId.BOUFFALANT, - { 1: [ SpeciesId.SMOLIV ], 25: [ SpeciesId.DOLLIV ], 35: [ SpeciesId.ARBOLIVA ]} + { 1: [ SpeciesId.SMOLIV ], 25: [ SpeciesId.DOLLIV ], 35: [ SpeciesId.ARBOLIVA ] } ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ]}, - { 1: [ SpeciesId.SNUBBULL ], 23: [ SpeciesId.GRANBULL ]}, - { 1: [ SpeciesId.SKITTY ], 30: [ SpeciesId.DELCATTY ]}, + { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ] }, + { 1: [ SpeciesId.SNUBBULL ], 23: [ SpeciesId.GRANBULL ] }, + { 1: [ SpeciesId.SKITTY ], 30: [ SpeciesId.DELCATTY ] }, SpeciesId.BOUFFALANT, - { 1: [ SpeciesId.SMOLIV ], 25: [ SpeciesId.DOLLIV ], 35: [ SpeciesId.ARBOLIVA ]} + { 1: [ SpeciesId.SMOLIV ], 25: [ SpeciesId.DOLLIV ], 35: [ SpeciesId.ARBOLIVA ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.JIGGLYPUFF ], 30: [ SpeciesId.WIGGLYTUFF ]}, - { 1: [ SpeciesId.MAREEP ], 15: [ SpeciesId.FLAAFFY ], 30: [ SpeciesId.AMPHAROS ]}, - { 1: [ SpeciesId.RALTS ], 20: [ SpeciesId.KIRLIA ], 30: [ SpeciesId.GARDEVOIR ]}, - { 1: [ SpeciesId.GLAMEOW ], 38: [ SpeciesId.PURUGLY ]}, + { 1: [ SpeciesId.JIGGLYPUFF ], 30: [ SpeciesId.WIGGLYTUFF ] }, + { 1: [ SpeciesId.MAREEP ], 15: [ SpeciesId.FLAAFFY ], 30: [ SpeciesId.AMPHAROS ] }, + { 1: [ SpeciesId.RALTS ], 20: [ SpeciesId.KIRLIA ], 30: [ SpeciesId.GARDEVOIR ] }, + { 1: [ SpeciesId.GLAMEOW ], 38: [ SpeciesId.PURUGLY ] }, SpeciesId.ORICORIO ] }, @@ -957,10 +957,10 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.VOLBEAT, SpeciesId.ILLUMISE ], - [TimeOfDay.ALL]: [ SpeciesId.TAUROS, SpeciesId.EEVEE, SpeciesId.MILTANK, SpeciesId.SPINDA, { 1: [ SpeciesId.APPLIN ], 30: [ SpeciesId.DIPPLIN ]}, { 1: [ SpeciesId.SPRIGATITO ], 16: [ SpeciesId.FLORAGATO ], 36: [ SpeciesId.MEOWSCARADA ]}] + [TimeOfDay.ALL]: [ SpeciesId.TAUROS, SpeciesId.EEVEE, SpeciesId.MILTANK, SpeciesId.SPINDA, { 1: [ SpeciesId.APPLIN ], 30: [ SpeciesId.DIPPLIN ] }, { 1: [ SpeciesId.SPRIGATITO ], 16: [ SpeciesId.FLORAGATO ], 36: [ SpeciesId.MEOWSCARADA ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CHANSEY, SpeciesId.SYLVEON ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MELOETTA ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CHANSEY, SpeciesId.SYLVEON ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MELOETTA ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.LEDIAN, SpeciesId.GRANBULL, SpeciesId.DELCATTY, SpeciesId.ROSERADE, SpeciesId.CINCCINO, SpeciesId.BOUFFALANT, SpeciesId.ARBOLIVA ], [TimeOfDay.DAY]: [ SpeciesId.GRANBULL, SpeciesId.DELCATTY, SpeciesId.ROSERADE, SpeciesId.CINCCINO, SpeciesId.BOUFFALANT, SpeciesId.ARBOLIVA ], @@ -968,9 +968,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TAUROS, SpeciesId.MILTANK, SpeciesId.GARDEVOIR, SpeciesId.PURUGLY, SpeciesId.ZEBSTRIKA, SpeciesId.FLORGES, SpeciesId.RIBOMBEE, SpeciesId.DUBWOOL ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.HISUI_LILLIGANT ], [TimeOfDay.DAY]: [ SpeciesId.HISUI_LILLIGANT ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLISSEY, SpeciesId.SYLVEON, SpeciesId.FLAPPLE, SpeciesId.APPLETUN, SpeciesId.MEOWSCARADA, SpeciesId.HYDRAPPLE ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MELOETTA ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHAYMIN ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.HISUI_LILLIGANT ], [TimeOfDay.DAY]: [ SpeciesId.HISUI_LILLIGANT ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLISSEY, SpeciesId.SYLVEON, SpeciesId.FLAPPLE, SpeciesId.APPLETUN, SpeciesId.MEOWSCARADA, SpeciesId.HYDRAPPLE ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MELOETTA ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SHAYMIN ] } }, [BiomeId.POWER_PLANT]: { [BiomePoolTier.COMMON]: { @@ -980,20 +980,20 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PIKACHU, - { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ]}, - { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ]}, - { 1: [ SpeciesId.ELECTRIKE ], 26: [ SpeciesId.MANECTRIC ]}, - { 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ]}, + { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ] }, + { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ] }, + { 1: [ SpeciesId.ELECTRIKE ], 26: [ SpeciesId.MANECTRIC ] }, + { 1: [ SpeciesId.SHINX ], 15: [ SpeciesId.LUXIO ], 30: [ SpeciesId.LUXRAY ] }, SpeciesId.DEDENNE, - { 1: [ SpeciesId.GRUBBIN ], 20: [ SpeciesId.CHARJABUG ]}, - { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ]}, - { 1: [ SpeciesId.TADBULB ], 30: [ SpeciesId.BELLIBOLT ]} + { 1: [ SpeciesId.GRUBBIN ], 20: [ SpeciesId.CHARJABUG ] }, + { 1: [ SpeciesId.PAWMI ], 18: [ SpeciesId.PAWMO ], 32: [ SpeciesId.PAWMOT ] }, + { 1: [ SpeciesId.TADBULB ], 30: [ SpeciesId.BELLIBOLT ] } ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ELECTABUZZ, SpeciesId.PLUSLE, SpeciesId.MINUN, SpeciesId.PACHIRISU, SpeciesId.EMOLGA, SpeciesId.TOGEDEMARU ]}, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.MAREEP ], 15: [ SpeciesId.FLAAFFY ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JOLTEON, SpeciesId.HISUI_VOLTORB ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.RAIKOU, SpeciesId.THUNDURUS, SpeciesId.XURKITREE, SpeciesId.ZERAORA, SpeciesId.REGIELEKI ]}, + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ELECTABUZZ, SpeciesId.PLUSLE, SpeciesId.MINUN, SpeciesId.PACHIRISU, SpeciesId.EMOLGA, SpeciesId.TOGEDEMARU ] }, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.MAREEP ], 15: [ SpeciesId.FLAAFFY ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JOLTEON, SpeciesId.HISUI_VOLTORB ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.RAIKOU, SpeciesId.THUNDURUS, SpeciesId.XURKITREE, SpeciesId.ZERAORA, SpeciesId.REGIELEKI ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -1001,9 +1001,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.RAICHU, SpeciesId.MANECTRIC, SpeciesId.LUXRAY, SpeciesId.MAGNEZONE, SpeciesId.ELECTIVIRE, SpeciesId.DEDENNE, SpeciesId.VIKAVOLT, SpeciesId.TOGEDEMARU, SpeciesId.PAWMOT, SpeciesId.BELLIBOLT ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JOLTEON, SpeciesId.AMPHAROS, SpeciesId.HISUI_ELECTRODE ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAPDOS, SpeciesId.RAIKOU, SpeciesId.THUNDURUS, SpeciesId.XURKITREE, SpeciesId.ZERAORA, SpeciesId.REGIELEKI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZEKROM ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.JOLTEON, SpeciesId.AMPHAROS, SpeciesId.HISUI_ELECTRODE ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAPDOS, SpeciesId.RAIKOU, SpeciesId.THUNDURUS, SpeciesId.XURKITREE, SpeciesId.ZERAORA, SpeciesId.REGIELEKI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZEKROM ] } }, [BiomeId.VOLCANO]: { [BiomePoolTier.COMMON]: { @@ -1014,32 +1014,32 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.ALL]: [ SpeciesId.VULPIX, SpeciesId.GROWLITHE, - { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ]}, - { 1: [ SpeciesId.SLUGMA ], 38: [ SpeciesId.MAGCARGO ]}, - { 1: [ SpeciesId.NUMEL ], 33: [ SpeciesId.CAMERUPT ]}, - { 1: [ SpeciesId.SALANDIT ], 33: [ SpeciesId.SALAZZLE ]}, - { 1: [ SpeciesId.ROLYCOLY ], 18: [ SpeciesId.CARKOL ], 34: [ SpeciesId.COALOSSAL ]} + { 1: [ SpeciesId.PONYTA ], 40: [ SpeciesId.RAPIDASH ] }, + { 1: [ SpeciesId.SLUGMA ], 38: [ SpeciesId.MAGCARGO ] }, + { 1: [ SpeciesId.NUMEL ], 33: [ SpeciesId.CAMERUPT ] }, + { 1: [ SpeciesId.SALANDIT ], 33: [ SpeciesId.SALAZZLE ] }, + { 1: [ SpeciesId.ROLYCOLY ], 18: [ SpeciesId.CARKOL ], 34: [ SpeciesId.COALOSSAL ] } ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MAGMAR, SpeciesId.TORKOAL, { 1: [ SpeciesId.PANSEAR ], 30: [ SpeciesId.SIMISEAR ]}, SpeciesId.HEATMOR, SpeciesId.TURTONATOR ]}, + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MAGMAR, SpeciesId.TORKOAL, { 1: [ SpeciesId.PANSEAR ], 30: [ SpeciesId.SIMISEAR ] }, SpeciesId.HEATMOR, SpeciesId.TURTONATOR ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.CHARMANDER ], 16: [ SpeciesId.CHARMELEON ], 36: [ SpeciesId.CHARIZARD ]}, - { 1: [ SpeciesId.CYNDAQUIL ], 14: [ SpeciesId.QUILAVA ], 36: [ SpeciesId.TYPHLOSION ]}, - { 1: [ SpeciesId.CHIMCHAR ], 14: [ SpeciesId.MONFERNO ], 36: [ SpeciesId.INFERNAPE ]}, - { 1: [ SpeciesId.TEPIG ], 17: [ SpeciesId.PIGNITE ], 36: [ SpeciesId.EMBOAR ]}, - { 1: [ SpeciesId.FENNEKIN ], 16: [ SpeciesId.BRAIXEN ], 36: [ SpeciesId.DELPHOX ]}, - { 1: [ SpeciesId.LITTEN ], 17: [ SpeciesId.TORRACAT ], 34: [ SpeciesId.INCINEROAR ]}, - { 1: [ SpeciesId.SCORBUNNY ], 16: [ SpeciesId.RABOOT ], 35: [ SpeciesId.CINDERACE ]}, - { 1: [ SpeciesId.CHARCADET ], 30: [ SpeciesId.ARMAROUGE ]} + { 1: [ SpeciesId.CHARMANDER ], 16: [ SpeciesId.CHARMELEON ], 36: [ SpeciesId.CHARIZARD ] }, + { 1: [ SpeciesId.CYNDAQUIL ], 14: [ SpeciesId.QUILAVA ], 36: [ SpeciesId.TYPHLOSION ] }, + { 1: [ SpeciesId.CHIMCHAR ], 14: [ SpeciesId.MONFERNO ], 36: [ SpeciesId.INFERNAPE ] }, + { 1: [ SpeciesId.TEPIG ], 17: [ SpeciesId.PIGNITE ], 36: [ SpeciesId.EMBOAR ] }, + { 1: [ SpeciesId.FENNEKIN ], 16: [ SpeciesId.BRAIXEN ], 36: [ SpeciesId.DELPHOX ] }, + { 1: [ SpeciesId.LITTEN ], 17: [ SpeciesId.TORRACAT ], 34: [ SpeciesId.INCINEROAR ] }, + { 1: [ SpeciesId.SCORBUNNY ], 16: [ SpeciesId.RABOOT ], 35: [ SpeciesId.CINDERACE ] }, + { 1: [ SpeciesId.CHARCADET ], 30: [ SpeciesId.ARMAROUGE ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FLAREON, SpeciesId.ROTOM, { 1: [ SpeciesId.LARVESTA ], 59: [ SpeciesId.VOLCARONA ]}, SpeciesId.HISUI_GROWLITHE ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ENTEI, SpeciesId.HEATRAN, SpeciesId.VOLCANION, SpeciesId.CHI_YU ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.FLAREON, SpeciesId.ROTOM, { 1: [ SpeciesId.LARVESTA ], 59: [ SpeciesId.VOLCARONA ] }, SpeciesId.HISUI_GROWLITHE ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ENTEI, SpeciesId.HEATRAN, SpeciesId.VOLCANION, SpeciesId.CHI_YU ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -1054,8 +1054,8 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CHARIZARD, SpeciesId.FLAREON, SpeciesId.TYPHLOSION, SpeciesId.INFERNAPE, SpeciesId.EMBOAR, SpeciesId.VOLCARONA, SpeciesId.DELPHOX, SpeciesId.INCINEROAR, SpeciesId.CINDERACE, SpeciesId.ARMAROUGE, SpeciesId.HISUI_ARCANINE ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MOLTRES, SpeciesId.ENTEI, SpeciesId.ROTOM, SpeciesId.HEATRAN, SpeciesId.VOLCANION, SpeciesId.CHI_YU ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.RESHIRAM ]} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MOLTRES, SpeciesId.ENTEI, SpeciesId.ROTOM, SpeciesId.HEATRAN, SpeciesId.VOLCANION, SpeciesId.CHI_YU ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.RESHIRAM ] } }, [BiomeId.GRAVEYARD]: { [BiomePoolTier.COMMON]: { @@ -1064,14 +1064,14 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.GASTLY ], 25: [ SpeciesId.HAUNTER ]}, - { 1: [ SpeciesId.SHUPPET ], 37: [ SpeciesId.BANETTE ]}, - { 1: [ SpeciesId.DUSKULL ], 37: [ SpeciesId.DUSCLOPS ]}, - { 1: [ SpeciesId.DRIFLOON ], 28: [ SpeciesId.DRIFBLIM ]}, - { 1: [ SpeciesId.LITWICK ], 41: [ SpeciesId.LAMPENT ]}, + { 1: [ SpeciesId.GASTLY ], 25: [ SpeciesId.HAUNTER ] }, + { 1: [ SpeciesId.SHUPPET ], 37: [ SpeciesId.BANETTE ] }, + { 1: [ SpeciesId.DUSKULL ], 37: [ SpeciesId.DUSCLOPS ] }, + { 1: [ SpeciesId.DRIFLOON ], 28: [ SpeciesId.DRIFBLIM ] }, + { 1: [ SpeciesId.LITWICK ], 41: [ SpeciesId.LAMPENT ] }, SpeciesId.PHANTUMP, SpeciesId.PUMPKABOO, - { 1: [ SpeciesId.GREAVARD ], 60: [ SpeciesId.HOUNDSTONE ]} + { 1: [ SpeciesId.GREAVARD ], 30: [ SpeciesId.HOUNDSTONE ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1079,11 +1079,11 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ]}, { 1: [ SpeciesId.YAMASK ], 34: [ SpeciesId.COFAGRIGUS ]}, { 1: [ SpeciesId.SINISTEA ], 30: [ SpeciesId.POLTEAGEIST ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ] }, { 1: [ SpeciesId.YAMASK ], 34: [ SpeciesId.COFAGRIGUS ] }, { 1: [ SpeciesId.SINISTEA ], 30: [ SpeciesId.POLTEAGEIST ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MISDREAVUS, SpeciesId.MIMIKYU, { 1: [ SpeciesId.FUECOCO ], 16: [ SpeciesId.CROCALOR ], 36: [ SpeciesId.SKELEDIRGE ]}, SpeciesId.CERULEDGE ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SPIRITOMB ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MARSHADOW, SpeciesId.SPECTRIER ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MISDREAVUS, SpeciesId.MIMIKYU, { 1: [ SpeciesId.FUECOCO ], 16: [ SpeciesId.CROCALOR ], 36: [ SpeciesId.SKELEDIRGE ] }, SpeciesId.CERULEDGE ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SPIRITOMB ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MARSHADOW, SpeciesId.SPECTRIER ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.MAROWAK ], [TimeOfDay.DAY]: [ SpeciesId.MAROWAK ], @@ -1091,9 +1091,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GENGAR, SpeciesId.BANETTE, SpeciesId.DRIFBLIM, SpeciesId.MISMAGIUS, SpeciesId.DUSKNOIR, SpeciesId.CHANDELURE, SpeciesId.TREVENANT, SpeciesId.GOURGEIST, SpeciesId.MIMIKYU, SpeciesId.POLTEAGEIST, SpeciesId.HOUNDSTONE ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SKELEDIRGE, SpeciesId.CERULEDGE, SpeciesId.HISUI_TYPHLOSION ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MARSHADOW, SpeciesId.SPECTRIER ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GIRATINA ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.SKELEDIRGE, SpeciesId.CERULEDGE, SpeciesId.HISUI_TYPHLOSION ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MARSHADOW, SpeciesId.SPECTRIER ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GIRATINA ] } }, [BiomeId.DOJO]: { [BiomePoolTier.COMMON]: { @@ -1102,11 +1102,11 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ]}, - { 1: [ SpeciesId.MAKUHITA ], 24: [ SpeciesId.HARIYAMA ]}, - { 1: [ SpeciesId.MEDITITE ], 37: [ SpeciesId.MEDICHAM ]}, - { 1: [ SpeciesId.STUFFUL ], 27: [ SpeciesId.BEWEAR ]}, - { 1: [ SpeciesId.CLOBBOPUS ], 55: [ SpeciesId.GRAPPLOCT ]} + { 1: [ SpeciesId.MANKEY ], 28: [ SpeciesId.PRIMEAPE ], 75: [ SpeciesId.ANNIHILAPE ] }, + { 1: [ SpeciesId.MAKUHITA ], 24: [ SpeciesId.HARIYAMA ] }, + { 1: [ SpeciesId.MEDITITE ], 37: [ SpeciesId.MEDICHAM ] }, + { 1: [ SpeciesId.STUFFUL ], 27: [ SpeciesId.BEWEAR ] }, + { 1: [ SpeciesId.CLOBBOPUS ], 35: [ SpeciesId.GRAPPLOCT ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1114,11 +1114,11 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ]}, { 1: [ SpeciesId.SCRAGGY ], 39: [ SpeciesId.SCRAFTY ]}, { 1: [ SpeciesId.MIENFOO ], 50: [ SpeciesId.MIENSHAO ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.CROAGUNK ], 37: [ SpeciesId.TOXICROAK ] }, { 1: [ SpeciesId.SCRAGGY ], 39: [ SpeciesId.SCRAFTY ] }, { 1: [ SpeciesId.MIENFOO ], 50: [ SpeciesId.MIENSHAO ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.LUCARIO, SpeciesId.THROH, SpeciesId.SAWK, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.GALAR_FARFETCHD ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, SpeciesId.KUBFU, SpeciesId.GALAR_ZAPDOS ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.LUCARIO, SpeciesId.THROH, SpeciesId.SAWK, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.GALAR_FARFETCHD ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU] }, SpeciesId.GALAR_ZAPDOS ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -1126,9 +1126,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.HARIYAMA, SpeciesId.MEDICHAM, SpeciesId.LUCARIO, SpeciesId.TOXICROAK, SpeciesId.THROH, SpeciesId.SAWK, SpeciesId.SCRAFTY, SpeciesId.MIENSHAO, SpeciesId.BEWEAR, SpeciesId.GRAPPLOCT, SpeciesId.ANNIHILAPE ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.PANGORO, SpeciesId.SIRFETCHD, SpeciesId.HISUI_DECIDUEYE ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, SpeciesId.URSHIFU ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAMAZENTA, SpeciesId.GALAR_ZAPDOS ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.PANGORO, SpeciesId.SIRFETCHD, SpeciesId.HISUI_DECIDUEYE ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU] } ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAMAZENTA, SpeciesId.GALAR_ZAPDOS ] } }, [BiomeId.FACTORY]: { [BiomePoolTier.COMMON]: { @@ -1137,21 +1137,21 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ]}, - { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ]}, - { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ]}, - { 1: [ SpeciesId.TIMBURR ], 25: [ SpeciesId.GURDURR ]}, - { 1: [ SpeciesId.KLINK ], 38: [ SpeciesId.KLANG ], 49: [ SpeciesId.KLINKLANG ]} + { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ] }, + { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ] }, + { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ] }, + { 1: [ SpeciesId.TIMBURR ], 25: [ SpeciesId.GURDURR ] }, + { 1: [ SpeciesId.KLINK ], 38: [ SpeciesId.KLANG ], 49: [ SpeciesId.KLINKLANG ] } ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ]}, SpeciesId.KLEFKI ]}, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BELDUM ], 20: [ SpeciesId.METANG ], 45: [ SpeciesId.METAGROSS ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GENESECT, SpeciesId.MAGEARNA ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KLINKLANG, SpeciesId.KLEFKI ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GENESECT, SpeciesId.MAGEARNA ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ] }, SpeciesId.KLEFKI ] }, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BELDUM ], 20: [ SpeciesId.METANG ], 45: [ SpeciesId.METAGROSS ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GENESECT, SpeciesId.MAGEARNA ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KLINKLANG, SpeciesId.KLEFKI ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GENESECT, SpeciesId.MAGEARNA ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.RUINS]: { [BiomePoolTier.COMMON]: { @@ -1160,12 +1160,12 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.DROWZEE ], 26: [ SpeciesId.HYPNO ]}, - { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ]}, + { 1: [ SpeciesId.DROWZEE ], 26: [ SpeciesId.HYPNO ] }, + { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ] }, SpeciesId.UNOWN, - { 1: [ SpeciesId.SPOINK ], 32: [ SpeciesId.GRUMPIG ]}, - { 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ]}, - { 1: [ SpeciesId.ELGYEM ], 42: [ SpeciesId.BEHEEYEM ]} + { 1: [ SpeciesId.SPOINK ], 32: [ SpeciesId.GRUMPIG ] }, + { 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ] }, + { 1: [ SpeciesId.ELGYEM ], 42: [ SpeciesId.BEHEEYEM ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1173,58 +1173,58 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.ABRA ], 16: [ SpeciesId.KADABRA ]}, SpeciesId.SIGILYPH, { 1: [ SpeciesId.TINKATINK ], 24: [ SpeciesId.TINKATUFF ], 38: [ SpeciesId.TINKATON ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.ABRA ], 16: [ SpeciesId.KADABRA ] }, SpeciesId.SIGILYPH, { 1: [ SpeciesId.TINKATINK ], 24: [ SpeciesId.TINKATUFF ], 38: [ SpeciesId.TINKATON ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MR_MIME, SpeciesId.WOBBUFFET, { 1: [ SpeciesId.GOTHITA ], 32: [ SpeciesId.GOTHORITA ], 41: [ SpeciesId.GOTHITELLE ]}, SpeciesId.STONJOURNER ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MR_MIME, SpeciesId.WOBBUFFET, { 1: [ SpeciesId.GOTHITA ], 32: [ SpeciesId.GOTHORITA ], 41: [ SpeciesId.GOTHITELLE ] }, SpeciesId.STONJOURNER ] }, [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.ESPEON ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.GALAR_YAMASK ], 34: [ SpeciesId.RUNERIGUS ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.GALAR_YAMASK ], 34: [ SpeciesId.RUNERIGUS ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.ARCHEN ], 37: [ SpeciesId.ARCHEOPS ]}] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.GALAR_YAMASK ], 34: [ SpeciesId.RUNERIGUS ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.GALAR_YAMASK ], 34: [ SpeciesId.RUNERIGUS ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.ARCHEN ], 37: [ SpeciesId.ARCHEOPS ] } ] }, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGISTEEL, SpeciesId.FEZANDIPITI ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ALAKAZAM, SpeciesId.HYPNO, SpeciesId.XATU, SpeciesId.GRUMPIG, SpeciesId.CLAYDOL, SpeciesId.SIGILYPH, SpeciesId.GOTHITELLE, SpeciesId.BEHEEYEM, SpeciesId.TINKATON ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.ESPEON ], [TimeOfDay.DUSK]: [ SpeciesId.RUNERIGUS ], [TimeOfDay.NIGHT]: [ SpeciesId.RUNERIGUS ], [TimeOfDay.ALL]: [ SpeciesId.MR_MIME, SpeciesId.WOBBUFFET, SpeciesId.ARCHEOPS ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGISTEEL, SpeciesId.FEZANDIPITI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KORAIDON ]} + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGISTEEL, SpeciesId.FEZANDIPITI ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ALAKAZAM, SpeciesId.HYPNO, SpeciesId.XATU, SpeciesId.GRUMPIG, SpeciesId.CLAYDOL, SpeciesId.SIGILYPH, SpeciesId.GOTHITELLE, SpeciesId.BEHEEYEM, SpeciesId.TINKATON ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.ESPEON ], [TimeOfDay.DUSK]: [ SpeciesId.RUNERIGUS ], [TimeOfDay.NIGHT]: [ SpeciesId.RUNERIGUS ], [TimeOfDay.ALL]: [ SpeciesId.MR_MIME, SpeciesId.WOBBUFFET, SpeciesId.ARCHEOPS ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGISTEEL, SpeciesId.FEZANDIPITI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KORAIDON ] } }, [BiomeId.WASTELAND]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [ - { 1: [ SpeciesId.BAGON ], 30: [ SpeciesId.SHELGON ], 50: [ SpeciesId.SALAMENCE ]}, - { 1: [ SpeciesId.GOOMY ], 40: [ SpeciesId.SLIGGOO ], 80: [ SpeciesId.GOODRA ]}, - { 1: [ SpeciesId.JANGMO_O ], 35: [ SpeciesId.HAKAMO_O ], 45: [ SpeciesId.KOMMO_O ]} + { 1: [ SpeciesId.BAGON ], 30: [ SpeciesId.SHELGON ], 50: [ SpeciesId.SALAMENCE ] }, + { 1: [ SpeciesId.GOOMY ], 40: [ SpeciesId.SLIGGOO ], 80: [ SpeciesId.GOODRA ] }, + { 1: [ SpeciesId.JANGMO_O ], 35: [ SpeciesId.HAKAMO_O ], 45: [ SpeciesId.KOMMO_O ] } ], [TimeOfDay.DAY]: [ - { 1: [ SpeciesId.BAGON ], 30: [ SpeciesId.SHELGON ], 50: [ SpeciesId.SALAMENCE ]}, - { 1: [ SpeciesId.GOOMY ], 40: [ SpeciesId.SLIGGOO ], 80: [ SpeciesId.GOODRA ]}, - { 1: [ SpeciesId.JANGMO_O ], 35: [ SpeciesId.HAKAMO_O ], 45: [ SpeciesId.KOMMO_O ]} + { 1: [ SpeciesId.BAGON ], 30: [ SpeciesId.SHELGON ], 50: [ SpeciesId.SALAMENCE ] }, + { 1: [ SpeciesId.GOOMY ], 40: [ SpeciesId.SLIGGOO ], 80: [ SpeciesId.GOODRA ] }, + { 1: [ SpeciesId.JANGMO_O ], 35: [ SpeciesId.HAKAMO_O ], 45: [ SpeciesId.KOMMO_O ] } ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ], 55: [ SpeciesId.TYRANITAR ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ], 55: [ SpeciesId.TYRANITAR ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ], 55: [ SpeciesId.TYRANITAR ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.LARVITAR ], 30: [ SpeciesId.PUPITAR ], 55: [ SpeciesId.TYRANITAR ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ]}, - { 1: [ SpeciesId.GIBLE ], 24: [ SpeciesId.GABITE ], 48: [ SpeciesId.GARCHOMP ]}, - { 1: [ SpeciesId.AXEW ], 38: [ SpeciesId.FRAXURE ], 48: [ SpeciesId.HAXORUS ]} + { 1: [ SpeciesId.VIBRAVA ], 45: [ SpeciesId.FLYGON ] }, + { 1: [ SpeciesId.GIBLE ], 24: [ SpeciesId.GABITE ], 48: [ SpeciesId.GARCHOMP ] }, + { 1: [ SpeciesId.AXEW ], 38: [ SpeciesId.FRAXURE ], 48: [ SpeciesId.HAXORUS ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ]}, SpeciesId.DRAMPA, SpeciesId.CYCLIZAR ] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SWABLU ], 35: [ SpeciesId.ALTARIA ] }, SpeciesId.DRAMPA, SpeciesId.CYCLIZAR ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.DREEPY ], 50: [ SpeciesId.DRAKLOAK ], 60: [ SpeciesId.DRAGAPULT ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.DREEPY ], 50: [ SpeciesId.DRAKLOAK ], 60: [ SpeciesId.DRAGAPULT ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.DRATINI ], 30: [ SpeciesId.DRAGONAIR ], 55: [ SpeciesId.DRAGONITE ]}, { 1: [ SpeciesId.FRIGIBAX ], 35: [ SpeciesId.ARCTIBAX ], 54: [ SpeciesId.BAXCALIBUR ]}] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.DREEPY ], 50: [ SpeciesId.DRAKLOAK ], 60: [ SpeciesId.DRAGAPULT ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.DREEPY ], 50: [ SpeciesId.DRAKLOAK ], 60: [ SpeciesId.DRAGAPULT ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.DRATINI ], 30: [ SpeciesId.DRAGONAIR ], 55: [ SpeciesId.DRAGONITE ] }, { 1: [ SpeciesId.FRIGIBAX ], 35: [ SpeciesId.ARCTIBAX ], 54: [ SpeciesId.BAXCALIBUR ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AERODACTYL, SpeciesId.DRUDDIGON, { 1: [ SpeciesId.TYRUNT ], 59: [ SpeciesId.TYRANTRUM ]}, SpeciesId.DRACOZOLT, SpeciesId.DRACOVISH ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIDRAGO ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AERODACTYL, SpeciesId.DRUDDIGON, { 1: [ SpeciesId.TYRUNT ], 39: [ SpeciesId.TYRANTRUM ] }, SpeciesId.DRACOZOLT, SpeciesId.DRACOVISH ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIDRAGO ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.SALAMENCE, SpeciesId.GOODRA, SpeciesId.KOMMO_O ], [TimeOfDay.DAY]: [ SpeciesId.SALAMENCE, SpeciesId.GOODRA, SpeciesId.KOMMO_O ], @@ -1232,9 +1232,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.TYRANITAR, SpeciesId.DRAGAPULT ], [TimeOfDay.ALL]: [ SpeciesId.DRAGONITE, SpeciesId.FLYGON, SpeciesId.GARCHOMP, SpeciesId.HAXORUS, SpeciesId.DRAMPA, SpeciesId.BAXCALIBUR ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AERODACTYL, SpeciesId.DRUDDIGON, SpeciesId.TYRANTRUM, SpeciesId.DRACOZOLT, SpeciesId.DRACOVISH ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIDRAGO ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIALGA ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AERODACTYL, SpeciesId.DRUDDIGON, SpeciesId.TYRANTRUM, SpeciesId.DRACOZOLT, SpeciesId.DRACOVISH ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIDRAGO ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIALGA ] } }, [BiomeId.ABYSS]: { [BiomePoolTier.COMMON]: { @@ -1244,25 +1244,25 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MURKROW, - { 1: [ SpeciesId.HOUNDOUR ], 24: [ SpeciesId.HOUNDOOM ]}, + { 1: [ SpeciesId.HOUNDOUR ], 24: [ SpeciesId.HOUNDOOM ] }, SpeciesId.SABLEYE, - { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ]}, - { 1: [ SpeciesId.PAWNIARD ], 52: [ SpeciesId.BISHARP ], 64: [ SpeciesId.KINGAMBIT ]}, - { 1: [ SpeciesId.NICKIT ], 18: [ SpeciesId.THIEVUL ]}, - { 1: [ SpeciesId.IMPIDIMP ], 32: [ SpeciesId.MORGREM ], 42: [ SpeciesId.GRIMMSNARL ]}, - { 1: [ SpeciesId.MASCHIFF ], 30: [ SpeciesId.MABOSSTIFF ]} + { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ] }, + { 1: [ SpeciesId.PAWNIARD ], 52: [ SpeciesId.BISHARP ], 64: [ SpeciesId.KINGAMBIT ] }, + { 1: [ SpeciesId.NICKIT ], 18: [ SpeciesId.THIEVUL ] }, + { 1: [ SpeciesId.IMPIDIMP ], 32: [ SpeciesId.MORGREM ], 42: [ SpeciesId.GRIMMSNARL ] }, + { 1: [ SpeciesId.MASCHIFF ], 30: [ SpeciesId.MABOSSTIFF ] } ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.ABSOL, SpeciesId.SPIRITOMB, { 1: [ SpeciesId.ZORUA ], 30: [ SpeciesId.ZOROARK ]}, { 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ]}] + [TimeOfDay.ALL]: [ SpeciesId.ABSOL, SpeciesId.SPIRITOMB, { 1: [ SpeciesId.ZORUA ], 30: [ SpeciesId.ZOROARK ] }, { 1: [ SpeciesId.DEINO ], 50: [ SpeciesId.ZWEILOUS ], 64: [ SpeciesId.HYDREIGON ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UMBREON ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DARKRAI, SpeciesId.GALAR_MOLTRES ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UMBREON ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DARKRAI, SpeciesId.GALAR_MOLTRES ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -1270,9 +1270,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HOUNDOOM, SpeciesId.SABLEYE, SpeciesId.ABSOL, SpeciesId.HONCHKROW, SpeciesId.SPIRITOMB, SpeciesId.LIEPARD, SpeciesId.ZOROARK, SpeciesId.HYDREIGON, SpeciesId.THIEVUL, SpeciesId.GRIMMSNARL, SpeciesId.MABOSSTIFF, SpeciesId.KINGAMBIT ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UMBREON, SpeciesId.HISUI_SAMUROTT ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DARKRAI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PALKIA, SpeciesId.YVELTAL, SpeciesId.GALAR_MOLTRES ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.UMBREON, SpeciesId.HISUI_SAMUROTT ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DARKRAI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.PALKIA, SpeciesId.YVELTAL, SpeciesId.GALAR_MOLTRES ] } }, [BiomeId.SPACE]: { [BiomePoolTier.COMMON]: { @@ -1280,22 +1280,22 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DAY]: [ SpeciesId.SOLROCK ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.LUNATONE ], - [TimeOfDay.ALL]: [ SpeciesId.CLEFAIRY, { 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ]}, { 1: [ SpeciesId.MUNNA ], 30: [ SpeciesId.MUSHARNA ]}, SpeciesId.MINIOR ] + [TimeOfDay.ALL]: [ SpeciesId.CLEFAIRY, { 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ] }, { 1: [ SpeciesId.MUNNA ], 30: [ SpeciesId.MUSHARNA ] }, SpeciesId.MINIOR ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ]}, { 1: [ SpeciesId.ELGYEM ], 42: [ SpeciesId.BEHEEYEM ]}]}, + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ] }, { 1: [ SpeciesId.ELGYEM ], 42: [ SpeciesId.BEHEEYEM ] } ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BELDUM ], 20: [ SpeciesId.METANG ], 45: [ SpeciesId.METAGROSS ]}, SpeciesId.SIGILYPH, { 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ]}] + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BELDUM ], 20: [ SpeciesId.METANG ], 45: [ SpeciesId.METAGROSS ] }, SpeciesId.SIGILYPH, { 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ]}]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.COSMOG ], 43: [ SpeciesId.COSMOEM ]}, SpeciesId.CELESTEELA ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.SOLROCK ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.LUNATONE ], [TimeOfDay.ALL]: [ SpeciesId.CLEFABLE, SpeciesId.BRONZONG, SpeciesId.MUSHARNA, SpeciesId.REUNICLUS, SpeciesId.MINIOR ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.METAGROSS, SpeciesId.PORYGON_Z ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CELESTEELA ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.SOLGALEO ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.LUNALA ], [TimeOfDay.ALL]: [ SpeciesId.RAYQUAZA, SpeciesId.NECROZMA ]} + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ] } ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.COSMOG ], 23: [ SpeciesId.COSMOEM ] }, SpeciesId.CELESTEELA ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.SOLROCK ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.LUNATONE ], [TimeOfDay.ALL]: [ SpeciesId.CLEFABLE, SpeciesId.BRONZONG, SpeciesId.MUSHARNA, SpeciesId.REUNICLUS, SpeciesId.MINIOR ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.METAGROSS, SpeciesId.PORYGON_Z ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CELESTEELA ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [ SpeciesId.SOLGALEO ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [ SpeciesId.LUNALA ], [TimeOfDay.ALL]: [ SpeciesId.RAYQUAZA, SpeciesId.NECROZMA ] } }, [BiomeId.CONSTRUCTION_SITE]: { [BiomePoolTier.COMMON]: { @@ -1304,10 +1304,10 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ]}, - { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ]}, - { 1: [ SpeciesId.DRILBUR ], 31: [ SpeciesId.EXCADRILL ]}, - { 1: [ SpeciesId.TIMBURR ], 25: [ SpeciesId.GURDURR ]} + { 1: [ SpeciesId.MACHOP ], 28: [ SpeciesId.MACHOKE ] }, + { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ] }, + { 1: [ SpeciesId.DRILBUR ], 31: [ SpeciesId.EXCADRILL ] }, + { 1: [ SpeciesId.TIMBURR ], 25: [ SpeciesId.GURDURR ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1316,60 +1316,60 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ]}, - { 1: [ SpeciesId.KOFFING ], 35: [ SpeciesId.WEEZING ]}, - { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ]}, - { 1: [ SpeciesId.SCRAGGY ], 39: [ SpeciesId.SCRAFTY ]} + { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ] }, + { 1: [ SpeciesId.KOFFING ], 35: [ SpeciesId.WEEZING ] }, + { 1: [ SpeciesId.RHYHORN ], 42: [ SpeciesId.RHYDON ] }, + { 1: [ SpeciesId.SCRAGGY ], 39: [ SpeciesId.SCRAFTY ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.GALAR_MEOWTH ], 28: [ SpeciesId.PERRSERKER ]}], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ONIX, SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.DURALUDON ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.HITMONTOP ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.COBALION, SpeciesId.STAKATAKA ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MACHAMP, SpeciesId.CONKELDURR ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.PERRSERKER ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARCHALUDON ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.COBALION, SpeciesId.STAKATAKA ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.GALAR_MEOWTH ], 28: [ SpeciesId.PERRSERKER ] } ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ONIX, SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.DURALUDON ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, SpeciesId.HITMONTOP ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.COBALION, SpeciesId.STAKATAKA ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MACHAMP, SpeciesId.CONKELDURR ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.PERRSERKER ], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ARCHALUDON ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.COBALION, SpeciesId.STAKATAKA ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.JUNGLE]: { [BiomePoolTier.COMMON]: { - [TimeOfDay.DAWN]: [ SpeciesId.VESPIQUEN, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ]}, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ]}], - [TimeOfDay.DAY]: [ SpeciesId.VESPIQUEN, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ]}, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ]}], - [TimeOfDay.DUSK]: [ SpeciesId.SHROOMISH, { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ]}, { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ]}, SpeciesId.SHROOMISH, { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ]}, { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}], - [TimeOfDay.ALL]: [ SpeciesId.AIPOM, { 1: [ SpeciesId.BLITZLE ], 27: [ SpeciesId.ZEBSTRIKA ]}, { 1: [ SpeciesId.PIKIPEK ], 14: [ SpeciesId.TRUMBEAK ], 28: [ SpeciesId.TOUCANNON ]}] + [TimeOfDay.DAWN]: [ SpeciesId.VESPIQUEN, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ] }, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ] } ], + [TimeOfDay.DAY]: [ SpeciesId.VESPIQUEN, { 1: [ SpeciesId.CHERUBI ], 25: [ SpeciesId.CHERRIM ] }, { 1: [ SpeciesId.SEWADDLE ], 20: [ SpeciesId.SWADLOON ], 30: [ SpeciesId.LEAVANNY ] } ], + [TimeOfDay.DUSK]: [ SpeciesId.SHROOMISH, { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ] }, { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ] }, SpeciesId.SHROOMISH, { 1: [ SpeciesId.PURRLOIN ], 20: [ SpeciesId.LIEPARD ] }, { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] } ], + [TimeOfDay.ALL]: [ SpeciesId.AIPOM, { 1: [ SpeciesId.BLITZLE ], 27: [ SpeciesId.ZEBSTRIKA ] }, { 1: [ SpeciesId.PIKIPEK ], 14: [ SpeciesId.TRUMBEAK ], 28: [ SpeciesId.TOUCANNON ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [ SpeciesId.EXEGGCUTE, SpeciesId.TROPIUS, SpeciesId.COMBEE, SpeciesId.KOMALA ], [TimeOfDay.DAY]: [ SpeciesId.EXEGGCUTE, SpeciesId.TROPIUS, SpeciesId.COMBEE, SpeciesId.KOMALA ], - [TimeOfDay.DUSK]: [ SpeciesId.TANGELA, { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ]}, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ]}], - [TimeOfDay.NIGHT]: [ SpeciesId.TANGELA, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ]}], + [TimeOfDay.DUSK]: [ SpeciesId.TANGELA, { 1: [ SpeciesId.SPINARAK ], 22: [ SpeciesId.ARIADOS ] }, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ] } ], + [TimeOfDay.NIGHT]: [ SpeciesId.TANGELA, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.PANSAGE ], 30: [ SpeciesId.SIMISAGE ]}, - { 1: [ SpeciesId.PANSEAR ], 30: [ SpeciesId.SIMISEAR ]}, - { 1: [ SpeciesId.PANPOUR ], 30: [ SpeciesId.SIMIPOUR ]}, - { 1: [ SpeciesId.JOLTIK ], 36: [ SpeciesId.GALVANTULA ]}, - { 1: [ SpeciesId.LITLEO ], 35: [ SpeciesId.PYROAR ]}, - { 1: [ SpeciesId.FOMANTIS ], 44: [ SpeciesId.LURANTIS ]}, + { 1: [ SpeciesId.PANSAGE ], 30: [ SpeciesId.SIMISAGE ] }, + { 1: [ SpeciesId.PANSEAR ], 30: [ SpeciesId.SIMISEAR ] }, + { 1: [ SpeciesId.PANPOUR ], 30: [ SpeciesId.SIMIPOUR ] }, + { 1: [ SpeciesId.JOLTIK ], 36: [ SpeciesId.GALVANTULA ] }, + { 1: [ SpeciesId.LITLEO ], 35: [ SpeciesId.PYROAR ] }, + { 1: [ SpeciesId.FOMANTIS ], 34: [ SpeciesId.LURANTIS ] }, SpeciesId.FALINKS ] }, [BiomePoolTier.RARE]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}, SpeciesId.PASSIMIAN, { 1: [ SpeciesId.GALAR_PONYTA ], 40: [ SpeciesId.GALAR_RAPIDASH ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ]}, SpeciesId.PASSIMIAN ], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] }, SpeciesId.PASSIMIAN, { 1: [ SpeciesId.GALAR_PONYTA ], 40: [ SpeciesId.GALAR_RAPIDASH ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.FOONGUS ], 39: [ SpeciesId.AMOONGUSS ] }, SpeciesId.PASSIMIAN ], [TimeOfDay.DUSK]: [ SpeciesId.ORANGURU ], [TimeOfDay.NIGHT]: [ SpeciesId.ORANGURU ], [TimeOfDay.ALL]: [ SpeciesId.SCYTHER, SpeciesId.YANMA, - { 1: [ SpeciesId.SLAKOTH ], 18: [ SpeciesId.VIGOROTH ], 36: [ SpeciesId.SLAKING ]}, + { 1: [ SpeciesId.SLAKOTH ], 18: [ SpeciesId.VIGOROTH ], 36: [ SpeciesId.SLAKING ] }, SpeciesId.SEVIPER, SpeciesId.CARNIVINE, - { 1: [ SpeciesId.SNIVY ], 17: [ SpeciesId.SERVINE ], 36: [ SpeciesId.SERPERIOR ]}, - { 1: [ SpeciesId.GROOKEY ], 16: [ SpeciesId.THWACKEY ], 35: [ SpeciesId.RILLABOOM ]} + { 1: [ SpeciesId.SNIVY ], 17: [ SpeciesId.SERVINE ], 36: [ SpeciesId.SERPERIOR ] }, + { 1: [ SpeciesId.GROOKEY ], 16: [ SpeciesId.THWACKEY ], 35: [ SpeciesId.RILLABOOM ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KANGASKHAN, SpeciesId.CHATOT, SpeciesId.KLEAVOR ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TAPU_LELE, SpeciesId.BUZZWOLE, SpeciesId.ZARUDE, SpeciesId.MUNKIDORI ]}, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KANGASKHAN, SpeciesId.CHATOT, SpeciesId.KLEAVOR ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TAPU_LELE, SpeciesId.BUZZWOLE, SpeciesId.ZARUDE, SpeciesId.MUNKIDORI ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.EXEGGUTOR, SpeciesId.TROPIUS, SpeciesId.CHERRIM, SpeciesId.LEAVANNY, SpeciesId.KOMALA ], [TimeOfDay.DAY]: [ SpeciesId.EXEGGUTOR, SpeciesId.TROPIUS, SpeciesId.CHERRIM, SpeciesId.LEAVANNY, SpeciesId.KOMALA ], @@ -1384,8 +1384,8 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KANGASKHAN, SpeciesId.SCIZOR, SpeciesId.SLAKING, SpeciesId.LEAFEON, SpeciesId.SERPERIOR, SpeciesId.RILLABOOM ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TAPU_LELE, SpeciesId.BUZZWOLE, SpeciesId.ZARUDE, SpeciesId.MUNKIDORI ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KLEAVOR ]} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TAPU_LELE, SpeciesId.BUZZWOLE, SpeciesId.ZARUDE, SpeciesId.MUNKIDORI ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.KLEAVOR ] } }, [BiomeId.FAIRY_CAVE]: { [BiomePoolTier.COMMON]: { @@ -1394,14 +1394,14 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.JIGGLYPUFF ], 30: [ SpeciesId.WIGGLYTUFF ]}, - { 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ]}, + { 1: [ SpeciesId.JIGGLYPUFF ], 30: [ SpeciesId.WIGGLYTUFF ] }, + { 1: [ SpeciesId.MARILL ], 18: [ SpeciesId.AZUMARILL ] }, SpeciesId.MAWILE, - { 1: [ SpeciesId.SPRITZEE ], 40: [ SpeciesId.AROMATISSE ]}, - { 1: [ SpeciesId.SWIRLIX ], 40: [ SpeciesId.SLURPUFF ]}, - { 1: [ SpeciesId.CUTIEFLY ], 25: [ SpeciesId.RIBOMBEE ]}, - { 1: [ SpeciesId.MORELULL ], 24: [ SpeciesId.SHIINOTIC ]}, - { 1: [ SpeciesId.MILCERY ], 30: [ SpeciesId.ALCREMIE ]} + { 1: [ SpeciesId.SPRITZEE ], 40: [ SpeciesId.AROMATISSE ] }, + { 1: [ SpeciesId.SWIRLIX ], 40: [ SpeciesId.SLURPUFF ] }, + { 1: [ SpeciesId.CUTIEFLY ], 25: [ SpeciesId.RIBOMBEE ] }, + { 1: [ SpeciesId.MORELULL ], 24: [ SpeciesId.SHIINOTIC ] }, + { 1: [ SpeciesId.MILCERY ], 30: [ SpeciesId.ALCREMIE ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1412,15 +1412,15 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.ALL]: [ SpeciesId.CLEFAIRY, SpeciesId.TOGETIC, - { 1: [ SpeciesId.RALTS ], 20: [ SpeciesId.KIRLIA ], 30: [ SpeciesId.GARDEVOIR ]}, + { 1: [ SpeciesId.RALTS ], 20: [ SpeciesId.KIRLIA ], 30: [ SpeciesId.GARDEVOIR ] }, SpeciesId.CARBINK, SpeciesId.COMFEY, - { 1: [ SpeciesId.HATENNA ], 32: [ SpeciesId.HATTREM ], 42: [ SpeciesId.HATTERENE ]} + { 1: [ SpeciesId.HATENNA ], 32: [ SpeciesId.HATTREM ], 42: [ SpeciesId.HATTERENE ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AUDINO, SpeciesId.ETERNAL_FLOETTE ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIANCIE, SpeciesId.ENAMORUS ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.AUDINO ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ETERNAL_FLOETTE ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIANCIE, SpeciesId.ENAMORUS ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], @@ -1428,9 +1428,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.WIGGLYTUFF, SpeciesId.MAWILE, SpeciesId.TOGEKISS, SpeciesId.AUDINO, SpeciesId.AROMATISSE, SpeciesId.SLURPUFF, SpeciesId.CARBINK, SpeciesId.RIBOMBEE, SpeciesId.SHIINOTIC, SpeciesId.COMFEY, SpeciesId.HATTERENE, SpeciesId.ALCREMIE ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ETERNAL_FLOETTE ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIANCIE, SpeciesId.ENAMORUS ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.XERNEAS ]} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ETERNAL_FLOETTE ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DIANCIE, SpeciesId.ENAMORUS ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.XERNEAS ] } }, [BiomeId.TEMPLE]: { [BiomePoolTier.COMMON]: { @@ -1439,12 +1439,12 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.GASTLY ], 25: [ SpeciesId.HAUNTER ]}, - { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ]}, - { 1: [ SpeciesId.DUSKULL ], 37: [ SpeciesId.DUSCLOPS ]}, - { 1: [ SpeciesId.YAMASK ], 34: [ SpeciesId.COFAGRIGUS ]}, - { 1: [ SpeciesId.GOLETT ], 43: [ SpeciesId.GOLURK ]}, - { 1: [ SpeciesId.HONEDGE ], 35: [ SpeciesId.DOUBLADE ]} + { 1: [ SpeciesId.GASTLY ], 25: [ SpeciesId.HAUNTER ] }, + { 1: [ SpeciesId.NATU ], 25: [ SpeciesId.XATU ] }, + { 1: [ SpeciesId.DUSKULL ], 37: [ SpeciesId.DUSCLOPS ] }, + { 1: [ SpeciesId.YAMASK ], 34: [ SpeciesId.COFAGRIGUS ] }, + { 1: [ SpeciesId.GOLETT ], 43: [ SpeciesId.GOLURK ] }, + { 1: [ SpeciesId.HONEDGE ], 35: [ SpeciesId.DOUBLADE ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1453,86 +1453,86 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ]}, - { 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ]}, - { 1: [ SpeciesId.CHINGLING ], 20: [ SpeciesId.CHIMECHO ]}, - { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ]}, - { 1: [ SpeciesId.LITWICK ], 41: [ SpeciesId.LAMPENT ]} + { 1: [ SpeciesId.CUBONE ], 28: [ SpeciesId.MAROWAK ] }, + { 1: [ SpeciesId.BALTOY ], 36: [ SpeciesId.CLAYDOL ] }, + { 1: [ SpeciesId.CHINGLING ], 20: [ SpeciesId.CHIMECHO ] }, + { 1: [ SpeciesId.SKORUPI ], 40: [ SpeciesId.DRAPION ] }, + { 1: [ SpeciesId.LITWICK ], 41: [ SpeciesId.LAMPENT ] } ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.GIMMIGHOUL ], 40: [ SpeciesId.GHOLDENGO ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HOOPA, SpeciesId.TAPU_KOKO ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CHIMECHO, SpeciesId.COFAGRIGUS, SpeciesId.GOLURK, SpeciesId.AEGISLASH ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GHOLDENGO ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HOOPA, SpeciesId.TAPU_KOKO ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIGIGAS ]} + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.GIMMIGHOUL ], 40: [ SpeciesId.GHOLDENGO ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HOOPA, SpeciesId.TAPU_KOKO ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.CHIMECHO, SpeciesId.COFAGRIGUS, SpeciesId.GOLURK, SpeciesId.AEGISLASH ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GHOLDENGO ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HOOPA, SpeciesId.TAPU_KOKO ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.REGIGIGAS ] } }, [BiomeId.SLUM]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.PATRAT ], 20: [ SpeciesId.WATCHOG ] } ], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.RATTATA ], 20: [ SpeciesId.RATICATE ]}, - { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ]}, - { 1: [ SpeciesId.KOFFING ], 35: [ SpeciesId.WEEZING ]}, - { 1: [ SpeciesId.TRUBBISH ], 36: [ SpeciesId.GARBODOR ]} + { 1: [ SpeciesId.RATTATA ], 20: [ SpeciesId.RATICATE ] }, + { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ] }, + { 1: [ SpeciesId.KOFFING ], 35: [ SpeciesId.WEEZING ] }, + { 1: [ SpeciesId.TRUBBISH ], 36: [ SpeciesId.GARBODOR ] } ] }, [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.STUNKY ], 34: [ SpeciesId.SKUNTANK ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.STUNKY ], 34: [ SpeciesId.SKUNTANK ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ]}] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.STUNKY ], 34: [ SpeciesId.SKUNTANK ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.STUNKY ], 34: [ SpeciesId.SKUNTANK ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.BURMY ], 20: [ SpeciesId.WORMADAM ] } ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [ SpeciesId.TOXTRICITY, { 1: [ SpeciesId.GALAR_LINOONE ], 65: [ SpeciesId.OBSTAGOON ]}, SpeciesId.GALAR_ZIGZAGOON ], - [TimeOfDay.NIGHT]: [ SpeciesId.TOXTRICITY, { 1: [ SpeciesId.GALAR_LINOONE ], 65: [ SpeciesId.OBSTAGOON ]}, SpeciesId.GALAR_ZIGZAGOON ], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.VAROOM ], 40: [ SpeciesId.REVAVROOM ]}] + [TimeOfDay.DUSK]: [ SpeciesId.TOXTRICITY, { 1: [ SpeciesId.GALAR_LINOONE ], 35: [ SpeciesId.OBSTAGOON ] }, SpeciesId.GALAR_ZIGZAGOON ], + [TimeOfDay.NIGHT]: [ SpeciesId.TOXTRICITY, { 1: [ SpeciesId.GALAR_LINOONE ], 35: [ SpeciesId.OBSTAGOON ] }, SpeciesId.GALAR_ZIGZAGOON ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.VAROOM ], 40: [ SpeciesId.REVAVROOM ] } ] }, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GUZZLORD ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.SKUNTANK, SpeciesId.WATCHOG ], [TimeOfDay.NIGHT]: [ SpeciesId.SKUNTANK, SpeciesId.WATCHOG ], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.WEEZING, SpeciesId.WORMADAM, SpeciesId.GARBODOR ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.TOXTRICITY, SpeciesId.OBSTAGOON ], [TimeOfDay.NIGHT]: [ SpeciesId.TOXTRICITY, SpeciesId.OBSTAGOON ], [TimeOfDay.ALL]: [ SpeciesId.REVAVROOM, SpeciesId.GALAR_WEEZING ]}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GUZZLORD ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GUZZLORD ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.SKUNTANK, SpeciesId.WATCHOG ], [TimeOfDay.NIGHT]: [ SpeciesId.SKUNTANK, SpeciesId.WATCHOG ], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.WEEZING, SpeciesId.WORMADAM, SpeciesId.GARBODOR ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [ SpeciesId.TOXTRICITY, SpeciesId.OBSTAGOON ], [TimeOfDay.NIGHT]: [ SpeciesId.TOXTRICITY, SpeciesId.OBSTAGOON ], [TimeOfDay.ALL]: [ SpeciesId.REVAVROOM, SpeciesId.GALAR_WEEZING ] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GUZZLORD ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.SNOWY_FOREST]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ]}], - [TimeOfDay.NIGHT]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.SWINUB ], 33: [ SpeciesId.PILOSWINE ]}, { 1: [ SpeciesId.SNOVER ], 40: [ SpeciesId.ABOMASNOW ]}, SpeciesId.EISCUE ] + [TimeOfDay.DUSK]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ] } ], + [TimeOfDay.NIGHT]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, { 1: [ SpeciesId.SNOM ], 20: [ SpeciesId.FROSMOTH ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SWINUB ], 33: [ SpeciesId.PILOSWINE ] }, { 1: [ SpeciesId.SNOVER ], 40: [ SpeciesId.ABOMASNOW ] }, SpeciesId.EISCUE ] }, [BiomePoolTier.UNCOMMON]: { - [TimeOfDay.DAWN]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, SpeciesId.STANTLER ], - [TimeOfDay.DAY]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ]}, SpeciesId.STANTLER ], + [TimeOfDay.DAWN]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, SpeciesId.STANTLER ], + [TimeOfDay.DAY]: [ SpeciesId.SNEASEL, { 1: [ SpeciesId.TEDDIURSA ], 30: [ SpeciesId.URSARING ] }, SpeciesId.STANTLER ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, [BiomePoolTier.RARE]: { - [TimeOfDay.DAWN]: [{ 1: [ SpeciesId.GALAR_DARUMAKA ], 30: [ SpeciesId.GALAR_DARMANITAN ]}], - [TimeOfDay.DAY]: [{ 1: [ SpeciesId.GALAR_DARUMAKA ], 30: [ SpeciesId.GALAR_DARMANITAN ]}], + [TimeOfDay.DAWN]: [ { 1: [ SpeciesId.GALAR_DARUMAKA ], 30: [ SpeciesId.GALAR_DARMANITAN ] } ], + [TimeOfDay.DAY]: [ { 1: [ SpeciesId.GALAR_DARUMAKA ], 30: [ SpeciesId.GALAR_DARMANITAN ] } ], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], - [TimeOfDay.ALL]: [ SpeciesId.DELIBIRD, { 1: [ SpeciesId.ALOLA_SANDSHREW ], 30: [ SpeciesId.ALOLA_SANDSLASH ]}, { 1: [ SpeciesId.ALOLA_VULPIX ], 30: [ SpeciesId.ALOLA_NINETALES ]}] + [TimeOfDay.ALL]: [ SpeciesId.DELIBIRD, { 1: [ SpeciesId.ALOLA_SANDSHREW ], 30: [ SpeciesId.ALOLA_SANDSLASH ] }, { 1: [ SpeciesId.ALOLA_VULPIX ], 30: [ SpeciesId.ALOLA_NINETALES ] } ] }, [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.HISUI_SNEASEL ], [TimeOfDay.DAY]: [ SpeciesId.HISUI_SNEASEL ], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.HISUI_ZORUA ], 30: [ SpeciesId.HISUI_ZOROARK ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.HISUI_ZORUA ], 30: [ SpeciesId.HISUI_ZOROARK ]}], - [TimeOfDay.ALL]: [{ 1: [ SpeciesId.GALAR_MR_MIME ], 42: [ SpeciesId.MR_RIME ]}, SpeciesId.ARCTOZOLT, SpeciesId.HISUI_AVALUGG ] + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.HISUI_ZORUA ], 30: [ SpeciesId.HISUI_ZOROARK ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.HISUI_ZORUA ], 30: [ SpeciesId.HISUI_ZOROARK ] } ], + [TimeOfDay.ALL]: [ { 1: [ SpeciesId.GALAR_MR_MIME ], 42: [ SpeciesId.MR_RIME ] }, SpeciesId.ARCTOZOLT, SpeciesId.HISUI_AVALUGG ] }, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GLASTRIER, SpeciesId.CHIEN_PAO, SpeciesId.GALAR_ARTICUNO ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.WYRDEER ], [TimeOfDay.DAY]: [ SpeciesId.WYRDEER ], [TimeOfDay.DUSK]: [ SpeciesId.FROSMOTH ], [TimeOfDay.NIGHT]: [ SpeciesId.FROSMOTH ], [TimeOfDay.ALL]: [ SpeciesId.ABOMASNOW, SpeciesId.URSALUNA ]}, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GLASTRIER, SpeciesId.CHIEN_PAO, SpeciesId.GALAR_ARTICUNO ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.WYRDEER ], [TimeOfDay.DAY]: [ SpeciesId.WYRDEER ], [TimeOfDay.DUSK]: [ SpeciesId.FROSMOTH ], [TimeOfDay.NIGHT]: [ SpeciesId.FROSMOTH ], [TimeOfDay.ALL]: [ SpeciesId.ABOMASNOW, SpeciesId.URSALUNA ] }, [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [ SpeciesId.SNEASLER, SpeciesId.GALAR_DARMANITAN ], [TimeOfDay.DAY]: [ SpeciesId.SNEASLER, SpeciesId.GALAR_DARMANITAN ], @@ -1540,22 +1540,22 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.HISUI_ZOROARK ], [TimeOfDay.ALL]: [ SpeciesId.MR_RIME, SpeciesId.ARCTOZOLT, SpeciesId.ALOLA_SANDSLASH, SpeciesId.ALOLA_NINETALES ] }, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GLASTRIER, SpeciesId.CHIEN_PAO ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZACIAN, SpeciesId.GALAR_ARTICUNO ]} + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.GLASTRIER, SpeciesId.CHIEN_PAO ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZACIAN, SpeciesId.GALAR_ARTICUNO ] } }, [BiomeId.ISLAND]: { [BiomePoolTier.COMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [{ 1: [ SpeciesId.ALOLA_RATTATA ], 30: [ SpeciesId.ALOLA_RATICATE ]}, { 1: [ SpeciesId.ALOLA_MEOWTH ], 30: [ SpeciesId.ALOLA_PERSIAN ]}], - [TimeOfDay.NIGHT]: [{ 1: [ SpeciesId.ALOLA_RATTATA ], 30: [ SpeciesId.ALOLA_RATICATE ]}, { 1: [ SpeciesId.ALOLA_MEOWTH ], 30: [ SpeciesId.ALOLA_PERSIAN ]}], + [TimeOfDay.DUSK]: [ { 1: [ SpeciesId.ALOLA_RATTATA ], 20: [ SpeciesId.ALOLA_RATICATE ] }, { 1: [ SpeciesId.ALOLA_MEOWTH ], 30: [ SpeciesId.ALOLA_PERSIAN ] } ], + [TimeOfDay.NIGHT]: [ { 1: [ SpeciesId.ALOLA_RATTATA ], 20: [ SpeciesId.ALOLA_RATICATE ] }, { 1: [ SpeciesId.ALOLA_MEOWTH ], 30: [ SpeciesId.ALOLA_PERSIAN ] } ], [TimeOfDay.ALL]: [ SpeciesId.ORICORIO, - { 1: [ SpeciesId.ALOLA_SANDSHREW ], 30: [ SpeciesId.ALOLA_SANDSLASH ]}, - { 1: [ SpeciesId.ALOLA_VULPIX ], 30: [ SpeciesId.ALOLA_NINETALES ]}, - { 1: [ SpeciesId.ALOLA_DIGLETT ], 26: [ SpeciesId.ALOLA_DUGTRIO ]}, - { 1: [ SpeciesId.ALOLA_GEODUDE ], 25: [ SpeciesId.ALOLA_GRAVELER ], 40: [ SpeciesId.ALOLA_GOLEM ]}, - { 1: [ SpeciesId.ALOLA_GRIMER ], 38: [ SpeciesId.ALOLA_MUK ]} + { 1: [ SpeciesId.ALOLA_SANDSHREW ], 30: [ SpeciesId.ALOLA_SANDSLASH ] }, + { 1: [ SpeciesId.ALOLA_VULPIX ], 30: [ SpeciesId.ALOLA_NINETALES ] }, + { 1: [ SpeciesId.ALOLA_DIGLETT ], 26: [ SpeciesId.ALOLA_DUGTRIO ] }, + { 1: [ SpeciesId.ALOLA_GEODUDE ], 25: [ SpeciesId.ALOLA_GRAVELER ], 40: [ SpeciesId.ALOLA_GOLEM ] }, + { 1: [ SpeciesId.ALOLA_GRIMER ], 38: [ SpeciesId.ALOLA_MUK ] } ] }, [BiomePoolTier.UNCOMMON]: { @@ -1565,9 +1565,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.ALOLA_MAROWAK ], [TimeOfDay.ALL]: [ SpeciesId.BRUXISH ] }, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLACEPHALON ]}, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLACEPHALON ] }, [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [ SpeciesId.ALOLA_RAICHU, SpeciesId.ALOLA_EXEGGUTOR ], [TimeOfDay.DAY]: [ SpeciesId.ALOLA_RAICHU, SpeciesId.ALOLA_EXEGGUTOR ], @@ -1575,9 +1575,9 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.NIGHT]: [ SpeciesId.ALOLA_RATICATE, SpeciesId.ALOLA_PERSIAN, SpeciesId.ALOLA_MAROWAK ], [TimeOfDay.ALL]: [ SpeciesId.ORICORIO, SpeciesId.BRUXISH, SpeciesId.ALOLA_SANDSLASH, SpeciesId.ALOLA_NINETALES, SpeciesId.ALOLA_DUGTRIO, SpeciesId.ALOLA_GOLEM, SpeciesId.ALOLA_MUK ] }, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLACEPHALON ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.BLACEPHALON ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } }, [BiomeId.LABORATORY]: { [BiomePoolTier.COMMON]: { @@ -1586,21 +1586,21 @@ export const biomePokemonPools: BiomePokemonPools = { [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ - { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ]}, - { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ]}, - { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ]}, - { 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ]}, - { 1: [ SpeciesId.KLINK ], 38: [ SpeciesId.KLANG ], 49: [ SpeciesId.KLINKLANG ]} + { 1: [ SpeciesId.MAGNEMITE ], 30: [ SpeciesId.MAGNETON ] }, + { 1: [ SpeciesId.GRIMER ], 38: [ SpeciesId.MUK ] }, + { 1: [ SpeciesId.VOLTORB ], 30: [ SpeciesId.ELECTRODE ] }, + { 1: [ SpeciesId.BRONZOR ], 33: [ SpeciesId.BRONZONG ] }, + { 1: [ SpeciesId.KLINK ], 38: [ SpeciesId.KLANG ], 49: [ SpeciesId.KLINKLANG ] } ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [{ 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ]}]}, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ]}]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ]}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TYPE_NULL ]}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.ELECTRODE, SpeciesId.BRONZONG, SpeciesId.MAGNEZONE, SpeciesId.PORYGON_Z, SpeciesId.REUNICLUS, SpeciesId.KLINKLANG ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.ZYGARDE, SpeciesId.SILVALLY ]}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MEWTWO, SpeciesId.MIRAIDON ]} + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ] } ] }, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ] } ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [SpeciesId.TYPE_NULL], 60: [ SpeciesId.SILVALLY ] } ] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.ELECTRODE, SpeciesId.BRONZONG, SpeciesId.MAGNEZONE, SpeciesId.PORYGON_Z, SpeciesId.REUNICLUS, SpeciesId.KLINKLANG ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.ZYGARDE, { 1: [SpeciesId.TYPE_NULL], 60: [ SpeciesId.SILVALLY ] } ] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MEWTWO, SpeciesId.MIRAIDON ] } }, [BiomeId.END]: { [BiomePoolTier.COMMON]: { @@ -1623,14 +1623,14 @@ export const biomePokemonPools: BiomePokemonPools = { SpeciesId.IRON_THORNS ] }, - [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROARING_MOON, SpeciesId.IRON_VALIANT ]}, - [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.WALKING_WAKE, SpeciesId.IRON_LEAVES, SpeciesId.GOUGING_FIRE, SpeciesId.RAGING_BOLT, SpeciesId.IRON_BOULDER, SpeciesId.IRON_CROWN ]}, - [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ETERNATUS ]}, - [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []}, - [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: []} + [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROARING_MOON, SpeciesId.IRON_VALIANT ] }, + [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.WALKING_WAKE, SpeciesId.IRON_LEAVES, SpeciesId.GOUGING_FIRE, SpeciesId.RAGING_BOLT, SpeciesId.IRON_BOULDER, SpeciesId.IRON_CROWN ] }, + [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ETERNATUS ] }, + [BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, + [BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] } } }; @@ -6969,7 +6969,7 @@ export function initBiomes() { ] ], [ SpeciesId.ETERNAL_FLOETTE, PokemonType.FAIRY, -1, [ - [ BiomeId.FAIRY_CAVE, BiomePoolTier.RARE ], + [ BiomeId.FAIRY_CAVE, BiomePoolTier.SUPER_RARE ], [ BiomeId.FAIRY_CAVE, BiomePoolTier.BOSS_RARE ] ] ], @@ -7708,10 +7708,10 @@ export function initBiomes() { const traverseBiome = (biome: BiomeId, depth: number) => { if (biome === BiomeId.END) { - const biomeList = Object.keys(BiomeId).filter(key => !Number.isNaN(Number(key))); + const biomeList = getEnumValues(BiomeId) biomeList.pop(); // Removes BiomeId.END from the list const randIndex = randSeedInt(biomeList.length, 1); // Will never be BiomeId.TOWN - biome = BiomeId[biomeList[randIndex]]; + biome = biomeList[randIndex]; } const linkedBiomes: (BiomeId | [ BiomeId, number ])[] = Array.isArray(biomeLinks[biome]) ? biomeLinks[biome] as (BiomeId | [ BiomeId, number ])[] @@ -7871,7 +7871,7 @@ export function initBiomes() { // const trainerOutput = {}; // for (const b of Object.keys(biomePokemonPools)) { - // const biome = Biome[b]; + // const biome = BiomeId[b]; // pokemonOutput[biome] = {}; // trainerOutput[biome] = {}; @@ -7887,12 +7887,12 @@ export function initBiomes() { // for (const f of biomePokemonPools[b][t][tod]) { // if (typeof f === "number") { - // pokemonOutput[biome][tier][timeOfDay].push(Species[f]); + // pokemonOutput[biome][tier][timeOfDay].push(SpeciesId[f]); // } else { // const tree = {}; // for (const l of Object.keys(f)) { - // tree[l] = f[l].map(s => Species[s]); + // tree[l] = f[l].map(s => SpeciesId[s]); // } // pokemonOutput[biome][tier][timeOfDay].push(tree); diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 436e6bc6e76..f5026abe2ef 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -1,8 +1,8 @@ -import { allMoves } from "../data-lists"; -import { getEnumKeys, getEnumValues } from "#app/utils/common"; +import { allMoves } from "#data/data-lists"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; - +import { toReadableString } from "#utils/common"; +import { getEnumKeys, getEnumValues } from "#utils/enums"; export const speciesEggMoves = { [SpeciesId.BULBASAUR]: [ MoveId.SAPPY_SEED, MoveId.MALIGNANT_CHAIN, MoveId.EARTH_POWER, MoveId.MATCHA_GOTCHA ], @@ -26,14 +26,14 @@ export const speciesEggMoves = { [SpeciesId.MEOWTH]: [ MoveId.HEART_STAMP, MoveId.SWORDS_DANCE, MoveId.SIZZLY_SLIDE, MoveId.TAIL_SLAP ], [SpeciesId.PSYDUCK]: [ MoveId.FROST_BREATH, MoveId.AQUA_STEP, MoveId.MYSTICAL_POWER, MoveId.BOUNCY_BUBBLE ], [SpeciesId.MANKEY]: [ MoveId.DRAIN_PUNCH, MoveId.SLACK_OFF, MoveId.METEOR_MASH, MoveId.NO_RETREAT ], - [SpeciesId.GROWLITHE]: [ MoveId.ZING_ZAP, MoveId.PARTING_SHOT, MoveId.MORNING_SUN, MoveId.SACRED_FIRE ], + [SpeciesId.GROWLITHE]: [ MoveId.ZING_ZAP, MoveId.DRAGON_DANCE, MoveId.MORNING_SUN, MoveId.SACRED_FIRE ], [SpeciesId.POLIWAG]: [ MoveId.SLACK_OFF, MoveId.WILDBOLT_STORM, MoveId.DRAIN_PUNCH, MoveId.SURGING_STRIKES ], [SpeciesId.ABRA]: [ MoveId.AURA_SPHERE, MoveId.BADDY_BAD, MoveId.ICE_BEAM, MoveId.PSYSTRIKE ], [SpeciesId.MACHOP]: [ MoveId.COMBAT_TORQUE, MoveId.METEOR_MASH, MoveId.MOUNTAIN_GALE, MoveId.FISSURE ], [SpeciesId.BELLSPROUT]: [ MoveId.SOLAR_BLADE, MoveId.STRENGTH_SAP, MoveId.FIRE_LASH, MoveId.VICTORY_DANCE ], [SpeciesId.TENTACOOL]: [ MoveId.BANEFUL_BUNKER, MoveId.MALIGNANT_CHAIN, MoveId.BOUNCY_BUBBLE, MoveId.STRENGTH_SAP ], [SpeciesId.GEODUDE]: [ MoveId.FLARE_BLITZ, MoveId.HEAD_SMASH, MoveId.SHORE_UP, MoveId.SHELL_SMASH ], - [SpeciesId.PONYTA]: [ MoveId.HEADLONG_RUSH, MoveId.FIRE_LASH, MoveId.SWORDS_DANCE, MoveId.VOLT_TACKLE ], + [SpeciesId.PONYTA]: [ MoveId.HEADLONG_RUSH, MoveId.HIGH_JUMP_KICK, MoveId.SWORDS_DANCE, MoveId.VOLT_TACKLE ], [SpeciesId.SLOWPOKE]: [ MoveId.SPLISHY_SPLASH, MoveId.FROST_BREATH, MoveId.SHED_TAIL, MoveId.MYSTICAL_POWER ], [SpeciesId.MAGNEMITE]: [ MoveId.PARABOLIC_CHARGE, MoveId.FLAMETHROWER, MoveId.ICE_BEAM, MoveId.THUNDERCLAP ], [SpeciesId.FARFETCHD]: [ MoveId.IVY_CUDGEL, MoveId.TRIPLE_ARROWS, MoveId.DRILL_RUN, MoveId.VICTORY_DANCE ], @@ -63,9 +63,9 @@ export const speciesEggMoves = { [SpeciesId.LAPRAS]: [ MoveId.RECOVER, MoveId.FREEZE_DRY, MoveId.SCALD, MoveId.SHELL_SMASH ], [SpeciesId.DITTO]: [ MoveId.MIMIC, MoveId.SKETCH, MoveId.METRONOME, MoveId.IMPRISON ], [SpeciesId.EEVEE]: [ MoveId.WISH, MoveId.NO_RETREAT, MoveId.ZIPPY_ZAP, MoveId.BOOMBURST ], - [SpeciesId.PORYGON]: [ MoveId.THUNDERCLAP, MoveId.AURA_SPHERE, MoveId.FLAMETHROWER, MoveId.TECHNO_BLAST ], + [SpeciesId.PORYGON]: [ MoveId.THUNDERCLAP, MoveId.DAZZLING_GLEAM, MoveId.FLAMETHROWER, MoveId.TECHNO_BLAST ], [SpeciesId.OMANYTE]: [ MoveId.FREEZE_DRY, MoveId.GIGA_DRAIN, MoveId.POWER_GEM, MoveId.STEAM_ERUPTION ], - [SpeciesId.KABUTO]: [ MoveId.CEASELESS_EDGE, MoveId.HIGH_HORSEPOWER, MoveId.CRABHAMMER, MoveId.MIGHTY_CLEAVE ], + [SpeciesId.KABUTO]: [ MoveId.CEASELESS_EDGE, MoveId.HIGH_HORSEPOWER, MoveId.MIGHTY_CLEAVE, MoveId.CRABHAMMER ], [SpeciesId.AERODACTYL]: [ MoveId.FLOATY_FALL, MoveId.HIGH_HORSEPOWER, MoveId.STONE_AXE, MoveId.SWORDS_DANCE ], [SpeciesId.ARTICUNO]: [ MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.AURORA_VEIL, MoveId.AEROBLAST ], [SpeciesId.ZAPDOS]: [ MoveId.BLEAKWIND_STORM, MoveId.CALM_MIND, MoveId.SANDSEAR_STORM, MoveId.ELECTRO_SHOT ], @@ -75,8 +75,8 @@ export const speciesEggMoves = { [SpeciesId.MEW]: [ MoveId.PHOTON_GEYSER, MoveId.MOONBLAST, MoveId.ASTRAL_BARRAGE, MoveId.SHELL_SMASH ], [SpeciesId.CHIKORITA]: [ MoveId.SAPPY_SEED, MoveId.STONE_AXE, MoveId.DRAGON_DANCE, MoveId.SPORE ], - [SpeciesId.CYNDAQUIL]: [ MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.FIERY_DANCE, MoveId.ELECTRO_DRIFT ], - [SpeciesId.TOTODILE]: [ MoveId.THUNDER_PUNCH, MoveId.DRAGON_DANCE, MoveId.PLAY_ROUGH, MoveId.SURGING_STRIKES ], + [SpeciesId.CYNDAQUIL]: [ MoveId.BURNING_BULWARK, MoveId.EARTH_POWER, MoveId.FIERY_DANCE, MoveId.ELECTRO_DRIFT ], + [SpeciesId.TOTODILE]: [ MoveId.THUNDER_FANG, MoveId.DRAGON_DANCE, MoveId.DRAIN_PUNCH, MoveId.SURGING_STRIKES ], [SpeciesId.SENTRET]: [ MoveId.TIDY_UP, MoveId.FAKE_OUT, MoveId.NUZZLE, MoveId.EXTREME_SPEED ], [SpeciesId.HOOTHOOT]: [ MoveId.TAKE_HEART, MoveId.ESPER_WING, MoveId.AEROBLAST, MoveId.BOOMBURST ], [SpeciesId.LEDYBA]: [ MoveId.POLLEN_PUFF, MoveId.MAT_BLOCK, MoveId.PARTING_SHOT, MoveId.SPORE ], @@ -97,7 +97,7 @@ export const speciesEggMoves = { [SpeciesId.MISDREAVUS]: [ MoveId.TAKE_HEART, MoveId.MOONBLAST, MoveId.AURA_SPHERE, MoveId.MOONGEIST_BEAM ], [SpeciesId.UNOWN]: [ MoveId.NATURE_POWER, MoveId.COSMIC_POWER, MoveId.ANCIENT_POWER, MoveId.MYSTICAL_POWER ], [SpeciesId.GIRAFARIG]: [ MoveId.MYSTICAL_POWER, MoveId.NIGHT_DAZE, MoveId.RECOVER, MoveId.BOOMBURST ], - [SpeciesId.PINECO]: [ MoveId.METAL_BURST, MoveId.SHORE_UP, MoveId.BODY_PRESS, MoveId.DIAMOND_STORM ], + [SpeciesId.PINECO]: [ MoveId.METAL_BURST, MoveId.RECOVER, MoveId.LEECH_LIFE, MoveId.SPIN_OUT ], [SpeciesId.DUNSPARCE]: [ MoveId.WICKED_TORQUE, MoveId.MAGICAL_TORQUE, MoveId.BLAZING_TORQUE, MoveId.EXTREME_SPEED ], [SpeciesId.GLIGAR]: [ MoveId.FLOATY_FALL, MoveId.THOUSAND_WAVES, MoveId.SPIKY_SHIELD, MoveId.MIGHTY_CLEAVE ], [SpeciesId.SNUBBULL]: [ MoveId.FACADE, MoveId.HIGH_HORSEPOWER, MoveId.SWORDS_DANCE, MoveId.EXTREME_SPEED ], @@ -111,12 +111,12 @@ export const speciesEggMoves = { [SpeciesId.CORSOLA]: [ MoveId.SCALD, MoveId.FREEZE_DRY, MoveId.STRENGTH_SAP, MoveId.SALT_CURE ], [SpeciesId.REMORAID]: [ MoveId.WATER_SHURIKEN, MoveId.TAKE_HEART, MoveId.SHELL_SIDE_ARM, MoveId.BOUNCY_BUBBLE ], [SpeciesId.DELIBIRD]: [ MoveId.BONEMERANG, MoveId.FLOATY_FALL, MoveId.VICTORY_DANCE, MoveId.GLACIAL_LANCE ], - [SpeciesId.SKARMORY]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.SPIKY_SHIELD, MoveId.BEAK_BLAST ], + [SpeciesId.SKARMORY]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.CEASELESS_EDGE, MoveId.BEAK_BLAST ], [SpeciesId.HOUNDOUR]: [ MoveId.FIERY_WRATH, MoveId.THUNDERBOLT, MoveId.MOONBLAST, MoveId.ARMOR_CANNON ], [SpeciesId.PHANPY]: [ MoveId.SHORE_UP, MoveId.SWORDS_DANCE, MoveId.MOUNTAIN_GALE, MoveId.COLLISION_COURSE ], [SpeciesId.STANTLER]: [ MoveId.THUNDEROUS_KICK, MoveId.PHOTON_GEYSER, MoveId.SWORDS_DANCE, MoveId.BOOMBURST ], [SpeciesId.SMEARGLE]: [ MoveId.CONVERSION, MoveId.BURNING_BULWARK, MoveId.SALT_CURE, MoveId.DARK_VOID ], - [SpeciesId.TYROGUE]: [ MoveId.VICTORY_DANCE, MoveId.THUNDEROUS_KICK, MoveId.METEOR_MASH, MoveId.WICKED_BLOW ], + [SpeciesId.TYROGUE]: [ MoveId.DARKEST_LARIAT, MoveId.THUNDEROUS_KICK, MoveId.METEOR_MASH, MoveId.VICTORY_DANCE ], [SpeciesId.SMOOCHUM]: [ MoveId.LUSTER_PURGE, MoveId.AURA_SPHERE, MoveId.FREEZE_DRY, MoveId.QUIVER_DANCE ], [SpeciesId.ELEKID]: [ MoveId.FIRE_LASH, MoveId.ZING_ZAP, MoveId.MOUNTAIN_GALE, MoveId.SHIFT_GEAR ], [SpeciesId.MAGBY]: [ MoveId.THUNDERCLAP, MoveId.EARTH_POWER, MoveId.ENERGY_BALL, MoveId.BLUE_FLARE ], @@ -142,7 +142,7 @@ export const speciesEggMoves = { [SpeciesId.RALTS]: [ MoveId.PSYBLADE, MoveId.BITTER_BLADE, MoveId.NO_RETREAT, MoveId.BOOMBURST ], [SpeciesId.SURSKIT]: [ MoveId.POLLEN_PUFF, MoveId.FIERY_DANCE, MoveId.BOUNCY_BUBBLE, MoveId.AEROBLAST ], [SpeciesId.SHROOMISH]: [ MoveId.ACCELEROCK, MoveId.TRAILBLAZE, MoveId.STORM_THROW, MoveId.SAPPY_SEED ], - [SpeciesId.SLAKOTH]: [ MoveId.FACADE, MoveId.DRAIN_PUNCH, MoveId.KNOCK_OFF, MoveId.SKILL_SWAP ], + [SpeciesId.SLAKOTH]: [ MoveId.CRUSH_GRIP, MoveId.DRAIN_PUNCH, MoveId.PARTING_SHOT, MoveId.SKILL_SWAP ], [SpeciesId.NINCADA]: [ MoveId.BULLDOZE, MoveId.STICKY_WEB, MoveId.SHADOW_BONE, MoveId.SHELL_SMASH ], [SpeciesId.WHISMUR]: [ MoveId.ALLURING_VOICE, MoveId.SHIFT_GEAR, MoveId.SPARKLING_ARIA, MoveId.TORCH_SONG ], [SpeciesId.MAKUHITA]: [ MoveId.COMBAT_TORQUE, MoveId.SLACK_OFF, MoveId.HEAT_CRASH, MoveId.DOUBLE_IRON_BASH ], @@ -167,7 +167,7 @@ export const speciesEggMoves = { [SpeciesId.SPINDA]: [ MoveId.SUPERPOWER, MoveId.SLACK_OFF, MoveId.FLEUR_CANNON, MoveId.V_CREATE ], [SpeciesId.TRAPINCH]: [ MoveId.FIRE_LASH, MoveId.DRAGON_DARTS, MoveId.THOUSAND_ARROWS, MoveId.DRAGON_ENERGY ], [SpeciesId.CACNEA]: [ MoveId.EARTH_POWER, MoveId.CEASELESS_EDGE, MoveId.NIGHT_DAZE, MoveId.IVY_CUDGEL ], - [SpeciesId.SWABLU]: [ MoveId.ROOST, MoveId.NASTY_PLOT, MoveId.FLOATY_FALL, MoveId.BOOMBURST ], + [SpeciesId.SWABLU]: [ MoveId.ROOST, MoveId.TAKE_HEART, MoveId.AEROBLAST, MoveId.BOOMBURST ], [SpeciesId.ZANGOOSE]: [ MoveId.FACADE, MoveId.HIGH_HORSEPOWER, MoveId.EXTREME_SPEED, MoveId.TIDY_UP ], [SpeciesId.SEVIPER]: [ MoveId.ICE_BEAM, MoveId.BITTER_BLADE, MoveId.SUCKER_PUNCH, MoveId.NO_RETREAT ], [SpeciesId.LUNATONE]: [ MoveId.REVELATION_DANCE, MoveId.MOONGEIST_BEAM, MoveId.SHELL_SMASH, MoveId.LUMINA_CRASH ], @@ -203,13 +203,13 @@ export const speciesEggMoves = { [SpeciesId.JIRACHI]: [ MoveId.TACHYON_CUTTER, MoveId.TRIPLE_ARROWS, MoveId.ROCK_SLIDE, MoveId.SHELL_SMASH ], [SpeciesId.DEOXYS]: [ MoveId.COLLISION_COURSE, MoveId.FUSION_FLARE, MoveId.PARTING_SHOT, MoveId.LUMINA_CRASH ], - [SpeciesId.TURTWIG]: [ MoveId.SHELL_SMASH, MoveId.MIGHTY_CLEAVE, MoveId.ICE_SPINNER, MoveId.SAPPY_SEED ], + [SpeciesId.TURTWIG]: [ MoveId.SHELL_SMASH, MoveId.STONE_AXE, MoveId.ICE_SPINNER, MoveId.SAPPY_SEED ], [SpeciesId.CHIMCHAR]: [ MoveId.THUNDERBOLT, MoveId.SECRET_SWORD, MoveId.TRIPLE_AXEL, MoveId.SACRED_FIRE ], [SpeciesId.PIPLUP]: [ MoveId.KINGS_SHIELD, MoveId.TACHYON_CUTTER, MoveId.FREEZE_DRY, MoveId.STEAM_ERUPTION ], [SpeciesId.STARLY]: [ MoveId.SWORDS_DANCE, MoveId.HEAD_CHARGE, MoveId.FLARE_BLITZ, MoveId.EXTREME_SPEED ], [SpeciesId.BIDOOF]: [ MoveId.EXTREME_SPEED, MoveId.COSMIC_POWER, MoveId.POWER_TRIP, MoveId.AQUA_STEP ], [SpeciesId.KRICKETOT]: [ MoveId.BONEMERANG, MoveId.VICTORY_DANCE, MoveId.STONE_AXE, MoveId.POPULATION_BOMB ], - [SpeciesId.SHINX]: [ MoveId.FIRE_LASH, MoveId.TRIPLE_AXEL, MoveId.ZIPPY_ZAP, MoveId.BOLT_STRIKE ], + [SpeciesId.SHINX]: [ MoveId.THUNDEROUS_KICK, MoveId.TRIPLE_AXEL, MoveId.ZIPPY_ZAP, MoveId.BOLT_STRIKE ], [SpeciesId.BUDEW]: [ MoveId.FIERY_DANCE, MoveId.ACID_SPRAY, MoveId.BOUNCY_BUBBLE, MoveId.QUIVER_DANCE ], [SpeciesId.CRANIDOS]: [ MoveId.VOLT_TACKLE, MoveId.ACCELEROCK, MoveId.FLARE_BLITZ, MoveId.SHIFT_GEAR ], [SpeciesId.SHIELDON]: [ MoveId.SHORE_UP, MoveId.BODY_PRESS, MoveId.KINGS_SHIELD, MoveId.DIAMOND_STORM ], @@ -253,14 +253,14 @@ export const speciesEggMoves = { [SpeciesId.PHIONE]: [ MoveId.BOUNCY_BUBBLE, MoveId.FREEZE_DRY, MoveId.STORED_POWER, MoveId.ORIGIN_PULSE ], [SpeciesId.MANAPHY]: [ MoveId.BOUNCY_BUBBLE, MoveId.FROST_BREATH, MoveId.WILDBOLT_STORM, MoveId.ORIGIN_PULSE ], [SpeciesId.DARKRAI]: [ MoveId.FIERY_WRATH, MoveId.MOONBLAST, MoveId.FIERY_DANCE, MoveId.MAKE_IT_RAIN ], - [SpeciesId.SHAYMIN]: [ MoveId.MATCHA_GOTCHA, MoveId.FIERY_DANCE, MoveId.AEROBLAST, MoveId.QUIVER_DANCE ], + [SpeciesId.SHAYMIN]: [ MoveId.MATCHA_GOTCHA, MoveId.HEAT_WAVE, MoveId.AEROBLAST, MoveId.QUIVER_DANCE ], [SpeciesId.ARCEUS]: [ MoveId.NO_RETREAT, MoveId.COLLISION_COURSE, MoveId.ASTRAL_BARRAGE, MoveId.MULTI_ATTACK ], [SpeciesId.VICTINI]: [ MoveId.BLUE_FLARE, MoveId.BOLT_STRIKE, MoveId.LUSTER_PURGE, MoveId.VICTORY_DANCE ], [SpeciesId.SNIVY]: [ MoveId.FLAMETHROWER, MoveId.CLANGING_SCALES, MoveId.MAKE_IT_RAIN, MoveId.FLEUR_CANNON ], [SpeciesId.TEPIG]: [ MoveId.WAVE_CRASH, MoveId.VOLT_TACKLE, MoveId.AXE_KICK, MoveId.VICTORY_DANCE ], [SpeciesId.OSHAWOTT]: [ MoveId.FREEZE_DRY, MoveId.SHELL_SIDE_ARM, MoveId.SACRED_SWORD, MoveId.SHELL_SMASH ], - [SpeciesId.PATRAT]: [ MoveId.FAKE_OUT, MoveId.SWORDS_DANCE, MoveId.DYNAMIC_PUNCH, MoveId.EXTREME_SPEED ], + [SpeciesId.PATRAT]: [ MoveId.FAKE_OUT, MoveId.INSTRUCT, MoveId.DYNAMIC_PUNCH, MoveId.EXTREME_SPEED ], [SpeciesId.LILLIPUP]: [ MoveId.CLOSE_COMBAT, MoveId.BODY_SLAM, MoveId.HIGH_HORSEPOWER, MoveId.LAST_RESPECTS ], [SpeciesId.PURRLOIN]: [ MoveId.ENCORE, MoveId.OBSTRUCT, MoveId.PARTING_SHOT, MoveId.WICKED_BLOW ], [SpeciesId.PANSAGE]: [ MoveId.SWORDS_DANCE, MoveId.FIRE_LASH, MoveId.EARTHQUAKE, MoveId.IVY_CUDGEL ], @@ -269,7 +269,7 @@ export const speciesEggMoves = { [SpeciesId.MUNNA]: [ MoveId.COSMIC_POWER, MoveId.AURA_SPHERE, MoveId.LUNAR_BLESSING, MoveId.MYSTICAL_POWER ], [SpeciesId.PIDOVE]: [ MoveId.SLASH, MoveId.TIDY_UP, MoveId.FLOATY_FALL, MoveId.TRIPLE_ARROWS ], [SpeciesId.BLITZLE]: [ MoveId.HORN_LEECH, MoveId.SWORDS_DANCE, MoveId.FLARE_BLITZ, MoveId.BOLT_STRIKE ], - [SpeciesId.ROGGENROLA]: [ MoveId.BODY_PRESS, MoveId.CURSE, MoveId.SHORE_UP, MoveId.DIAMOND_STORM ], + [SpeciesId.ROGGENROLA]: [ MoveId.BODY_PRESS, MoveId.SPIKY_SHIELD, MoveId.SHORE_UP, MoveId.DIAMOND_STORM ], [SpeciesId.WOOBAT]: [ MoveId.ESPER_WING, MoveId.STORED_POWER, MoveId.MYSTICAL_FIRE, MoveId.OBLIVION_WING ], [SpeciesId.DRILBUR]: [ MoveId.METEOR_MASH, MoveId.ICE_SPINNER, MoveId.SHIFT_GEAR, MoveId.THOUSAND_ARROWS ], [SpeciesId.AUDINO]: [ MoveId.TAKE_HEART, MoveId.MOONBLAST, MoveId.WISH, MoveId.MATCHA_GOTCHA ], @@ -298,7 +298,7 @@ export const speciesEggMoves = { [SpeciesId.SOLOSIS]: [ MoveId.MIST_BALL, MoveId.SPEED_SWAP, MoveId.FLAMETHROWER, MoveId.LIGHT_OF_RUIN ], [SpeciesId.DUCKLETT]: [ MoveId.SPLISHY_SPLASH, MoveId.SANDSEAR_STORM, MoveId.WILDBOLT_STORM, MoveId.QUIVER_DANCE ], [SpeciesId.VANILLITE]: [ MoveId.EARTH_POWER, MoveId.AURORA_VEIL, MoveId.CALM_MIND, MoveId.SPARKLY_SWIRL ], - [SpeciesId.DEERLING]: [ MoveId.TIDY_UP, MoveId.HEADBUTT, MoveId.COMBAT_TORQUE, MoveId.FLOWER_TRICK ], + [SpeciesId.DEERLING]: [ MoveId.TIDY_UP, MoveId.HEADBUTT, MoveId.AXE_KICK, MoveId.FLOWER_TRICK ], [SpeciesId.EMOLGA]: [ MoveId.ICICLE_CRASH, MoveId.ZING_ZAP, MoveId.FLOATY_FALL, MoveId.ELECTRIFY ], [SpeciesId.KARRABLAST]: [ MoveId.LEECH_LIFE, MoveId.BITTER_BLADE, MoveId.OBSTRUCT, MoveId.DOUBLE_IRON_BASH ], [SpeciesId.FOONGUS]: [ MoveId.POLLEN_PUFF, MoveId.PARTING_SHOT, MoveId.FOUL_PLAY, MoveId.SAPPY_SEED ], @@ -322,8 +322,8 @@ export const speciesEggMoves = { [SpeciesId.BOUFFALANT]: [ MoveId.HORN_LEECH, MoveId.HIGH_JUMP_KICK, MoveId.HEAD_SMASH, MoveId.FLARE_BLITZ ], [SpeciesId.RUFFLET]: [ MoveId.FLOATY_FALL, MoveId.AURA_SPHERE, MoveId.NO_RETREAT, MoveId.BOLT_BEAK ], [SpeciesId.VULLABY]: [ MoveId.FOUL_PLAY, MoveId.BODY_PRESS, MoveId.ROOST, MoveId.RUINATION ], - [SpeciesId.HEATMOR]: [ MoveId.EARTH_POWER, MoveId.OVERHEAT, MoveId.THUNDERBOLT, MoveId.V_CREATE ], - [SpeciesId.DURANT]: [ MoveId.HIGH_HORSEPOWER, MoveId.FIRST_IMPRESSION, MoveId.SWORDS_DANCE, MoveId.BEHEMOTH_BASH ], + [SpeciesId.HEATMOR]: [ MoveId.EARTH_POWER, MoveId.OVERHEAT, MoveId.SUPERCELL_SLAM, MoveId.V_CREATE ], + [SpeciesId.DURANT]: [ MoveId.HIGH_HORSEPOWER, MoveId.FIRST_IMPRESSION, MoveId.U_TURN, MoveId.BEHEMOTH_BASH ], [SpeciesId.DEINO]: [ MoveId.FIERY_WRATH, MoveId.ESPER_WING, MoveId.SLUDGE_BOMB, MoveId.FICKLE_BEAM ], [SpeciesId.LARVESTA]: [ MoveId.THUNDERBOLT, MoveId.DAZZLING_GLEAM, MoveId.EARTH_POWER, MoveId.HYDRO_STEAM ], [SpeciesId.COBALION]: [ MoveId.BEHEMOTH_BLADE, MoveId.MIGHTY_CLEAVE, MoveId.CEASELESS_EDGE, MoveId.VICTORY_DANCE ], @@ -381,8 +381,8 @@ export const speciesEggMoves = { [SpeciesId.ROWLET]: [ MoveId.THOUSAND_ARROWS, MoveId.SHADOW_BONE, MoveId.FIRST_IMPRESSION, MoveId.VICTORY_DANCE ], [SpeciesId.LITTEN]: [ MoveId.SUCKER_PUNCH, MoveId.PARTING_SHOT, MoveId.SLACK_OFF, MoveId.SACRED_FIRE ], [SpeciesId.POPPLIO]: [ MoveId.PSYCHIC_NOISE, MoveId.MOONLIGHT, MoveId.OVERDRIVE, MoveId.TORCH_SONG ], - [SpeciesId.PIKIPEK]: [ MoveId.DUAL_WINGBEAT, MoveId.BONE_RUSH, MoveId.BURNING_BULWARK, MoveId.POPULATION_BOMB ], - [SpeciesId.YUNGOOS]: [ MoveId.EXTREME_SPEED, MoveId.KNOCK_OFF, MoveId.TIDY_UP, MoveId.MULTI_ATTACK ], + [SpeciesId.PIKIPEK]: [ MoveId.TRAILBLAZE, MoveId.BONE_RUSH, MoveId.BURNING_BULWARK, MoveId.POPULATION_BOMB ], + [SpeciesId.YUNGOOS]: [ MoveId.FAKE_OUT, MoveId.HIGH_HORSEPOWER, MoveId.TIDY_UP, MoveId.EXTREME_SPEED ], [SpeciesId.GRUBBIN]: [ MoveId.ICE_BEAM, MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.THUNDERCLAP ], [SpeciesId.CRABRAWLER]: [ MoveId.JET_PUNCH, MoveId.SHORE_UP, MoveId.MACH_PUNCH, MoveId.SURGING_STRIKES ], [SpeciesId.ORICORIO]: [ MoveId.QUIVER_DANCE, MoveId.FIERY_DANCE, MoveId.THUNDERCLAP, MoveId.OBLIVION_WING ], @@ -396,7 +396,7 @@ export const speciesEggMoves = { [SpeciesId.MORELULL]: [ MoveId.CALM_MIND, MoveId.SAPPY_SEED, MoveId.DRAINING_KISS, MoveId.MATCHA_GOTCHA ], [SpeciesId.SALANDIT]: [ MoveId.SCALD, MoveId.MALIGNANT_CHAIN, MoveId.CORE_ENFORCER, MoveId.ERUPTION ], [SpeciesId.STUFFUL]: [ MoveId.DRAIN_PUNCH, MoveId.METEOR_MASH, MoveId.TRIPLE_AXEL, MoveId.RAGE_FIST ], - [SpeciesId.BOUNSWEET]: [ MoveId.TRIPLE_AXEL, MoveId.AQUA_STEP, MoveId.THUNDEROUS_KICK, MoveId.SAPPY_SEED ], + [SpeciesId.BOUNSWEET]: [ MoveId.TRIPLE_AXEL, MoveId.AQUA_STEP, MoveId.THUNDEROUS_KICK, MoveId.FLOWER_TRICK ], [SpeciesId.COMFEY]: [ MoveId.REVIVAL_BLESSING, MoveId.TAKE_HEART, MoveId.STRENGTH_SAP, MoveId.MATCHA_GOTCHA ], [SpeciesId.ORANGURU]: [ MoveId.JUNGLE_HEALING, MoveId.YAWN, MoveId.FOLLOW_ME, MoveId.LUMINA_CRASH ], [SpeciesId.PASSIMIAN]: [ MoveId.PYRO_BALL, MoveId.SUCKER_PUNCH, MoveId.ZING_ZAP, MoveId.VICTORY_DANCE ], @@ -423,7 +423,7 @@ export const speciesEggMoves = { [SpeciesId.PHEROMOSA]: [ MoveId.SECRET_SWORD, MoveId.MAKE_IT_RAIN, MoveId.ATTACK_ORDER, MoveId.DIAMOND_STORM ], [SpeciesId.XURKITREE]: [ MoveId.FLAMETHROWER, MoveId.GIGA_DRAIN, MoveId.TAIL_GLOW, MoveId.THUNDERCLAP ], [SpeciesId.CELESTEELA]: [ MoveId.RECOVER, MoveId.BUZZY_BUZZ, MoveId.EARTH_POWER, MoveId.OBLIVION_WING ], - [SpeciesId.KARTANA]: [ MoveId.MIGHTY_CLEAVE, MoveId.DUAL_CHOP, MoveId.BITTER_BLADE, MoveId.BEHEMOTH_BLADE ], + [SpeciesId.KARTANA]: [ MoveId.MIGHTY_CLEAVE, MoveId.DUAL_CHOP, MoveId.BEHEMOTH_BLADE, MoveId.BITTER_BLADE ], [SpeciesId.GUZZLORD]: [ MoveId.SUCKER_PUNCH, MoveId.COMEUPPANCE, MoveId.SLACK_OFF, MoveId.SHED_TAIL ], [SpeciesId.NECROZMA]: [ MoveId.DYNAMAX_CANNON, MoveId.SACRED_FIRE, MoveId.ASTRAL_BARRAGE, MoveId.CLANGOROUS_SOUL ], [SpeciesId.MAGEARNA]: [ MoveId.STRENGTH_SAP, MoveId.EARTH_POWER, MoveId.MOONBLAST, MoveId.MAKE_IT_RAIN ], @@ -431,23 +431,23 @@ export const speciesEggMoves = { [SpeciesId.POIPOLE]: [ MoveId.MALIGNANT_CHAIN, MoveId.ICE_BEAM, MoveId.ARMOR_CANNON, MoveId.CLANGING_SCALES ], [SpeciesId.STAKATAKA]: [ MoveId.HEAVY_SLAM, MoveId.SHORE_UP, MoveId.CURSE, MoveId.SALT_CURE ], [SpeciesId.BLACEPHALON]: [ MoveId.STEEL_BEAM, MoveId.MOONBLAST, MoveId.CHLOROBLAST, MoveId.MOONGEIST_BEAM ], - [SpeciesId.ZERAORA]: [ MoveId.SWORDS_DANCE, MoveId.U_TURN, MoveId.COLLISION_COURSE, MoveId.TRIPLE_AXEL ], + [SpeciesId.ZERAORA]: [ MoveId.SWORDS_DANCE, MoveId.FIRE_LASH, MoveId.COLLISION_COURSE, MoveId.TRIPLE_AXEL ], [SpeciesId.MELTAN]: [ MoveId.BULLET_PUNCH, MoveId.DRAIN_PUNCH, MoveId.BULK_UP, MoveId.PLASMA_FISTS ], [SpeciesId.ALOLA_RATTATA]: [ MoveId.FALSE_SURRENDER, MoveId.PSYCHIC_FANGS, MoveId.COIL, MoveId.EXTREME_SPEED ], [SpeciesId.ALOLA_SANDSHREW]: [ MoveId.SPIKY_SHIELD, MoveId.LIQUIDATION, MoveId.SHIFT_GEAR, MoveId.GLACIAL_LANCE ], - [SpeciesId.ALOLA_VULPIX]: [ MoveId.MOONBLAST, MoveId.GLARE, MoveId.MYSTICAL_FIRE, MoveId.REVIVAL_BLESSING ], + [SpeciesId.ALOLA_VULPIX]: [ MoveId.MOONBLAST, MoveId.GLARE, MoveId.MYSTICAL_FIRE, MoveId.LUNAR_BLESSING ], [SpeciesId.ALOLA_DIGLETT]: [ MoveId.THOUSAND_WAVES, MoveId.SWORDS_DANCE, MoveId.TRIPLE_DIVE, MoveId.PYRO_BALL ], [SpeciesId.ALOLA_MEOWTH]: [ MoveId.BADDY_BAD, MoveId.BUZZY_BUZZ, MoveId.PARTING_SHOT, MoveId.MAKE_IT_RAIN ], - [SpeciesId.ALOLA_GEODUDE]: [ MoveId.THOUSAND_WAVES, MoveId.BULK_UP, MoveId.STONE_AXE, MoveId.EXTREME_SPEED ], + [SpeciesId.ALOLA_GEODUDE]: [ MoveId.LANDS_WRATH, MoveId.FUSION_BOLT, MoveId.STONE_AXE, MoveId.EXTREME_SPEED ], [SpeciesId.ALOLA_GRIMER]: [ MoveId.SUCKER_PUNCH, MoveId.BARB_BARRAGE, MoveId.RECOVER, MoveId.SURGING_STRIKES ], [SpeciesId.GROOKEY]: [ MoveId.ROCK_SLIDE, MoveId.PLAY_ROUGH, MoveId.GRASSY_GLIDE, MoveId.CLANGOROUS_SOUL ], - [SpeciesId.SCORBUNNY]: [ MoveId.EXTREME_SPEED, MoveId.HIGH_JUMP_KICK, MoveId.TRIPLE_AXEL, MoveId.BOLT_STRIKE ], + [SpeciesId.SCORBUNNY]: [ MoveId.EXTREME_SPEED, MoveId.HIGH_JUMP_KICK, MoveId.SUPERCELL_SLAM, MoveId.TRIPLE_AXEL ], [SpeciesId.SOBBLE]: [ MoveId.AEROBLAST, MoveId.FROST_BREATH, MoveId.ENERGY_BALL, MoveId.NASTY_PLOT ], [SpeciesId.SKWOVET]: [ MoveId.SUCKER_PUNCH, MoveId.SLACK_OFF, MoveId.COIL, MoveId.POPULATION_BOMB ], [SpeciesId.ROOKIDEE]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.KINGS_SHIELD, MoveId.BEHEMOTH_BASH ], [SpeciesId.BLIPBUG]: [ MoveId.HEAL_ORDER, MoveId.LUSTER_PURGE, MoveId.SLEEP_POWDER, MoveId.TAIL_GLOW ], - [SpeciesId.NICKIT]: [ MoveId.BADDY_BAD, MoveId.FLAMETHROWER, MoveId.SPARKLY_SWIRL, MoveId.MAKE_IT_RAIN ], + [SpeciesId.NICKIT]: [ MoveId.BADDY_BAD, MoveId.MYSTICAL_FIRE, MoveId.SPARKLY_SWIRL, MoveId.MAKE_IT_RAIN ], [SpeciesId.GOSSIFLEUR]: [ MoveId.PARTING_SHOT, MoveId.STRENGTH_SAP, MoveId.SAPPY_SEED, MoveId.SEED_FLARE ], [SpeciesId.WOOLOO]: [ MoveId.NUZZLE, MoveId.MILK_DRINK, MoveId.BODY_PRESS, MoveId.MULTI_ATTACK ], [SpeciesId.CHEWTLE]: [ MoveId.ICE_FANG, MoveId.PSYCHIC_FANGS, MoveId.SHELL_SMASH, MoveId.MIGHTY_CLEAVE ], @@ -467,7 +467,7 @@ export const speciesEggMoves = { [SpeciesId.FALINKS]: [ MoveId.BATON_PASS, MoveId.POWER_TRIP, MoveId.COMBAT_TORQUE, MoveId.HEAL_ORDER ], [SpeciesId.PINCURCHIN]: [ MoveId.TRICK_ROOM, MoveId.VOLT_SWITCH, MoveId.STRENGTH_SAP, MoveId.THUNDERCLAP ], [SpeciesId.SNOM]: [ MoveId.FROST_BREATH, MoveId.HEAL_ORDER, MoveId.EARTH_POWER, MoveId.SPORE ], - [SpeciesId.STONJOURNER]: [ MoveId.BODY_PRESS, MoveId.HELPING_HAND, MoveId.ACCELEROCK, MoveId.DIAMOND_STORM ], + [SpeciesId.STONJOURNER]: [ MoveId.AXE_KICK, MoveId.HELPING_HAND, MoveId.ACCELEROCK, MoveId.DIAMOND_STORM ], [SpeciesId.EISCUE]: [ MoveId.TRIPLE_AXEL, MoveId.AQUA_STEP, MoveId.AXE_KICK, MoveId.SHELL_SMASH ], [SpeciesId.INDEEDEE]: [ MoveId.MATCHA_GOTCHA, MoveId.EXPANDING_FORCE, MoveId.MOONBLAST, MoveId.REVIVAL_BLESSING ], [SpeciesId.MORPEKO]: [ MoveId.TRIPLE_AXEL, MoveId.OBSTRUCT, MoveId.SWORDS_DANCE, MoveId.COLLISION_COURSE ], @@ -478,8 +478,8 @@ export const speciesEggMoves = { [SpeciesId.ARCTOVISH]: [ MoveId.ICE_FANG, MoveId.THUNDER_FANG, MoveId.HIGH_HORSEPOWER, MoveId.SHIFT_GEAR ], [SpeciesId.DURALUDON]: [ MoveId.CORE_ENFORCER, MoveId.BODY_PRESS, MoveId.RECOVER, MoveId.TACHYON_CUTTER ], [SpeciesId.DREEPY]: [ MoveId.SHADOW_BONE, MoveId.POWER_UP_PUNCH, MoveId.FIRE_LASH, MoveId.DIRE_CLAW ], - [SpeciesId.ZACIAN]: [ MoveId.MAGICAL_TORQUE, MoveId.MIGHTY_CLEAVE, MoveId.BITTER_BLADE, MoveId.PRECIPICE_BLADES ], - [SpeciesId.ZAMAZENTA]: [ MoveId.BULK_UP, MoveId.BODY_PRESS, MoveId.SLACK_OFF, MoveId.DIAMOND_STORM ], + [SpeciesId.ZACIAN]: [ MoveId.MAGICAL_TORQUE, MoveId.MIGHTY_CLEAVE, MoveId.EARTHQUAKE, MoveId.BITTER_BLADE ], + [SpeciesId.ZAMAZENTA]: [ MoveId.BULK_UP, MoveId.BODY_PRESS, MoveId.POWER_TRIP, MoveId.SLACK_OFF ], [SpeciesId.ETERNATUS]: [ MoveId.BODY_PRESS, MoveId.NASTY_PLOT, MoveId.MALIGNANT_CHAIN, MoveId.DRAGON_ENERGY ], [SpeciesId.KUBFU]: [ MoveId.METEOR_MASH, MoveId.DRAIN_PUNCH, MoveId.JET_PUNCH, MoveId.DRAGON_DANCE ], [SpeciesId.ZARUDE]: [ MoveId.SAPPY_SEED, MoveId.MIGHTY_CLEAVE, MoveId.WICKED_BLOW, MoveId.VICTORY_DANCE ], @@ -511,18 +511,18 @@ export const speciesEggMoves = { [SpeciesId.FUECOCO]: [ MoveId.ALLURING_VOICE, MoveId.SLACK_OFF, MoveId.OVERDRIVE, MoveId.MOONGEIST_BEAM ], [SpeciesId.QUAXLY]: [ MoveId.DRAGON_DANCE, MoveId.TRIPLE_AXEL, MoveId.POWER_TRIP, MoveId.THUNDEROUS_KICK ], [SpeciesId.LECHONK]: [ MoveId.MILK_DRINK, MoveId.PSYSHIELD_BASH, MoveId.BLAZING_TORQUE, MoveId.FILLET_AWAY ], - [SpeciesId.TAROUNTULA]: [ MoveId.STONE_AXE, MoveId.LEECH_LIFE, MoveId.THIEF, MoveId.SPORE ], + [SpeciesId.TAROUNTULA]: [ MoveId.STONE_AXE, MoveId.LEECH_LIFE, MoveId.FAKE_OUT, MoveId.SPORE ], [SpeciesId.NYMBLE]: [ MoveId.KNOCK_OFF, MoveId.FELL_STINGER, MoveId.ATTACK_ORDER, MoveId.WICKED_BLOW ], [SpeciesId.PAWMI]: [ MoveId.DRAIN_PUNCH, MoveId.METEOR_MASH, MoveId.JET_PUNCH, MoveId.PLASMA_FISTS ], [SpeciesId.TANDEMAUS]: [ MoveId.BATON_PASS, MoveId.COVET, MoveId.SIZZLY_SLIDE, MoveId.REVIVAL_BLESSING ], [SpeciesId.FIDOUGH]: [ MoveId.SOFT_BOILED, MoveId.HIGH_HORSEPOWER, MoveId.SIZZLY_SLIDE, MoveId.TIDY_UP ], [SpeciesId.SMOLIV]: [ MoveId.STRENGTH_SAP, MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.BOOMBURST ], [SpeciesId.SQUAWKABILLY]: [ MoveId.PARTING_SHOT, MoveId.EARTHQUAKE, MoveId.FLARE_BLITZ, MoveId.EXTREME_SPEED ], - [SpeciesId.NACLI]: [ MoveId.BODY_PRESS, MoveId.TOXIC, MoveId.CURSE, MoveId.DIAMOND_STORM ], + [SpeciesId.NACLI]: [ MoveId.KNOCK_OFF, MoveId.TOXIC, MoveId.SAND_TOMB, MoveId.DIAMOND_STORM ], [SpeciesId.CHARCADET]: [ MoveId.SACRED_SWORD, MoveId.PHOTON_GEYSER, MoveId.MOONBLAST, MoveId.SPECTRAL_THIEF ], [SpeciesId.TADBULB]: [ MoveId.PARABOLIC_CHARGE, MoveId.SCALD, MoveId.EARTH_POWER, MoveId.ELECTRO_SHOT ], [SpeciesId.WATTREL]: [ MoveId.NASTY_PLOT, MoveId.SPLISHY_SPLASH, MoveId.SANDSEAR_STORM, MoveId.WILDBOLT_STORM ], - [SpeciesId.MASCHIFF]: [ MoveId.PARTING_SHOT, MoveId.COMBAT_TORQUE, MoveId.PSYCHIC_FANGS, MoveId.NO_RETREAT ], + [SpeciesId.MASCHIFF]: [ MoveId.PARTING_SHOT, MoveId.LEECH_LIFE, MoveId.PSYCHIC_FANGS, MoveId.NO_RETREAT ], [SpeciesId.SHROODLE]: [ MoveId.GASTRO_ACID, MoveId.PARTING_SHOT, MoveId.TOXIC, MoveId.SKETCH ], [SpeciesId.BRAMBLIN]: [ MoveId.TAILWIND, MoveId.STRENGTH_SAP, MoveId.FLOWER_TRICK, MoveId.LAST_RESPECTS ], [SpeciesId.TOEDSCOOL]: [ MoveId.STRENGTH_SAP, MoveId.TOPSY_TURVY, MoveId.SAPPY_SEED, MoveId.TAIL_GLOW ], @@ -535,7 +535,7 @@ export const speciesEggMoves = { [SpeciesId.BOMBIRDIER]: [ MoveId.FLOATY_FALL, MoveId.SWORDS_DANCE, MoveId.SUCKER_PUNCH, MoveId.MIGHTY_CLEAVE ], [SpeciesId.FINIZEN]: [ MoveId.TRIPLE_AXEL, MoveId.DRAIN_PUNCH, MoveId.HEADLONG_RUSH, MoveId.SURGING_STRIKES ], [SpeciesId.VAROOM]: [ MoveId.COMBAT_TORQUE, MoveId.U_TURN, MoveId.BLAZING_TORQUE, MoveId.NOXIOUS_TORQUE ], - [SpeciesId.CYCLIZAR]: [ MoveId.PARTING_SHOT, MoveId.FIRE_LASH, MoveId.MAGICAL_TORQUE, MoveId.GLAIVE_RUSH ], + [SpeciesId.CYCLIZAR]: [ MoveId.PARTING_SHOT, MoveId.FIRE_LASH, MoveId.HIGH_HORSEPOWER, MoveId.MAGICAL_TORQUE ], [SpeciesId.ORTHWORM]: [ MoveId.SIZZLY_SLIDE, MoveId.COIL, MoveId.BODY_PRESS, MoveId.SHORE_UP ], [SpeciesId.GLIMMET]: [ MoveId.CALM_MIND, MoveId.GIGA_DRAIN, MoveId.FIERY_DANCE, MoveId.MALIGNANT_CHAIN ], [SpeciesId.GREAVARD]: [ MoveId.SHADOW_BONE, MoveId.SIZZLY_SLIDE, MoveId.SHORE_UP, MoveId.COLLISION_COURSE ], @@ -548,7 +548,7 @@ export const speciesEggMoves = { [SpeciesId.SCREAM_TAIL]: [ MoveId.TORCH_SONG, MoveId.GLITZY_GLOW, MoveId.MOONLIGHT, MoveId.SPARKLY_SWIRL ], [SpeciesId.BRUTE_BONNET]: [ MoveId.SAPPY_SEED, MoveId.STRENGTH_SAP, MoveId.EARTHQUAKE, MoveId.WICKED_BLOW ], [SpeciesId.FLUTTER_MANE]: [ MoveId.MOONLIGHT, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.MOONGEIST_BEAM ], - [SpeciesId.SLITHER_WING]: [ MoveId.MIGHTY_CLEAVE, MoveId.THUNDEROUS_KICK, MoveId.FIRE_LASH, MoveId.VICTORY_DANCE ], + [SpeciesId.SLITHER_WING]: [ MoveId.ROCK_SLIDE, MoveId.THUNDEROUS_KICK, MoveId.SUNSTEEL_STRIKE, MoveId.VICTORY_DANCE ], [SpeciesId.SANDY_SHOCKS]: [ MoveId.MORNING_SUN, MoveId.ICE_BEAM, MoveId.NASTY_PLOT, MoveId.THUNDERCLAP ], [SpeciesId.IRON_TREADS]: [ MoveId.FUSION_BOLT, MoveId.SHIFT_GEAR, MoveId.SHORE_UP, MoveId.SUNSTEEL_STRIKE ], [SpeciesId.IRON_BUNDLE]: [ MoveId.EARTH_POWER, MoveId.SPLISHY_SPLASH, MoveId.VOLT_SWITCH, MoveId.NASTY_PLOT ], @@ -563,7 +563,7 @@ export const speciesEggMoves = { [SpeciesId.TING_LU]: [ MoveId.SHORE_UP, MoveId.CEASELESS_EDGE, MoveId.SAPPY_SEED, MoveId.PRECIPICE_BLADES ], [SpeciesId.CHI_YU]: [ MoveId.FIERY_WRATH, MoveId.HYDRO_STEAM, MoveId.MORNING_SUN, MoveId.BLUE_FLARE ], [SpeciesId.ROARING_MOON]: [ MoveId.FIRE_LASH, MoveId.DRAGON_HAMMER, MoveId.METEOR_MASH, MoveId.DRAGON_ASCENT ], - [SpeciesId.IRON_VALIANT]: [ MoveId.PLASMA_FISTS, MoveId.NO_RETREAT, MoveId.SECRET_SWORD, MoveId.MAGICAL_TORQUE ], + [SpeciesId.IRON_VALIANT]: [ MoveId.PHOTON_GEYSER, MoveId.NO_RETREAT, MoveId.SECRET_SWORD, MoveId.MAGICAL_TORQUE ], [SpeciesId.KORAIDON]: [ MoveId.SUNSTEEL_STRIKE, MoveId.SOLAR_BLADE, MoveId.DRAGON_DARTS, MoveId.BITTER_BLADE ], [SpeciesId.MIRAIDON]: [ MoveId.FROST_BREATH, MoveId.WILDBOLT_STORM, MoveId.SPACIAL_REND, MoveId.RISING_VOLTAGE ], [SpeciesId.WALKING_WAKE]: [ MoveId.BOUNCY_BUBBLE, MoveId.FUSION_FLARE, MoveId.SLUDGE_WAVE, MoveId.CORE_ENFORCER ], @@ -573,7 +573,7 @@ export const speciesEggMoves = { [SpeciesId.MUNKIDORI]: [ MoveId.TWIN_BEAM, MoveId.HEAT_WAVE, MoveId.EARTH_POWER, MoveId.MALIGNANT_CHAIN ], [SpeciesId.FEZANDIPITI]: [ MoveId.BARB_BARRAGE, MoveId.BONEMERANG, MoveId.TRIPLE_AXEL, MoveId.VICTORY_DANCE ], [SpeciesId.OGERPON]: [ MoveId.SLEEP_POWDER, MoveId.BONEMERANG, MoveId.TRIPLE_AXEL, MoveId.FLOWER_TRICK ], - [SpeciesId.GOUGING_FIRE]: [ MoveId.EXTREME_SPEED, MoveId.BULK_UP, MoveId.SACRED_FIRE, MoveId.GLAIVE_RUSH ], + [SpeciesId.GOUGING_FIRE]: [ MoveId.EXTREME_SPEED, MoveId.DRAGON_DANCE, MoveId.ZING_ZAP, MoveId.SACRED_FIRE ], [SpeciesId.RAGING_BOLT]: [ MoveId.NASTY_PLOT, MoveId.FLAMETHROWER, MoveId.MORNING_SUN, MoveId.ELECTRO_DRIFT ], [SpeciesId.IRON_BOULDER]: [ MoveId.PSYBLADE, MoveId.KOWTOW_CLEAVE, MoveId.STONE_AXE, MoveId.BITTER_BLADE ], [SpeciesId.IRON_CROWN]: [ MoveId.NASTY_PLOT, MoveId.SECRET_SWORD, MoveId.PSYSTRIKE, MoveId.ELECTRO_DRIFT ], @@ -584,32 +584,41 @@ export const speciesEggMoves = { [SpeciesId.BLOODMOON_URSALUNA]: [ MoveId.NASTY_PLOT, MoveId.ROCK_POLISH, MoveId.SANDSEAR_STORM, MoveId.BOOMBURST ] }; +/** + * Parse a CSV-separated list of Egg Moves (such as one sourced from a Google Sheets) + * into code able to form the `speciesEggMoves` const object as above. + * @param content - The CSV-formatted string to convert into code. + */ +// TODO: Move this into the scripts folder and stop running it on initialization function parseEggMoves(content: string): void { let output = ""; const speciesNames = getEnumKeys(SpeciesId); const speciesValues = getEnumValues(SpeciesId); + const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase()); const lines = content.split(/\n/g); for (const line of lines) { const cols = line.split(",").slice(0, 5); - const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase()); - const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_"); - const species = speciesValues[speciesNames.findIndex(s => s === enumSpeciesName)]; + const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_") as keyof typeof SpeciesId; + // TODO: This should use reverse mapping instead of `indexOf` + const species = speciesValues[speciesNames.indexOf(enumSpeciesName)]; const eggMoves: MoveId[] = []; for (let m = 0; m < 4; m++) { const moveName = cols[m + 1].trim(); - const moveIndex = moveName !== "N/A" ? moveNames.findIndex(mn => mn === moveName.toLowerCase()) : -1; - eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE); - + const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1; if (moveIndex === -1) { console.warn(moveName, "could not be parsed"); } + + eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE); } - if (eggMoves.find(m => m !== MoveId.NONE)) { + if (eggMoves.every(m => m === MoveId.NONE)) { + console.warn(`Species ${toReadableString(SpeciesId[species])} could not be parsed, excluding from output...`) + } else { output += `[SpeciesId.${SpeciesId[species]}]: [ ${eggMoves.map(m => `MoveId.${MoveId[m]}`).join(", ")} ],\n`; } } diff --git a/src/data/balance/passives.ts b/src/data/balance/passives.ts index 80790b44735..0a76b3036b9 100644 --- a/src/data/balance/passives.ts +++ b/src/data/balance/passives.ts @@ -178,9 +178,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.QUILAVA]: { 0: AbilityId.DROUGHT }, [SpeciesId.TYPHLOSION]: { 0: AbilityId.DROUGHT }, [SpeciesId.HISUI_TYPHLOSION]: { 0: AbilityId.DROUGHT }, - [SpeciesId.TOTODILE]: { 0: AbilityId.TOUGH_CLAWS }, - [SpeciesId.CROCONAW]: { 0: AbilityId.TOUGH_CLAWS }, - [SpeciesId.FERALIGATR]: { 0: AbilityId.TOUGH_CLAWS }, + [SpeciesId.TOTODILE]: { 0: AbilityId.STRONG_JAW }, + [SpeciesId.CROCONAW]: { 0: AbilityId.STRONG_JAW }, + [SpeciesId.FERALIGATR]: { 0: AbilityId.STRONG_JAW }, [SpeciesId.SENTRET]: { 0: AbilityId.PICKUP }, [SpeciesId.FURRET]: { 0: AbilityId.PICKUP }, [SpeciesId.HOOTHOOT]: { 0: AbilityId.AERILATE }, @@ -252,7 +252,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.REMORAID]: { 0: AbilityId.SIMPLE }, [SpeciesId.OCTILLERY]: { 0: AbilityId.SIMPLE }, [SpeciesId.DELIBIRD]: { 0: AbilityId.HUGE_POWER }, - [SpeciesId.SKARMORY]: { 0: AbilityId.LIGHTNING_ROD }, + [SpeciesId.SKARMORY]: { 0: AbilityId.STAMINA }, [SpeciesId.HOUNDOUR]: { 0: AbilityId.BALL_FETCH }, [SpeciesId.HOUNDOOM]: { 0: AbilityId.LIGHTNING_ROD, 1: AbilityId.LIGHTNING_ROD }, [SpeciesId.PHANPY]: { 0: AbilityId.STURDY }, @@ -319,9 +319,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.MASQUERAIN]: { 0: AbilityId.WATER_BUBBLE }, [SpeciesId.SHROOMISH]: { 0: AbilityId.GUTS }, [SpeciesId.BRELOOM]: { 0: AbilityId.GUTS }, - [SpeciesId.SLAKOTH]: { 0: AbilityId.GUTS }, - [SpeciesId.VIGOROTH]: { 0: AbilityId.GUTS }, - [SpeciesId.SLAKING]: { 0: AbilityId.GUTS }, + [SpeciesId.SLAKOTH]: { 0: AbilityId.COMATOSE }, + [SpeciesId.VIGOROTH]: { 0: AbilityId.TOUGH_CLAWS }, + [SpeciesId.SLAKING]: { 0: AbilityId.COMATOSE }, [SpeciesId.NINCADA]: { 0: AbilityId.TECHNICIAN }, [SpeciesId.NINJASK]: { 0: AbilityId.TECHNICIAN }, [SpeciesId.SHEDINJA]: { 0: AbilityId.MAGIC_GUARD }, @@ -421,7 +421,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.KYOGRE]: { 0: AbilityId.MOLD_BREAKER, 1: AbilityId.TERAVOLT }, [SpeciesId.GROUDON]: { 0: AbilityId.MOLD_BREAKER, 1: AbilityId.TURBOBLAZE }, [SpeciesId.RAYQUAZA]: { 0: AbilityId.UNNERVE, 1: AbilityId.UNNERVE }, - [SpeciesId.JIRACHI]: { 0: AbilityId.COMATOSE }, + [SpeciesId.JIRACHI]: { 0: AbilityId.PURIFYING_SALT }, [SpeciesId.DEOXYS]: { 0: AbilityId.PROTEAN, 1: AbilityId.ADAPTABILITY, 2: AbilityId.REGENERATOR, 3: AbilityId.SHADOW_SHIELD }, [SpeciesId.TURTWIG]: { 0: AbilityId.SOLID_ROCK }, @@ -689,9 +689,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.FENNEKIN]: { 0: AbilityId.FLUFFY }, [SpeciesId.BRAIXEN]: { 0: AbilityId.PSYCHIC_SURGE }, [SpeciesId.DELPHOX]: { 0: AbilityId.PSYCHIC_SURGE }, - [SpeciesId.FROAKIE]: { 0: AbilityId.STAKEOUT, 1: AbilityId.STAKEOUT }, - [SpeciesId.FROGADIER]: { 0: AbilityId.STAKEOUT, 1: AbilityId.STAKEOUT }, - [SpeciesId.GRENINJA]: { 0: AbilityId.STAKEOUT, 1: AbilityId.STAKEOUT, 2: AbilityId.STAKEOUT }, + [SpeciesId.FROAKIE]: { 0: AbilityId.TECHNICIAN, 1: AbilityId.STAKEOUT }, + [SpeciesId.FROGADIER]: { 0: AbilityId.TECHNICIAN, 1: AbilityId.STAKEOUT }, + [SpeciesId.GRENINJA]: { 0: AbilityId.TECHNICIAN, 1: AbilityId.STAKEOUT, 2: AbilityId.SUPER_LUCK }, [SpeciesId.BUNNELBY]: { 0: AbilityId.INNER_FOCUS }, [SpeciesId.DIGGERSBY]: { 0: AbilityId.THICK_FAT }, [SpeciesId.FLETCHLING]: { 0: AbilityId.FLAME_BODY }, @@ -763,9 +763,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.DARTRIX]: { 0: AbilityId.WIND_RIDER }, [SpeciesId.DECIDUEYE]: { 0: AbilityId.SNIPER }, [SpeciesId.HISUI_DECIDUEYE]: { 0: AbilityId.SNIPER }, - [SpeciesId.LITTEN]: { 0: AbilityId.OPPORTUNIST }, - [SpeciesId.TORRACAT]: { 0: AbilityId.OPPORTUNIST }, - [SpeciesId.INCINEROAR]: { 0: AbilityId.OPPORTUNIST }, + [SpeciesId.LITTEN]: { 0: AbilityId.TOUGH_CLAWS }, + [SpeciesId.TORRACAT]: { 0: AbilityId.TOUGH_CLAWS }, + [SpeciesId.INCINEROAR]: { 0: AbilityId.TOUGH_CLAWS }, [SpeciesId.POPPLIO]: { 0: AbilityId.PUNK_ROCK }, [SpeciesId.BRIONNE]: { 0: AbilityId.PUNK_ROCK }, [SpeciesId.PRIMARINA]: { 0: AbilityId.PUNK_ROCK }, @@ -815,7 +815,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.MINIOR]: { 0: AbilityId.STURDY, 1: AbilityId.STURDY, 2: AbilityId.STURDY, 3: AbilityId.STURDY, 4: AbilityId.STURDY, 5: AbilityId.STURDY, 6: AbilityId.STURDY, 7: AbilityId.AERILATE, 8: AbilityId.AERILATE, 9: AbilityId.AERILATE, 10: AbilityId.AERILATE, 11: AbilityId.AERILATE, 12: AbilityId.AERILATE, 13: AbilityId.AERILATE }, [SpeciesId.KOMALA]: { 0: AbilityId.GUTS }, [SpeciesId.TURTONATOR]: { 0: AbilityId.DAUNTLESS_SHIELD }, - [SpeciesId.TOGEDEMARU]: { 0: AbilityId.ROUGH_SKIN }, + [SpeciesId.TOGEDEMARU]: { 0: AbilityId.CHEEK_POUCH }, [SpeciesId.MIMIKYU]: { 0: AbilityId.TOUGH_CLAWS, 1: AbilityId.TOUGH_CLAWS }, [SpeciesId.BRUXISH]: { 0: AbilityId.MULTISCALE }, [SpeciesId.DRAMPA]: { 0: AbilityId.THICK_FAT }, @@ -856,8 +856,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.ALOLA_NINETALES]: { 0: AbilityId.ICE_BODY }, [SpeciesId.ALOLA_DIGLETT]: { 0: AbilityId.STURDY }, [SpeciesId.ALOLA_DUGTRIO]: { 0: AbilityId.STURDY }, - [SpeciesId.ALOLA_MEOWTH]: { 0: AbilityId.DARK_AURA }, - [SpeciesId.ALOLA_PERSIAN]: { 0: AbilityId.DARK_AURA }, + [SpeciesId.ALOLA_MEOWTH]: { 0: AbilityId.DAZZLING }, + [SpeciesId.ALOLA_PERSIAN]: { 0: AbilityId.DAZZLING }, [SpeciesId.ALOLA_GEODUDE]: { 0: AbilityId.DRY_SKIN }, [SpeciesId.ALOLA_GRAVELER]: { 0: AbilityId.DRY_SKIN }, [SpeciesId.ALOLA_GOLEM]: { 0: AbilityId.DRY_SKIN }, @@ -867,9 +867,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.GROOKEY]: { 0: AbilityId.PICKPOCKET }, [SpeciesId.THWACKEY]: { 0: AbilityId.PICKPOCKET }, [SpeciesId.RILLABOOM]: { 0: AbilityId.GRASS_PELT, 1: AbilityId.GRASS_PELT }, - [SpeciesId.SCORBUNNY]: { 0: AbilityId.SHEER_FORCE }, - [SpeciesId.RABOOT]: { 0: AbilityId.SHEER_FORCE }, - [SpeciesId.CINDERACE]: { 0: AbilityId.NO_GUARD, 1: AbilityId.NO_GUARD }, + [SpeciesId.SCORBUNNY]: { 0: AbilityId.OPPORTUNIST }, + [SpeciesId.RABOOT]: { 0: AbilityId.OPPORTUNIST }, + [SpeciesId.CINDERACE]: { 0: AbilityId.OPPORTUNIST, 1: AbilityId.OPPORTUNIST }, [SpeciesId.SOBBLE]: { 0: AbilityId.SUPER_LUCK }, [SpeciesId.DRIZZILE]: { 0: AbilityId.SUPER_LUCK }, [SpeciesId.INTELEON]: { 0: AbilityId.SUPER_LUCK, 1: AbilityId.SUPER_LUCK }, @@ -1041,7 +1041,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.WIGLETT]: { 0: AbilityId.STURDY }, [SpeciesId.WUGTRIO]: { 0: AbilityId.STURDY }, [SpeciesId.BOMBIRDIER]: { 0: AbilityId.UNBURDEN }, - [SpeciesId.FINIZEN]: { 0: AbilityId.SWIFT_SWIM }, + [SpeciesId.FINIZEN]: { 0: AbilityId.FRIEND_GUARD }, [SpeciesId.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST }, [SpeciesId.VAROOM]: { 0: AbilityId.LEVITATE }, [SpeciesId.REVAVROOM]: { 0: AbilityId.LEVITATE, 1: AbilityId.DARK_AURA, 2: AbilityId.FLASH_FIRE, 3: AbilityId.MERCILESS, 4: AbilityId.FILTER, 5: AbilityId.SCRAPPY }, diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 5dda1912e44..c632889326d 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -1,21 +1,21 @@ import { globalScene } from "#app/global-scene"; -import { Gender, getGenderSymbol } from "#app/data/gender"; -import { PokeballType } from "#enums/pokeball"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonType } from "#enums/pokemon-type"; -import { coerceArray, isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import { WeatherType } from "#enums/weather-type"; -import { Nature } from "#enums/nature"; +import { speciesStarterCosts } from "#balance/starters"; +import { allMoves } from "#data/data-lists"; +import { Gender, getGenderSymbol } from "#data/gender"; import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; +import { Nature } from "#enums/nature"; +import { PokeballType } from "#enums/pokeball"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesFormKey } from "#enums/species-form-key"; +import { SpeciesId } from "#enums/species-id"; import { TimeOfDay } from "#enums/time-of-day"; -import type { SpeciesStatBoosterItem, SpeciesStatBoosterModifierType } from "#app/modifier/modifier-type"; -import { speciesStarterCosts } from "./starters"; +import { WeatherType } from "#enums/weather-type"; +import type { Pokemon } from "#field/pokemon"; +import type { SpeciesStatBoosterItem, SpeciesStatBoosterModifierType } from "#modifiers/modifier-type"; +import { coerceArray, isNullOrUndefined, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { allMoves } from "#app/data/data-lists"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; export enum SpeciesWildEvolutionDelay { NONE, @@ -650,8 +650,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.KIRLIA, 20, null, null) ], [SpeciesId.KIRLIA]: [ - new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}), - new SpeciesEvolution(SpeciesId.GALLADE, 30, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}) + new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null), + new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}) ], [SpeciesId.SURSKIT]: [ new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null) @@ -739,8 +739,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.DUSCLOPS, 37, null, null) ], [SpeciesId.SNORUNT]: [ - new SpeciesEvolution(SpeciesId.GLALIE, 42, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}), - new SpeciesEvolution(SpeciesId.FROSLASS, 42, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}) + new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null), + new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}) ], [SpeciesId.SPHEAL]: [ new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null) @@ -1630,7 +1630,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.TSAREENA, 28, null, {key: EvoCondKey.MOVE, move: MoveId.STOMP}, SpeciesWildEvolutionDelay.LONG) ], [SpeciesId.POIPOLE]: [ - new SpeciesEvolution(SpeciesId.NAGANADEL, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_PULSE}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.NAGANADEL, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_PULSE}, SpeciesWildEvolutionDelay.VERY_LONG) ], [SpeciesId.ALOLA_SANDSHREW]: [ new SpeciesEvolution(SpeciesId.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1845,7 +1845,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.LEAVANNY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.LONG) ], [SpeciesId.TYPE_NULL]: [ - new SpeciesEvolution(SpeciesId.SILVALLY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 100}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SILVALLY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 100}, SpeciesWildEvolutionDelay.VERY_LONG) ], [SpeciesId.ALOLA_MEOWTH]: [ new SpeciesEvolution(SpeciesId.ALOLA_PERSIAN, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.LONG) diff --git a/src/data/balance/species-egg-tiers.ts b/src/data/balance/species-egg-tiers.ts index 4d953921e92..4c125fb07fa 100644 --- a/src/data/balance/species-egg-tiers.ts +++ b/src/data/balance/species-egg-tiers.ts @@ -1,5 +1,5 @@ -import { SpeciesId } from "#enums/species-id"; import { EggTier } from "#enums/egg-type"; +import { SpeciesId } from "#enums/species-id"; /** * Map of all starters and their respective {@linkcode EggTier}, which determines the type of egg the starter hatches from. diff --git a/src/data/balance/starters.ts b/src/data/balance/starters.ts index 2db10f2e67a..8b91c12ae2d 100644 --- a/src/data/balance/starters.ts +++ b/src/data/balance/starters.ts @@ -234,7 +234,7 @@ export const speciesStarterCosts = { [SpeciesId.KYOGRE]: 9, [SpeciesId.GROUDON]: 9, [SpeciesId.RAYQUAZA]: 9, - [SpeciesId.JIRACHI]: 7, + [SpeciesId.JIRACHI]: 6, [SpeciesId.DEOXYS]: 7, [SpeciesId.TURTWIG]: 3, @@ -319,7 +319,7 @@ export const speciesStarterCosts = { [SpeciesId.SANDILE]: 4, [SpeciesId.DARUMAKA]: 4, [SpeciesId.MARACTUS]: 2, - [SpeciesId.DWEBBLE]: 2, + [SpeciesId.DWEBBLE]: 3, [SpeciesId.SCRAGGY]: 3, [SpeciesId.SIGILYPH]: 4, [SpeciesId.YAMASK]: 3, diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index be060b57e9c..d26f0700f7f 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -1,22 +1,15 @@ import { globalScene } from "#app/global-scene"; -import { allMoves } from "#app/data/data-lists"; -import { MoveFlags } from "#enums/MoveFlags"; -import type Pokemon from "#app/field/pokemon"; -import { - type nil, - getFrameMs, - getEnumKeys, - getEnumValues, - animationFileName, - coerceArray, - isNullOrUndefined, -} from "#app/utils/common"; +import { allMoves } from "#data/data-lists"; import type { BattlerIndex } from "#enums/battler-index"; -import { MoveId } from "#enums/move-id"; -import Phaser from "phaser"; -import { EncounterAnim } from "#enums/encounter-anims"; -import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { EncounterAnim } from "#enums/encounter-anims"; +import { MoveFlags } from "#enums/MoveFlags"; +import { AnimBlendType, AnimFocus, AnimFrameTarget, ChargeAnim, CommonAnim } from "#enums/move-anims-common"; +import { MoveId } from "#enums/move-id"; +import type { Pokemon } from "#field/pokemon"; +import { animationFileName, coerceArray, getFrameMs, isNullOrUndefined, type nil } from "#utils/common"; +import { getEnumKeys, getEnumValues } from "#utils/enums"; +import Phaser from "phaser"; export class AnimConfig { public id: number; @@ -346,7 +339,7 @@ abstract class AnimTimedBgEvent extends AnimTimedEvent { } class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { - // biome-ignore lint/correctness/noUnusedVariables: seems intentional + // biome-ignore lint/correctness/noUnusedFunctionParameters: seems intentional execute(moveAnim: MoveAnim, priority?: number): number { const tweenProps = {}; if (this.bgX !== undefined) { @@ -359,15 +352,11 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { tweenProps["alpha"] = (this.opacity || 0) / 255; } if (Object.keys(tweenProps).length) { - globalScene.tweens.add( - Object.assign( - { - targets: moveAnim.bgSprite, - duration: getFrameMs(this.duration * 3), - }, - tweenProps, - ), - ); + globalScene.tweens.add({ + targets: moveAnim.bgSprite, + duration: getFrameMs(this.duration * 3), + ...tweenProps, + }); } return this.duration * 2; } @@ -423,7 +412,7 @@ export function initCommonAnims(): Promise { const commonAnimId = commonAnimIds[ca]; commonAnimFetches.push( globalScene - .cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))), ); @@ -535,7 +524,7 @@ export async function initEncounterAnims(encounterAnim: EncounterAnim | Encounte } encounterAnimFetches.push( globalScene - .cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(cas => encounterAnims.set(anim, new AnimConfig(cas))), ); @@ -559,7 +548,7 @@ export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise { } else { chargeAnims.set(chargeAnim, null); globalScene - .cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(ca => { if (Array.isArray(ca)) { @@ -884,6 +873,10 @@ export abstract class BattleAnim { targetSprite.pipelineData["tone"] = [0.0, 0.0, 0.0, 0.0]; targetSprite.setAngle(0); + // Remove animation event listeners to enable sprites to be freed. + userSprite.off("animationupdate"); + targetSprite.off("animationupdate"); + /** * This and `targetSpriteToShow` are used to restore context lost * from the `isOppAnim` swap. Using these references instead of `this.user` @@ -1405,15 +1398,15 @@ export class EncounterBattleAnim extends BattleAnim { export async function populateAnims() { const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase()); - const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, "")); - const commonAnimIds = getEnumValues(CommonAnim) as CommonAnim[]; + const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/_/g, "")); + const commonAnimIds = getEnumValues(CommonAnim); const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase()); - const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, " ")); - const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[]; + const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/_/g, " ")); + const chargeAnimIds = getEnumValues(ChargeAnim); const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/; const moveNameToId = {}; for (const move of getEnumValues(MoveId).slice(1)) { - const moveName = MoveId[move].toUpperCase().replace(/\_/g, ""); + const moveName = MoveId[move].toUpperCase().replace(/_/g, ""); moveNameToId[moveName] = move; } @@ -1469,7 +1462,7 @@ export async function populateAnims() { const frameData = framesData[fd]; const focusFramesData = frameData.split(" - - "); for (let tf = 0; tf < focusFramesData.length; tf++) { - const values = focusFramesData[tf].replace(/ {6}\- /g, "").split("\n"); + const values = focusFramesData[tf].replace(/ {6}- /g, "").split("\n"); const targetFrame = new AnimFrame( Number.parseFloat(values[0]), Number.parseFloat(values[1]), @@ -1516,7 +1509,7 @@ export async function populateAnims() { .replace(/[a-z]+: ! '', /gi, "") .replace(/name: (.*?),/, 'name: "$1",') .replace( - /flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, + /flashColor: !ruby\/object:Color { alpha: ([\d.]+), blue: ([\d.]+), green: ([\d.]+), red: ([\d.]+)}/, "flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1", ); const frameIndex = Number.parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct? @@ -1641,12 +1634,12 @@ export async function populateAnims() { let props: string[]; for (let p = 0; p < propSets.length; p++) { props = propSets[p]; - // @ts-ignore TODO + // @ts-expect-error TODO const ai = props.indexOf(a.key); if (ai === -1) { continue; } - // @ts-ignore TODO + // @ts-expect-error TODO const bi = props.indexOf(b.key); return ai < bi ? -1 : ai > bi ? 1 : 0; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 8405fd1dd4d..060b17e889b 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,39 +1,37 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import Overrides from "#app/overrides"; -import { applyAbAttrs } from "./abilities/apply-ab-attrs"; -import { allAbilities } from "./data-lists"; -import { CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims"; -import { ChargeAnim, CommonAnim } from "#enums/move-anims-common"; -import type Move from "#app/data/moves/move"; -import { applyMoveAttrs } from "./moves/apply-attrs"; -import { allMoves } from "./data-lists"; -import { MoveFlags } from "#enums/MoveFlags"; -import { MoveCategory } from "#enums/MoveCategory"; -import { SpeciesFormChangeAbilityTrigger } from "./pokemon-forms/form-change-triggers"; -import { getStatusEffectHealText } from "#app/data/status-effect"; -import { TerrainType } from "#app/data/terrain"; -import { PokemonType } from "#enums/pokemon-type"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; -import { HitResult } from "#enums/hit-result"; import { getPokemonNameWithAffix } from "#app/messages"; -import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import type { MovePhase } from "#app/phases/move-phase"; -import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; -import i18next from "#app/plugins/i18n"; -import { BooleanHolder, coerceArray, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common"; +import Overrides from "#app/overrides"; +import { CommonBattleAnim, MoveChargeAnim } from "#data/battle-anims"; +import { allAbilities, allMoves } from "#data/data-lists"; +import { SpeciesFormChangeAbilityTrigger } from "#data/form-change-triggers"; +import { getStatusEffectHealText } from "#data/status-effect"; +import { TerrainType } from "#data/terrain"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { HitResult } from "#enums/hit-result"; +import { MoveCategory } from "#enums/MoveCategory"; +import { MoveFlags } from "#enums/MoveFlags"; +import { ChargeAnim, CommonAnim } from "#enums/move-anims-common"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import { EFFECTIVE_STATS, getStatKey, Stat, type BattleStat, type EffectiveStat } from "#enums/stat"; +import { type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; -import { isNullOrUndefined } from "#app/utils/common"; -import { MoveUseMode } from "#enums/move-use-mode"; -import { invalidEncoreMoves } from "./moves/invalid-moves"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; +import type { Pokemon } from "#field/pokemon"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import { invalidEncoreMoves } from "#moves/invalid-moves"; +import type { Move } from "#moves/move"; +import type { MoveEffectPhase } from "#phases/move-effect-phase"; +import type { MovePhase } from "#phases/move-phase"; +import type { StatStageChangeCallback } from "#phases/stat-stage-change-phase"; +import i18next from "#plugins/i18n"; +import { BooleanHolder, coerceArray, getFrameMs, isNullOrUndefined, NumberHolder, toDmgValue } from "#utils/common"; /** * A {@linkcode BattlerTag} represents a semi-persistent effect that can be attached to a {@linkcode Pokemon}. @@ -76,10 +74,13 @@ export class BattlerTag { /** * Tick down this {@linkcode BattlerTag}'s duration. - * @returns `true` if the tag should be kept (`turnCount > 0`) + * @param _pokemon - The {@linkcode Pokemon} whom this tag belongs to. + * Unused by default but can be used by subclasses. + * @param _lapseType - The {@linkcode BattlerTagLapseType} being lapsed. + * Unused by default but can be used by subclasses. + * @returns `true` if the tag should be kept (`turnCount` > 0`) */ lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - // TODO: Maybe flip this (return `true` if tag needs removal) return --this.turnCount > 0; } @@ -111,7 +112,7 @@ export class BattlerTag { * @returns The source {@linkcode Pokemon}, or `null` if none is found */ public getSourcePokemon(): Pokemon | null { - return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + return globalScene.getPokemonById(this.sourceId); } } @@ -125,7 +126,7 @@ export interface TerrainBattlerTag { /** * Base class for tags that restrict the usage of moves. This effect is generally referred to as "disabling" a move - * in-game. This is not to be confused with {@linkcode MoveId.DISABLE}. + * in-game (not to be confused with {@linkcode MoveId.DISABLE}). * * Descendants can override {@linkcode isMoveRestricted} to restrict moves that * match a condition. A restricted move gets cancelled before it is used. @@ -540,9 +541,13 @@ export class TrappedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - const source = globalScene.getPokemonById(this.sourceId!)!; - const move = allMoves[this.sourceMove]; + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for TrappedTag canAdd; id: ${this.sourceId}`); + return false; + } + const move = allMoves[this.sourceMove]; const isGhost = pokemon.isOfType(PokemonType.GHOST); const isTrapped = pokemon.getTag(TrappedTag); const hasSubstitute = move.hitsSubstitute(source, pokemon); @@ -621,7 +626,7 @@ export class FlinchedTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - applyAbAttrs("FlinchEffectAbAttr", pokemon, null); + applyAbAttrs("FlinchEffectAbAttr", { pokemon }); return true; } @@ -669,7 +674,12 @@ export class ConfusedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - return globalScene.arena.terrain?.terrainType !== TerrainType.MISTY || !pokemon.isGrounded(); + const blockedByTerrain = pokemon.isGrounded() && globalScene.arena.terrain?.terrainType === TerrainType.MISTY; + if (blockedByTerrain) { + pokemon.queueStatusImmuneMessage(false, TerrainType.MISTY); + return false; + } + return true; } onAdd(pokemon: Pokemon): void { @@ -763,12 +773,20 @@ export class DestinyBondTag extends BattlerTag { if (lapseType !== BattlerTagLapseType.CUSTOM) { return super.lapse(pokemon, lapseType); } - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!source?.isFainted()) { + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for DestinyBondTag lapse; id: ${this.sourceId}`); + return false; + } + + // Destiny bond stays active until the user faints + if (!source.isFainted()) { return true; } - if (source?.getAlly() === pokemon) { + // Don't kill allies or opposing bosses. + if (source.getAlly() === pokemon) { return false; } @@ -781,6 +799,7 @@ export class DestinyBondTag extends BattlerTag { return false; } + // Drag the foe down with the user globalScene.phaseManager.queueMessage( i18next.t("battlerTags:destinyBondLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(source), @@ -798,17 +817,13 @@ export class InfatuatedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - if (this.sourceId) { - const pkm = globalScene.getPokemonById(this.sourceId); - - if (pkm) { - return pokemon.isOppositeGender(pkm); - } - console.warn("canAdd: this.sourceId is not a valid pokemon id!", this.sourceId); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfatuatedTag canAdd; id: ${this.sourceId}`); return false; } - console.warn("canAdd: this.sourceId is undefined"); - return false; + + return pokemon.isOppositeGender(source); } onAdd(pokemon: Pokemon): void { @@ -817,7 +832,7 @@ export class InfatuatedTag extends BattlerTag { globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(this.getSourcePokemon()!), // Tag not added + console warns if no source }), ); } @@ -835,28 +850,36 @@ export class InfatuatedTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); - const phaseManager = globalScene.phaseManager; - - if (ret) { - phaseManager.queueMessage( - i18next.t("battlerTags:infatuatedLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? - }), - ); - phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT); - - if (pokemon.randBattleSeedInt(2)) { - phaseManager.queueMessage( - i18next.t("battlerTags:infatuatedLapseImmobilize", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - ); - (phaseManager.getCurrentPhase() as MovePhase).cancel(); - } + if (!ret) { + return false; } - return ret; + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfatuatedTag lapse; id: ${this.sourceId}`); + return false; + } + + const phaseManager = globalScene.phaseManager; + phaseManager.queueMessage( + i18next.t("battlerTags:infatuatedLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + }), + ); + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT); + + // 50% chance to disrupt the target's action + if (pokemon.randBattleSeedInt(2)) { + phaseManager.queueMessage( + i18next.t("battlerTags:infatuatedLapseImmobilize", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + ); + (phaseManager.getCurrentPhase() as MovePhase).cancel(); + } + + return true; } onRemove(pokemon: Pokemon): void { @@ -899,6 +922,12 @@ export class SeedTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SeedTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); globalScene.phaseManager.queueMessage( @@ -906,47 +935,51 @@ export class SeedTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); - if (ret) { - const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex); - if (source) { - const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); - - if (!cancelled.value) { - globalScene.phaseManager.unshiftNew( - "CommonAnimPhase", - source.getBattlerIndex(), - pokemon.getBattlerIndex(), - CommonAnim.LEECH_SEED, - ); - - const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); - const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false); - globalScene.phaseManager.unshiftNew( - "PokemonHealPhase", - source.getBattlerIndex(), - !reverseDrain ? damage : damage * -1, - !reverseDrain - ? i18next.t("battlerTags:seededLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }) - : i18next.t("battlerTags:seededLapseShed", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - false, - true, - ); - } - } + if (!ret) { + return false; } - return ret; + // Check which opponent to restore HP to + const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex); + if (!source) { + console.warn(`Failed to get source Pokemon for SeedTag lapse; id: ${this.sourceId}`); + return false; + } + + const cancelled = new BooleanHolder(false); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); + + if (cancelled.value) { + return true; + } + + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + source.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.LEECH_SEED, + ); + + // Damage the target and restore our HP (or take damage in the case of liquid ooze) + const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); + const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false); + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + source.getBattlerIndex(), + reverseDrain ? -damage : damage, + i18next.t(reverseDrain ? "battlerTags:seededLapseShed" : "battlerTags:seededLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + false, + true, + ); + return true; } getDescriptor(): string { @@ -1006,7 +1039,7 @@ export class PowderTag extends BattlerTag { globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER); const cancelDamage = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelDamage); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled: cancelDamage }); if (!cancelDamage.value) { pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); } @@ -1056,7 +1089,7 @@ export class NightmareTag extends BattlerTag { phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); @@ -1195,9 +1228,15 @@ export class HelpingHandTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for HelpingHandTag onAdd; id: ${this.sourceId}`); + return; + } + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:helpingHandOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + pokemonNameWithAffix: getPokemonNameWithAffix(source), pokemonName: getPokemonNameWithAffix(pokemon), }), ); @@ -1219,9 +1258,7 @@ export class IngrainTag extends TrappedTag { * @returns boolean True if the tag can be added, false otherwise */ canAdd(pokemon: Pokemon): boolean { - const isTrapped = pokemon.getTag(BattlerTagType.TRAPPED); - - return !isTrapped; + return !pokemon.getTag(BattlerTagType.TRAPPED); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1409,7 +1446,7 @@ export abstract class DamagingTrapTag extends TrappedTag { phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); @@ -1420,15 +1457,22 @@ export abstract class DamagingTrapTag extends TrappedTag { } } +// TODO: Condense all these tags into 1 singular tag with a modified message func export class BindTag extends DamagingTrapTag { constructor(turnCount: number, sourceId: number) { super(BattlerTagType.BIND, CommonAnim.BIND, turnCount, MoveId.BIND, sourceId); } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for BindTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:bindOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(source), moveName: this.getMoveName(), }); } @@ -1440,9 +1484,16 @@ export class WrapTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for WrapTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:wrapOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(source), + moveName: this.getMoveName(), }); } } @@ -1473,8 +1524,14 @@ export class ClampTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ClampTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT ASAP"; + } + return i18next.t("battlerTags:clampOnTrap", { - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), pokemonName: getPokemonNameWithAffix(pokemon), }); } @@ -1523,9 +1580,15 @@ export class ThunderCageTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ThunderCageTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - PLEASE REPORT ASAP"; + } + return i18next.t("battlerTags:thunderCageOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), }); } } @@ -1536,9 +1599,15 @@ export class InfestationTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfestationTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:infestationOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), }); } } @@ -1642,7 +1711,7 @@ export class ContactDamageProtectedTag extends ContactProtectedTag { */ override onContact(attacker: Pokemon, user: Pokemon): void { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: user, cancelled }); if (!cancelled.value) { attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT, @@ -2221,14 +2290,19 @@ export class SaltCuredTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SaltCureTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); globalScene.phaseManager.queueMessage( i18next.t("battlerTags:saltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -2243,7 +2317,7 @@ export class SaltCuredTag extends BattlerTag { ); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER); @@ -2281,8 +2355,14 @@ export class CursedTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for CursedTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -2297,7 +2377,7 @@ export class CursedTag extends BattlerTag { ); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); @@ -2632,7 +2712,7 @@ export class GulpMissileTag extends BattlerTag { } const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", attacker, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: attacker, cancelled }); if (!cancelled.value) { attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT }); @@ -2902,7 +2982,13 @@ export class SubstituteTag extends BattlerTag { /** Sets the Substitute's HP and queues an on-add battle animation that initializes the Substitute's sprite. */ onAdd(pokemon: Pokemon): void { - this.hp = Math.floor(globalScene.getPokemonById(this.sourceId!)!.getMaxHp() / 4); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SubstituteTag onAdd; id: ${this.sourceId}`); + return; + } + + this.hp = Math.floor(source.getMaxHp() / 4); this.sourceInFocus = false; // Queue battle animation and message @@ -3021,14 +3107,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag { const ret = super.lapse(pokemon, lapseType); if (lapseType === BattlerTagLapseType.CUSTOM) { - const cancelled = new BooleanHolder(false); - applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled); - applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", pokemon, cancelled, false, pokemon); - if (!cancelled.value) { - if (pokemon.mysteryEncounterBattleEffects) { - pokemon.mysteryEncounterBattleEffects(pokemon); - } - } + pokemon.mysteryEncounterBattleEffects?.(pokemon); } return ret; @@ -3182,13 +3261,14 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { */ public override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const source = this.getSourcePokemon(); - if (source) { - if (lapseType === BattlerTagLapseType.PRE_MOVE) { - return super.lapse(pokemon, lapseType) && source.isActive(true); - } - return source.isActive(true); + if (!source) { + console.warn(`Failed to get source Pokemon for ImprisonTag lapse; id: ${this.sourceId}`); + return false; } - return false; + if (lapseType === BattlerTagLapseType.PRE_MOVE) { + return super.lapse(pokemon, lapseType) && source.isActive(true); + } + return source.isActive(true); } /** @@ -3248,12 +3328,20 @@ export class SyrupBombTag extends BattlerTag { * 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 + * @returns Whether the tag should persist (`turnsRemaining > 0` and source still on field) */ override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - if (this.sourceId && !globalScene.getPokemonById(this.sourceId)?.isActive(true)) { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SyrupBombTag lapse; id: ${this.sourceId}`); return false; } + + // Syrup bomb clears immediately if source leaves field/faints + if (!source.isActive(true)) { + return false; + } + // Custom message in lieu of an animation in mainline globalScene.phaseManager.queueMessage( i18next.t("battlerTags:syrupBombLapse", { @@ -3270,7 +3358,7 @@ export class SyrupBombTag extends BattlerTag { false, true, ); - return --this.turnCount > 0; + return super.lapse(pokemon, _lapseType); } } diff --git a/src/data/berry.ts b/src/data/berry.ts index 7d1e62362a8..61235b75e21 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -1,14 +1,14 @@ -import { getPokemonNameWithAffix } from "../messages"; -import type Pokemon from "../field/pokemon"; -import { HitResult } from "#enums/hit-result"; -import { getStatusEffectHealText } from "./status-effect"; -import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common"; -import { applyAbAttrs } from "./abilities/apply-ab-attrs"; -import i18next from "i18next"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { getStatusEffectHealText } from "#data/status-effect"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { Stat, type BattleStat } from "#app/enums/stat"; -import { globalScene } from "#app/global-scene"; +import { HitResult } from "#enums/hit-result"; +import { type BattleStat, Stat } from "#enums/stat"; +import type { Pokemon } from "#field/pokemon"; +import { NumberHolder, randSeedInt, toDmgValue } from "#utils/common"; +import i18next from "i18next"; export function getBerryName(berryType: BerryType): string { return i18next.t(`berry:${BerryType[berryType]}.name`); @@ -35,28 +35,28 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { case BerryType.APICOT: case BerryType.SALAC: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); + const hpRatioReq = new NumberHolder(0.25); // Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth const stat: BattleStat = berryType - BerryType.ENIGMA; - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); - return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6; + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); + return pokemon.getHpRatio() < hpRatioReq.value && pokemon.getStatStage(stat) < 6; }; case BerryType.LANSAT: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return pokemon.getHpRatio() < 0.25; }; case BerryType.LEPPA: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return !!pokemon.getMoveset().find(m => !m.getPpRatio()); }; } @@ -72,7 +72,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.ENIGMA: { const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4)); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: hpHealed }); globalScene.phaseManager.unshiftNew( "PokemonHealPhase", consumer.getBattlerIndex(), @@ -105,7 +105,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { // Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc. const stat: BattleStat = berryType - BerryType.ENIGMA; const statStages = new NumberHolder(1); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: statStages }); globalScene.phaseManager.unshiftNew( "StatStageChangePhase", consumer.getBattlerIndex(), @@ -126,7 +126,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { { const randStat = randSeedInt(Stat.SPD, Stat.ATK); const stages = new NumberHolder(2); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: stages }); globalScene.phaseManager.unshiftNew( "StatStageChangePhase", consumer.getBattlerIndex(), diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 3fdd83c185d..938ee482d01 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,33 +1,33 @@ -import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/common"; -import { deepCopy } from "#app/utils/data"; -import i18next from "i18next"; -import type { DexAttrProps, GameData } from "#app/system/game-data"; -import { defaultStarterSpecies } from "#app/constants"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "./moves/pokemon-move"; import type { FixedBattleConfig } from "#app/battle"; import { getRandomTrainerFunc } from "#app/battle"; -import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; +import { defaultStarterSpecies } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { speciesStarterCosts } from "#balance/starters"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getPokemonSpeciesForm } from "#data/pokemon-species"; import { BattleType } from "#enums/battle-type"; -import Trainer from "#app/field/trainer"; -import { TrainerVariant } from "#enums/trainer-variant"; -import { PokemonType } from "#enums/pokemon-type"; +import { ChallengeType } from "#enums/challenge-type"; import { Challenges } from "#enums/challenges"; +import { TypeColor, TypeShadow } from "#enums/color"; +import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; +import { ModifierTier } from "#enums/modifier-tier"; +import type { MoveId } from "#enums/move-id"; +import type { MoveSourceType } from "#enums/move-source-type"; +import { Nature } from "#enums/nature"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; -import { Nature } from "#enums/nature"; -import type { MoveId } from "#enums/move-id"; -import { TypeColor, TypeShadow } from "#enums/color"; -import { ModifierTier } from "#enums/modifier-tier"; -import { globalScene } from "#app/global-scene"; -import { pokemonFormChanges } from "./pokemon-forms"; -import { pokemonEvolutions } from "./balance/pokemon-evolutions"; -import { ChallengeType } from "#enums/challenge-type"; -import type { MoveSourceType } from "#enums/move-source-type"; +import { TrainerVariant } from "#enums/trainer-variant"; +import type { Pokemon } from "#field/pokemon"; +import { Trainer } from "#field/trainer"; +import { PokemonMove } from "#moves/pokemon-move"; +import type { DexAttrProps, GameData } from "#system/game-data"; +import { BooleanHolder, type NumberHolder, randSeedItem } from "#utils/common"; +import { deepCopy } from "#utils/data"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; /** A constant for the default max cost of the starting party before a run */ const DEFAULT_PARTY_MAX_COST = 10; diff --git a/src/data/custom-pokemon-data.ts b/src/data/custom-pokemon-data.ts deleted file mode 100644 index 252e302ccf3..00000000000 --- a/src/data/custom-pokemon-data.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { AbilityId } from "#enums/ability-id"; -import type { PokemonType } from "#enums/pokemon-type"; -import type { Nature } from "#enums/nature"; - -/** - * Data that can customize a Pokemon in non-standard ways from its Species. - * Includes abilities, nature, changed types, etc. - */ -export class CustomPokemonData { - // TODO: Change the default value for all these from -1 to something a bit more sensible - /** - * The scale at which to render this Pokemon's sprite. - */ - public spriteScale = -1; - public ability: AbilityId | -1; - public passive: AbilityId | -1; - public nature: Nature | -1; - public types: PokemonType[]; - /** Deprecated but needed for session save migration */ - // TODO: Remove this once pre-session migration is implemented - public hitsRecCount: number | null = null; - - constructor(data?: CustomPokemonData | Partial) { - this.spriteScale = data?.spriteScale ?? -1; - this.ability = data?.ability ?? -1; - this.passive = data?.passive ?? -1; - this.nature = data?.nature ?? -1; - this.types = data?.types ?? []; - this.hitsRecCount = data?.hitsRecCount ?? null; - } -} diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index e869ba7f28f..9a6f560933a 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -1,15 +1,16 @@ +import { pokerogueApi } from "#api/pokerogue-api"; +import { globalScene } from "#app/global-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import type { PokemonSpeciesForm } from "#data/pokemon-species"; +import { getPokemonSpeciesForm, PokemonSpecies } from "#data/pokemon-species"; +import { BiomeId } from "#enums/biome-id"; import { PartyMemberStrength } from "#enums/party-member-strength"; import type { SpeciesId } from "#enums/species-id"; -import { globalScene } from "#app/global-scene"; -import { PlayerPokemon } from "#app/field/pokemon"; -import type { Starter } from "#app/ui/starter-select-ui-handler"; -import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils/common"; -import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; -import PokemonSpecies, { getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { BiomeId } from "#enums/biome-id"; +import { PlayerPokemon } from "#field/pokemon"; +import type { Starter } from "#ui/starter-select-ui-handler"; +import { randSeedGauss, randSeedInt, randSeedItem } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; export interface DailyRunConfig { seed: number; @@ -165,5 +166,6 @@ export function getDailyStartingBiome(): BiomeId { } // Fallback in case something went wrong + // TODO: should this use `randSeedItem`? return biomes[randSeedInt(biomes.length)]; } diff --git a/src/data/data-lists.ts b/src/data/data-lists.ts index ed172846fe1..ae3d0acc77f 100644 --- a/src/data/data-lists.ts +++ b/src/data/data-lists.ts @@ -1,7 +1,7 @@ -import type PokemonSpecies from "#app/data/pokemon-species"; -import type { ModifierTypes } from "#app/modifier/modifier-type"; -import type { Ability } from "./abilities/ability"; -import type Move from "./moves/move"; +import type { Ability } from "#abilities/ability"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import type { ModifierTypes } from "#modifiers/modifier-type"; +import type { Move } from "#moves/move"; export const allAbilities: Ability[] = []; export const allMoves: Move[] = []; diff --git a/src/data/dialogue.ts b/src/data/dialogue.ts index 6bb96f0efb2..406e72ee82b 100644 --- a/src/data/dialogue.ts +++ b/src/data/dialogue.ts @@ -1,6 +1,6 @@ import { BattleSpec } from "#enums/battle-spec"; import { TrainerType } from "#enums/trainer-type"; -import { trainerConfigs } from "./trainers/trainer-config"; +import { trainerConfigs } from "#trainers/trainer-config"; export interface TrainerTypeMessages { encounter?: string | string[]; @@ -1744,6 +1744,7 @@ export function getCharVariantFromDialogue(message: string): string { } export function initTrainerTypeDialogue(): void { + // TODO: this should not be using `Object.Keys` const trainerTypes = Object.keys(trainerTypeDialogue).map(t => Number.parseInt(t) as TrainerType); for (const trainerType of trainerTypes) { const messages = trainerTypeDialogue[trainerType]; diff --git a/src/data/egg-hatch-data.ts b/src/data/egg-hatch-data.ts index e81ae69515c..6aead19eb7f 100644 --- a/src/data/egg-hatch-data.ts +++ b/src/data/egg-hatch-data.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { StarterDataEntry } from "#app/system/game-data"; -import type { DexEntry } from "#app/@types/dex-data"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { StarterDataEntry } from "#system/game-data"; +import type { DexEntry } from "#types/dex-data"; /** * Stores data associated with a specific egg and the hatched pokemon diff --git a/src/data/egg.ts b/src/data/egg.ts index a6e2e04a5fe..2c567686fc2 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -1,43 +1,43 @@ -import type BattleScene from "#app/battle-scene"; +import type { BattleScene } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { VariantTier } from "#enums/variant-tier"; -import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils/common"; import Overrides from "#app/overrides"; -import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import i18next from "i18next"; -import { EggTier } from "#enums/egg-type"; -import { SpeciesId } from "#enums/species-id"; -import { EggSourceType } from "#enums/egg-source-types"; +import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; 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_LEGENDARY_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_DEFAULT_RARE_EGG_THRESHOLD, + GACHA_DEFAULT_RARE_EGGMOVE_RATE, + GACHA_DEFAULT_SHINY_RATE, + GACHA_EGG_HA_RATE, GACHA_LEGENDARY_UP_THRESHOLD_OFFSET, - HATCH_WAVES_MANAPHY_EGG, + GACHA_MOVE_UP_RARE_EGGMOVE_RATE, + GACHA_SHINY_UP_SHINY_RATE, 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"; + HATCH_WAVES_MANAPHY_EGG, + HATCH_WAVES_RARE_EGG, + MANAPHY_EGG_MANAPHY_RATE, + SAME_SPECIES_EGG_HA_RATE, + SAME_SPECIES_EGG_RARE_EGGMOVE_RATE, + SAME_SPECIES_EGG_SHINY_RATE, + SHINY_EPIC_CHANCE, + SHINY_VARIANT_CHANCE, +} from "#balance/rates"; +import { speciesEggTiers } from "#balance/species-egg-tiers"; +import { speciesStarterCosts } from "#balance/starters"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; +import { SpeciesId } from "#enums/species-id"; +import { VariantTier } from "#enums/variant-tier"; +import type { PlayerPokemon } from "#field/pokemon"; +import { getIvsFromId, randInt, randomString, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; export const EGG_SEED = 1073741824; @@ -175,7 +175,7 @@ export class Egg { this._sourceType = eggOptions?.sourceType ?? undefined; this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); + this._timestamp = eggOptions?.timestamp ?? Date.now(); // First roll shiny and variant so we can filter if species with an variant exist this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); @@ -255,7 +255,7 @@ export class Egg { // Sets the hidden ability if a hidden ability exists and // the override is set or the egg hits the chance - let abilityIndex: number | undefined = undefined; + let abilityIndex: number | undefined; const sameSpeciesEggHACheck = this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE); const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE); @@ -294,7 +294,7 @@ export class Egg { public getEggDescriptor(): string { if (this.isManaphyEgg()) { - return "Manaphy"; + return i18next.t("egg:manaphyTier"); } switch (this.tier) { case EggTier.RARE: @@ -524,7 +524,7 @@ export class Egg { /** * Rolls whether the egg is shiny or not. * @returns `true` if the egg is shiny - **/ + */ private rollShiny(): boolean { let shinyChance = GACHA_DEFAULT_SHINY_RATE; switch (this._sourceType) { diff --git a/src/data/moves/apply-attrs.ts b/src/data/moves/apply-attrs.ts index 98e7b6d791f..3162ba02bde 100644 --- a/src/data/moves/apply-attrs.ts +++ b/src/data/moves/apply-attrs.ts @@ -1,11 +1,11 @@ -/** +/* * Module holding functions to apply move attributes. * Must not import anything that is not a type. */ -import type Pokemon from "#app/field/pokemon"; -import type { default as Move, MoveAttr } from "./move"; -import type { ChargingMove } from "#app/@types/move-types"; -import type { MoveAttrFilter, MoveAttrString } from "#app/@types/move-types"; + +import type { Pokemon } from "#field/pokemon"; +import type { Move, MoveAttr } from "#moves/move"; +import type { ChargingMove, MoveAttrFilter, MoveAttrString } from "#types/move-types"; function applyMoveAttrsInternal( attrFilter: MoveAttrFilter, diff --git a/src/data/moves/move-utils.ts b/src/data/moves/move-utils.ts index 20723ab0347..39891f595aa 100644 --- a/src/data/moves/move-utils.ts +++ b/src/data/moves/move-utils.ts @@ -1,14 +1,13 @@ -import type Pokemon from "#app/field/pokemon"; +import { allMoves } from "#data/data-lists"; import type { BattlerIndex } from "#enums/battler-index"; -import type { MoveId } from "#enums/move-id"; -import type { MoveTargetSet, UserMoveConditionFunc } from "./move"; -import type Move from "./move"; -import { NumberHolder, isNullOrUndefined } from "#app/utils/common"; -import { MoveTarget } from "#enums/MoveTarget"; -import { PokemonType } from "#enums/pokemon-type"; -import { allMoves } from "#app/data/data-lists"; -import { applyMoveAttrs } from "./apply-attrs"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveTarget } from "#enums/MoveTarget"; +import type { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; +import type { Pokemon } from "#field/pokemon"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import type { Move, MoveTargetSet, UserMoveConditionFunc } from "#moves/move"; +import { isNullOrUndefined, NumberHolder } from "#utils/common"; /** * Return whether the move targets the field diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f94c59bb463..d8863016002 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1,5 +1,14 @@ -import { MoveChargeAnim } from "../battle-anims"; -import { ChargeAnim } from "#enums/move-anims-common"; +import { AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "#abilities/ability"; +import { + applyAbAttrs +} from "#abilities/apply-ab-attrs"; +import { loggedInUser } from "#app/account"; +import type { GameMode } from "#app/game-mode"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import type { ArenaTrapTag } from "#data/arena-tag"; +import { WeakenMoveTypeTag } from "#data/arena-tag"; +import { MoveChargeAnim } from "#data/battle-anims"; import { CommandedTag, EncoreTag, @@ -11,35 +20,53 @@ import { SubstituteTag, TrappedTag, TypeBoostTag, -} from "../battler-tags"; -import { getPokemonNameWithAffix } from "../../messages"; -import type { AttackMoveResult, TurnMove } from "../../field/pokemon"; -import type Pokemon from "../../field/pokemon"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "./pokemon-move"; -import { MoveResult } from "#enums/move-result"; -import { HitResult } from "#enums/hit-result"; -import { FieldPosition } from "#enums/field-position"; +} from "#data/battler-tags"; +import { getBerryEffectFunc } from "#data/berry"; +import { applyChallenges } from "#data/challenge"; +import { allAbilities, allMoves } from "#data/data-lists"; +import { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-change-triggers"; import { getNonVolatileStatusEffects, getStatusEffectHealText, isNonVolatileStatusEffect, -} from "../status-effect"; -import { getTypeDamageMultiplier } from "../type"; -import { PokemonType } from "#enums/pokemon-type"; -import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor, randSeedFloat } from "#app/utils/common"; -import { WeatherType } from "#enums/weather-type"; -import type { ArenaTrapTag } from "../arena-tag"; -import { WeakenMoveTypeTag } from "../arena-tag"; +} from "#data/status-effect"; +import { TerrainType } from "#data/terrain"; +import { getTypeDamageMultiplier } from "#data/type"; +import { AbilityId } from "#enums/ability-id"; import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattleType } from "#enums/battle-type"; +import type { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BiomeId } from "#enums/biome-id"; +import { ChallengeType } from "#enums/challenge-type"; +import { Command } from "#enums/command"; +import { FieldPosition } from "#enums/field-position"; +import { HitResult } from "#enums/hit-result"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ChargeAnim } from "#enums/move-anims-common"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { isVirtual, MoveUseMode } from "#enums/move-use-mode"; +import { MoveCategory } from "#enums/MoveCategory"; +import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; +import { MoveFlags } from "#enums/MoveFlags"; +import { MoveTarget } from "#enums/MoveTarget"; +import { MultiHitType } from "#enums/MultiHitType"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; import { - applyAbAttrs, - applyPostAttackAbAttrs, - applyPostItemLostAbAttrs, - applyPreAttackAbAttrs, - applyPreDefendAbAttrs -} from "../abilities/apply-ab-attrs"; -import { allAbilities, allMoves } from "../data-lists"; + BATTLE_STATS, + type BattleStat, + type EffectiveStat, + getStatKey, + Stat, +} from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { SwitchType } from "#enums/switch-type"; +import { WeatherType } from "#enums/weather-type"; +import { MoveUsedEvent } from "#events/battle-scene"; +import type { EnemyPokemon, Pokemon } from "#field/pokemon"; import { AttackTypeBoosterModifier, BerryModifier, @@ -47,51 +74,22 @@ import { PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier, -} from "../../modifier/modifier"; -import type { BattlerIndex } from "#enums/battler-index"; -import { BattleType } from "#enums/battle-type"; -import { TerrainType } from "../terrain"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { Command } from "#enums/command"; +} from "#modifiers/modifier"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves"; +import { frenzyMissFunc, getMoveTargets } from "#moves/move-utils"; +import { PokemonMove } from "#moves/pokemon-move"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { MovePhase } from "#phases/move-phase"; +import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; +import { SwitchSummonPhase } from "#phases/switch-summon-phase"; +import type { AttackMoveResult } from "#types/attack-move-result"; +import type { Localizable } from "#types/locales"; +import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString } from "#types/move-types"; +import type { TurnMove } from "#types/turn-move"; +import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue, toReadableString } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; import i18next from "i18next"; -import type { Localizable } from "#app/@types/locales"; -import { getBerryEffectFunc } from "../berry"; -import { AbilityId } from "#enums/ability-id"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BiomeId } from "#enums/biome-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { MoveUsedEvent } from "#app/events/battle-scene"; -import { - BATTLE_STATS, - type BattleStat, - type EffectiveStat, - getStatKey, - Stat, -} from "#app/enums/stat"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms/form-change-triggers"; -import type { GameMode } from "#app/game-mode"; -import { applyChallenges } from "../challenge"; -import { ChallengeType } from "#enums/challenge-type"; -import { SwitchType } from "#enums/switch-type"; -import { StatusEffect } from "#enums/status-effect"; -import { globalScene } from "#app/global-scene"; -import { loggedInUser } from "#app/account"; -import { MoveCategory } from "#enums/MoveCategory"; -import { MoveTarget } from "#enums/MoveTarget"; -import { MoveFlags } from "#enums/MoveFlags"; -import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; -import { MultiHitType } from "#enums/MultiHitType"; -import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves, invalidSketchMoves } from "./invalid-moves"; -import { isVirtual, MoveUseMode } from "#enums/move-use-mode"; -import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types"; -import { applyMoveAttrs } from "./apply-attrs"; -import { frenzyMissFunc, getMoveTargets } from "./move-utils"; /** * A function used to conditionally determine execution of a given {@linkcode MoveAttr}. @@ -100,7 +98,7 @@ import { frenzyMissFunc, getMoveTargets } from "./move-utils"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; -export default abstract class Move implements Localizable { +export abstract class Move implements Localizable { public id: MoveId; public name: string; private _type: PokemonType; @@ -168,9 +166,9 @@ export default abstract class Move implements Localizable { } /** - * Get all move attributes that match `attrType` - * @param attrType any attribute that extends {@linkcode MoveAttr} - * @returns Array of attributes that match `attrType`, Empty Array if none match. + * Get all move attributes that match `attrType`. + * @param attrType - The name of a {@linkcode MoveAttr} to search for + * @returns An array containing all attributes matching `attrType`, or an empty array if none match. */ getAttrs(attrType: T): (MoveAttrMap[T])[] { const targetAttr = MoveAttrs[attrType]; @@ -181,9 +179,9 @@ export default abstract class Move implements Localizable { } /** - * Check if a move has an attribute that matches `attrType` - * @param attrType any attribute that extends {@linkcode MoveAttr} - * @returns true if the move has attribute `attrType` + * Check if a move has an attribute that matches `attrType`. + * @param attrType - The name of a {@linkcode MoveAttr} to search for + * @returns Whether this move has at least 1 attribute that matches `attrType` */ hasAttr(attrType: MoveAttrString): boolean { const targetAttr = MoveAttrs[attrType]; @@ -195,23 +193,25 @@ export default abstract class Move implements Localizable { } /** - * Takes as input a boolean function and returns the first MoveAttr in attrs that matches true - * @param attrPredicate - * @returns the first {@linkcode MoveAttr} element in attrs that makes the input function return true + * Find the first attribute that matches a given predicate function. + * @param attrPredicate - The predicate function to search `MoveAttr`s by + * @returns The first {@linkcode MoveAttr} for which `attrPredicate` returns `true` */ findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr { - return this.attrs.find(attrPredicate)!; // TODO: is the bang correct? + // TODO: Remove bang and make return type `MoveAttr | undefined`, + // as well as add overload for functions of type `x is T` + return this.attrs.find(attrPredicate)!; } /** - * Adds a new MoveAttr to the move (appends to the attr array) - * if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition} - * @param AttrType {@linkcode MoveAttr} the constructor of a MoveAttr class - * @param args the args needed to instantiate a the given class - * @returns the called object {@linkcode Move} + * Adds a new MoveAttr to this move (appends to the attr array). + * If the MoveAttr also comes with a condition, it is added to its {@linkcode MoveCondition} array. + * @param attrType - The {@linkcode MoveAttr} to add + * @param args - The arguments needed to instantiate the given class + * @returns `this` */ - attr>(AttrType: T, ...args: ConstructorParameters): this { - const attr = new AttrType(...args); + attr>(attrType: T, ...args: ConstructorParameters): this { + const attr = new attrType(...args); this.attrs.push(attr); let attrCondition = attr.getCondition(); if (attrCondition) { @@ -225,11 +225,13 @@ export default abstract class Move implements Localizable { } /** - * Adds a new MoveAttr to the move (appends to the attr array) - * if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition} - * Almost identical to {@link attr}, except you are passing in a MoveAttr object, instead of a constructor and it's arguments - * @param attrAdd {@linkcode MoveAttr} the attribute to add - * @returns the called object {@linkcode Move} + * Adds a new MoveAttr to this move (appends to the attr array). + * If the MoveAttr also comes with a condition, it is added to its {@linkcode MoveCondition} array. + * + * Similar to {@linkcode attr}, except this takes an already instantiated {@linkcode MoveAttr} object + * as opposed to a constructor and its arguments. + * @param attrAdd - The {@linkcode MoveAttr} to add + * @returns `this` */ addAttr(attrAdd: MoveAttr): this { this.attrs.push(attrAdd); @@ -246,8 +248,8 @@ export default abstract class Move implements Localizable { /** * Sets the move target of this move - * @param moveTarget {@linkcode MoveTarget} the move target to set - * @returns the called object {@linkcode Move} + * @param moveTarget - The {@linkcode MoveTarget} to set + * @returns `this` */ target(moveTarget: MoveTarget): this { this.moveTarget = moveTarget; @@ -255,13 +257,13 @@ export default abstract class Move implements Localizable { } /** - * Getter function that returns if this Move has a MoveFlag - * @param flag {@linkcode MoveFlags} to check - * @returns boolean + * Getter function that returns if this Move has a given MoveFlag. + * @param flag - The {@linkcode MoveFlags} to check + * @returns Whether this Move has the specified flag. */ hasFlag(flag: MoveFlags): boolean { - // internally it is taking the bitwise AND (MoveFlags are represented as bit-shifts) and returning False if result is 0 and true otherwise - return !!(this.flags & flag); + // Flags are internally represented as bitmasks, so we check by taking the bitwise AND. + return (this.flags & flag) !== MoveFlags.NONE; } /** @@ -306,12 +308,13 @@ export default abstract class Move implements Localizable { } /** - * Checks if the move is immune to certain types. + * Checks if the target is immune to this Move's type. * Currently looks at cases of Grass types with powder moves and Dark types with moves affected by Prankster. - * @param {Pokemon} user the source of this move - * @param {Pokemon} target the target of this move - * @param {PokemonType} type the type of the move's target - * @returns boolean + * @param user - The {@linkcode Pokemon} using this move + * @param target - The {@linkcode Pokemon} targeted by this move + * @param type - The {@linkcode PokemonType} of the target + * @returns Whether the move is blocked by the target's type. + * Self-targeted moves will return `false` regardless of circumstances. */ isTypeImmune(user: Pokemon, target: Pokemon, type: PokemonType): boolean { if (this.moveTarget === MoveTarget.USER) { @@ -325,7 +328,7 @@ export default abstract class Move implements Localizable { } break; case PokemonType.DARK: - if (user.hasAbility(AbilityId.PRANKSTER) && this.category === MoveCategory.STATUS && (user.isPlayer() !== target.isPlayer())) { + if (user.hasAbility(AbilityId.PRANKSTER) && this.category === MoveCategory.STATUS && user.isOpponent(target)) { return true; } break; @@ -335,9 +338,9 @@ export default abstract class Move implements Localizable { /** * Checks if the move would hit its target's Substitute instead of the target itself. - * @param user The {@linkcode Pokemon} using this move - * @param target The {@linkcode Pokemon} targeted by this move - * @returns `true` if the move can bypass the target's Substitute; `false` otherwise. + * @param user - The {@linkcode Pokemon} using this move + * @param target - The {@linkcode Pokemon} targeted by this move + * @returns Whether this Move will hit the target's Substitute (assuming one exists). */ hitsSubstitute(user: Pokemon, target?: Pokemon): boolean { if ([ MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES ].includes(this.moveTarget) @@ -347,7 +350,7 @@ export default abstract class Move implements Localizable { const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated - applyAbAttrs("InfiltratorAbAttr", user, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", {pokemon: user, bypassed}); return !bypassed.value && !this.hasFlag(MoveFlags.SOUND_BASED) @@ -355,13 +358,14 @@ export default abstract class Move implements Localizable { } /** - * Adds a move condition to the move - * @param condition {@linkcode MoveCondition} or {@linkcode MoveConditionFunc}, appends to conditions array a new MoveCondition object - * @returns the called object {@linkcode Move} + * Adds a condition to this move (in addition to any provided by its prior {@linkcode MoveAttr}s). + * The move will fail upon use if at least 1 of its conditions is not met. + * @param condition - The {@linkcode MoveCondition} or {@linkcode MoveConditionFunc} to add to the conditions array. + * @returns `this` */ condition(condition: MoveCondition | MoveConditionFunc): this { if (typeof condition === "function") { - condition = new MoveCondition(condition as MoveConditionFunc); + condition = new MoveCondition(condition); } this.conditions.push(condition); @@ -369,16 +373,22 @@ export default abstract class Move implements Localizable { } /** - * Internal dev flag for documenting edge cases. When using this, please document the known edge case. - * @returns the called object {@linkcode Move} + * Mark a move as having one or more edge cases. + * The move may lack certain niche interactions with other moves/abilities, + * but still functions as intended in most cases. + * + * When using this, **make sure to document the edge case** (or else this becomes pointless). + * @returns `this` */ edgeCase(): this { return this; } /** - * Marks the move as "partial": appends texts to the move name - * @returns the called object {@linkcode Move} + * Mark this move as partially implemented. + * Partial moves are expected to have some core functionality implemented, but may lack + * certain notable features or interactions with other moves or abilities. + * @returns `this` */ partial(): this { this.nameAppend += " (P)"; @@ -386,8 +396,10 @@ export default abstract class Move implements Localizable { } /** - * Marks the move as "unimplemented": appends texts to the move name - * @returns the called object {@linkcode Move} + * Mark this move as unimplemented. + * Unimplemented moves are ones which have _none_ of their basic functionality enabled, + * and cannot be used. + * @returns `this` */ unimplemented(): this { this.nameAppend += " (N)"; @@ -645,7 +657,7 @@ export default abstract class Move implements Localizable { case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr("MoveAbilityBypassAbAttr")) { const abilityEffectsIgnored = new BooleanHolder(false); - applyAbAttrs("MoveAbilityBypassAbAttr", user, abilityEffectsIgnored, false, this); + applyAbAttrs("MoveAbilityBypassAbAttr", {pokemon: user, cancelled: abilityEffectsIgnored, move: this}); if (abilityEffectsIgnored.value) { return true; } @@ -683,13 +695,7 @@ export default abstract class Move implements Localizable { * @returns boolean: false if any of the apply()'s return false, else true */ applyConditions(user: Pokemon, target: Pokemon, move: Move): boolean { - for (const condition of this.conditions) { - if (!condition.apply(user, target, move)) { - return false; - } - } - - return true; + return this.conditions.every(cond => cond.apply(user, target, move)); } /** @@ -762,7 +768,7 @@ export default abstract class Move implements Localizable { const moveAccuracy = new NumberHolder(this.accuracy); applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy); - applyPreDefendAbAttrs("WonderSkinAbAttr", target, user, this, { value: false }, simulated, moveAccuracy); + applyAbAttrs("WonderSkinAbAttr", {pokemon: target, opponent: user, move: this, simulated, accuracy: moveAccuracy}); if (moveAccuracy.value === -1) { return moveAccuracy.value; @@ -805,17 +811,25 @@ export default abstract class Move implements Localizable { const typeChangeMovePowerMultiplier = new NumberHolder(1); const typeChangeHolder = new NumberHolder(this.type); - applyPreAttackAbAttrs("MoveTypeChangeAbAttr", source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier); + applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier}); const sourceTeraType = source.getTeraType(); if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { power.value = 60; } - applyPreAttackAbAttrs("VariableMovePowerAbAttr", source, target, this, simulated, power); + const abAttrParams: PreAttackModifyPowerAbAttrParams = { + pokemon: source, + opponent: target, + simulated, + power, + move: this, + } + + applyAbAttrs("VariableMovePowerAbAttr", abAttrParams); const ally = source.getAlly(); if (!isNullOrUndefined(ally)) { - applyPreAttackAbAttrs("AllyMoveCategoryPowerBoostAbAttr", ally, target, this, simulated, power); + applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally}); } const fieldAuras = new Set( @@ -827,11 +841,12 @@ export default abstract class Move implements Localizable { .flat(), ); for (const aura of fieldAuras) { - aura.applyPreAttack(source, null, simulated, target, this, [ power ]); + // TODO: Refactor the fieldAura attribute so that its apply method is not directly called + aura.apply({pokemon: source, simulated, opponent: target, move: this, power}); } const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); - alliedField.forEach(p => applyPreAttackAbAttrs("UserFieldMoveTypePowerBoostAbAttr", p, target, this, simulated, power)); + alliedField.forEach(p => applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power})); power.value *= typeChangeMovePowerMultiplier.value; @@ -858,7 +873,7 @@ export default abstract class Move implements Localizable { const priority = new NumberHolder(this.priority); applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority); - applyAbAttrs("ChangeMovePriorityAbAttr", user, null, simulated, this, priority); + applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority}); return priority.value; } @@ -959,10 +974,8 @@ export class AttackMove extends Move { constructor(id: MoveId, type: PokemonType, category: MoveCategory, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) { super(id, type, category, MoveTarget.NEAR_OTHER, power, accuracy, pp, chance, priority, generation); - /** - * {@link https://bulbapedia.bulbagarden.net/wiki/Freeze_(status_condition)} - * > All damaging Fire-type moves can now thaw a frozen target, regardless of whether or not they have a chance to burn; - */ + // > All damaging Fire-type moves can... thaw a frozen target, regardless of whether or not they have a chance to burn. + // - https://bulbapedia.bulbagarden.net/wiki/Freeze_(status_condition) if (this.type === PokemonType.FIRE) { this.addAttr(new HealStatusEffectAttr(false, StatusEffect.FREEZE)); } @@ -1218,7 +1231,8 @@ interface MoveEffectAttrOptions { effectChanceOverride?: number; } -/** Base class defining all Move Effect Attributes +/** + * Base class defining all Move Effect Attributes * @extends MoveAttr * @see {@linkcode apply} */ @@ -1236,8 +1250,7 @@ export class MoveEffectAttr extends MoveAttr { /** * Defines when this effect should trigger in the move's effect order. - * @default MoveEffectTrigger.POST_APPLY - * @see {@linkcode MoveEffectTrigger} + * @defaultValue {@linkcode MoveEffectTrigger.POST_APPLY} */ public get trigger () { return this.options?.trigger ?? MoveEffectTrigger.POST_APPLY; @@ -1246,7 +1259,7 @@ export class MoveEffectAttr extends MoveAttr { /** * `true` if this effect should only trigger on the first hit of * multi-hit moves. - * @default false + * @defaultValue `false` */ public get firstHitOnly () { return this.options?.firstHitOnly ?? false; @@ -1255,7 +1268,7 @@ export class MoveEffectAttr extends MoveAttr { /** * `true` if this effect should only trigger on the last hit of * multi-hit moves. - * @default false + * @defaultValue `false` */ public get lastHitOnly () { return this.options?.lastHitOnly ?? false; @@ -1264,7 +1277,7 @@ export class MoveEffectAttr extends MoveAttr { /** * `true` if this effect should apply only upon hitting a target * for the first time when targeting multiple {@linkcode Pokemon}. - * @default false + * @defaultValue `false` */ public get firstTargetOnly () { return this.options?.firstTargetOnly ?? false; @@ -1310,7 +1323,7 @@ export class MoveEffectAttr extends MoveAttr { getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number { const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance); - applyAbAttrs("MoveEffectChanceMultiplierAbAttr", user, null, !showAbility, moveChance, move); + applyAbAttrs("MoveEffectChanceMultiplierAbAttr", {pokemon: user, simulated: !showAbility, chance: moveChance, move}); if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) { const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -1318,7 +1331,7 @@ export class MoveEffectAttr extends MoveAttr { } if (!selfEffect) { - applyPreDefendAbAttrs("IgnoreMoveEffectsAbAttr", target, user, null, null, !showAbility, moveChance); + applyAbAttrs("IgnoreMoveEffectsAbAttr", {pokemon: target, move, simulated: !showAbility, chance: moveChance}); } return moveChance.value; } @@ -1709,8 +1722,9 @@ export class RecoilAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); if (!this.unblockable) { - applyAbAttrs("BlockRecoilDamageAttr", user, cancelled); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + const abAttrParams: AbAttrParamsWithCancel = {pokemon: user, cancelled}; + applyAbAttrs("BlockRecoilDamageAttr", abAttrParams); + applyAbAttrs("BlockNonDirectDamageAbAttr", abAttrParams); } if (cancelled.value) { @@ -1843,7 +1857,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); // Check to see if the Pokemon has an ability that blocks non-direct damage - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); if (!cancelled.value) { user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message @@ -2042,7 +2056,7 @@ export class FlameBurstAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); if (!isNullOrUndefined(targetAlly)) { - applyAbAttrs("BlockNonDirectDamageAbAttr", targetAlly, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: targetAlly, cancelled}); } if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) { @@ -2414,7 +2428,7 @@ export class MultiHitAttr extends MoveAttr { { const rand = user.randBattleSeedInt(20); const hitValue = new NumberHolder(rand); - applyAbAttrs("MaxMultiHitAbAttr", user, null, false, hitValue); + applyAbAttrs("MaxMultiHitAbAttr", {pokemon: user, hits: hitValue}); if (hitValue.value >= 13) { return 2; } else if (hitValue.value >= 6) { @@ -2522,7 +2536,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, quiet)) { - applyPostAttackAbAttrs("ConfusionOnStatusEffectAbAttr", user, target, move, null, false, this.effect); + applyAbAttrs("ConfusionOnStatusEffectAbAttr", {pokemon: user, opponent: target, move, effect: this.effect}); return true; } } @@ -2567,14 +2581,17 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } /** - * Applies the effect of Psycho Shift to its target - * Psycho Shift takes the user's status effect and passes it onto the target. The user is then healed after the move has been successfully executed. - * @returns `true` if Psycho Shift's effect is able to be applied to the target + * Applies the effect of {@linkcode Moves.PSYCHO_SHIFT} to its target. + * Psycho Shift takes the user's status effect and passes it onto the target. + * The user is then healed after the move has been successfully executed. + * @param user - The {@linkcode Pokemon} using the move + * @param target - The {@linkcode Pokemon} targeted by the move. + * @returns - Whether the effect was successfully applied to the target. */ apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { const statusToApply: StatusEffect | undefined = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined); - if (target.status) { + if (target.status || !statusToApply) { return false; } else { const canSetStatus = target.canSetStatus(statusToApply, true, false, user); @@ -2590,7 +2607,8 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return !target.status && target.canSetStatus(user.status?.effect, true, false, user) ? -10 : 0; + const statusToApply = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined); + return !target.status && statusToApply && target.canSetStatus(statusToApply, true, false, user) ? -10 : 0; } } @@ -2678,7 +2696,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { // Check for abilities that block item theft // TODO: This should not trigger if the target would faint beforehand const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", target, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); if (cancelled.value) { return false; @@ -2795,8 +2813,8 @@ export class EatBerryAttr extends MoveEffectAttr { protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) { // consumer eats berry, owner triggers unburden and similar effects getBerryEffectFunc(this.chosenBerry.berryType)(consumer); - applyPostItemLostAbAttrs("PostItemLostAbAttr", berryOwner, false); - applyAbAttrs("HealFromBerryUseAbAttr", consumer, new BooleanHolder(false)); + applyAbAttrs("PostItemLostAbAttr", {pokemon: berryOwner}); + applyAbAttrs("HealFromBerryUseAbAttr", {pokemon: consumer}); consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest); } } @@ -2821,7 +2839,7 @@ export class StealEatBerryAttr extends EatBerryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // check for abilities that block item theft const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", target, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); if (cancelled.value === true) { return false; } @@ -2835,7 +2853,7 @@ export class StealEatBerryAttr extends EatBerryAttr { // pick a random berry and eat it this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)]; - applyPostItemLostAbAttrs("PostItemLostAbAttr", target, false); + applyAbAttrs("PostItemLostAbAttr", {pokemon: target}); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name }); globalScene.phaseManager.queueMessage(message); this.reduceBerryModifier(target); @@ -2901,6 +2919,12 @@ export class HealStatusEffectAttr extends MoveEffectAttr { } } +/** + * Attribute to add the {@linkcode BattlerTagType.BYPASS_SLEEP | BYPASS_SLEEP Battler Tag} for 1 turn to the user before move use. + * Used by {@linkcode Moves.SNORE} and {@linkcode Moves.SLEEP_TALK}. + */ +// TODO: Should this use a battler tag? +// TODO: Give this `userSleptOrComatoseCondition` by default export class BypassSleepAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.status?.effect === StatusEffect.SLEEP) { @@ -2918,7 +2942,7 @@ export class BypassSleepAttr extends MoveAttr { * @param move */ getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return user.status && user.status.effect === StatusEffect.SLEEP ? 200 : -10; + return user.status?.effect === StatusEffect.SLEEP ? 200 : -10; } } @@ -3026,7 +3050,7 @@ export class OneHitKOAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockOneHitKOAbAttr", target, cancelled); + applyAbAttrs("BlockOneHitKOAbAttr", {pokemon: target, cancelled}); return !cancelled.value && user.level >= target.level; }; } @@ -3249,7 +3273,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { /** * `true` to display a message for the stat change. - * @default true + * @defaultValue `true` */ private get showMessage () { return this.options?.showMessage ?? true; @@ -4090,30 +4114,6 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { } } -/** - * Attribute to double this move's power if the target hasn't acted yet in the current turn. - * Used by {@linkcode Moves.BOLT_BEAK} and {@linkcode Moves.FISHIOUS_REND} - */ -export class FirstAttackDoublePowerAttr extends VariablePowerAttr { - /** - * Double this move's power if the user is acting before the target. - * @param user - Unused - * @param target - The {@linkcode Pokemon} being targeted by this move - * @param move - Unused - * @param args `[0]` - A {@linkcode NumberHolder} containing move base power - * @returns Whether the attribute was successfully applied - */ - apply(_user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder]): boolean { - if (target.turnData.acted) { - return false; - } - - args[0].value *= 2; - return true; - } -} - - export class TurnDamagedDoublePowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.turnData.attacksReceived.find(r => r.damage && r.sourceId === target.id)) { @@ -5436,9 +5436,12 @@ export class NoEffectAttr extends MoveAttr { } } -const crashDamageFunc = (user: Pokemon, move: Move) => { +/** + * Function to deal Crash Damage (1/2 max hp) to the user on apply. + */ +const crashDamageFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); if (cancelled.value) { return false; } @@ -6437,9 +6440,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined { - const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); - if (blockedByAbility.value) { + const cancelled = new BooleanHolder(false); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled}); + if (cancelled.value) { return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }); } } @@ -6478,7 +6481,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled: blockedByAbility}); if (blockedByAbility.value) { return false; } @@ -6887,12 +6890,12 @@ export class RandomMovesetMoveAttr extends CallMoveAttr { // includeParty will be true for Assist, false for Sleep Talk let allies: Pokemon[]; if (this.includeParty) { - allies = user.isPlayer() ? globalScene.getPlayerParty().filter(p => p !== user) : globalScene.getEnemyParty().filter(p => p !== user); + allies = (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter(p => p !== user); } else { allies = [ user ]; } - const partyMoveset = allies.map(p => p.moveset).flat(); - const moves = partyMoveset.filter(m => !this.invalidMoves.has(m!.moveId) && !m!.getMove().name.endsWith(" (N)")); + const partyMoveset = allies.flatMap(p => p.moveset); + const moves = partyMoveset.filter(m => !this.invalidMoves.has(m.moveId) && !m.getMove().name.endsWith(" (N)")); if (moves.length === 0) { return false; } @@ -7075,7 +7078,8 @@ export class CopyMoveAttr extends CallMoveAttr { /** * Attribute used for moves that cause the target to repeat their last used move. * - * Used for [Instruct](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)). + * Used by {@linkcode Moves.INSTRUCT | Instruct}. + * @see [Instruct on Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)) */ export class RepeatMoveAttr extends MoveEffectAttr { private movesetMove: PokemonMove; @@ -7606,7 +7610,9 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { } /** - * Used by Transform + * Attribute used to transform into the target on move use. + * + * Used for {@linkcode MoveId.TRANSFORM}. */ export class TransformAttr extends MoveEffectAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -7615,11 +7621,12 @@ export class TransformAttr extends MoveEffectAttr { } globalScene.phaseManager.unshiftNew("PokemonTransformPhase", user.getBattlerIndex(), target.getBattlerIndex()); - - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); - return true; } + + getCondition(): MoveConditionFunc { + return (user, target) => user.canTransformInto(target) + } } /** @@ -7987,7 +7994,7 @@ const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScen const failIfDampCondition: MoveConditionFunc = (user, target, move) => { const cancelled = new BooleanHolder(false); - globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled)); + globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled})); // Queue a message if an ability prevented usage of the move if (cancelled.value) { globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name })); @@ -8293,7 +8300,6 @@ const MoveAttrs = Object.freeze({ CompareWeightPowerAttr, HpPowerAttr, OpponentHighHpPowerAttr, - FirstAttackDoublePowerAttr, TurnDamagedDoublePowerAttr, MagnitudePowerAttr, AntiSunlightPowerDecreaseAttr, @@ -8841,12 +8847,12 @@ export function initMoves() { .makesContact(false), new StatusMove(MoveId.TRANSFORM, PokemonType.NORMAL, -1, 10, -1, 0, 1) .attr(TransformAttr) - .condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE)) - .condition((user, target, move) => !target.summonData.illusion && !user.summonData.illusion) - // transforming from or into fusion pokemon causes various problems (such as crashes) - .condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE) && !user.fusionSpecies && !target.fusionSpecies) .ignoresProtect() - // Transforming should copy the target's rage fist hit count + /* Transform: + * Does not copy the target's rage fist hit count + * Does not copy the target's volatile status conditions (ie BattlerTags) + * Renders user typeless when copying typeless opponent (should revert to original typing) + */ .edgeCase(), new AttackMove(MoveId.BUBBLE, PokemonType.WATER, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) .attr(StatStageChangeAttr, [ Stat.SPD ], -1) @@ -9596,7 +9602,8 @@ export function initMoves() { new AttackMove(MoveId.CLOSE_COMBAT, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), new AttackMove(MoveId.PAYBACK, PokemonType.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === globalScene.currentBattle.turn) || globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), + // Payback boosts power on item use + .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted || globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), new AttackMove(MoveId.ASSURANCE, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => target.turnData.damageTaken > 0 ? 2 : 1), new StatusMove(MoveId.EMBARGO, PokemonType.DARK, 100, 15, -1, 0, 4) @@ -10808,9 +10815,9 @@ export function initMoves() { .condition(failIfGhostTypeCondition) .attr(AddBattlerTagAttr, BattlerTagType.OCTOLOCK, false, true, 1), new AttackMove(MoveId.BOLT_BEAK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8) - .attr(FirstAttackDoublePowerAttr), + .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted ? 1 : 2), new AttackMove(MoveId.FISHIOUS_REND, PokemonType.WATER, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8) - .attr(FirstAttackDoublePowerAttr) + .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted ? 1 : 2) .bitingMove(), new StatusMove(MoveId.COURT_CHANGE, PokemonType.NORMAL, 100, 10, -1, 0, 8) .attr(SwapArenaTagsAttr, [ ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES ]), diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts index daad199fbbd..d3f68fe9db4 100644 --- a/src/data/moves/pokemon-move.ts +++ b/src/data/moves/pokemon-move.ts @@ -1,8 +1,8 @@ -import type Pokemon from "#app/field/pokemon"; -import { toDmgValue } from "#app/utils/common"; +import { allMoves } from "#data/data-lists"; import type { MoveId } from "#enums/move-id"; -import { allMoves } from "../data-lists"; -import type Move from "./move"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { toDmgValue } from "#utils/common"; /** * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. @@ -16,7 +16,7 @@ import type Move from "./move"; * @see {@linkcode getMovePp} - returns amount of PP a move currently has. * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. * @see {@linkcode getName} - returns name of {@linkcode Move}. - **/ + */ export class PokemonMove { public moveId: MoveId; public ppUsed: number; @@ -36,13 +36,13 @@ export class PokemonMove { } /** - * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. + * Checks whether this move can be selected/performed by a Pokemon, without consideration for the move's targets. * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. * - * @param pokemon - {@linkcode Pokemon} that would be using this move - * @param ignorePp - If `true`, skips the PP check - * @param ignoreRestrictionTags - If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) - * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. + * @param pokemon - The {@linkcode Pokemon} attempting to use this move + * @param ignorePp - Whether to ignore checking if the move is out of PP; default `false` + * @param ignoreRestrictionTags - Whether to skip checks for {@linkcode MoveRestrictionBattlerTag}s; default `false` + * @returns Whether this {@linkcode PokemonMove} can be selected by this Pokemon. */ isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean { // TODO: Add Sky Drop's 1 turn stall diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index 915cf1703c9..9c782cb28f3 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -1,27 +1,27 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { modifierTypes } from "#data/data-lists"; +import type { IEggOptions } from "#data/egg"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerType } from "#enums/trainer-type"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { TrainerType } from "#enums/trainer-type"; -import { SpeciesId } from "#enums/species-id"; -import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { randSeedInt } from "#app/utils/common"; +} from "#mystery-encounters/encounter-phase-utils"; +import { getSpriteKeysFromSpecies } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { randSeedInt } from "#utils/common"; import i18next from "i18next"; -import type { IEggOptions } from "#app/data/egg"; -import { EggSourceType } from "#enums/egg-source-types"; -import { EggTier } from "#enums/egg-type"; -import { ModifierTier } from "#enums/modifier-tier"; -import { modifierTypes } from "#app/data/data-lists"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/aTrainersTest"; diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index bec75288837..d6058eb9eaf 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -1,44 +1,44 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import type { BerryType } from "#enums/berry-type"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TrainerSlot } from "#enums/trainer-slot"; +import type { Pokemon } from "#field/pokemon"; +import { EnemyPokemon } from "#field/pokemon"; +import { BerryModifier, PokemonInstantReviveModifier } from "#modifiers/modifier"; +import type { BerryModifierType, PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoveId } from "#enums/move-id"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { randInt } from "#app/utils/common"; -import { BattlerIndex } from "#enums/battler-index"; +} from "#mystery-encounters/encounter-phase-utils"; import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { PokeballType } from "#enums/pokeball"; -import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import type { BerryType } from "#enums/berry-type"; -import { Stat } from "#enums/stat"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { PersistentModifierRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import type { HeldModifierConfig } from "#types/held-modifier-config"; +import { randInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { MoveUseMode } from "#enums/move-use-mode"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/absoluteAvarice"; diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index f8a904cdb45..619acabe200 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -1,28 +1,28 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import { modifierTypes } from "#data/data-lists"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; import { generateModifierType, leaveEncounterWithoutBattle, setEncounterExp, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes } from "#app/data/data-lists"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +} from "#mystery-encounters/encounter-phase-utils"; +import { getHighestStatTotalPlayerPokemon } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; 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 } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +} from "#mystery-encounters/mystery-encounter-requirements"; +import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#mystery-encounters/requirement-groups"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; /** the i18n namespace for this encounter */ diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index d86a8439804..a827c3fcc0a 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -1,5 +1,20 @@ -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PERMANENT_STATS, Stat } from "#enums/stat"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { BerryModifier } from "#modifiers/modifier"; +import type { BerryModifierType, ModifierTypeOption } from "#modifiers/modifier-type"; +import { regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, generateModifierTypeOption, @@ -8,36 +23,21 @@ import { leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type"; -import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { randSeedInt } from "#app/utils/common"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +} from "#mystery-encounters/encounter-phase-utils"; import { applyModifierTypeToPlayerPokemon, getEncounterPokemonLevelForWave, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import PokemonData from "#app/system/pokemon-data"; -import { BerryModifier } from "#app/modifier/modifier"; -import i18next from "#app/plugins/i18n"; -import { BerryType } from "#enums/berry-type"; -import { PERMANENT_STATS, Stat } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import i18next from "#plugins/i18n"; +import { PokemonData } from "#system/pokemon-data"; +import { randSeedItem } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/berriesAbound"; @@ -311,7 +311,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. .build(); function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) { - const berryType = randSeedInt(Object.keys(BerryType).filter(s => !Number.isNaN(Number(s))).length) as BerryType; + const berryType = randSeedItem(getEnumValues(BerryType)); const berry = generateModifierType(modifierTypes.BERRY, [berryType]) as BerryModifierType; const party = globalScene.getPlayerParty(); diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index df06f40c159..d60ebe690ac 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -1,4 +1,29 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { allMoves, modifierTypes } from "#data/data-lists"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { TrainerType } from "#enums/trainer-type"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import { + AttackTypeBoosterModifier, + BypassSpeedChanceModifier, + ContactHeldItemTransferChanceModifier, + GigantamaxAccessModifier, + MegaEvolutionAccessModifier, +} from "#modifiers/modifier"; +import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { getEncounterText, showEncounterDialogue } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, generateModifierTypeOption, @@ -8,51 +33,23 @@ import { selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getRandomPartyMemberFunc, trainerConfigs } from "#app/data/trainers/trainer-config"; -import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { globalScene } from "#app/global-scene"; -import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { TrainerType } from "#enums/trainer-type"; -import { SpeciesId } from "#enums/species-id"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { MoveId } from "#enums/move-id"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +} from "#mystery-encounters/encounter-phase-utils"; +import { getSpriteKeysFromSpecies } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { AttackTypeBoosterHeldItemTypeRequirement, CombinationPokemonRequirement, HeldItemRequirement, TypeRequirement, -} from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { PokemonType } from "#enums/pokemon-type"; -import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { - AttackTypeBoosterModifier, - BypassSpeedChanceModifier, - ContactHeldItemTransferChanceModifier, - GigantamaxAccessModifier, - MegaEvolutionAccessModifier, -} from "#app/modifier/modifier"; +} from "#mystery-encounters/mystery-encounter-requirements"; +import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config"; +import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#utils/common"; import i18next from "i18next"; -import MoveInfoOverlay from "#app/ui/move-info-overlay"; -import { allMoves } from "#app/data/data-lists"; -import { ModifierTier } from "#enums/modifier-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/bugTypeSuperfan"; @@ -292,6 +289,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde // Init the moves available for tutor const moveTutorOptions: PokemonMove[] = []; + // TODO: should this use `randSeedItem`? moveTutorOptions.push(new PokemonMove(PHYSICAL_TUTOR_MOVES[randSeedInt(PHYSICAL_TUTOR_MOVES.length)])); moveTutorOptions.push(new PokemonMove(SPECIAL_TUTOR_MOVES[randSeedInt(SPECIAL_TUTOR_MOVES.length)])); moveTutorOptions.push(new PokemonMove(STATUS_TUTOR_MOVES[randSeedInt(STATUS_TUTOR_MOVES.length)])); @@ -389,6 +387,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde specialOptions.push(rareFormChangeModifier); } if (specialOptions.length > 0) { + // TODO: should this use `randSeedItem`? modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); } diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 9bdaa603540..1c3335b859f 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -1,4 +1,32 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { EncounterBattleAnim } from "#data/battle-anims"; +import { allAbilities, modifierTypes } from "#data/data-lists"; +import { CustomPokemonData } from "#data/pokemon-data"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BerryType } from "#enums/berry-type"; +import { Challenges } from "#enums/challenges"; +import { EncounterAnim } from "#enums/encounter-anims"; +import { MoveCategory } from "#enums/MoveCategory"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerType } from "#enums/trainer-type"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon } from "#field/pokemon"; +import { BerryModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, initBattleWithEnemyConfig, @@ -7,49 +35,20 @@ import { selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { ModifierTier } from "#enums/modifier-tier"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { SpeciesId } from "#enums/species-id"; -import { TrainerType } from "#enums/trainer-type"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { AbilityId } from "#enums/ability-id"; +} from "#mystery-encounters/encounter-phase-utils"; import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { PokemonType } from "#enums/pokemon-type"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { randSeedInt, randSeedShuffle } from "#app/utils/common"; -import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { UiMode } from "#enums/ui-mode"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; +import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; +import { randSeedInt, randSeedShuffle } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { BerryModifier } from "#app/modifier/modifier"; -import { BerryType } from "#enums/berry-type"; -import { BattlerIndex } from "#enums/battler-index"; -import { MoveId } from "#enums/move-id"; -import { EncounterBattleAnim } from "#app/data/battle-anims"; -import { MoveCategory } from "#enums/MoveCategory"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { EncounterAnim } from "#enums/encounter-anims"; -import { Challenges } from "#enums/challenges"; -import { MoveUseMode } from "#enums/move-use-mode"; -import { allAbilities, modifierTypes } from "#app/data/data-lists"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/clowningAround"; @@ -135,10 +134,11 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder ); clownConfig.setPartyTemplates(clownPartyTemplate); clownConfig.setDoubleOnly(); - // @ts-ignore + // @ts-expect-error clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists // Generate random ability for Blacephalon from pool + // TODO: should this use `randSeedItem`? const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)]; encounter.setDialogueToken("ability", allAbilities[ability].name); encounter.misc = { ability }; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index c68e395b379..94006a43837 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -1,46 +1,45 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { globalScene } from "#app/global-scene"; -import { EncounterBattleAnim } from "#app/data/battle-anims"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, - selectPokemonForOption, - setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - catchPokemon, - getEncounterPokemonLevelForWave, - STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { TrainerSlot } from "#enums/trainer-slot"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { modifierTypes } from "#app/data/data-lists"; -import PokemonData from "#app/system/pokemon-data"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { globalScene } from "#app/global-scene"; +import { EncounterBattleAnim } from "#data/battle-anims"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; import { EncounterAnim } from "#enums/encounter-anims"; import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokeballType } from "#enums/pokeball"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; +import { TrainerSlot } from "#enums/trainer-slot"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { EnemyPokemon } from "#field/pokemon"; +import { PokemonMove } from "#moves/pokemon-move"; +import { getEncounterText, queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; +import { + initBattleWithEnemyConfig, + leaveEncounterWithoutBattle, + selectPokemonForOption, + setEncounterRewards, +} from "#mystery-encounters/encounter-phase-utils"; +import { + catchPokemon, + getEncounterPokemonLevelForWave, + STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { DANCING_MOVES } from "#mystery-encounters/requirement-groups"; +import { PokemonData } from "#system/pokemon-data"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { MoveUseMode } from "#enums/move-use-mode"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/dancingLessons"; @@ -328,7 +327,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder .withOptionPhase(async () => { // Show the Oricorio a dance, and recruit it const encounter = globalScene.currentBattle.mysteryEncounter!; - const oricorio = encounter.misc.oricorioData.toPokemon(); + const oricorio = encounter.misc.oricorioData.toPokemon() as EnemyPokemon; oricorio.passive = true; // Ensure the Oricorio's moveset gains the Dance move the player used diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index e919182ce5c..d90e207cc9a 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -1,25 +1,22 @@ -import type { PokemonType } from "#enums/pokemon-type"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#app/data/data-lists"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils"; -import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle } from "../utils/encounter-phase-utils"; -import { - getRandomPlayerPokemon, - getRandomSpeciesByStarterCost, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { PokemonFormChangeItemModifier } from "#app/modifier/modifier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; import { Challenges } from "#enums/challenges"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import type { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import { PokemonFormChangeItemModifier } from "#modifiers/modifier"; +import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils"; +import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle } from "#mystery-encounters/encounter-phase-utils"; +import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { isNullOrUndefined, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** i18n namespace for encounter */ const namespace = "mysteryEncounters/darkDeal"; diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 842fc9d73bd..66f50f134dd 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -1,42 +1,41 @@ -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { - CombinationPokemonRequirement, - HeldItemRequirement, - MoneyRequirement, -} from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { - generateModifierType, - leaveEncounterWithoutBattle, - selectPokemonForOption, - updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier"; +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#modifiers/modifier"; import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PreserveBerryModifier, -} from "#app/modifier/modifier"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { randSeedItem } from "#app/utils/common"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { timedEventManager } from "#app/global-event-manager"; +} from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import { + generateModifierType, + leaveEncounterWithoutBattle, + selectPokemonForOption, + updatePlayerMoney, +} from "#mystery-encounters/encounter-phase-utils"; +import { applyModifierTypeToPlayerPokemon } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { + CombinationPokemonRequirement, + HeldItemRequirement, + MoneyRequirement, +} from "#mystery-encounters/mystery-encounter-requirements"; +import i18next from "#plugins/i18n"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { randSeedItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/delibirdy"; diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 46152a7dc41..a45c5301a3e 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -1,16 +1,13 @@ -import { - leaveEncounterWithoutBattle, - setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { ModifierTypeFunc } from "#app/@types/modifier-types"; -import { modifierTypes } from "#app/data/data-lists"; -import { randSeedInt } from "#app/utils/common"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { modifierTypes } from "#data/data-lists"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { leaveEncounterWithoutBattle, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import type { ModifierTypeFunc } from "#types/modifier-types"; +import { randSeedInt } from "#utils/common"; /** i18n namespace for encounter */ const namespace = "mysteryEncounters/departmentStoreSale"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 6ab0f8a6a4b..84374c87e58 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -1,25 +1,25 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; import { MoveCategory } from "#enums/MoveCategory"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Stat } from "#enums/stat"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { PokemonMove } from "#moves/pokemon-move"; import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import { modifierTypes } from "#app/data/data-lists"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { Stat } from "#enums/stat"; +} from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import i18next from "i18next"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounters/fieldTrip"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index e8900f8def4..7d2583a00cb 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -1,52 +1,52 @@ -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { EncounterBattleAnim } from "#data/battle-anims"; +import { allAbilities, modifierTypes } from "#data/data-lists"; +import { Gender } from "#data/gender"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { EncounterAnim } from "#enums/encounter-anims"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { WeatherType } from "#enums/weather-type"; +import type { Pokemon } from "#field/pokemon"; +import type { AttackTypeBoosterModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { + generateModifierType, initBattleWithEnemyConfig, - loadCustomMovesForEncounter, leaveEncounterWithoutBattle, + loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, - generateModifierType, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { - AbilityRequirement, - CombinationPokemonRequirement, - TypeRequirement, -} from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { SpeciesId } from "#enums/species-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { Gender } from "#app/data/gender"; -import { PokemonType } from "#enums/pokemon-type"; -import { BattlerIndex } from "#enums/battler-index"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { MoveId } from "#enums/move-id"; -import { EncounterBattleAnim } from "#app/data/battle-anims"; -import { WeatherType } from "#enums/weather-type"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import { StatusEffect } from "#enums/status-effect"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +} from "#mystery-encounters/encounter-phase-utils"; import { applyAbilityOverrideToPokemon, applyDamageToPokemon, applyModifierTypeToPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { EncounterAnim } from "#enums/encounter-anims"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { Stat } from "#enums/stat"; -import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import { MoveUseMode } from "#enums/move-use-mode"; -import { allAbilities, modifierTypes } from "#app/data/data-lists"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { + AbilityRequirement, + CombinationPokemonRequirement, + TypeRequirement, +} from "#mystery-encounters/mystery-encounter-requirements"; +import { FIRE_RESISTANT_ABILITIES } from "#mystery-encounters/requirement-groups"; +import { isNullOrUndefined, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/fieryFallout"; diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index c53ff610c48..6ee2ebcdf67 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -1,35 +1,35 @@ -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import type { Pokemon } from "#field/pokemon"; +import type { ModifierTypeOption } from "#modifiers/modifier-type"; +import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { getRandomEncounterSpecies, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import type Pokemon from "#app/field/pokemon"; -import { ModifierTier } from "#enums/modifier-tier"; -import type { ModifierTypeOption } from "#app/modifier/modifier-type"; -import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +} from "#mystery-encounters/encounter-phase-utils"; import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import PokemonData from "#app/system/pokemon-data"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { randSeedInt } from "#app/utils/common"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { STEALING_MOVES } from "#mystery-encounters/requirement-groups"; +import { PokemonData } from "#system/pokemon-data"; +import { randSeedInt } from "#utils/common"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/fightOrFlight"; diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 9611560fe62..bf376c04843 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -1,35 +1,34 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { modifierTypes } from "#data/data-lists"; +import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; +import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; +import { FieldPosition } from "#enums/field-position"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PlayerGender } from "#enums/player-gender"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { addPokeballOpenParticles } from "#field/anims"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { TrainerSlot } from "#enums/trainer-slot"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { SpeciesId } from "#enums/species-id"; +} from "#mystery-encounters/encounter-phase-utils"; +import { isPokemonValidForEncounterOptionSelection } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { PlayerGender } from "#enums/player-gender"; -import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; -import { addPokeballOpenParticles } from "#app/field/anims"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { modifierTypes } from "#app/data/data-lists"; -import { Nature } from "#enums/nature"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/funAndGames"; diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 6bbc1a68772..b914927123a 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -1,58 +1,50 @@ -import { - leaveEncounterWithoutBattle, - selectPokemonForOption, - setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { ModifierTier } from "#enums/modifier-tier"; -import { MusicPreference } from "#app/system/settings/settings"; -import type { ModifierTypeOption } from "#app/modifier/modifier-type"; -import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { SpeciesId } from "#enums/species-id"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; -import { getTypeRgb } from "#app/data/type"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { allSpecies } from "#data/data-lists"; +import { Gender, getGenderSymbol } from "#data/gender"; +import { getNatureName } from "#data/nature"; +import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getTypeRgb } from "#data/type"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ModifierTier } from "#enums/modifier-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { - NumberHolder, - isNullOrUndefined, - randInt, - randSeedInt, - randSeedShuffle, - randSeedItem, -} from "#app/utils/common"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import type { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { TrainerType } from "#enums/trainer-type"; +import { doShinySparkleAnim } from "#field/anims"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { EnemyPokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier, -} from "#app/modifier/modifier"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import PokemonData from "#app/system/pokemon-data"; +} from "#modifiers/modifier"; +import type { ModifierTypeOption } from "#modifiers/modifier-type"; +import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import { + leaveEncounterWithoutBattle, + selectPokemonForOption, + setEncounterRewards, +} from "#mystery-encounters/encounter-phase-utils"; +import { addPokemonDataToDexAndValidateAchievements } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { PokemonData } from "#system/pokemon-data"; +import { MusicPreference } from "#system/settings"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { Gender, getGenderSymbol } from "#app/data/gender"; -import { getNatureName } from "#app/data/nature"; -import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; -import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import type { PokeballType } from "#enums/pokeball"; -import { doShinySparkleAnim } from "#app/field/anims"; -import { TrainerType } from "#enums/trainer-type"; -import { timedEventManager } from "#app/global-event-manager"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/globalTradeSystem"; diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 2b54b0a42f5..10f45c21b68 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,17 +1,17 @@ -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; -import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { globalScene } from "#app/global-scene"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { PokemonMove } from "#moves/pokemon-move"; +import { leaveEncounterWithoutBattle, setEncounterExp } from "#mystery-encounters/encounter-phase-utils"; +import { applyDamageToPokemon } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; const OPTION_1_REQUIRED_MOVE = MoveId.SURF; const OPTION_2_REQUIRED_MOVE = MoveId.FLY; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 010358ea3b2..6f15f150d8b 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -1,22 +1,21 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - initBattleWithEnemyConfig, - setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; import { ModifierTier } from "#enums/modifier-tier"; -import { modifierTypes } from "#app/data/data-lists"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; -import { globalScene } from "#app/global-scene"; -import { randSeedInt } from "#app/utils/common"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; +import { initBattleWithEnemyConfig, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { + TrainerPartyCompoundTemplate, + TrainerPartyTemplate, + trainerPartyTemplates, +} from "#trainers/trainer-party-template"; +import { randSeedInt } from "#utils/common"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/mysteriousChallengers"; @@ -92,7 +91,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter const brutalConfig = trainerConfigs[brutalTrainerType].clone(); brutalConfig.title = trainerConfigs[brutalTrainerType].title; brutalConfig.setPartyTemplates(e4Template); - // @ts-ignore + // @ts-expect-error brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func female = false; if (brutalConfig.hasGenders) { diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 321e65d7008..1bc2404dc27 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -1,28 +1,25 @@ -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, - setEncounterRewards, - transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - getHighestLevelPlayerPokemon, - koPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; import { ModifierTier } from "#enums/modifier-tier"; -import { randSeedInt } from "#app/utils/common"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; +import { + initBattleWithEnemyConfig, + leaveEncounterWithoutBattle, + setEncounterRewards, + transitionMysteryEncounterIntroVisuals, +} from "#mystery-encounters/encounter-phase-utils"; +import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** i18n namespace for encounter */ const namespace = "mysteryEncounters/mysteriousChest"; diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 1074eaf8c81..a865de3b19d 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -1,4 +1,11 @@ -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Stat } from "#enums/stat"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, @@ -6,22 +13,14 @@ import { setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { Stat } from "#enums/stat"; -import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +} from "#mystery-encounters/encounter-phase-utils"; +import { isPokemonValidForEncounterOptionSelection } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { CHARMING_MOVES } from "#mystery-encounters/requirement-groups"; import i18next from "i18next"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/partTimer"; diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 207e6ca400d..c3400b41327 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -1,36 +1,36 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { NON_LEGEND_PARADOX_POKEMON } from "#balance/special-species-groups"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PlayerGender } from "#enums/player-gender"; +import { PokeballType } from "#enums/pokeball"; +import { TrainerSlot } from "#enums/trainer-slot"; +import type { EnemyPokemon } from "#field/pokemon"; +import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#modifiers/modifier"; +import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { PokeballType } from "#enums/pokeball"; -import { PlayerGender } from "#enums/player-gender"; -import { NumberHolder, randSeedInt } from "#app/utils/common"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; +} from "#mystery-encounters/encounter-phase-utils"; import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterCost, trainerThrowPokeball, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { NumberHolder, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/safariZone"; diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 967c105c740..47317c12b50 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -1,32 +1,31 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { getNatureName } from "#data/nature"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import type { Nature } from "#enums/nature"; +import { SpeciesId } from "#enums/species-id"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { getEncounterText, queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { modifierTypes } from "#app/data/data-lists"; -import { randSeedInt } from "#app/utils/common"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +} from "#mystery-encounters/encounter-phase-utils"; import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon, isPokemonValidForEncounterOptionSelection, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type { Nature } from "#enums/nature"; -import { getNatureName } from "#app/data/nature"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { randSeedInt } from "#utils/common"; import i18next from "i18next"; /** the i18n namespace for this encounter */ diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 4169fd6d7c5..cddfef1ef76 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -1,15 +1,22 @@ -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { CustomPokemonData } from "#data/pokemon-data"; +import { AiType } from "#enums/ai-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BerryType } from "#enums/berry-type"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, initBattleWithEnemyConfig, @@ -17,21 +24,14 @@ import { loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, -} from "../utils/encounter-phase-utils"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { Nature } from "#enums/nature"; -import { MoveId } from "#enums/move-id"; -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { AiType } from "#enums/ai-type"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { BerryType } from "#enums/berry-type"; -import { Stat } from "#enums/stat"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { randSeedInt } from "#app/utils/common"; -import { MoveUseMode } from "#enums/move-use-mode"; +} from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { STEALING_MOVES } from "#mystery-encounters/requirement-groups"; +import { randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounters/slumberingSnorlax"; diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index 9c9232612c6..b547064fd66 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -1,4 +1,21 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BiomeId } from "#enums/biome-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokemonType } from "#enums/pokemon-type"; +import { Stat } from "#enums/stat"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { getBiomeKey } from "#field/arena"; +import type { Pokemon } from "#field/pokemon"; +import { EnemyPokemon } from "#field/pokemon"; +import { getPartyLuckValue } from "#modifiers/modifier-type"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierTypeOption, initBattleWithEnemyConfig, @@ -6,34 +23,17 @@ import { setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { randSeedInt } from "#app/utils/common"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import PokemonData from "#app/system/pokemon-data"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { BiomeId } from "#enums/biome-id"; -import { getBiomeKey } from "#app/field/arena"; -import { PokemonType } from "#enums/pokemon-type"; -import { getPartyLuckValue } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { Stat } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +} from "#mystery-encounters/encounter-phase-utils"; import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement, WaveModulusRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { PokemonData } from "#system/pokemon-data"; +import { randSeedInt } from "#utils/common"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/teleportingHijinks"; @@ -190,6 +190,7 @@ async function doBiomeTransitionDialogueAndBattleInit() { // Calculate new biome (cannot be current biome) const filteredBiomes = BIOME_CANDIDATES.filter(b => globalScene.arena.biomeType !== b); + // TODO: should this use `randSeedItem`? const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)]; // Show dialogue and transition biome diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 03c09f6918e..235bd322ef8 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -1,34 +1,34 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { globalScene } from "#app/global-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import { modifierTypes } from "#data/data-lists"; +import type { IEggOptions } from "#data/egg"; +import { getPokeballTintColor } from "#data/pokeball"; +import { BiomeId } from "#enums/biome-id"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerType } from "#enums/trainer-type"; +import type { PlayerPokemon } from "#field/pokemon"; +import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import { randSeedShuffle } from "#app/utils/common"; -import type MysteryEncounter from "../mystery-encounter"; -import { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { BiomeId } from "#enums/biome-id"; -import { TrainerType } from "#enums/trainer-type"; +} from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { randSeedShuffle } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { SpeciesId } from "#enums/species-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { Nature } from "#enums/nature"; -import { MoveId } from "#enums/move-id"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { IEggOptions } from "#app/data/egg"; -import { EggSourceType } from "#enums/egg-source-types"; -import { EggTier } from "#enums/egg-type"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { modifierTypes } from "#app/data/data-lists"; -import { PokemonType } from "#enums/pokemon-type"; -import { getPokeballTintColor } from "#app/data/pokeball"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/theExpertPokemonBreeder"; diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index c3bcf9ceb24..91662993a51 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -1,35 +1,35 @@ +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#balance/special-species-groups"; +import { speciesStarterCosts } from "#balance/starters"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { AbilityId } from "#enums/ability-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import type { EnemyPokemon } from "#field/pokemon"; +import { PlayerPokemon } from "#field/pokemon"; +import { showEncounterDialogue } from "#mystery-encounters/encounter-dialogue-utils"; import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; +} from "#mystery-encounters/encounter-phase-utils"; import { catchPokemon, getRandomSpeciesByStarterCost, getSpriteKeysFromPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { SpeciesId } from "#enums/species-id"; -import { PokeballType } from "#enums/pokeball"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { PlayerPokemon } from "#app/field/pokemon"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import PokemonData from "#app/system/pokemon-data"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { AbilityId } from "#enums/ability-id"; -import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#app/data/balance/special-species-groups"; -import { timedEventManager } from "#app/global-event-manager"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { PokemonData } from "#system/pokemon-data"; +import { isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/thePokemonSalesman"; diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 37d16075543..568dc5de8b1 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -1,34 +1,34 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - initBattleWithEnemyConfig, - loadCustomMovesForEncounter, - leaveEncounterWithoutBattle, - setEncounterRewards, - transitionMysteryEncounterIntroVisuals, - generateModifierType, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { SpeciesId } from "#enums/species-id"; -import { Nature } from "#enums/nature"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { MoveId } from "#enums/move-id"; +import { modifierTypes } from "#data/data-lists"; +import { CustomPokemonData } from "#data/pokemon-data"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { Stat } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { MoveId } from "#enums/move-id"; import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import type { Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; +import { + generateModifierType, + initBattleWithEnemyConfig, + leaveEncounterWithoutBattle, + loadCustomMovesForEncounter, + setEncounterRewards, + transitionMysteryEncounterIntroVisuals, +} from "#mystery-encounters/encounter-phase-utils"; +import { modifyPlayerPokemonBST } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/theStrongStuff"; @@ -54,7 +54,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder .withFleeAllowed(false) .withIntroSpriteConfigs([ { - spriteKey: "berry_juice", + spriteKey: "berry_juice_good", fileRoot: "items", hasShadow: true, isItem: true, @@ -171,11 +171,11 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder sortedParty.forEach((pokemon, index) => { if (index < 2) { // -15 to the two highest BST mons - modifyPlayerPokemonBST(pokemon, -HIGH_BST_REDUCTION_VALUE); + modifyPlayerPokemonBST(pokemon, false); encounter.setDialogueToken("highBstPokemon" + (index + 1), pokemon.getNameToRender()); } else { // +10 for the rest - modifyPlayerPokemonBST(pokemon, BST_INCREASE_VALUE); + modifyPlayerPokemonBST(pokemon, true); } }); diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index eba8a6ba00e..e17bf0575d7 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -1,4 +1,24 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { SpeciesFormChangeAbilityTrigger } from "#data/form-change-triggers"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TrainerType } from "#enums/trainer-type"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, generateModifierTypeOption, @@ -6,31 +26,11 @@ import { leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { TrainerType } from "#enums/trainer-type"; -import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoveId } from "#enums/move-id"; -import { Nature } from "#enums/nature"; -import { PokemonType } from "#enums/pokemon-type"; -import { BerryType } from "#enums/berry-type"; -import { Stat } from "#enums/stat"; -import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +} from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { ModifierTier } from "#enums/modifier-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { BattlerTagType } from "#enums/battler-tag-type"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/theWinstrateChallenge"; @@ -221,7 +221,7 @@ function endTrainerBattleAndShowDialogue(): Promise { // Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects pokemon.resetBattleAndWaveData(); - applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon); + applyAbAttrs("PostBattleInitAbAttr", { pokemon }); } globalScene.phaseManager.unshiftNew("ShowTrainerPhase"); diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 04e64083602..393f8a24e51 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -1,36 +1,36 @@ -import type { Ability } from "#app/data/abilities/ability"; -import { allAbilities } from "#app/data/data-lists"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import type { Ability } from "#abilities/ability"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import { allAbilities } from "#data/data-lists"; +import { getNatureName } from "#data/nature"; +import { AbilityAttr } from "#enums/ability-attr"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { getStatKey } from "#enums/stat"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getNatureName } from "#app/data/nature"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { AbilityAttr } from "#enums/ability-attr"; -import PokemonData from "#app/system/pokemon-data"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type HeldModifierConfig from "#app/@types/held-modifier-config"; +} from "#mystery-encounters/encounter-phase-utils"; +import { isPokemonValidForEncounterOptionSelection } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { PokemonData } from "#system/pokemon-data"; +import type { HeldModifierConfig } from "#types/held-modifier-config"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { isNullOrUndefined, randSeedShuffle } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; import i18next from "i18next"; -import { getStatKey } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import type { Nature } from "#enums/nature"; /** The i18n namespace for the encounter */ const namespace = "mysteryEncounters/trainingSession"; @@ -184,10 +184,9 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde .withPreOptionPhase(async (): Promise => { // Open menu for selecting pokemon and Nature const encounter = globalScene.currentBattle.mysteryEncounter!; - const natures = new Array(25).fill(null).map((_val, i) => i as Nature); const onPokemonSelected = (pokemon: PlayerPokemon) => { // Return the options for nature selection - return natures.map((nature: Nature) => { + return getEnumValues(Nature).map((nature: Nature) => { const option: OptionSelectItem = { label: getNatureName(nature, true, true, true, globalScene.uiTheme), handler: () => { diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index 4632a000a91..74a36a280d3 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -1,34 +1,34 @@ -import type { EnemyPartyConfig, EnemyPokemonConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { + type EnemyPartyConfig, + type EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { SpeciesId } from "#enums/species-id"; -import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app/modifier/modifier"; -import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import i18next from "#app/plugins/i18n"; -import { ModifierTier } from "#enums/modifier-tier"; -import { modifierTypes } from "#app/data/data-lists"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoveId } from "#enums/move-id"; -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { randSeedInt } from "#app/utils/common"; -import { MoveUseMode } from "#enums/move-use-mode"; +} from "#mystery-encounters/encounter-phase-utils"; +import { applyModifierTypeToPlayerPokemon } from "#mystery-encounters/encounter-pokemon-utils"; +import { type MysteryEncounter, MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import i18next from "#plugins/i18n"; +import { randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/trashToTreasure"; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 44e578540dd..c9e2ffedeec 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -1,42 +1,38 @@ -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import type { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokeballType } from "#enums/pokeball"; +import { Stat } from "#enums/stat"; +import type { EnemyPokemon, Pokemon } from "#field/pokemon"; +import { BerryModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { getRandomEncounterSpecies, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import type Pokemon from "#app/field/pokemon"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { - MoveRequirement, - PersistentModifierRequirement, -} from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +} from "#mystery-encounters/encounter-phase-utils"; import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import PokemonData from "#app/system/pokemon-data"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import type { MoveId } from "#enums/move-id"; -import { BattlerIndex } from "#enums/battler-index"; -import { PokeballType } from "#enums/pokeball"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { BerryModifier } from "#app/modifier/modifier"; -import { Stat } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { MoveUseMode } from "#enums/move-use-mode"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import { MoveRequirement, PersistentModifierRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { CHARMING_MOVES } from "#mystery-encounters/requirement-groups"; +import { PokemonData } from "#system/pokemon-data"; +import { isNullOrUndefined, randSeedInt } from "#utils/common"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/uncommonBreed"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 831ce9870e3..b4a2ba4abd5 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -1,49 +1,46 @@ -import { PokemonType } from "#enums/pokemon-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils"; +import { allSpecies, modifierTypes } from "#data/data-lists"; +import { getLevelTotalExp } from "#data/exp"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { Challenges } from "#enums/challenges"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import { PlayerGender } from "#enums/player-gender"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerType } from "#enums/trainer-type"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, -} from "../utils/encounter-phase-utils"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier"; -import { achvs } from "#app/system/achv"; -import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; +} from "#mystery-encounters/encounter-phase-utils"; import { doPokemonTransformationSequence, TransformationScreenPosition, -} from "#app/data/mystery-encounters/utils/encounter-transformation-sequence"; -import { getLevelTotalExp } from "#app/data/exp"; -import { Stat } from "#enums/stat"; -import { Challenges } from "#enums/challenges"; -import { ModifierTier } from "#enums/modifier-tier"; -import { PlayerGender } from "#enums/player-gender"; -import { TrainerType } from "#enums/trainer-type"; -import PokemonData from "#app/system/pokemon-data"; -import { Nature } from "#enums/nature"; -import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { PartyMemberStrength } from "#enums/party-member-strength"; +} from "#mystery-encounters/encounter-transformation-sequence"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; +import i18next from "#plugins/i18n"; +import { achvs } from "#system/achv"; +import { PokemonData } from "#system/pokemon-data"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { TrainerPartyTemplate } from "#trainers/trainer-party-template"; +import type { HeldModifierConfig } from "#types/held-modifier-config"; +import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedShuffle } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** i18n namespace for encounter */ const namespace = "mysteryEncounters/weirdDream"; @@ -104,8 +101,6 @@ const EXCLUDED_TRANSFORMATION_SPECIES = [ const SUPER_LEGENDARY_BST_THRESHOLD = 600; const NON_LEGENDARY_BST_THRESHOLD = 570; -const OLD_GATEAU_STATS_UP = 20; - /** 0-100 */ const PERCENT_LEVEL_LOSS_ON_REFUSE = 10; @@ -276,12 +271,8 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit } // Any pokemon that is below 570 BST gets +20 permanent BST to 3 stats if (shouldGetOldGateau(newPokemon)) { - const stats = getOldGateauBoostedStats(newPokemon); newPokemonHeldItemConfigs.push({ - modifier: generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU, [ - OLD_GATEAU_STATS_UP, - stats, - ]) as PokemonHeldItemModifierType, + modifier: generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU) as PokemonHeldItemModifierType, stackCount: 1, isTransferable: false, }); @@ -315,6 +306,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit // One random pokemon will get its passive unlocked const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); if (passiveDisabledPokemon?.length > 0) { + // TODO: should this use `randSeedItem`? const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; enablePassiveMon.passive = true; enablePassiveMon.updateInfo(true); @@ -462,11 +454,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) { } // Any pokemon that is below 570 BST gets +20 permanent BST to 3 stats if (shouldGetOldGateau(newPokemon)) { - const stats = getOldGateauBoostedStats(newPokemon); - const modType = modifierTypes - .MYSTERY_ENCOUNTER_OLD_GATEAU() - .generateType(globalScene.getPlayerParty(), [OLD_GATEAU_STATS_UP, stats]) - ?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU); + const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU(); const modifier = modType?.newModifier(newPokemon); if (modifier) { globalScene.addModifier(modifier, false, false, false, true); @@ -480,6 +468,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) { // One random pokemon will get its passive unlocked const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); if (passiveDisabledPokemon?.length > 0) { + // TODO: should this use `randSeedItem`? const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; enablePassiveMon.passive = true; await enablePassiveMon.updateInfo(true); @@ -617,22 +606,6 @@ function shouldGetOldGateau(pokemon: Pokemon): boolean { return pokemon.getSpeciesForm().getBaseStatTotal() < NON_LEGENDARY_BST_THRESHOLD; } -/** - * Get the lowest of HP/Spd, lowest of Atk/SpAtk, and lowest of Def/SpDef - * @returns Array of 3 {@linkcode Stat}s to boost - */ -function getOldGateauBoostedStats(pokemon: Pokemon): Stat[] { - const stats: Stat[] = []; - const baseStats = pokemon.getSpeciesForm().baseStats.slice(0); - // HP or Speed - stats.push(baseStats[Stat.HP] < baseStats[Stat.SPD] ? Stat.HP : Stat.SPD); - // 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); - return stats; -} - function getTransformedSpecies( originalBst: number, bstSearchRange: [number, number], diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 71e1b382f61..42383940755 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,4 +1,4 @@ -import type { TextStyle } from "#app/ui/text"; +import type { TextStyle } from "#ui/text"; export class TextDisplay { speaker?: string; @@ -67,7 +67,7 @@ export class EncounterOptionsDialogue { } * */ -export default class MysteryEncounterDialogue { +export class MysteryEncounterDialogue { intro?: TextDisplay[]; encounterOptionsDialogue?: EncounterOptionsDialogue; outro?: TextDisplay[]; diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 53b53392bb8..504310eeabd 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -1,19 +1,18 @@ -import type { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; -import type { MoveId } from "#enums/move-id"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; +import type { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import type { PokemonType } from "#enums/pokemon-type"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { CanLearnMoveRequirementOptions } from "#mystery-encounters/can-learn-move-requirement"; +import { CanLearnMoveRequirement } from "#mystery-encounters/can-learn-move-requirement"; +import type { OptionTextDisplay } from "#mystery-encounters/mystery-encounter-dialogue"; import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement, -} from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import type { CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; -import { CanLearnMoveRequirement } from "./requirements/can-learn-move-requirement"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +} from "#mystery-encounters/mystery-encounter-requirements"; +import { isNullOrUndefined, randSeedInt } from "#utils/common"; // biome-ignore lint/suspicious/noConfusingVoidType: void unions in callbacks are OK export type OptionPhaseCallback = () => Promise; @@ -39,7 +38,7 @@ export interface IMysteryEncounterOption { onPostOptionPhase?: OptionPhaseCallback; } -export default class MysteryEncounterOption implements IMysteryEncounterOption { +export class MysteryEncounterOption implements IMysteryEncounterOption { optionMode: MysteryEncounterOptionMode; hasDexProgress: boolean; requirements: EncounterSceneRequirement[]; @@ -145,11 +144,13 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption { } if (truePrimaryPool.length > 0) { // always choose from the non-overlapping pokemon first + // TODO: should this use `randSeedItem`? this.primaryPokemon = truePrimaryPool[randSeedInt(truePrimaryPool.length)]; return true; } // if there are multiple overlapping pokemon, we're okay - just choose one and take it out of the supporting pokemon pool if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) { + // TODO: should this use `randSeedItem`? this.primaryPokemon = overlap[randSeedInt(overlap.length)]; this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon); return true; diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index 07fd155b2b2..d71964db4b8 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -1,21 +1,21 @@ import { globalScene } from "#app/global-scene"; -import { allAbilities } from "../data-lists"; -import { Nature } from "#enums/nature"; -import { pokemonFormChanges } from "#app/data/pokemon-forms"; -import { SpeciesFormChangeItemTrigger } from "../pokemon-forms/form-change-triggers"; -import { FormChangeItem } from "#enums/form-change-item"; -import { StatusEffect } from "#enums/status-effect"; -import { PokemonType } from "#enums/pokemon-type"; -import { WeatherType } from "#enums/weather-type"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { AttackTypeBoosterModifier } from "#app/modifier/modifier"; -import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; -import { coerceArray, isNullOrUndefined } from "#app/utils/common"; +import { allAbilities } from "#data/data-lists"; +import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; +import { pokemonFormChanges } from "#data/pokemon-forms"; import type { AbilityId } from "#enums/ability-id"; +import { FormChangeItem } from "#enums/form-change-item"; import { MoveId } from "#enums/move-id"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; import { TimeOfDay } from "#enums/time-of-day"; +import { WeatherType } from "#enums/weather-type"; +import type { PlayerPokemon } from "#field/pokemon"; +import { AttackTypeBoosterModifier } from "#modifiers/modifier"; +import type { AttackTypeBoosterModifierType } from "#modifiers/modifier-type"; +import { coerceArray, isNullOrUndefined } from "#utils/common"; export interface EncounterRequirement { meetsRequirement(): boolean; // Boolean to see if a requirement is met diff --git a/src/data/mystery-encounters/mystery-encounter-save-data.ts b/src/data/mystery-encounters/mystery-encounter-save-data.ts index 20c10c7c5b9..f04abccba5f 100644 --- a/src/data/mystery-encounters/mystery-encounter-save-data.ts +++ b/src/data/mystery-encounters/mystery-encounter-save-data.ts @@ -1,7 +1,7 @@ -import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/constants"; -import { isNullOrUndefined } from "#app/utils/common"; import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { isNullOrUndefined } from "#utils/common"; export class SeenEncounterData { type: MysteryEncounterType; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index fa97a7f4d40..a2ca2b20ce7 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -1,18 +1,22 @@ -import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "../moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import { capitalizeFirstLetter, coerceArray, isNullOrUndefined } from "#app/utils/common"; +import { globalScene } from "#app/global-scene"; +import type { BattlerIndex } from "#enums/battler-index"; +import type { Challenges } from "#enums/challenges"; +import type { EncounterAnim } from "#enums/encounter-anims"; +import type { GameModes } from "#enums/game-modes"; +import type { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro"; -import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; -import { randSeedInt } from "#app/utils/common"; import type { StatusEffect } from "#enums/status-effect"; -import type { OptionTextDisplay } from "./mystery-encounter-dialogue"; -import type MysteryEncounterDialogue from "./mystery-encounter-dialogue"; -import type { OptionPhaseCallback } from "./mystery-encounter-option"; -import type MysteryEncounterOption from "./mystery-encounter-option"; -import { MysteryEncounterOptionBuilder } from "./mystery-encounter-option"; +import type { MysteryEncounterSpriteConfig } from "#field/mystery-encounter-intro"; +import { MysteryEncounterIntroVisuals } from "#field/mystery-encounter-intro"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonMove } from "#moves/pokemon-move"; +import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounterDialogue, OptionTextDisplay } from "#mystery-encounters/mystery-encounter-dialogue"; +import type { MysteryEncounterOption, OptionPhaseCallback } from "#mystery-encounters/mystery-encounter-option"; +import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { EncounterPokemonRequirement, EncounterSceneRequirement, @@ -20,16 +24,8 @@ import { PartySizeRequirement, StatusEffectRequirement, WaveRangeRequirement, -} from "./mystery-encounter-requirements"; -import type { BattlerIndex } from "#enums/battler-index"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type { GameModes } from "#enums/game-modes"; -import type { EncounterAnim } from "#enums/encounter-anims"; -import type { Challenges } from "#enums/challenges"; -import { globalScene } from "#app/global-scene"; -import type { MoveUseMode } from "#enums/move-use-mode"; +} from "#mystery-encounters/mystery-encounter-requirements"; +import { capitalizeFirstLetter, coerceArray, isNullOrUndefined, randSeedInt } from "#utils/common"; export interface EncounterStartOfBattleEffect { sourcePokemon?: Pokemon; @@ -91,7 +87,7 @@ export interface IMysteryEncounter { * These objects will be saved as part of session data any time the player is on a floor with an encounter * Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class */ -export default class MysteryEncounter implements IMysteryEncounter { +export class MysteryEncounter implements IMysteryEncounter { // #region Required params encounterType: MysteryEncounterType; @@ -385,6 +381,7 @@ export default class MysteryEncounter implements IMysteryEncounter { // If there are multiple overlapping pokemon, we're okay - just choose one and take it out of the primary pokemon pool if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) { // is this working? + // TODO: should this use `randSeedItem`? this.primaryPokemon = overlap[randSeedInt(overlap.length, 0)]; this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon); return true; @@ -395,6 +392,7 @@ export default class MysteryEncounter implements IMysteryEncounter { return false; } // this means we CAN have the same pokemon be a primary and secondary pokemon, so just choose any qualifying one randomly. + // TODO: should this use `randSeedItem`? this.primaryPokemon = qualified[randSeedInt(qualified.length, 0)]; return true; } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 5ee289a6c56..34eac5cab94 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -1,38 +1,38 @@ +import { getBiomeName } from "#balance/biomes"; import { BiomeId } from "#enums/biome-id"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { DarkDealEncounter } from "./encounters/dark-deal-encounter"; -import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter"; -import { FieldTripEncounter } from "./encounters/field-trip-encounter"; -import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter"; -import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; -import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter"; -import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter"; -import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; -import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter"; -import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; -import type MysteryEncounter from "./mystery-encounter"; -import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; -import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; -import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; -import { ThePokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/the-pokemon-salesman-encounter"; -import { AnOfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter"; -import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter"; -import { AbsoluteAvariceEncounter } from "#app/data/mystery-encounters/encounters/absolute-avarice-encounter"; -import { ATrainersTestEncounter } from "#app/data/mystery-encounters/encounters/a-trainers-test-encounter"; -import { TrashToTreasureEncounter } from "#app/data/mystery-encounters/encounters/trash-to-treasure-encounter"; -import { BerriesAboundEncounter } from "#app/data/mystery-encounters/encounters/berries-abound-encounter"; -import { ClowningAroundEncounter } from "#app/data/mystery-encounters/encounters/clowning-around-encounter"; -import { PartTimerEncounter } from "#app/data/mystery-encounters/encounters/part-timer-encounter"; -import { DancingLessonsEncounter } from "#app/data/mystery-encounters/encounters/dancing-lessons-encounter"; -import { WeirdDreamEncounter } from "#app/data/mystery-encounters/encounters/weird-dream-encounter"; -import { TheWinstrateChallengeEncounter } from "#app/data/mystery-encounters/encounters/the-winstrate-challenge-encounter"; -import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; -import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounters/bug-type-superfan-encounter"; -import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter"; -import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; -import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; -import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; -import { getBiomeName } from "#app/data/balance/biomes"; +import { ATrainersTestEncounter } from "#mystery-encounters/a-trainers-test-encounter"; +import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter"; +import { AnOfferYouCantRefuseEncounter } from "#mystery-encounters/an-offer-you-cant-refuse-encounter"; +import { BerriesAboundEncounter } from "#mystery-encounters/berries-abound-encounter"; +import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter"; +import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-encounter"; +import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter"; +import { DarkDealEncounter } from "#mystery-encounters/dark-deal-encounter"; +import { DelibirdyEncounter } from "#mystery-encounters/delibirdy-encounter"; +import { DepartmentStoreSaleEncounter } from "#mystery-encounters/department-store-sale-encounter"; +import { FieldTripEncounter } from "#mystery-encounters/field-trip-encounter"; +import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter"; +import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter"; +import { FunAndGamesEncounter } from "#mystery-encounters/fun-and-games-encounter"; +import { GlobalTradeSystemEncounter } from "#mystery-encounters/global-trade-system-encounter"; +import { LostAtSeaEncounter } from "#mystery-encounters/lost-at-sea-encounter"; +import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-challengers-encounter"; +import { MysteriousChestEncounter } from "#mystery-encounters/mysterious-chest-encounter"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { PartTimerEncounter } from "#mystery-encounters/part-timer-encounter"; +import { SafariZoneEncounter } from "#mystery-encounters/safari-zone-encounter"; +import { ShadyVitaminDealerEncounter } from "#mystery-encounters/shady-vitamin-dealer-encounter"; +import { SlumberingSnorlaxEncounter } from "#mystery-encounters/slumbering-snorlax-encounter"; +import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter"; +import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter"; +import { ThePokemonSalesmanEncounter } from "#mystery-encounters/the-pokemon-salesman-encounter"; +import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter"; +import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter"; +import { TrainingSessionEncounter } from "#mystery-encounters/training-session-encounter"; +import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter"; +import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter"; +import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter"; export const EXTREME_ENCOUNTER_BIOMES = [ BiomeId.SEA, diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index 0123ea7d6ba..26602b8ae31 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,9 +1,9 @@ -import type { MoveId } from "#enums/move-id"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { coerceArray, isNullOrUndefined } from "#app/utils/common"; -import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { globalScene } from "#app/global-scene"; +import type { MoveId } from "#enums/move-id"; +import type { PlayerPokemon } from "#field/pokemon"; +import { PokemonMove } from "#moves/pokemon-move"; +import { EncounterPokemonRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import { coerceArray, isNullOrUndefined } from "#utils/common"; /** * {@linkcode CanLearnMoveRequirement} options diff --git a/src/data/mystery-encounters/requirements/requirement-groups.ts b/src/data/mystery-encounters/requirements/requirement-groups.ts index 0140a5fe320..8981dfe173c 100644 --- a/src/data/mystery-encounters/requirements/requirement-groups.ts +++ b/src/data/mystery-encounters/requirements/requirement-groups.ts @@ -1,5 +1,5 @@ -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; /** * Moves that "steal" things diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index e1055f57496..4f4af94a88d 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; -import type { TextStyle } from "#app/ui/text"; -import { getTextWithColors } from "#app/ui/text"; import { UiTheme } from "#enums/ui-theme"; -import { isNullOrUndefined } from "#app/utils/common"; +import type { TextStyle } from "#ui/text"; +import { getTextWithColors } from "#ui/text"; +import { isNullOrUndefined } from "#utils/common"; import i18next from "i18next"; /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index bb74f11ce60..6b085978b27 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,58 +1,57 @@ -import type Battle from "#app/battle"; -import { BattleType } from "#enums/battle-type"; -import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes"; -import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; +import type { Battle } from "#app/battle"; import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants"; -import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { BiomePoolTier, biomeLinks } from "#balance/biomes"; +import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; +import { modifierTypes } from "#data/data-lists"; +import type { IEggOptions } from "#data/egg"; +import { Egg } from "#data/egg"; +import type { Gender } from "#data/gender"; +import { getNatureName } from "#data/nature"; +import type { CustomPokemonData } from "#data/pokemon-data"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { Status } from "#data/status-effect"; import type { AiType } from "#enums/ai-type"; -import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { BattleType } from "#enums/battle-type"; +import type { BattlerTagType } from "#enums/battler-tag-type"; +import { BiomeId } from "#enums/biome-id"; import { FieldPosition } from "#enums/field-position"; -import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import type { MoveId } from "#enums/move-id"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import type { Nature } from "#enums/nature"; +import { PokemonType } from "#enums/pokemon-type"; +import { StatusEffect } from "#enums/status-effect"; +import { TrainerSlot } from "#enums/trainer-slot"; +import type { TrainerType } from "#enums/trainer-type"; +import { TrainerVariant } from "#enums/trainer-variant"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { EnemyPokemon } from "#field/pokemon"; +import { Trainer } from "#field/trainer"; +import type { CustomModifierSettings, ModifierType } from "#modifiers/modifier-type"; import { getPartyLuckValue, ModifierTypeGenerator, ModifierTypeOption, regenerateModifierPoolThresholds, -} from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import type PokemonData from "#app/system/pokemon-data"; -import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler"; -import { PartyUiMode } from "#app/ui/party-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { isNullOrUndefined, randSeedInt, randomString, randSeedItem, coerceArray } from "#app/utils/common"; -import type { BattlerTagType } from "#enums/battler-tag-type"; -import { BiomeId } from "#enums/biome-id"; -import type { TrainerType } from "#enums/trainer-type"; +} from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option"; +import type { Variant } from "#sprites/variant"; +import type { PokemonData } from "#system/pokemon-data"; +import type { TrainerConfig } from "#trainers/trainer-config"; +import { trainerConfigs } from "#trainers/trainer-config"; +import type { HeldModifierConfig } from "#types/held-modifier-config"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler"; +import { PartyUiMode } from "#ui/party-ui-handler"; +import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import Trainer from "#app/field/trainer"; -import { TrainerVariant } from "#enums/trainer-variant"; -import type { Gender } from "#app/data/gender"; -import type { Nature } from "#enums/nature"; -import type { MoveId } from "#enums/move-id"; -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { Status } from "#app/data/status-effect"; -import type { TrainerConfig } from "#app/data/trainers/trainer-config"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { TrainerSlot } from "#enums/trainer-slot"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import type { IEggOptions } from "#app/data/egg"; -import { Egg } from "#app/data/egg"; -import type { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import type { Variant } from "#app/sprites/variant"; -import { StatusEffect } from "#enums/status-effect"; -import { globalScene } from "#app/global-scene"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { PokemonType } from "#enums/pokemon-type"; -import { getNatureName } from "#app/data/nature"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { timedEventManager } from "#app/global-event-manager"; /** * Animates exclamation sprite over trainer's head at start of encounter @@ -1099,8 +1098,10 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { if (biomes! && biomes.length > 0) { const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b)); if (specialBiomes.length > 0) { + // TODO: should this use `randSeedItem`? currentBiome = specialBiomes[randSeedInt(specialBiomes.length)]; } else { + // TODO: should this use `randSeedItem`? currentBiome = biomes[randSeedInt(biomes.length)]; } } @@ -1226,7 +1227,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { ); for (const value of meanEncountersPerRunPerBiomeSorted) { - stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + ", avg MEs ${value[1]},\n"; + stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + `, avg MEs ${value[1]},\n`; } console.log(stats); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index f6ac5b0d38b..19f06707257 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -1,42 +1,41 @@ import { globalScene } from "#app/global-scene"; -import i18next from "i18next"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; -import { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { speciesStarterCosts } from "#balance/starters"; +import { modifierTypes } from "#data/data-lists"; +import { Gender } from "#data/gender"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, -} from "#app/data/pokeball"; +} from "#data/pokeball"; +import { CustomPokemonData } from "#data/pokemon-data"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getStatusEffectCatchRateMultiplier } from "#data/status-effect"; +import type { AbilityId } from "#enums/ability-id"; import { PlayerGender } from "#enums/player-gender"; -import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims"; -import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; -import { achvs } from "#app/system/achv"; -import { UiMode } from "#enums/ui-mode"; -import type { PartyOption } from "#app/ui/party-ui-handler"; -import { PartyUiMode } from "#app/ui/party-ui-handler"; -import { SpeciesId } from "#enums/species-id"; +import type { PokeballType } from "#enums/pokeball"; import type { PokemonType } from "#enums/pokemon-type"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; +import { SpeciesId } from "#enums/species-id"; +import type { PermanentStat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import { addPokeballCaptureStars, addPokeballOpenParticles } from "#field/anims"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; +import { PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; import { getEncounterText, queueEncounterMessage, showEncounterText, -} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { getPokemonNameWithAffix } from "#app/messages"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { Gender } from "#app/data/gender"; -import type { PermanentStat } from "#enums/stat"; -import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import type { AbilityId } from "#enums/ability-id"; -import type { PokeballType } from "#enums/pokeball"; -import { StatusEffect } from "#enums/status-effect"; +} from "#mystery-encounters/encounter-dialogue-utils"; +import { achvs } from "#system/achv"; +import type { PartyOption } from "#ui/party-ui-handler"; +import { PartyUiMode } from "#ui/party-ui-handler"; +import { SummaryUiMode } from "#ui/summary-ui-handler"; +import { isNullOrUndefined, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; /** Will give +1 level every 10 waves */ export const STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER = 1; @@ -111,20 +110,24 @@ export function getRandomPlayerPokemon( // If there is only 1 legal/unfainted mon left, select from fainted legal mons const faintedLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && p.isFainted()); if (faintedLegalMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(faintedLegalMons.length); chosenPokemon = faintedLegalMons[chosenIndex]; } } if (!chosenPokemon && fullyLegalMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(fullyLegalMons.length); chosenPokemon = fullyLegalMons[chosenIndex]; } if (!chosenPokemon && isAllowed && allowedOnlyMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(allowedOnlyMons.length); chosenPokemon = allowedOnlyMons[chosenIndex]; } if (!chosenPokemon) { // If no other options worked, returns fully random + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(party.length); chosenPokemon = party[chosenIndex]; } @@ -375,10 +378,10 @@ export function applyHealToPokemon(pokemon: PlayerPokemon, heal: number) { * @param pokemon * @param value */ -export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) { +export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, good: boolean) { const modType = modifierTypes .MYSTERY_ENCOUNTER_SHUCKLE_JUICE() - .generateType(globalScene.getPlayerParty(), [value]) + .generateType(globalScene.getPlayerParty(), [good ? 10 : -15]) ?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE); const modifier = modType?.newModifier(pokemon); if (modifier) { @@ -403,12 +406,12 @@ export async function applyModifierTypeToPlayerPokemon( // Check if the Pokemon has max stacks of that item already const modifier = modType.newModifier(pokemon); const existing = globalScene.findModifier( - m => + (m): m is PokemonHeldItemModifier => m instanceof PokemonHeldItemModifier && m.type.id === modType.id && m.pokemonId === pokemon.id && m.matchType(modifier), - ) as PokemonHeldItemModifier; + ) as PokemonHeldItemModifier | undefined; // At max stacks if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { @@ -518,7 +521,7 @@ export function trainerThrowPokeball( repeatDelay: 500, onUpdate: t => { if (shakeCount && shakeCount < 4) { - const value = t.getValue(); + const value = t.getValue() ?? 0; const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1; pokeball.setX(pbX + value * 4 * directionMultiplier); pokeball.setAngle(value * 27.5 * directionMultiplier); diff --git a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts index ebef47eac2d..718d97c0875 100644 --- a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts +++ b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts @@ -1,8 +1,8 @@ -import type { PlayerPokemon } from "#app/field/pokemon"; -import { getFrameMs } from "#app/utils/common"; -import { cos, sin } from "#app/field/anims"; -import { getTypeRgb } from "#app/data/type"; import { globalScene } from "#app/global-scene"; +import { getTypeRgb } from "#data/type"; +import { cos, sin } from "#field/anims"; +import type { PlayerPokemon } from "#field/pokemon"; +import { getFrameMs } from "#utils/common"; export enum TransformationScreenPosition { CENTER, @@ -127,7 +127,7 @@ export function doPokemonTransformationSequence( to: 1, duration: 1000, onUpdate: t => { - pokemonTintSprite.setAlpha(t.getValue()); + pokemonTintSprite.setAlpha(t.getValue() ?? 1); }, onComplete: () => { pokemonSprite.setVisible(false); diff --git a/src/data/nature.ts b/src/data/nature.ts index 83b3ee7538d..4f4e627daf3 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -1,9 +1,9 @@ -import { toReadableString } from "#app/utils/common"; -import { TextStyle, getBBCodeFrag } from "../ui/text"; import { Nature } from "#enums/nature"; +import { EFFECTIVE_STATS, getShortenedStatKey, Stat } from "#enums/stat"; import { UiTheme } from "#enums/ui-theme"; +import { getBBCodeFrag, TextStyle } from "#ui/text"; +import { toReadableString } from "#utils/common"; import i18next from "i18next"; -import { Stat, EFFECTIVE_STATS, getShortenedStatKey } from "#enums/stat"; export function getNatureName( nature: Nature, diff --git a/src/data/phase-priority-queue.ts b/src/data/phase-priority-queue.ts index b815a6ac34f..88361b0f4fa 100644 --- a/src/data/phase-priority-queue.ts +++ b/src/data/phase-priority-queue.ts @@ -1,12 +1,12 @@ import { globalScene } from "#app/global-scene"; import type { Phase } from "#app/phase"; -import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase"; -import type { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { PostSummonActivateAbilityPhase } from "#app/phases/post-summon-activate-ability-phase"; -import { Stat } from "#enums/stat"; -import { BooleanHolder } from "#app/utils/common"; -import { TrickRoomTag } from "#app/data/arena-tag"; +import { TrickRoomTag } from "#data/arena-tag"; import { DynamicPhaseType } from "#enums/dynamic-phase-type"; +import { Stat } from "#enums/stat"; +import { ActivatePriorityQueuePhase } from "#phases/activate-priority-queue-phase"; +import { PostSummonActivateAbilityPhase } from "#phases/post-summon-activate-ability-phase"; +import type { PostSummonPhase } from "#phases/post-summon-phase"; +import { BooleanHolder } from "#utils/common"; /** * Stores a list of {@linkcode Phase}s diff --git a/src/data/pokeball.ts b/src/data/pokeball.ts index a479fd8068a..50ea5076aee 100644 --- a/src/data/pokeball.ts +++ b/src/data/pokeball.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import { NumberHolder } from "#app/utils/common"; import { PokeballType } from "#enums/pokeball"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; export const MAX_PER_TYPE_POKEBALLS: number = 99; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index ea129454034..6d1db296e55 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -1,14 +1,5 @@ -import type Pokemon from "../field/pokemon"; -import { allMoves } from "./data-lists"; -import { MoveCategory } from "#enums/MoveCategory"; -import type { Constructor, nil } from "#app/utils/common"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { WeatherType } from "#enums/weather-type"; -import { SpeciesFormKey } from "#enums/species-form-key"; import { globalScene } from "#app/global-scene"; -import { FormChangeItem } from "#enums/form-change-item"; +import { allMoves } from "#data/data-lists"; import { MeloettaFormChangePostMoveTrigger, SpeciesDefaultFormMatchTrigger, @@ -24,7 +15,16 @@ import { SpeciesFormChangeTeraTrigger, type SpeciesFormChangeTrigger, SpeciesFormChangeWeatherTrigger, -} from "./pokemon-forms/form-change-triggers"; +} from "#data/form-change-triggers"; +import { AbilityId } from "#enums/ability-id"; +import { FormChangeItem } from "#enums/form-change-item"; +import { MoveCategory } from "#enums/MoveCategory"; +import { MoveId } from "#enums/move-id"; +import { SpeciesFormKey } from "#enums/species-form-key"; +import { SpeciesId } from "#enums/species-id"; +import { WeatherType } from "#enums/weather-type"; +import type { Pokemon } from "#field/pokemon"; +import type { Constructor, nil } from "#utils/common"; export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void; diff --git a/src/data/pokemon-forms/form-change-triggers.ts b/src/data/pokemon-forms/form-change-triggers.ts index 3726781d9e3..75734bf085b 100644 --- a/src/data/pokemon-forms/form-change-triggers.ts +++ b/src/data/pokemon-forms/form-change-triggers.ts @@ -1,18 +1,18 @@ -import i18next from "i18next"; -import { coerceArray, type Constructor } from "#app/utils/common"; -import type { TimeOfDay } from "#enums/time-of-day"; -import type Pokemon from "#app/field/pokemon"; -import type { SpeciesFormChange } from "#app/data/pokemon-forms"; -import type { PokemonFormChangeItemModifier } from "#app/modifier/modifier"; -import { getPokemonNameWithAffix } from "#app/messages"; import { globalScene } from "#app/global-scene"; -import { FormChangeItem } from "#enums/form-change-item"; +import { getPokemonNameWithAffix } from "#app/messages"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; import { AbilityId } from "#enums/ability-id"; import { Challenges } from "#enums/challenges"; +import { FormChangeItem } from "#enums/form-change-item"; import { MoveId } from "#enums/move-id"; import { SpeciesFormKey } from "#enums/species-form-key"; import { StatusEffect } from "#enums/status-effect"; +import type { TimeOfDay } from "#enums/time-of-day"; import { WeatherType } from "#enums/weather-type"; +import type { Pokemon } from "#field/pokemon"; +import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; +import { type Constructor, coerceArray } from "#utils/common"; +import i18next from "i18next"; export abstract class SpeciesFormChangeTrigger { public description = ""; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 39e8fbd18e6..140b03e6d4c 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -1,49 +1,45 @@ -import type { Localizable } from "#app/@types/locales"; -import { AbilityId } from "#enums/ability-id"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { SpeciesId } from "#enums/species-id"; -import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; -import i18next from "i18next"; import type { AnySound } from "#app/battle-scene"; -import { globalScene } from "#app/global-scene"; import type { GameMode } from "#app/game-mode"; -import type { StarterMoveset } from "#app/system/game-data"; -import { DexAttr } from "#enums/dex-attr"; -import { - isNullOrUndefined, - capitalizeString, - randSeedInt, - randSeedGauss, - randSeedItem, - randSeedFloat, -} from "#app/utils/common"; -import { uncatchableSpecies } from "#app/data/balance/biomes"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { GrowthRate } from "#app/data/exp"; -import type { EvolutionLevel } from "#app/data/balance/pokemon-evolutions"; -import { - SpeciesWildEvolutionDelay, - pokemonEvolutions, - pokemonPrevolutions, -} from "#app/data/balance/pokemon-evolutions"; -import { PokemonType } from "#enums/pokemon-type"; -import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; +import { globalScene } from "#app/global-scene"; +import { uncatchableSpecies } from "#balance/biomes"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { starterPassiveAbilities } from "#balance/passives"; +import type { EvolutionLevel } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions, pokemonPrevolutions, SpeciesWildEvolutionDelay } from "#balance/pokemon-evolutions"; +import type { LevelMoves } from "#balance/pokemon-level-moves"; import { pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves, -} from "#app/data/balance/pokemon-level-moves"; -import type { Stat } from "#enums/stat"; -import type { Variant, VariantSet } from "#app/sprites/variant"; -import { populateVariantColorCache, variantColorCache, variantData } from "#app/sprites/variant"; -import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; +} from "#balance/pokemon-level-moves"; +import { POKERUS_STARTER_COUNT, speciesStarterCosts } from "#balance/starters"; +import { allSpecies } from "#data/data-lists"; +import { GrowthRate } from "#data/exp"; +import { Gender } from "#data/gender"; +import { AbilityId } from "#enums/ability-id"; +import { DexAttr } from "#enums/dex-attr"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesFormKey } from "#enums/species-form-key"; -import { starterPassiveAbilities } from "#app/data/balance/passives"; -import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite"; -import { hasExpSprite } from "#app/sprites/sprite-utils"; -import { Gender } from "./gender"; -import { allSpecies } from "#app/data/data-lists"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; +import { SpeciesId } from "#enums/species-id"; +import type { Stat } from "#enums/stat"; +import { loadPokemonVariantAssets } from "#sprites/pokemon-sprite"; +import { hasExpSprite } from "#sprites/sprite-utils"; +import type { Variant, VariantSet } from "#sprites/variant"; +import { populateVariantColorCache, variantColorCache, variantData } from "#sprites/variant"; +import type { StarterMoveset } from "#system/game-data"; +import type { Localizable } from "#types/locales"; +import { + capitalizeString, + isNullOrUndefined, + randSeedFloat, + randSeedGauss, + randSeedInt, + randSeedItem, +} from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { argbFromRgba, QuantizerCelebi, rgbaFromArgb } from "@material/material-color-utilities"; +import i18next from "i18next"; export enum Region { NORMAL, @@ -96,8 +92,8 @@ export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): Po } export function getFusedSpeciesName(speciesAName: string, speciesBName: string): string { - const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\']+)(.*?)$/i; - const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\'])(.*?)$/i; + const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-']+)(.*?)$/i; + const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-'])(.*?)$/i; const [speciesAPrefixMatch, speciesBPrefixMatch] = [speciesAName, speciesBName].map(n => /^(?:[^ ]+) /.exec(n)); const [speciesAPrefix, speciesBPrefix] = [speciesAPrefixMatch, speciesBPrefixMatch].map(m => (m ? m[0] : "")); @@ -134,7 +130,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string): if (fragBMatch) { const lastCharA = fragA.slice(fragA.length - 1); const prevCharB = fragBMatch[1].slice(fragBMatch.length - 1); - fragB = (/[\-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB; + fragB = (/[-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB; if (lastCharA === fragB[0]) { if (/[aiu]/.test(lastCharA)) { fragB = fragB.slice(1); @@ -379,7 +375,7 @@ export abstract class PokemonSpeciesForm { } getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string { - const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/\_{2}/g, "/"); + const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } @@ -478,8 +474,8 @@ export abstract class PokemonSpeciesForm { case SpeciesId.DUDUNSPARCE: break; case SpeciesId.ZACIAN: + // biome-ignore lint/suspicious/noFallthroughSwitchClause: Intentionally falls through case SpeciesId.ZAMAZENTA: - // biome-ignore lint/suspicious/noFallthroughSwitchClause: Falls through if (formSpriteKey.startsWith("behemoth")) { formSpriteKey = "crowned"; } @@ -569,7 +565,7 @@ export abstract class PokemonSpeciesForm { const rootSpeciesId = this.getRootSpeciesId(); for (const moveId of moveset) { if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) { - const eggMoveIndex = speciesEggMoves[rootSpeciesId].findIndex(m => m === moveId); + const eggMoveIndex = speciesEggMoves[rootSpeciesId].indexOf(moveId); if (eggMoveIndex > -1 && eggMoves & (1 << eggMoveIndex)) { continue; } @@ -759,7 +755,7 @@ export abstract class PokemonSpeciesForm { } } -export default class PokemonSpecies extends PokemonSpeciesForm implements Localizable { +export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { public name: string; readonly subLegendary: boolean; readonly legendary: boolean; diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts new file mode 100644 index 00000000000..6ae86bed5e7 --- /dev/null +++ b/src/data/pokemon/pokemon-data.ts @@ -0,0 +1,208 @@ +import { type BattlerTag, loadBattlerTag } from "#data/battler-tags"; +import type { Gender } from "#data/gender"; +import { PokemonMove } from "#data/moves/pokemon-move"; +import type { PokemonSpeciesForm } from "#data/pokemon-species"; +import type { TypeDamageMultiplier } from "#data/type"; +import type { AbilityId } from "#enums/ability-id"; +import type { BerryType } from "#enums/berry-type"; +import type { MoveId } from "#enums/move-id"; +import type { Nature } from "#enums/nature"; +import type { PokemonType } from "#enums/pokemon-type"; +import type { AttackMoveResult } from "#types/attack-move-result"; +import type { IllusionData } from "#types/illusion-data"; +import type { TurnMove } from "#types/turn-move"; +import { isNullOrUndefined } from "#utils/common"; + +/** + * Permanent data that can customize a Pokemon in non-standard ways from its Species. + * Includes abilities, nature, changed types, etc. + */ +export class CustomPokemonData { + // TODO: Change the default value for all these from -1 to something a bit more sensible + /** + * The scale at which to render this Pokemon's sprite. + */ + public spriteScale = -1; + public ability: AbilityId | -1; + public passive: AbilityId | -1; + public nature: Nature | -1; + public types: PokemonType[]; + /** Deprecated but needed for session save migration */ + // TODO: Remove this once pre-session migration is implemented + public hitsRecCount: number | null = null; + + constructor(data?: CustomPokemonData | Partial) { + this.spriteScale = data?.spriteScale ?? -1; + this.ability = data?.ability ?? -1; + this.passive = data?.passive ?? -1; + this.nature = data?.nature ?? -1; + this.types = data?.types ?? []; + this.hitsRecCount = data?.hitsRecCount ?? null; + } +} + +/** + * Persistent in-battle data for a {@linkcode Pokemon}. + * Resets on switch or new battle. + */ +export class PokemonSummonData { + /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ + public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; + /** + * A queue of moves yet to be executed, used by charging, recharging and frenzy moves. + * So long as this array is nonempty, this Pokemon's corresponding `CommandPhase` will be skipped over entirely + * in favor of using the queued move. + * TODO: Clean up a lot of the code surrounding the move queue. + */ + public moveQueue: TurnMove[] = []; + public tags: BattlerTag[] = []; + public abilitySuppressed = false; + + // Overrides for transform. + // TODO: Move these into a separate class & add rage fist hit count + public speciesForm: PokemonSpeciesForm | null = null; + public fusionSpeciesForm: PokemonSpeciesForm | null = null; + public ability: AbilityId | undefined; + public passiveAbility: AbilityId | undefined; + public gender: Gender | undefined; + public fusionGender: Gender | undefined; + public stats: number[] = [0, 0, 0, 0, 0, 0]; + public moveset: PokemonMove[] | null; + + // If not initialized this value will not be populated from save data. + public types: PokemonType[] = []; + public addedType: PokemonType | null = null; + + /** Data pertaining to this pokemon's illusion. */ + public illusion: IllusionData | null = null; + public illusionBroken = false; + + /** Array containing all berries eaten in the last turn; used by {@linkcode AbilityId.CUD_CHEW} */ + public berriesEatenLast: BerryType[] = []; + + /** + * An array of all moves this pokemon has used since entering the battle. + * Used for most moves and abilities that check prior move usage or copy already-used moves. + */ + public moveHistory: TurnMove[] = []; + + constructor(source?: PokemonSummonData | Partial) { + if (isNullOrUndefined(source)) { + return; + } + + // TODO: Rework this into an actual generic function for use elsewhere + for (const [key, value] of Object.entries(source)) { + if (isNullOrUndefined(value) && this.hasOwnProperty(key)) { + continue; + } + + if (key === "moveset") { + this.moveset = value?.map((m: any) => PokemonMove.loadMove(m)); + continue; + } + + if (key === "tags") { + // load battler tags + this.tags = value.map((t: BattlerTag) => loadBattlerTag(t)); + continue; + } + this[key] = value; + } + } +} + +// TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added +export class PokemonTempSummonData { + /** + * The number of turns this pokemon has spent without switching out. + * Only currently used for positioning the battle cursor. + */ + turnCount = 1; + + /** + * The number of turns this pokemon has spent in the active position since the start of the wave + * without switching out. + * Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file. + + * Used to evaluate "first turn only" conditions such as + * {@linkcode MoveId.FAKE_OUT | Fake Out} and {@linkcode MoveId.FIRST_IMPRESSION | First Impression}). + */ + waveTurnCount = 1; +} + +/** + * Persistent data for a {@linkcode Pokemon}. + * Resets at the start of a new battle (but not on switch). + */ +export class PokemonBattleData { + /** Counter tracking direct hits this Pokemon has received during this battle; used for {@linkcode MoveId.RAGE_FIST} */ + public hitCount = 0; + /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode MoveId.BELCH} */ + public hasEatenBerry = false; + /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode AbilityId.HARVEST} */ + public berriesEaten: BerryType[] = []; + + constructor(source?: PokemonBattleData | Partial) { + if (!isNullOrUndefined(source)) { + this.hitCount = source.hitCount ?? 0; + this.hasEatenBerry = source.hasEatenBerry ?? false; + this.berriesEaten = source.berriesEaten ?? []; + } + } +} + +/** + * Temporary data for a {@linkcode Pokemon}. + * Resets on new wave/battle start (but not on switch). + */ +export class PokemonWaveData { + /** Whether the pokemon has endured due to a {@linkcode BattlerTagType.ENDURE_TOKEN} */ + public endured = false; + /** + * A set of all the abilities this {@linkcode Pokemon} has used in this wave. + * Used to track once per battle conditions, as well as (hopefully) by the updated AI for move effectiveness. + */ + public abilitiesApplied: Set = new Set(); + /** Whether the pokemon's ability has been revealed or not */ + public abilityRevealed = false; +} + +/** + * Temporary data for a {@linkcode Pokemon}. + * Resets at the start of a new turn, as well as on switch. + */ +export class PokemonTurnData { + public acted = false; + /** How many times the current move should hit the target(s) */ + public hitCount = 0; + /** + * - `-1` = Calculate how many hits are left + * - `0` = Move is finished + */ + public hitsLeft = -1; + public totalDamageDealt = 0; + public singleHitDamageDealt = 0; + public damageTaken = 0; + public attacksReceived: AttackMoveResult[] = []; + public order: number; + public statStagesIncreased = false; + public statStagesDecreased = false; + public moveEffectiveness: TypeDamageMultiplier | null = null; + public combiningPledge?: MoveId; + public switchedInThisTurn = false; + public failedRunAway = false; + public joinedRound = false; + /** + * The amount of times this Pokemon has acted again and used a move in the current turn. + * Used to make sure multi-hits occur properly when the user is + * forced to act again in the same turn, and **must be incremented** by any effects that grant extra actions. + */ + public extraTurns = 0; + /** + * All berries eaten by this pokemon in this turn. + * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode AbilityId.CUD_CHEW} on turn end. + * @see {@linkcode PokemonSummonData.berriesEatenLast} + */ + public berriesEaten: BerryType[] = []; +} diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index a90304c9f7d..211e265ccf6 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -1,5 +1,5 @@ -import { randIntRange } from "#app/utils/common"; import { StatusEffect } from "#enums/status-effect"; +import { randIntRange } from "#utils/common"; import type { ParseKeys } from "i18next"; import i18next from "i18next"; diff --git a/src/data/terrain.ts b/src/data/terrain.ts index b3ee62ac2f9..f5382b1c3ec 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -1,7 +1,8 @@ -import type Pokemon from "../field/pokemon"; -import type Move from "./moves/move"; -import { PokemonType } from "#enums/pokemon-type"; +import { getPokemonNameWithAffix } from "#app/messages"; import type { BattlerIndex } from "#enums/battler-index"; +import { PokemonType } from "#enums/pokemon-type"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; import i18next from "i18next"; export enum TerrainType { @@ -12,6 +13,11 @@ export enum TerrainType { PSYCHIC, } +export interface SerializedTerrain { + terrainType: TerrainType; + turnsLeft: number; +} + export class Terrain { public terrainType: TerrainType; public turnsLeft: number; @@ -96,3 +102,76 @@ export function getTerrainColor(terrainType: TerrainType): [number, number, numb return [0, 0, 0]; } + +/** + * Return the message associated with a terrain effect starting. + * @param terrainType - The {@linkcode TerrainType} starting. + * @returns A string containing the appropriate terrain start text. + */ +export function getTerrainStartMessage(terrainType: TerrainType): string { + switch (terrainType) { + case TerrainType.MISTY: + return i18next.t("terrain:mistyStartMessage"); + case TerrainType.ELECTRIC: + return i18next.t("terrain:electricStartMessage"); + case TerrainType.GRASSY: + return i18next.t("terrain:grassyStartMessage"); + case TerrainType.PSYCHIC: + return i18next.t("terrain:psychicStartMessage"); + case TerrainType.NONE: + default: + terrainType satisfies TerrainType.NONE; + console.warn(`${terrainType} unexpectedly provided as terrain type to getTerrainStartMessage!`); + return ""; + } +} + +/** + * Return the message associated with a terrain effect ceasing to exist. + * @param terrainType - The {@linkcode TerrainType} being cleared. + * @returns A string containing the appropriate terrain clear text. + */ +export function getTerrainClearMessage(terrainType: TerrainType): string { + switch (terrainType) { + case TerrainType.MISTY: + return i18next.t("terrain:mistyClearMessage"); + case TerrainType.ELECTRIC: + return i18next.t("terrain:electricClearMessage"); + case TerrainType.GRASSY: + return i18next.t("terrain:grassyClearMessage"); + case TerrainType.PSYCHIC: + return i18next.t("terrain:psychicClearMessage"); + case TerrainType.NONE: + default: + terrainType satisfies TerrainType.NONE; + console.warn(`${terrainType} unexpectedly provided as terrain type to getTerrainClearMessage!`); + return ""; + } +} + +/** + * Return the message associated with a terrain-induced move/effect blockage. + * @param pokemon - The {@linkcode Pokemon} being protected. + * @param terrainType - The {@linkcode TerrainType} in question + * @returns A string containing the appropriate terrain block text. + */ +export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainType): string { + switch (terrainType) { + case TerrainType.MISTY: + return i18next.t("terrain:mistyBlockMessage", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }); + case TerrainType.ELECTRIC: + case TerrainType.GRASSY: + case TerrainType.PSYCHIC: + return i18next.t("terrain:defaultBlockMessage", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + terrainName: getTerrainName(terrainType), + }); + case TerrainType.NONE: + default: + terrainType satisfies TerrainType.NONE; + console.warn(`${terrainType} unexpectedly provided as terrain type to getTerrainBlockMessage!`); + return ""; + } +} diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index 8714dad0fc9..6b882d1ddc1 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -1,5 +1,5 @@ import { TrainerType } from "#enums/trainer-type"; -import { toReadableString } from "#app/utils/common"; +import { toReadableString } from "#utils/common"; class TrainerNameConfig { public urls: string[]; diff --git a/src/data/trainers/evil-admin-trainer-pools.ts b/src/data/trainers/evil-admin-trainer-pools.ts index 74ee3e8cb3d..ad08a17d29f 100644 --- a/src/data/trainers/evil-admin-trainer-pools.ts +++ b/src/data/trainers/evil-admin-trainer-pools.ts @@ -1,6 +1,6 @@ -import type { TrainerTierPools } from "#app/@types/trainer-funcs"; -import { TrainerPoolTier } from "#enums/trainer-pool-tier"; import { SpeciesId } from "#enums/species-id"; +import { TrainerPoolTier } from "#enums/trainer-pool-tier"; +import type { TrainerTierPools } from "#types/trainer-funcs"; /** Team Rocket's admin trainer pool. */ const ROCKET: TrainerTierPools = { diff --git a/src/data/trainers/fixed-battle-configs.ts b/src/data/trainers/fixed-battle-configs.ts new file mode 100644 index 00000000000..bb6d591654b --- /dev/null +++ b/src/data/trainers/fixed-battle-configs.ts @@ -0,0 +1,376 @@ +import { FixedBattleConfig, getRandomTrainerFunc } from "#app/battle"; +import { Trainer } from "#app/field/trainer"; +import { globalScene } from "#app/global-scene"; +import { randSeedInt } from "#app/utils/common"; +import { BattleType } from "#enums/battle-type"; +import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; +import { ModifierTier } from "#enums/modifier-tier"; +import { PlayerGender } from "#enums/player-gender"; +import { TrainerType } from "#enums/trainer-type"; +import { TrainerVariant } from "#enums/trainer-variant"; + +export interface FixedBattleConfigs { + [key: number]: FixedBattleConfig; +} +/** + * Youngster/Lass on 5 + * Rival on 8, 55, 95, 145, 195 + * Evil team grunts on 35, 62, 64, and 112 + * Evil team admin on 66 and 114 + * Evil leader on 115, 165 + * E4 on 182, 184, 186, 188 + * Champion on 190 + */ +export const classicFixedBattles: FixedBattleConfigs = { + [ClassicFixedBossWaves.TOWN_YOUNGSTER]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => new Trainer(TrainerType.YOUNGSTER, randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT), + ), + [ClassicFixedBossWaves.RIVAL_1]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ), + [ClassicFixedBossWaves.RIVAL_2]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL_2, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ) + .setCustomModifierRewards({ + guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], + allowLuckUpgrades: false, + }), + [ClassicFixedBossWaves.EVIL_GRUNT_1]: 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, + ), + ), + [ClassicFixedBossWaves.RIVAL_3]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL_3, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ) + .setCustomModifierRewards({ + guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], + allowLuckUpgrades: false, + }), + [ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) + .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, + ), + ), + [ClassicFixedBossWaves.EVIL_GRUNT_3]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) + .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, + ), + ), + [ClassicFixedBossWaves.EVIL_ADMIN_1]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_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.COLRESS], + [TrainerType.XEROSIC, TrainerType.BRYONY], + TrainerType.FABA, + TrainerType.PLUMERIA, + TrainerType.OLEANA, + [TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI], + ], + true, + ), + ), + [ClassicFixedBossWaves.RIVAL_4]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL_4, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ) + .setCustomModifierRewards({ + guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], + allowLuckUpgrades: false, + }), + [ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) + .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, + ), + ), + [ClassicFixedBossWaves.EVIL_ADMIN_2]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_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.COLRESS], + [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(ClassicFixedBossWaves.EVIL_GRUNT_1) + .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, + }), + [ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL_5, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ) + .setCustomModifierRewards({ + guaranteedModifierTiers: [ + ModifierTier.ROGUE, + ModifierTier.ROGUE, + ModifierTier.ROGUE, + ModifierTier.ULTRA, + ModifierTier.ULTRA, + ], + allowLuckUpgrades: false, + }), + [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) + .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, + }), + [ClassicFixedBossWaves.ELITE_FOUR_1]: 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, + ]), + ), + [ClassicFixedBossWaves.ELITE_FOUR_2]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) + .setGetTrainerFunc( + getRandomTrainerFunc([ + TrainerType.BRUNO, + TrainerType.KOGA, + TrainerType.PHOEBE, + TrainerType.BERTHA, + TrainerType.MARSHAL, + TrainerType.SIEBOLD, + TrainerType.OLIVIA, + TrainerType.NESSA_ELITE, + TrainerType.POPPY, + TrainerType.AMARYS, + ]), + ), + [ClassicFixedBossWaves.ELITE_FOUR_3]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) + .setGetTrainerFunc( + getRandomTrainerFunc([ + TrainerType.AGATHA, + TrainerType.BRUNO, + TrainerType.GLACIA, + TrainerType.FLINT, + TrainerType.GRIMSLEY, + TrainerType.WIKSTROM, + TrainerType.ACEROLA, + [TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE], + TrainerType.LARRY_ELITE, + TrainerType.LACEY, + ]), + ), + [ClassicFixedBossWaves.ELITE_FOUR_4]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) + .setGetTrainerFunc( + getRandomTrainerFunc([ + TrainerType.LANCE, + TrainerType.KAREN, + TrainerType.DRAKE, + TrainerType.LUCIAN, + TrainerType.CAITLIN, + TrainerType.DRASNA, + TrainerType.KAHILI, + TrainerType.RAIHAN_ELITE, + TrainerType.HASSEL, + TrainerType.DRAYTON, + ]), + ), + [ClassicFixedBossWaves.CHAMPION]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1) + .setGetTrainerFunc( + getRandomTrainerFunc([ + TrainerType.BLUE, + [TrainerType.RED, TrainerType.LANCE_CHAMPION], + [TrainerType.STEVEN, TrainerType.WALLACE], + TrainerType.CYNTHIA, + [TrainerType.ALDER, TrainerType.IRIS], + TrainerType.DIANTHA, + [TrainerType.KUKUI, TrainerType.HAU], + [TrainerType.LEON, TrainerType.MUSTARD], + [TrainerType.GEETA, TrainerType.NEMONA], + TrainerType.KIERAN, + ]), + ), + [ClassicFixedBossWaves.RIVAL_6]: new FixedBattleConfig() + .setBattleType(BattleType.TRAINER) + .setGetTrainerFunc( + () => + new Trainer( + TrainerType.RIVAL_6, + globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, + ), + ) + .setCustomModifierRewards({ + guaranteedModifierTiers: [ + ModifierTier.ROGUE, + ModifierTier.ROGUE, + ModifierTier.ULTRA, + ModifierTier.ULTRA, + ModifierTier.GREAT, + ModifierTier.GREAT, + ], + allowLuckUpgrades: false, + }), +}; diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 6786aa00ef7..4e88399bec3 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1,23 +1,28 @@ +import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "../data-lists"; -import { PokemonMove } from "../moves/pokemon-move"; -import { - toReadableString, - isNullOrUndefined, - randSeedItem, - randSeedInt, - coerceArray, - randSeedIntRange, -} from "#app/utils/common"; -import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { tmSpecies } from "#app/data/balance/tms"; -import { doubleBattleDialogue } from "../double-battle-dialogue"; +import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import { signatureSpecies } from "#balance/signature-species"; +import { tmSpecies } from "#balance/tms"; +import { modifierTypes } from "#data/data-lists"; +import { doubleBattleDialogue } from "#data/double-battle-dialogue"; +import { Gender } from "#data/gender"; +import type { PokemonSpecies, PokemonSpeciesFilter } from "#data/pokemon-species"; +import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import { PokeballType } from "#enums/pokeball"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TeraAIMode } from "#enums/tera-ai-mode"; +import { TrainerPoolTier } from "#enums/trainer-pool-tier"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { TrainerType } from "#enums/trainer-type"; import { TrainerVariant } from "#enums/trainer-variant"; -import { getIsInitialized, initI18n } from "#app/plugins/i18n"; -import i18next from "i18next"; -import { Gender } from "#app/data/gender"; -import { signatureSpecies } from "../balance/signature-species"; +import type { EnemyPokemon } from "#field/pokemon"; +import { PokemonMove } from "#moves/pokemon-move"; +import { getIsInitialized, initI18n } from "#plugins/i18n"; +import type { EvilTeam } from "#trainers/evil-admin-trainer-pools"; +import { evilAdminTrainerPools } from "#trainers/evil-admin-trainer-pools"; import { getEvilGruntPartyTemplate, getGymLeaderPartyTemplate, @@ -25,37 +30,27 @@ import { TrainerPartyCompoundTemplate, TrainerPartyTemplate, trainerPartyTemplates, -} from "./TrainerPartyTemplate"; -import { evilAdminTrainerPools } from "./evil-admin-trainer-pools"; - -// Enum imports -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { SpeciesId } from "#enums/species-id"; -import { PokeballType } from "#enums/pokeball"; -import { PokemonType } from "#enums/pokemon-type"; -import { MoveId } from "#enums/move-id"; -import { AbilityId } from "#enums/ability-id"; -import { TeraAIMode } from "#enums/tera-ai-mode"; -import { TrainerPoolTier } from "#enums/trainer-pool-tier"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { TrainerType } from "#enums/trainer-type"; -import { timedEventManager } from "#app/global-event-manager"; - -// Type imports -import type { PokemonSpeciesFilter } from "#app/data/pokemon-species"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import type { ModifierTypeFunc } from "#app/@types/modifier-types"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import type { EvilTeam } from "./evil-admin-trainer-pools"; +} from "#trainers/trainer-party-template"; +import type { ModifierTypeFunc } from "#types/modifier-types"; import type { - PartyMemberFunc, - GenModifiersFunc, GenAIFunc, - PartyTemplateFunc, - TrainerTierPools, - TrainerConfigs, + GenModifiersFunc, + PartyMemberFunc, PartyMemberFuncs, -} from "../../@types/trainer-funcs"; + PartyTemplateFunc, + TrainerConfigs, + TrainerTierPools, +} from "#types/trainer-funcs"; +import { + coerceArray, + isNullOrUndefined, + randSeedInt, + randSeedIntRange, + randSeedItem, + toReadableString, +} from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; /** Minimum BST for Pokemon generated onto the Elite Four's teams */ const ELITE_FOUR_MINIMUM_BST = 460; @@ -290,7 +285,7 @@ export class TrainerConfig { * @param {string} [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned. * @param {TrainerType | string} [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string. * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { // If the female name is 'Ivy' (the rival), assign a localized name. if (nameFemale === "Ivy") { @@ -335,7 +330,7 @@ export class TrainerConfig { if (doubleEncounterBgm) { this.doubleEncounterBgm = typeof doubleEncounterBgm === "number" - ? TrainerType[doubleEncounterBgm].toString().replace(/\_/g, " ").toLowerCase() + ? TrainerType[doubleEncounterBgm].toString().replace(/_/g, " ").toLowerCase() : doubleEncounterBgm; } return this; @@ -540,7 +535,7 @@ export class TrainerConfig { * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader. * @param specialtyType The specialty Type of the admin, if they have one * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForEvilTeamAdmin( title: string, poolName: EvilTeam, @@ -581,7 +576,7 @@ export class TrainerConfig { * Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter. * @param _isMale Whether the stat trainer is Male or Female (for localization of the title). * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ initForStatTrainer(_isMale = false): TrainerConfig { if (!getIsInitialized()) { initI18n(); @@ -608,7 +603,7 @@ export class TrainerConfig { * @param {PokemonType} specialtyType The specialty type for the evil team Leader. * @param boolean Whether or not this is the rematch fight * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForEvilTeamLeader( title: string, signatureSpecies: (SpeciesId | SpeciesId[])[], @@ -651,7 +646,7 @@ export class TrainerConfig { * @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false. * @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size. * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForGymLeader( signatureSpecies: (SpeciesId | SpeciesId[])[], isMale: boolean, @@ -709,7 +704,7 @@ export class TrainerConfig { * @param specialtyType - The specialty type for the Elite Four member. * @param teraSlot - Optional, sets the party member in this slot to Terastallize. * @returns The updated TrainerConfig instance. - **/ + */ initForEliteFour( signatureSpecies: (SpeciesId | SpeciesId[])[], isMale: boolean, @@ -765,7 +760,7 @@ export class TrainerConfig { * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Champion. * @param isMale Whether the Champion is Male or Female (for localization of the title). * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ initForChampion(isMale: boolean): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { @@ -815,7 +810,7 @@ export class TrainerConfig { * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. * @returns {string} - The title of the trainer. - **/ + */ getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { const ret = this.name; diff --git a/src/data/trainers/TrainerPartyTemplate.ts b/src/data/trainers/trainer-party-template.ts similarity index 100% rename from src/data/trainers/TrainerPartyTemplate.ts rename to src/data/trainers/trainer-party-template.ts index 135fe669825..d4e7fe7a261 100644 --- a/src/data/trainers/TrainerPartyTemplate.ts +++ b/src/data/trainers/trainer-party-template.ts @@ -1,8 +1,8 @@ -import { startingWave } from "#app/starting-wave"; import { globalScene } from "#app/global-scene"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { GameModes } from "#enums/game-modes"; +import { startingWave } from "#app/starting-wave"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; +import { GameModes } from "#enums/game-modes"; +import { PartyMemberStrength } from "#enums/party-member-strength"; export class TrainerPartyTemplate { public size: number; diff --git a/src/data/weather.ts b/src/data/weather.ts index 425e15b12a8..59be56826a4 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -1,16 +1,20 @@ -import { BiomeId } from "#enums/biome-id"; -import { WeatherType } from "#enums/weather-type"; -import { getPokemonNameWithAffix } from "../messages"; -import type Pokemon from "../field/pokemon"; -import { PokemonType } from "#enums/pokemon-type"; -import type Move from "./moves/move"; -import { randSeedInt } from "#app/utils/common"; -import { TerrainType, getTerrainName } from "./terrain"; -import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; -import type { Arena } from "#app/field/arena"; +import type { SuppressWeatherEffectAbAttr } from "#abilities/ability"; import { timedEventManager } from "#app/global-event-manager"; -import type { SuppressWeatherEffectAbAttr } from "./abilities/ability"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { BiomeId } from "#enums/biome-id"; +import { PokemonType } from "#enums/pokemon-type"; +import { WeatherType } from "#enums/weather-type"; +import type { Arena } from "#field/arena"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { randSeedInt } from "#utils/common"; +import i18next from "i18next"; + +export interface SerializedWeather { + weatherType: WeatherType; + turnsLeft: number; +} export class Weather { public weatherType: WeatherType; @@ -235,50 +239,6 @@ export function getWeatherBlockMessage(weatherType: WeatherType): string { return i18next.t("weather:defaultEffectMessage"); } -export function getTerrainStartMessage(terrainType: TerrainType): string | null { - switch (terrainType) { - case TerrainType.MISTY: - return i18next.t("terrain:mistyStartMessage"); - case TerrainType.ELECTRIC: - return i18next.t("terrain:electricStartMessage"); - case TerrainType.GRASSY: - return i18next.t("terrain:grassyStartMessage"); - case TerrainType.PSYCHIC: - return i18next.t("terrain:psychicStartMessage"); - default: - console.warn("getTerrainStartMessage not defined. Using default null"); - return null; - } -} - -export function getTerrainClearMessage(terrainType: TerrainType): string | null { - switch (terrainType) { - case TerrainType.MISTY: - return i18next.t("terrain:mistyClearMessage"); - case TerrainType.ELECTRIC: - return i18next.t("terrain:electricClearMessage"); - case TerrainType.GRASSY: - return i18next.t("terrain:grassyClearMessage"); - case TerrainType.PSYCHIC: - return i18next.t("terrain:psychicClearMessage"); - default: - console.warn("getTerrainClearMessage not defined. Using default null"); - return null; - } -} - -export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainType): string { - if (terrainType === TerrainType.MISTY) { - return i18next.t("terrain:mistyBlockMessage", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }); - } - return i18next.t("terrain:defaultBlockMessage", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - terrainName: getTerrainName(terrainType), - }); -} - export interface WeatherPoolEntry { weatherType: WeatherType; weight: number; diff --git a/src/enums/MoveFlags.ts b/src/enums/MoveFlags.ts index 1155417da6d..06de265df09 100644 --- a/src/enums/MoveFlags.ts +++ b/src/enums/MoveFlags.ts @@ -1,3 +1,7 @@ +/** + * A list of possible flags that various moves may have. + * Represented internally as a bitmask. + */ export enum MoveFlags { NONE = 0, MAKES_CONTACT = 1 << 0, diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index 4180aa00ef5..214826993b3 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -1,3 +1,13 @@ +import type { ArenaTagTypeMap } from "#data/arena-tag"; +import type { NonSerializableArenaTagType, SerializableArenaTagType } from "#types/arena-tags"; + +/** + * Enum representing all different types of {@linkcode ArenaTag}s. + * @privateRemarks + * ⚠️ When modifying the fields in this enum, ensure that: + * - The entry is added to / removed from {@linkcode ArenaTagTypeMap} + * - The tag is added to / removed from {@linkcode NonSerializableArenaTagType} or {@linkcode SerializableArenaTagType} +*/ export enum ArenaTagType { NONE = "NONE", MUD_SPORT = "MUD_SPORT", diff --git a/src/enums/battle-style.ts b/src/enums/battle-style.ts index 3f3e7f6d8f6..2fba10f1bf9 100644 --- a/src/enums/battle-style.ts +++ b/src/enums/battle-style.ts @@ -1,9 +1,7 @@ -/** - * Determines the selected battle style. - * - 'Switch' - The option to switch the active pokemon at the start of a battle will be displayed. - * - 'Set' - The option to switch the active pokemon at the start of a battle will not display. -*/ +/** Enum for selected battle style. */ export enum BattleStyle { - SWITCH, - SET + /** Display option to switch active pokemon at battle start. */ + SWITCH, + /** Hide option to switch active pokemon at battle start. */ + SET } diff --git a/src/enums/battler-index.ts b/src/enums/battler-index.ts index 32b1684c86c..253e5bfc3ed 100644 --- a/src/enums/battler-index.ts +++ b/src/enums/battler-index.ts @@ -1,3 +1,7 @@ +/** + * The index of a given Pokemon on-field. + * Used as an index into `globalScene.getField`, as well as for most target-specifying effects. + */ export enum BattlerIndex { ATTACKER = -1, PLAYER, diff --git a/src/enums/exp-gains-speed.ts b/src/enums/exp-gains-speed.ts index 964c4f99c70..f5f36a1c78d 100644 --- a/src/enums/exp-gains-speed.ts +++ b/src/enums/exp-gains-speed.ts @@ -1,15 +1,4 @@ -/** - * Defines the speed of gaining experience. - * - * @remarks - * The `expGainSpeed` can have several modes: - * - `0` - Default: The normal speed. - * - `1` - Fast: Fast speed. - * - `2` - Faster: Faster speed. - * - `3` - Skip: Skip gaining exp animation. - * - * @default 0 - Uses the default normal speed. - */ +/** Defines the speed of gaining experience. */ export enum ExpGainsSpeed { /** The normal speed. */ DEFAULT, diff --git a/src/enums/exp-notification.ts b/src/enums/exp-notification.ts index b7f50814d3a..a64d8591e76 100644 --- a/src/enums/exp-notification.ts +++ b/src/enums/exp-notification.ts @@ -1,11 +1,9 @@ -/** - * Determines exp notification style. - * - Default - the normal exp gain display, nothing changed - * - Only level up - we display the level up in the small frame instead of a message - * - Skip - no level up frame nor message -*/ +/** Enum for party experience gain notification style. */ export enum ExpNotification { - DEFAULT, - ONLY_LEVEL_UP, - SKIP + /** Display amount flyout for all off-field party members upon gaining any amount of EXP. */ + DEFAULT, + /** Display smaller flyout showing level gained on gaining a new level. */ + ONLY_LEVEL_UP, + /** Do not show any flyouts for EXP gains or levelups. */ + SKIP } diff --git a/src/enums/gacha-types.ts b/src/enums/gacha-types.ts index c8beff5cad2..cd0bc67eae0 100644 --- a/src/enums/gacha-types.ts +++ b/src/enums/gacha-types.ts @@ -1,5 +1,7 @@ -export enum GachaType { - MOVE, - LEGENDARY, - SHINY -} +export const GachaType = Object.freeze({ + MOVE: 0, + LEGENDARY: 1, + SHINY: 2 +}); + +export type GachaType = typeof GachaType[keyof typeof GachaType]; diff --git a/src/enums/move-use-mode.ts b/src/enums/move-use-mode.ts index 31694ad4081..1bb9d6374b7 100644 --- a/src/enums/move-use-mode.ts +++ b/src/enums/move-use-mode.ts @@ -1,4 +1,4 @@ -import type { PostDancingMoveAbAttr } from "#app/data/abilities/ability"; +import type { PostDancingMoveAbAttr } from "#abilities/ability"; import type { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; /** diff --git a/src/events/arena.ts b/src/events/arena.ts index 3b65506db98..5415b8eb026 100644 --- a/src/events/arena.ts +++ b/src/events/arena.ts @@ -1,6 +1,6 @@ +import type { TerrainType } from "#data/terrain"; import type { ArenaTagSide } from "#enums/arena-tag-side"; import type { ArenaTagType } from "#enums/arena-tag-type"; -import type { TerrainType } from "#app/data/terrain"; import type { WeatherType } from "#enums/weather-type"; /** Alias for all {@linkcode ArenaEvent} type strings */ diff --git a/src/events/battle-scene.ts b/src/events/battle-scene.ts index 83d260bd7d2..29aee1053cd 100644 --- a/src/events/battle-scene.ts +++ b/src/events/battle-scene.ts @@ -1,5 +1,5 @@ -import type Move from "../data/moves/move"; -import type { BerryModifier } from "../modifier/modifier"; +import type { BerryModifier } from "#modifiers/modifier"; +import type { Move } from "#moves/move"; /** Alias for all {@linkcode BattleScene} events */ export enum BattleSceneEventType { diff --git a/src/field/anims.ts b/src/field/anims.ts index 2fd23e4262b..82aa7b7e894 100644 --- a/src/field/anims.ts +++ b/src/field/anims.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { PokeballType } from "#enums/pokeball"; -import type { Variant } from "#app/sprites/variant"; -import { getFrameMs, randGauss } from "#app/utils/common"; +import type { Variant } from "#sprites/variant"; +import { getFrameMs, randGauss } from "#utils/common"; export function addPokeballOpenParticles(x: number, y: number, pokeballType: PokeballType): void { switch (pokeballType) { @@ -150,7 +150,7 @@ function doFanOutParticle( } export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): void { - const addParticle = () => { + const addParticle = (): void => { const particle = globalScene.add.sprite(pokeball.x, pokeball.y, "pb_particles", "4.png"); particle.setOrigin(pokeball.originX, pokeball.originY); particle.setAlpha(0.5); @@ -188,7 +188,9 @@ export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): vo }); }; - new Array(3).fill(null).map(() => addParticle()); + for (let i = 0; i < 3; i++) { + addParticle(); + } } export function sin(index: number, amplitude: number): number { diff --git a/src/field/arena.ts b/src/field/arena.ts index 8d7e5037852..8f27ddb22e9 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -1,46 +1,38 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biomes"; -import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes"; -import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils/common"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; +import Overrides from "#app/overrides"; +import type { BiomeTierTrainerPools, PokemonPools } from "#balance/biomes"; +import { BiomePoolTier, biomePokemonPools, biomeTrainerPools } from "#balance/biomes"; +import type { ArenaTag } from "#data/arena-tag"; +import { ArenaTrapTag, getArenaTag } from "#data/arena-tag"; +import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#data/form-change-triggers"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getTerrainClearMessage, getTerrainStartMessage, Terrain, TerrainType } from "#data/terrain"; import { - getTerrainClearMessage, - getTerrainStartMessage, + getLegendaryWeatherContinuesMessage, getWeatherClearMessage, getWeatherStartMessage, - getLegendaryWeatherContinuesMessage, Weather, -} from "#app/data/weather"; -import { CommonAnim } from "#enums/move-anims-common"; -import type { PokemonType } from "#enums/pokemon-type"; -import type Move from "#app/data/moves/move"; -import type { ArenaTag } from "#app/data/arena-tag"; -import { ArenaTrapTag, getArenaTag } from "#app/data/arena-tag"; +} from "#data/weather"; +import { AbilityId } from "#enums/ability-id"; import { ArenaTagSide } from "#enums/arena-tag-side"; -import type { BattlerIndex } from "#enums/battler-index"; -import { Terrain, TerrainType } from "#app/data/terrain"; -import { - applyAbAttrs, - applyPostTerrainChangeAbAttrs, - applyPostWeatherChangeAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import type Pokemon from "#app/field/pokemon"; -import Overrides from "#app/overrides"; -import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import type { ArenaTagType } from "#enums/arena-tag-type"; +import type { BattlerIndex } from "#enums/battler-index"; import { BiomeId } from "#enums/biome-id"; +import { CommonAnim } from "#enums/move-anims-common"; import type { MoveId } from "#enums/move-id"; +import type { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { TimeOfDay } from "#enums/time-of-day"; import { TrainerType } from "#enums/trainer-type"; -import { AbilityId } from "#enums/ability-id"; -import { - SpeciesFormChangeRevertWeatherFormTrigger, - SpeciesFormChangeWeatherTrigger, -} from "#app/data/pokemon-forms/form-change-triggers"; import { WeatherType } from "#enums/weather-type"; -import { FieldEffectModifier } from "#app/modifier/modifier"; +import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena"; +import type { Pokemon } from "#field/pokemon"; +import { FieldEffectModifier } from "#modifiers/modifier"; +import type { Move } from "#moves/move"; +import type { AbstractConstructor } from "#types/type-helpers"; +import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; export class Arena { public biomeType: BiomeId; @@ -152,6 +144,7 @@ export class Arena { if (!tierPool.length) { ret = globalScene.randomSpecies(waveIndex, level); } else { + // TODO: should this use `randSeedItem`? const entry = tierPool[randSeedInt(tierPool.length)]; let species: SpeciesId; if (typeof entry === "number") { @@ -163,6 +156,7 @@ export class Arena { if (level >= levelThreshold) { const speciesIds = entry[levelThreshold]; if (speciesIds.length > 1) { + // TODO: should this use `randSeedItem`? species = speciesIds[randSeedInt(speciesIds.length)]; } else { species = speciesIds[0]; @@ -175,19 +169,11 @@ export class Arena { ret = getPokemonSpecies(species!); if (ret.subLegendary || ret.legendary || ret.mythical) { - switch (true) { - case ret.baseTotal >= 720: - regen = level < 90; - break; - case ret.baseTotal >= 670: - regen = level < 70; - break; - case ret.baseTotal >= 580: - regen = level < 50; - break; - default: - regen = level < 30; - break; + const waveDifficulty = globalScene.gameMode.getWaveForDifficulty(waveIndex); + if (ret.baseTotal >= 660) { + regen = waveDifficulty < 80; // Wave 50+ in daily (however, max Daily wave is 50 currently so not possible) + } else { + regen = waveDifficulty < 55; // Wave 25+ in daily } } } @@ -372,7 +358,7 @@ export class Arena { pokemon.findAndRemoveTags( t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather), ); - applyPostWeatherChangeAbAttrs("PostWeatherChangeAbAttr", pokemon, weather); + applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather }); }); return true; @@ -449,9 +435,9 @@ export class Arena { CommonAnim.MISTY_TERRAIN + (terrain - 1), ); } - globalScene.phaseManager.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? + globalScene.phaseManager.queueMessage(getTerrainStartMessage(terrain)); } else { - globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? + globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType)); } globalScene @@ -461,8 +447,8 @@ export class Arena { pokemon.findAndRemoveTags( t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain), ); - applyPostTerrainChangeAbAttrs("PostTerrainChangeAbAttr", pokemon, terrain); - applyAbAttrs("TerrainEventTypeChangeAbAttr", pokemon, null, false); + applyAbAttrs("PostTerrainChangeAbAttr", { pokemon, terrain }); + applyAbAttrs("TerrainEventTypeChangeAbAttr", { pokemon }); }); return true; @@ -505,38 +491,37 @@ export class Arena { getTrainerChance(): number { switch (this.biomeType) { case BiomeId.METROPOLIS: - return 2; - case BiomeId.SLUM: - case BiomeId.BEACH: case BiomeId.DOJO: - case BiomeId.CONSTRUCTION_SITE: return 4; case BiomeId.PLAINS: case BiomeId.GRASS: + case BiomeId.BEACH: case BiomeId.LAKE: case BiomeId.CAVE: + case BiomeId.DESERT: + case BiomeId.CONSTRUCTION_SITE: + case BiomeId.SLUM: return 6; case BiomeId.TALL_GRASS: case BiomeId.FOREST: - case BiomeId.SEA: case BiomeId.SWAMP: case BiomeId.MOUNTAIN: case BiomeId.BADLANDS: - case BiomeId.DESERT: case BiomeId.MEADOW: case BiomeId.POWER_PLANT: - case BiomeId.GRAVEYARD: case BiomeId.FACTORY: case BiomeId.SNOWY_FOREST: return 8; + case BiomeId.SEA: case BiomeId.ICE_CAVE: case BiomeId.VOLCANO: + case BiomeId.GRAVEYARD: case BiomeId.RUINS: case BiomeId.WASTELAND: case BiomeId.JUNGLE: case BiomeId.FAIRY_CAVE: + case BiomeId.ISLAND: return 12; - case BiomeId.SEABED: case BiomeId.ABYSS: case BiomeId.SPACE: case BiomeId.TEMPLE: @@ -660,7 +645,7 @@ export class Arena { * @param args array of parameters that the called upon tags may need */ applyTagsForSide( - tagType: ArenaTagType | Constructor, + tagType: ArenaTagType | Constructor | AbstractConstructor, side: ArenaTagSide, simulated: boolean, ...args: unknown[] @@ -682,7 +667,11 @@ export class Arena { * @param simulated if `true`, this applies arena tags without changing game state * @param args array of parameters that the called upon tags may need */ - applyTags(tagType: ArenaTagType | Constructor, simulated: boolean, ...args: unknown[]): void { + applyTags( + tagType: ArenaTagType | Constructor | AbstractConstructor, + simulated: boolean, + ...args: unknown[] + ): void { this.applyTagsForSide(tagType, ArenaTagSide.BOTH, simulated, ...args); } @@ -739,7 +728,7 @@ export class Arena { * @param tagType The {@linkcode ArenaTagType} or {@linkcode ArenaTag} to get * @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there */ - getTag(tagType: ArenaTagType | Constructor): ArenaTag | undefined { + getTag(tagType: ArenaTagType | Constructor | AbstractConstructor): ArenaTag | undefined { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } @@ -755,7 +744,10 @@ export class Arena { * @param side The {@linkcode ArenaTagSide} to look at * @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there */ - getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { + getTagOnSide( + tagType: ArenaTagType | Constructor | AbstractConstructor, + side: ArenaTagSide, + ): ArenaTag | undefined { return typeof tagType === "string" ? this.tags.find( t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side), @@ -903,7 +895,7 @@ export class Arena { case BiomeId.WASTELAND: return 6.336; case BiomeId.ABYSS: - return 5.13; + return 20.113; case BiomeId.SPACE: return 20.036; case BiomeId.CONSTRUCTION_SITE: @@ -941,6 +933,7 @@ export function getBiomeKey(biome: BiomeId): string { export function getBiomeHasProps(biomeType: BiomeId): boolean { switch (biomeType) { + case BiomeId.PLAINS: case BiomeId.METROPOLIS: case BiomeId.BEACH: case BiomeId.LAKE: @@ -987,14 +980,15 @@ export class ArenaBase extends Phaser.GameObjects.Container { this.base = globalScene.addFieldSprite(0, 0, "plains_a", undefined, 1); this.base.setOrigin(0, 0); - this.props = !player - ? new Array(3).fill(null).map(() => { - const ret = globalScene.addFieldSprite(0, 0, "plains_b", undefined, 1); - ret.setOrigin(0, 0); - ret.setVisible(false); - return ret; - }) - : []; + this.props = []; + if (!player) { + for (let i = 0; i < 3; i++) { + const ret = globalScene.addFieldSprite(0, 0, "plains_b", undefined, 1); + ret.setOrigin(0, 0); + ret.setVisible(false); + this.props.push(ret); + } + } } setBiome(biome: BiomeId, propValue?: number): void { diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index b8b3ed76e18..acb279a17a0 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -1,14 +1,14 @@ -import { TextStyle, addTextObject } from "../ui/text"; -import type { DamageResult } from "./pokemon"; -import type Pokemon from "./pokemon"; -import { HitResult } from "#enums/hit-result"; -import { formatStat, fixedInt } from "#app/utils/common"; -import type { BattlerIndex } from "#enums/battler-index"; import { globalScene } from "#app/global-scene"; +import type { BattlerIndex } from "#enums/battler-index"; +import { HitResult } from "#enums/hit-result"; +import type { Pokemon } from "#field/pokemon"; +import type { DamageResult } from "#types/damage-result"; +import { addTextObject, TextStyle } from "#ui/text"; +import { fixedInt, formatStat } from "#utils/common"; type TextAndShadowArr = [string | null, string | null]; -export default class DamageNumberHandler { +export class DamageNumberHandler { private damageNumbers: Map; constructor() { diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index f6702c690bd..d3776f349e6 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -1,13 +1,14 @@ -import type { GameObjects } from "phaser"; import { globalScene } from "#app/global-scene"; -import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import type { SpeciesId } from "#enums/species-id"; -import { isNullOrUndefined } from "#app/utils/common"; -import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import type { Variant } from "#app/sprites/variant"; -import { doShinySparkleAnim } from "#app/field/anims"; -import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite"; -import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; +import { doShinySparkleAnim } from "#field/anims"; +import { getSpriteKeysFromSpecies } from "#mystery-encounters/encounter-pokemon-utils"; +import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { loadPokemonVariantAssets } from "#sprites/pokemon-sprite"; +import type { Variant } from "#sprites/variant"; +import { isNullOrUndefined } from "#utils/common"; +import type { GameObjects } from "phaser"; + +type PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; type KnownFileRoot = | "arenas" @@ -77,7 +78,7 @@ export class MysteryEncounterSpriteConfig { * These slide in with the field as part of standard field change cycle, and will typically be hidden after the player has selected an option for the encounter * Note: intro visuals are not "Trainers" or any other specific game object, though they may contain trainer sprites */ -export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container { +export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container { public encounter: MysteryEncounter; public spriteConfigs: MysteryEncounterSpriteConfig[]; public enterFromRight: boolean; diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index bda9414bb92..725229ce723 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; -import Pokemon from "./pokemon"; -import { fixedInt, coerceArray, randInt } from "#app/utils/common"; +import { Pokemon } from "#field/pokemon"; +import { coerceArray, fixedInt, randInt } from "#utils/common"; -export default class PokemonSpriteSparkleHandler { +export class PokemonSpriteSparkleHandler { private sprites: Set; setup(): void { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e9cc4f70d70..01f137b28fd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1,195 +1,179 @@ -import Phaser from "phaser"; -import type { AnySound } from "#app/battle-scene"; -import type BattleScene from "#app/battle-scene"; +import type { Ability, PreAttackModifyDamageAbAttrParams } from "#abilities/ability"; +import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#abilities/apply-ab-attrs"; +import type { AnySound, BattleScene } from "#app/battle-scene"; +import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; +import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import type { Variant } from "#app/sprites/variant"; -import { populateVariantColors, variantColorCache } from "#app/sprites/variant"; -import { variantData } from "#app/sprites/variant"; -import BattleInfo from "#app/ui/battle-info/battle-info"; -import { EnemyBattleInfo } from "#app/ui/battle-info/enemy-battle-info"; -import { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info"; -import type Move from "#app/data/moves/move"; -import { getMoveTargets } from "#app/data/moves/move-utils"; -import { applyMoveAttrs } from "#app/data/moves/apply-attrs"; -import { allMoves } from "#app/data/data-lists"; -import { MoveTarget } from "#enums/MoveTarget"; -import { MoveCategory } from "#enums/MoveCategory"; -import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; -import { default as PokemonSpecies, getFusedSpeciesName, getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; -import { - NumberHolder, - randSeedInt, - getIvsFromId, - BooleanHolder, - randSeedItem, - isNullOrUndefined, - getEnumValues, - toDmgValue, - fixedInt, - rgbaToInt, - rgbHexToRgba, - rgbToHsv, - deltaRgb, - isBetween, - randSeedFloat, - type Constructor, - randSeedIntRange, - coerceArray, -} from "#app/utils/common"; -import type { TypeDamageMultiplier } from "#app/data/type"; -import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; -import { PokemonType } from "#enums/pokemon-type"; -import { getLevelTotalExp } from "#app/data/exp"; -import { - Stat, - type PermanentStat, - type BattleStat, - type EffectiveStat, - PERMANENT_STATS, - BATTLE_STATS, - EFFECTIVE_STATS, -} from "#enums/stat"; -import { - EnemyDamageBoosterModifier, - EnemyDamageReducerModifier, - EnemyFusionChanceModifier, - HiddenAbilityRateBoosterModifier, - BaseStatModifier, - PokemonFriendshipBoosterModifier, - PokemonHeldItemModifier, - PokemonNatureWeightModifier, - ShinyRateBoosterModifier, - SurviveDamageModifier, - TempStatStageBoosterModifier, - TempCritBoosterModifier, - StatBoosterModifier, - CritBoosterModifier, - PokemonBaseStatFlatModifier, - PokemonBaseStatTotalModifier, - PokemonIncrementingStatModifier, - EvoTrackerModifier, - PokemonMultiHitModifier, -} from "#app/modifier/modifier"; -import { PokeballType } from "#enums/pokeball"; -import { Gender } from "#app/data/gender"; -import { Status, getRandomStatus } from "#app/data/status-effect"; -import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; +import { getPokemonNameWithAffix } from "#app/messages"; +import Overrides from "#app/overrides"; +import { speciesEggMoves } from "#balance/egg-moves"; +import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; import { + FusionSpeciesFormEvolution, pokemonEvolutions, pokemonPrevolutions, - FusionSpeciesFormEvolution, validateShedinjaEvo, -} from "#app/data/balance/pokemon-evolutions"; -import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms"; +} from "#balance/pokemon-evolutions"; +import type { LevelMoves } from "#balance/pokemon-level-moves"; +import { EVOLVE_MOVE, RELEARN_MOVE } from "#balance/pokemon-level-moves"; +import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE, SHINY_EPIC_CHANCE, SHINY_VARIANT_CHANCE } from "#balance/rates"; +import { getStarterValueFriendshipCap, speciesStarterCosts } from "#balance/starters"; +import { reverseCompatibleTms, tmPoolTiers, tmSpecies } from "#balance/tms"; +import type { SuppressAbilitiesTag } from "#data/arena-tag"; +import { NoCritTag, WeakenMoveScreenTag } from "#data/arena-tag"; import { - BattlerTag, - EncoreTag, - GroundedTag, - HighestStatBoostTag, - SubstituteTag, - TypeImmuneTag, - getBattlerTag, - SemiInvulnerableTag, - MoveRestrictionBattlerTag, - ExposedTag, - DragonCheerTag, - CritBoostTag, - TrappedTag, - TarShotTag, AutotomizedTag, - PowerTrickTag, - loadBattlerTag, + BattlerTag, + CritBoostTag, + DragonCheerTag, + EncoreTag, + ExposedTag, + GroundedTag, type GrudgeTag, -} from "../data/battler-tags"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { WeatherType } from "#enums/weather-type"; -import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type { SuppressAbilitiesTag } from "#app/data/arena-tag"; -import type { Ability } from "#app/data/abilities/ability"; -import { - applyAbAttrs, - applyStatMultiplierAbAttrs, - applyPreApplyBattlerTagAbAttrs, - applyPreAttackAbAttrs, - applyPreDefendAbAttrs, - applyPreSetStatusAbAttrs, - applyFieldStatMultiplierAbAttrs, - applyCheckTrappedAbAttrs, - applyPostDamageAbAttrs, - applyPostItemLostAbAttrs, - applyOnGainAbAttrs, - applyPreLeaveFieldAbAttrs, - applyOnLoseAbAttrs, - applyAllyStatMultiplierAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import { allAbilities } from "#app/data/data-lists"; -import type PokemonData from "#app/system/pokemon-data"; -import { BattlerIndex } from "#enums/battler-index"; -import { UiMode } from "#enums/ui-mode"; -import type { PartyOption } from "#app/ui/party-ui-handler"; -import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; -import { EVOLVE_MOVE, RELEARN_MOVE } from "#app/data/balance/pokemon-level-moves"; -import { achvs } from "#app/system/achv"; -import type { StarterDataEntry, StarterMoveset } from "#app/system/game-data"; -import { DexAttr } from "#enums/dex-attr"; -import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; -import { getNatureStatMultiplier } from "#app/data/nature"; -import type { SpeciesFormChange } from "#app/data/pokemon-forms"; + getBattlerTag, + HighestStatBoostTag, + MoveRestrictionBattlerTag, + PowerTrickTag, + SemiInvulnerableTag, + SubstituteTag, + TarShotTag, + TrappedTag, + TypeImmuneTag, +} from "#data/battler-tags"; +import { applyChallenges } from "#data/challenge"; +import { allAbilities, allMoves } from "#data/data-lists"; +import { getLevelTotalExp } from "#data/exp"; import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, -} from "#app/data/pokemon-forms/form-change-triggers"; -import { TerrainType } from "#app/data/terrain"; -import type { TrainerSlot } from "#enums/trainer-slot"; -import Overrides from "#app/overrides"; -import i18next from "i18next"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { ModifierTier } from "#enums/modifier-tier"; -import { applyChallenges } from "#app/data/challenge"; -import { ChallengeType } from "#enums/challenge-type"; +} from "#data/form-change-triggers"; +import { Gender } from "#data/gender"; +import { getNatureStatMultiplier } from "#data/nature"; +import { + CustomPokemonData, + PokemonBattleData, + PokemonSummonData, + PokemonTempSummonData, + PokemonTurnData, + PokemonWaveData, +} from "#data/pokemon-data"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; +import type { PokemonSpeciesForm } from "#data/pokemon-species"; +import { getFusedSpeciesName, getPokemonSpeciesForm, PokemonSpecies } from "#data/pokemon-species"; +import { getRandomStatus, getStatusEffectOverlapText, Status } from "#data/status-effect"; +import { getTerrainBlockMessage, TerrainType } from "#data/terrain"; +import type { TypeDamageMultiplier } from "#data/type"; +import { getTypeDamageMultiplier, getTypeRgb } from "#data/type"; import { AbilityId } from "#enums/ability-id"; +import { AiType } from "#enums/ai-type"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import type { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { getPokemonNameWithAffix } from "#app/messages"; +import { ChallengeType } from "#enums/challenge-type"; import { Challenges } from "#enums/challenges"; -import { PokemonAnimType } from "#enums/pokemon-anim-type"; -import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { SwitchType } from "#enums/switch-type"; -import { SpeciesFormKey } from "#enums/species-form-key"; -import { getStatusEffectOverlapText } from "#app/data/status-effect"; -import { - BASE_HIDDEN_ABILITY_CHANCE, - BASE_SHINY_CHANCE, - SHINY_EPIC_CHANCE, - SHINY_VARIANT_CHANCE, -} from "#app/data/balance/rates"; -import { Nature } from "#enums/nature"; -import { StatusEffect } from "#enums/status-effect"; -import { doShinySparkleAnim } from "#app/field/anims"; -import { MoveFlags } from "#enums/MoveFlags"; -import { timedEventManager } from "#app/global-event-manager"; -import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader"; -import { isVirtual, isIgnorePP, MoveUseMode } from "#enums/move-use-mode"; +import { DexAttr } from "#enums/dex-attr"; import { FieldPosition } from "#enums/field-position"; -import { LearnMoveSituation } from "#enums/learn-move-situation"; import { HitResult } from "#enums/hit-result"; -import { AiType } from "#enums/ai-type"; -import type { MoveResult } from "#enums/move-result"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types"; +import { LearnMoveSituation } from "#enums/learn-move-situation"; +import { MoveCategory } from "#enums/MoveCategory"; +import { MoveFlags } from "#enums/MoveFlags"; +import { MoveTarget } from "#enums/MoveTarget"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { isIgnorePP, isVirtual, MoveUseMode } from "#enums/move-use-mode"; +import { Nature } from "#enums/nature"; +import { PokeballType } from "#enums/pokeball"; +import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesFormKey } from "#enums/species-form-key"; +import { SpeciesId } from "#enums/species-id"; +import { + BATTLE_STATS, + type BattleStat, + EFFECTIVE_STATS, + type EffectiveStat, + PERMANENT_STATS, + type PermanentStat, + Stat, +} from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { SwitchType } from "#enums/switch-type"; +import type { TrainerSlot } from "#enums/trainer-slot"; +import { UiMode } from "#enums/ui-mode"; +import { WeatherType } from "#enums/weather-type"; +import { doShinySparkleAnim } from "#field/anims"; +import { + BaseStatModifier, + CritBoosterModifier, + EnemyDamageBoosterModifier, + EnemyDamageReducerModifier, + EnemyFusionChanceModifier, + EvoTrackerModifier, + HiddenAbilityRateBoosterModifier, + PokemonBaseStatFlatModifier, + PokemonBaseStatTotalModifier, + PokemonFriendshipBoosterModifier, + PokemonHeldItemModifier, + PokemonIncrementingStatModifier, + PokemonMultiHitModifier, + PokemonNatureWeightModifier, + ShinyRateBoosterModifier, + StatBoosterModifier, + SurviveDamageModifier, + TempCritBoosterModifier, + TempStatStageBoosterModifier, +} from "#modifiers/modifier"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import type { Move } from "#moves/move"; +import { getMoveTargets } from "#moves/move-utils"; +import { PokemonMove } from "#moves/pokemon-move"; +import { loadMoveAnimations } from "#sprites/pokemon-asset-loader"; +import type { Variant } from "#sprites/variant"; +import { populateVariantColors, variantColorCache, variantData } from "#sprites/variant"; +import { achvs } from "#system/achv"; +import type { StarterDataEntry, StarterMoveset } from "#system/game-data"; +import type { PokemonData } from "#system/pokemon-data"; +import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#types/ability-types"; +import type { DamageCalculationResult, DamageResult } from "#types/damage-result"; +import type { IllusionData } from "#types/illusion-data"; +import type { TurnMove } from "#types/turn-move"; +import { BattleInfo } from "#ui/battle-info"; +import { EnemyBattleInfo } from "#ui/enemy-battle-info"; +import type { PartyOption } from "#ui/party-ui-handler"; +import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; +import { PlayerBattleInfo } from "#ui/player-battle-info"; +import { + BooleanHolder, + type Constructor, + coerceArray, + deltaRgb, + fixedInt, + getIvsFromId, + isBetween, + isNullOrUndefined, + NumberHolder, + randSeedFloat, + randSeedInt, + randSeedIntRange, + randSeedItem, + rgbaToInt, + rgbHexToRgba, + rgbToHsv, + toDmgValue, +} from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { argbFromRgba, QuantizerCelebi, rgbaFromArgb } from "@material/material-color-utilities"; +import i18next from "i18next"; +import Phaser from "phaser"; +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; /** Base typeclass for damage parameter methods, used for DRY */ type damageParams = { @@ -221,7 +205,7 @@ type getBaseDamageParams = Omit; /** Type for the parameters of {@linkcode Pokemon#getAttackDamage | getAttackDamage} */ type getAttackDamageParams = Omit; -export default abstract class Pokemon extends Phaser.GameObjects.Container { +export abstract class Pokemon extends Phaser.GameObjects.Container { /** * This pokemon's {@link https://bulbapedia.bulbagarden.net/wiki/Personality_value | Personality value/PID}, * used to determine various parameters of this Pokemon. @@ -391,7 +375,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.ivs = ivs || getIvsFromId(this.id); if (this.gender === undefined) { - this.generateGender(); + this.gender = this.species.generateGender(); } if (this.formIndex === undefined) { @@ -453,7 +437,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + * Return the name that will be displayed when this Pokemon is sent out into battle. + * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `true` + * @returns The name to render for this {@linkcode Pokemon}. */ getNameToRender(useIllusion = true) { const name: string = @@ -462,7 +448,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { !useIllusion && this.summonData.illusion ? this.summonData.illusion.basePokemon.nickname : this.nickname; try { if (nickname) { - return decodeURIComponent(escape(atob(nickname))); + return decodeURIComponent(escape(atob(nickname))); // TODO: Remove `atob` and `escape`... eventually... } return name; } catch (err) { @@ -471,11 +457,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - getPokeball(useIllusion = false) { - if (useIllusion) { - return this.summonData.illusion?.pokeball ?? this.pokeball; - } - return this.pokeball; + /** + * Return this Pokemon's {@linkcode PokeballType}. + * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false` + * @returns The {@linkcode PokeballType} that will be shown when this Pokemon is sent out into battle. + */ + getPokeball(useIllusion = false): PokeballType { + return useIllusion && this.summonData.illusion ? this.summonData.illusion.pokeball : this.pokeball; } init(): void { @@ -532,17 +520,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Checks if a pokemon is fainted (ie: its `hp <= 0`). - * It's usually better to call {@linkcode isAllowedInBattle()} - * @param checkStatus `true` to also check that the pokemon's status is {@linkcode StatusEffect.FAINT} - * @returns `true` if the pokemon is fainted + * Usually should not be called directly in favor of calling {@linkcode isAllowedInBattle()}. + * @param checkStatus - Whether to also check that the pokemon's status is {@linkcode StatusEffect.FAINT}; default `false` + * @returns Whether this Pokemon is fainted, as described above. */ public isFainted(checkStatus = false): boolean { return this.hp <= 0 && (!checkStatus || this.status?.effect === StatusEffect.FAINT); } /** - * Check if this pokemon is both not fainted and allowed to be in battle based on currently active challenges. - * @returns {boolean} `true` if pokemon is allowed in battle + * Check if this pokemon is both not fainted and allowed to be used based on currently active challenges. + * @returns Whether this Pokemon is allowed to partake in battle. */ public isAllowedInBattle(): boolean { return !this.isFainted() && this.isAllowedInChallenge(); @@ -550,8 +538,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Check if this pokemon is allowed based on any active challenges. - * It's usually better to call {@linkcode isAllowedInBattle()} - * @returns {boolean} `true` if pokemon is allowed in battle + * Usually should not be called directly in favor of consulting {@linkcode isAllowedInBattle()}. + * @returns Whether this Pokemon is allowed under the current challenge conditions. */ public isAllowedInChallenge(): boolean { const challengeAllowed = new BooleanHolder(true); @@ -561,8 +549,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Checks if this {@linkcode Pokemon} is allowed in battle (ie: not fainted, and allowed under any active challenges). - * @param onField `true` to also check if the pokemon is currently on the field; default `false` - * @returns `true` if the pokemon is "active", as described above. + * @param onField - Whether to also check if the pokemon is currently on the field; default `false` + * @returns Whether this pokemon is considered "active", as described above. * Returns `false` if there is no active {@linkcode BattleScene} or the pokemon is disallowed. */ public isActive(onField = false): boolean { @@ -574,7 +562,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getDexAttr(): bigint { let ret = 0n; - ret |= this.gender !== Gender.FEMALE ? DexAttr.MALE : DexAttr.FEMALE; + if (this.gender !== Gender.GENDERLESS) { + ret |= this.gender !== Gender.FEMALE ? DexAttr.MALE : DexAttr.FEMALE; + } ret |= !this.shiny ? DexAttr.NON_SHINY : DexAttr.SHINY; ret |= this.variant >= 2 ? DexAttr.VARIANT_3 : this.variant === 1 ? DexAttr.VARIANT_2 : DexAttr.DEFAULT_VARIANT; ret |= globalScene.gameData.getFormAttr(this.formIndex); @@ -717,7 +707,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract getBattlerIndex(): BattlerIndex; /** - * @param useIllusion - Whether we want the illusion or not. + * Load all assets needed for this Pokemon's use in battle + * @param ignoreOverride - Whether to ignore overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `true` + * @param useIllusion - Whether to consider this pokemon's active illusion; default `false` + * @returns A promise that resolves once all the corresponding assets have been loaded. */ async loadAssets(ignoreOverride = true, useIllusion = false): Promise { /** Promises that are loading assets and can be run concurrently. */ @@ -846,11 +839,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Attempt to process variant sprite. - * - * @param cacheKey the cache key for the variant color sprite - * @param useExpSprite should the experimental sprite be used - * @param battleSpritePath the filename of the sprite + * Attempt to process variant sprite color caches. + * @param cacheKey - the cache key for the variant color sprite + * @param useExpSprite - Whether experimental sprites should be used if present + * @param battleSpritePath - the filename of the sprite */ async populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) { const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`; @@ -897,13 +889,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.fusionSpecies.forms[this.fusionFormIndex].formKey; } - getSpriteAtlasPath(ignoreOverride?: boolean): string { - const spriteId = this.getSpriteId(ignoreOverride).replace(/\_{2}/g, "/"); + // TODO: Add more documentation for all these attributes. + // They may be all similar, but what each one actually _does_ is quite unclear at first glance + + getSpriteAtlasPath(ignoreOverride = false): string { + const spriteId = this.getSpriteId(ignoreOverride).replace(/_{2}/g, "/"); + return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } getBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { - const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); + const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } @@ -977,7 +973,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { - return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); + return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/"); } getIconAtlasKey(ignoreOverride = false, useIllusion = true): string { @@ -1041,10 +1037,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Get this {@linkcode Pokemon}'s {@linkcode PokemonSpeciesForm}. - * @param ignoreOverride - Whether to ignore overridden species from {@linkcode MoveId.TRANSFORM}, default `false`. - * This overrides `useIllusion` if `true`. - * @param useIllusion - `true` to use the speciesForm of the illusion; default `false`. + * Return this Pokemon's {@linkcode PokemonSpeciesForm | SpeciesForm}. + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * and overrides `useIllusion`. + * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false`. + * @returns This Pokemon's {@linkcode PokemonSpeciesForm}. */ getSpeciesForm(ignoreOverride = false, useIllusion = false): PokemonSpeciesForm { if (!ignoreOverride && this.summonData.speciesForm) { @@ -1063,9 +1060,45 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. + * Getter function that returns whether this {@linkcode Pokemon} is currently transformed into another one + * (such as by the effects of {@linkcode MoveId.TRANSFORM} or {@linkcode AbilityId.IMPOSTER}. + * @returns Whether this Pokemon is currently transformed. */ - getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion = false): PokemonSpeciesForm { + isTransformed(): boolean { + return this.summonData.speciesForm !== null; + } + + /** + * Return whether this Pokemon can transform into an opposing Pokemon. + * @param target - The {@linkcode Pokemon} being transformed into + * @returns Whether this Pokemon can transform into `target`. + */ + canTransformInto(target: Pokemon): boolean { + return !( + // Neither pokemon can be already transformed + ( + this.isTransformed() || + target.isTransformed() || + // Neither pokemon can be behind an illusion + target.summonData.illusion || + this.summonData.illusion || + // The target cannot be behind a substitute + target.getTag(BattlerTagType.SUBSTITUTE) || + // Transforming to/from fusion pokemon causes various problems (crashes, etc.) + // TODO: Consider lifting restriction once bug is fixed + this.isFusion() || + target.isFusion() + ) + ); + } + + /** + * Return the {@linkcode PokemonSpeciesForm | SpeciesForm} of this Pokemon's fusion counterpart. + * @param ignoreOverride - Whether to ignore species overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param useIllusion - Whether to consider the species of this Pokemon's illusion; default `false` + * @returns The {@linkcode PokemonSpeciesForm} of this Pokemon's fusion counterpart. + */ + getFusionSpeciesForm(ignoreOverride = false, useIllusion = false): PokemonSpeciesForm { const fusionSpecies: PokemonSpecies = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionSpecies! : this.fusionSpecies!; const fusionFormIndex = @@ -1276,39 +1309,39 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Retrieves the entire set of stats of this {@linkcode Pokemon}. - * @param bypassSummonData - whether to use actual stats or in-battle overriden stats from Transform; default `true` - * @returns the numeric values of this {@linkcode Pokemon}'s stats + * @param bypassSummonData - Whether to prefer actual stats (`true`) or in-battle overridden stats (`false`); default `true` + * @returns The numeric values of this {@linkcode Pokemon}'s stats as an array. */ getStats(bypassSummonData = true): number[] { - if (!bypassSummonData && this.summonData.stats) { - return this.summonData.stats; + if (!bypassSummonData) { + // Only grab summon data stats if nonzero + return this.summonData.stats.map((s, i) => s || this.stats[i]); } return this.stats; } /** * Retrieves the corresponding {@linkcode PermanentStat} of the {@linkcode Pokemon}. - * @param stat the desired {@linkcode PermanentStat} - * @param bypassSummonData prefer actual stats (`true` by default) or in-battle overridden stats (`false`) - * @returns the numeric value of the desired {@linkcode Stat} + * @param stat - The {@linkcode PermanentStat} to retrieve + * @param bypassSummonData - Whether to prefer actual stats (`true`) or in-battle overridden stats (`false`); default `true` + * @returns The numeric value of the desired {@linkcode Stat}. */ getStat(stat: PermanentStat, bypassSummonData = true): number { - if (!bypassSummonData && this.summonData.stats[stat] !== 0) { - return this.summonData.stats[stat]; + if (!bypassSummonData) { + // 0 = no override + return this.summonData.stats[stat] || this.stats[stat]; } return this.stats[stat]; } /** - * Writes the value to the corrseponding {@linkcode PermanentStat} of the {@linkcode Pokemon}. - * - * Note that this does nothing if {@linkcode value} is less than 0. - * @param stat the desired {@linkcode PermanentStat} to be overwritten - * @param value the desired numeric value - * @param bypassSummonData write to actual stats (`true` by default) or in-battle overridden stats (`false`) + * Change one of this {@linkcode Pokemon}'s {@linkcode PermanentStat}s to the specified value. + * @param stat - The {@linkcode PermanentStat} to be overwritten + * @param value - The stat value to set. Ignored if `<=0` + * @param bypassSummonData - Whether to write to actual stats (`true`) or in-battle overridden stats (`false`); default `true` */ setStat(stat: PermanentStat, value: number, bypassSummonData = true): void { - if (value < 0) { + if (value <= 0) { return; } @@ -1324,31 +1357,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns the numeric values of the {@linkcode Pokemon}'s in-battle stat stages if available, a fresh stat stage array otherwise */ getStatStages(): number[] { - return this.summonData ? this.summonData.statStages : [0, 0, 0, 0, 0, 0, 0]; + return this.summonData.statStages; } /** - * Retrieves the in-battle stage of the specified {@linkcode BattleStat}. - * @param stat the {@linkcode BattleStat} whose stage is desired - * @returns the stage of the desired {@linkcode BattleStat} if available, 0 otherwise + * Retrieve the value of the given stat stage for this {@linkcode Pokemon}. + * @param stat - The {@linkcode BattleStat} to retrieve the stat stage for + * @returns The value of the desired stat stage as a number within the range `[-6, +6]`. */ getStatStage(stat: BattleStat): number { - return this.summonData ? this.summonData.statStages[stat - 1] : 0; + return this.summonData.statStages[stat - 1]; } /** - * Writes the value to the in-battle stage of the corresponding {@linkcode BattleStat} of the {@linkcode Pokemon}. - * - * Note that, if the value is not within a range of [-6, 6], it will be forced to the closest range bound. - * @param stat the {@linkcode BattleStat} whose stage is to be overwritten - * @param value the desired numeric value + * Sets this {@linkcode Pokemon}'s in-battle stat stage to the corresponding value. + * @param stat - The {@linkcode BattleStat} whose stage is to be overwritten + * @param value - The value of the stat stage to set, forcibly clamped within the range `[-6, +6]`. */ setStatStage(stat: BattleStat, value: number): void { - if (value >= -6) { - this.summonData.statStages[stat - 1] = Math.min(value, 6); - } else { - this.summonData.statStages[stat - 1] = Math.max(value, -6); - } + this.summonData.statStages[stat - 1] = Phaser.Math.Clamp(value, -6, 6); } /** @@ -1364,7 +1391,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("HighCritAttr", source, this, move, critStage); globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage); globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage); - applyAbAttrs("BonusCritAbAttr", source, null, false, critStage); + applyAbAttrs("BonusCritAbAttr", { pokemon: source, critStage }); const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { // Dragon cheer only gives +1 crit stage to non-dragon types @@ -1393,17 +1420,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Calculates and retrieves the final value of a stat considering any held * items, move effects, opponent abilities, and whether there was a critical * hit. - * @param stat the desired {@linkcode EffectiveStat} - * @param opponent the target {@linkcode Pokemon} - * @param move the {@linkcode Move} being used - * @param ignoreAbility determines whether this Pokemon's abilities should be ignored during the stat calculation - * @param ignoreOppAbility during an attack, determines whether the opposing Pokemon's abilities should be ignored during the stat calculation. - * @param ignoreAllyAbility during an attack, determines whether the ally Pokemon's abilities should be ignored during the stat calculation. - * @param isCritical determines whether a critical hit has occurred or not (`false` by default) - * @param simulated if `true`, nullifies any effects that produce any changes to game state from triggering - * @param ignoreHeldItems determines whether this Pokemon's held items should be ignored during the stat calculation, default `false` - * @returns the final in-battle value of a stat + * @param stat - The desired {@linkcode EffectiveStat | Stat} to check. + * @param opponent - The {@linkcode Pokemon} being targeted, if applicable. + * @param move - The {@linkcode Move} being used, if any. Used to check ability ignoring effects and similar. + * @param ignoreAbility - Whether to ignore ability effects of the user; default `false`. + * @param ignoreOppAbility - Whether to ignore ability effects of the target; default `false`. + * @param ignoreAllyAbility - Whether to ignore ability effects of the user's allies; default `false`. + * @param isCritical - Whether a critical hit has occurred or not; default `false`. + * If `true`, will nullify offensive stat drops or defensive stat boosts. + * @param simulated - Whether to nullify any effects that produce changes to game state during calculations; default `true` + * @param ignoreHeldItems - Whether to ignore the user's held items during stat calculation; default `false`. + * @returns The final in-battle value for the given stat. */ + // TODO: Replace the optional parameters with an object to make calling this method less cumbersome getEffectiveStat( stat: EffectiveStat, opponent?: Pokemon, @@ -1415,46 +1444,54 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { simulated = true, ignoreHeldItems = false, ): number { - const statValue = new NumberHolder(this.getStat(stat, false)); + const statVal = new NumberHolder(this.getStat(stat, false)); if (!ignoreHeldItems) { - globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue); + globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statVal); } // The Ruin abilities here are never ignored, but they reveal themselves on summon anyway const fieldApplied = new BooleanHolder(false); for (const pokemon of globalScene.getField(true)) { - applyFieldStatMultiplierAbAttrs( - "FieldMultiplyStatAbAttr", + // TODO: remove `canStack` toggle from ability as breaking out renders it useless + applyAbAttrs("FieldMultiplyStatAbAttr", { pokemon, stat, - statValue, - this, - fieldApplied, + statVal, + target: this, + hasApplied: fieldApplied, simulated, - ); + }); if (fieldApplied.value) { break; } } + if (!ignoreAbility) { - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, stat, statValue, simulated); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: this, + stat, + statVal, + simulated, + // TODO: maybe just don't call this if the move is none? + move: move ?? allMoves[MoveId.NONE], + }); } const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, stat, - statValue, + statVal, simulated, - this, - move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility, - ); + // TODO: maybe just don't call this if the move is none? + move: move ?? allMoves[MoveId.NONE], + ignoreAbility: move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility, + }); } let ret = - statValue.value * + statVal.value * this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems); switch (stat) { @@ -1628,9 +1665,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param useIllusion - Whether we want the fake or real gender (illusion ability). + * Return this Pokemon's {@linkcode Gender}. + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns the {@linkcode Gender} of this {@linkcode Pokemon}. */ - getGender(ignoreOverride?: boolean, useIllusion = false): Gender { + getGender(ignoreOverride = false, useIllusion = false): Gender { if (useIllusion && this.summonData.illusion) { return this.summonData.illusion.gender; } @@ -1641,9 +1681,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param useIllusion - Whether we want the fake or real gender (illusion ability). + * Return this Pokemon's fusion's {@linkcode Gender}. + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns The {@linkcode Gender} of this {@linkcode Pokemon}'s fusion. */ - getFusionGender(ignoreOverride?: boolean, useIllusion = false): Gender { + getFusionGender(ignoreOverride = false, useIllusion = false): Gender { if (useIllusion && this.summonData.illusion?.fusionGender) { return this.summonData.illusion.fusionGender; } @@ -1654,15 +1697,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param useIllusion - Whether we want the fake or real shininess (illusion ability). + * Check whether this Pokemon is shiny. + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns Whether this Pokemon is shiny */ isShiny(useIllusion = false): boolean { if (!useIllusion && this.summonData.illusion) { - return !!( + return ( this.summonData.illusion.basePokemon?.shiny || - (this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) + (this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) || + false ); } + return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } @@ -1681,9 +1728,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * - * @param useIllusion - Whether we want the fake or real shininess (illusion ability). - * @returns `true` if the {@linkcode Pokemon} is shiny and the fusion is shiny as well, `false` otherwise + * Check whether this Pokemon is doubly shiny (both normal and fusion are shiny). + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns Whether this pokemon's base and fusion counterparts are both shiny. */ isDoubleShiny(useIllusion = false): boolean { if (!useIllusion && this.summonData.illusion?.basePokemon) { @@ -1693,11 +1740,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.summonData.illusion.basePokemon.fusionShiny ); } + return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } /** - * @param useIllusion - Whether we want the fake or real variant (illusion ability). + * Return this Pokemon's {@linkcode Variant | shiny variant}. + * Only meaningful if this pokemon is actually shiny. + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns The shiny variant of this Pokemon. */ getVariant(useIllusion = false): Variant { if (!useIllusion && this.summonData.illusion) { @@ -1705,9 +1756,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? this.summonData.illusion.basePokemon!.variant : (Math.max(this.variant, this.fusionVariant) as Variant); } + return !this.isFusion(true) ? this.variant : (Math.max(this.variant, this.fusionVariant) as Variant); } + // TODO: Clarify how this differs from `getVariant` getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { return this.summonData.illusion?.basePokemon?.variant ?? this.variant; @@ -1715,19 +1768,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getVariant(); } + /** + * Return this pokemon's overall luck value, based on its shininess (1 pt per variant lvl). + * @returns The luck value of this Pokemon. + */ getLuck(): number { return this.luck + (this.isFusion() ? this.fusionLuck : 0); } + /** + * Return whether this {@linkcode Pokemon} is currently fused with anything. + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns Whether this Pokemon is currently fused with another species. + */ isFusion(useIllusion = false): boolean { - if (useIllusion && this.summonData.illusion) { - return !!this.summonData.illusion.fusionSpecies; - } - return !!this.fusionSpecies; + return useIllusion && this.summonData.illusion ? !!this.summonData.illusion.fusionSpecies : !!this.fusionSpecies; } /** - * @param useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + * Return this {@linkcode Pokemon}'s name. + * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` + * @returns This Pokemon's name. + * @see {@linkcode getNameToRender} - gets this Pokemon's display name. */ getName(useIllusion = false): string { return !useIllusion && this.summonData.illusion?.basePokemon @@ -1736,19 +1798,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Checks if the {@linkcode Pokemon} has a fusion with the specified {@linkcode SpeciesId}. - * @param species the pokemon {@linkcode SpeciesId} to check - * @returns `true` if the {@linkcode Pokemon} has a fusion with the specified {@linkcode SpeciesId}, `false` otherwise + * Check whether this {@linkcode Pokemon} has a fusion with the specified {@linkcode SpeciesId}. + * @param species - The {@linkcode SpeciesId} to check against. + * @returns Whether this Pokemon is currently fused with the specified {@linkcode SpeciesId}. */ hasFusionSpecies(species: SpeciesId): boolean { return this.fusionSpecies?.speciesId === species; } /** - * Checks if the {@linkcode Pokemon} has is the specified {@linkcode SpeciesId} or is fused with it. - * @param species the pokemon {@linkcode SpeciesId} to check - * @param formKey If provided, requires the species to be in that form - * @returns `true` if the pokemon is the species or is fused with it, `false` otherwise + * Check whether this {@linkcode Pokemon} either is or is fused with the given {@linkcode SpeciesId}. + * @param species - The {@linkcode SpeciesId} to check against. + * @param formKey - If provided, will require the species to be in the given form. + * @returns Whether this Pokemon has this species as either its base or fusion counterpart. */ hasSpecies(species: SpeciesId, formKey?: string): boolean { if (isNullOrUndefined(formKey)) { @@ -1763,7 +1825,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract isBoss(): boolean; - getMoveset(ignoreOverride?: boolean): PokemonMove[] { + /** + * Return all the {@linkcode PokemonMove}s that make up this Pokemon's moveset. + * Takes into account player/enemy moveset overrides (which will also override PP count). + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @returns An array of {@linkcode PokemonMove}, as described above. + */ + getMoveset(ignoreOverride = false): PokemonMove[] { const ret = !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; // Overrides moveset based on arrays specified in overrides.ts @@ -1785,11 +1853,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Checks which egg moves have been unlocked for the {@linkcode Pokemon} based - * on the species it was met at or by the first {@linkcode Pokemon} in its evolution + * Check which egg moves have been unlocked for this {@linkcode Pokemon}. + * Looks at either the species it was met at or the first {@linkcode Species} in its evolution * line that can act as a starter and provides those egg moves. - * @returns an array of {@linkcode MoveId}, the length of which is determined by how many - * egg moves are unlocked for that species. + * @returns An array of all {@linkcode MoveId}s that are egg moves and unlocked for this Pokemon. */ getUnlockedEggMoves(): MoveId[] { const moves: MoveId[] = []; @@ -1806,13 +1873,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Gets all possible learnable level moves for the {@linkcode Pokemon}, + * Get all possible learnable level moves for the {@linkcode Pokemon}, * excluding any moves already known. * * Available egg moves are only included if the {@linkcode Pokemon} was * in the starting party of the run and if Fresh Start is not active. - * @returns an array of {@linkcode MoveId}, the length of which is determined - * by how many learnable moves there are for the {@linkcode Pokemon}. + * @returns An array of {@linkcode MoveId}s, as described above. */ public getLearnableLevelMoves(): MoveId[] { let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]); @@ -1827,12 +1893,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Gets the types of a pokemon - * @param includeTeraType - `true` to include tera-formed type; Default: `false` - * @param forDefend - `true` if the pokemon is defending from an attack; Default: `false` - * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false` - * @param useIllusion - `true` to return the types of the illusion instead of the actual types; Default: `false` - * @returns array of {@linkcode PokemonType} + * Evaluate and return this Pokemon's typing. + * @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `false` + * @param forDefend - Whether this Pokemon is currently receiving an attack; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false` + * @returns An array of {@linkcode PokemonType}s corresponding to this Pokemon's typing (real or percieved). */ public getTypes( includeTeraType = false, @@ -1928,7 +1994,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // remove UNKNOWN if other types are present - if (types.length > 1 && types.includes(PokemonType.UNKNOWN)) { + if (types.length > 1) { const index = types.indexOf(PokemonType.UNKNOWN); if (index !== -1) { types.splice(index, 1); @@ -1949,24 +2015,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Checks if the pokemon's typing includes the specified type - * @param type - {@linkcode PokemonType} to check - * @param includeTeraType - `true` to include tera-formed type; Default: `true` - * @param forDefend - `true` if the pokemon is defending from an attack; Default: `false` - * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false` - * @returns `true` if the Pokemon's type matches + * Check if this Pokemon's typing includes the specified type. + * @param type - The {@linkcode PokemonType} to check + * @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `true` + * @param forDefend - Whether this Pokemon is currently receiving an attack; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @returns Whether this Pokemon is of the specified type. */ public isOfType(type: PokemonType, includeTeraType = true, forDefend = false, ignoreOverride = false): boolean { - return this.getTypes(includeTeraType, forDefend, ignoreOverride).some(t => t === type); + return this.getTypes(includeTeraType, forDefend, ignoreOverride).includes(type); } /** - * Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects. - * This should rarely be called, most of the time {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as - * those check both the passive and non-passive abilities and account for ability suppression. - * @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases - * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false` - * @returns The non-passive {@linkcode Ability} of the pokemon + * Get this Pokemon's non-passive {@linkcode Ability}, factoring in fusions, overrides and ability-changing effects. + + * Should rarely be called directly in favor of {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr}, + * both of which check both ability slots and account for suppression. + * @see {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} are the intended ways to check abilities in most cases + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @returns The non-passive {@linkcode Ability} of this Pokemon. */ public getAbility(ignoreOverride = false): Ability { if (!ignoreOverride && this.summonData.ability) { @@ -2045,20 +2112,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ability New Ability */ public setTempAbility(ability: Ability, passive = false): void { - applyOnLoseAbAttrs(this, passive); + applyOnLoseAbAttrs({ pokemon: this, passive }); if (passive) { this.summonData.passiveAbility = ability.id; } else { this.summonData.ability = ability.id; } - applyOnGainAbAttrs(this, passive); + applyOnGainAbAttrs({ pokemon: this, passive }); } /** * Suppresses an ability and calls its onlose attributes */ public suppressAbility() { - [true, false].forEach(passive => applyOnLoseAbAttrs(this, passive)); + [true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: this, passive })); this.summonData.abilitySuppressed = true; } @@ -2112,7 +2179,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (passive && !this.hasPassive()) { return false; } - const ability = !passive ? this.getAbility() : this.getPassiveAbility(); + const ability = passive ? this.getPassiveAbility() : this.getAbility(); if (this.isFusion() && ability.hasAttr("NoFusionAbilityAbAttr")) { return false; } @@ -2125,7 +2192,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; const suppressOffField = ability.hasAttr("PreSummonAbAttr"); - if ((this.isOnField() || suppressOffField) && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) { + if ((this.isOnField() || suppressOffField) && suppressAbilitiesTag && !suppressAbilitiesTag.beingRemoved) { const thisAbilitySuppressing = ability.hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"); const hasSuppressingAbility = this.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false); // Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas @@ -2143,13 +2210,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Checks whether a pokemon has the specified ability and it's in effect. Accounts for all the various - * effects which can affect whether an ability will be present or in effect, and both passive and - * non-passive. This is the primary way to check whether a pokemon has a particular ability. - * @param ability The ability to check for + * Check whether a pokemon has the specified ability in effect, either as a normal or passive ability. + * Accounts for all the various effects which can disable or modify abilities. + * @param ability - The {@linkcode Abilities | Ability} to check for * @param canApply - Whether to check if the ability is currently active; default `true` - * @param ignoreOverride Whether to ignore ability changing effects; default `false` - * @returns `true` if the ability is present and active + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @returns Whether this {@linkcode Pokemon} has the given ability */ public hasAbility(ability: AbilityId, canApply = true, ignoreOverride = false): boolean { if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) { @@ -2159,14 +2225,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Checks whether a pokemon has an ability with the specified attribute and it's in effect. - * Accounts for all the various effects which can affect whether an ability will be present or - * in effect, and both passive and non-passive. This is one of the two primary ways to check - * whether a pokemon has a particular ability. - * @param attrType The {@link AbAttr | ability attribute} to check for + * Check whether this pokemon has an ability with the specified attribute in effect, either as a normal or passive ability. + * Accounts for all the various effects which can disable or modify abilities. + * @param attrType - The {@linkcode AbAttr | attribute} to check for * @param canApply - Whether to check if the ability is currently active; default `true` - * @param ignoreOverride Whether to ignore ability changing effects; default `false` - * @returns `true` if an ability with the given {@linkcode AbAttr} is present and active + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @returns Whether this Pokemon has an ability with the given {@linkcode AbAttr}. */ public hasAbilityWithAttr(attrType: AbAttrString, canApply = true, ignoreOverride = false): boolean { if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) { @@ -2188,13 +2252,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const autotomizedTag = this.getTag(AutotomizedTag); let weightRemoved = 0; if (!isNullOrUndefined(autotomizedTag)) { - weightRemoved = 100 * autotomizedTag!.autotomizeCount; + weightRemoved = 100 * autotomizedTag.autotomizeCount; } const minWeight = 0.1; const weight = new NumberHolder(this.species.weight - weightRemoved); // This will trigger the ability overlay so only call this function when necessary - applyAbAttrs("WeightMultiplierAbAttr", this, null, false, weight); + applyAbAttrs("WeightMultiplierAbAttr", { pokemon: this, weight }); return Math.max(minWeight, weight.value); } @@ -2256,7 +2320,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const trappedByAbility = new BooleanHolder(false); + /** Holds whether the pokemon is trapped due to an ability */ + const trapped = new BooleanHolder(false); /** * Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective * Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger @@ -2265,14 +2330,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false); for (const opponent of opposingField) { - applyCheckTrappedAbAttrs("CheckTrappedAbAttr", opponent, trappedByAbility, this, trappedAbMessages, simulated); + applyAbAttrs("CheckTrappedAbAttr", { pokemon: opponent, trapped, opponent: this, simulated }, trappedAbMessages); } const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; return ( - trappedByAbility.value || - !!this.getTag(TrappedTag) || - !!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side) + trapped.value || !!this.getTag(TrappedTag) || !!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side) ); } @@ -2287,7 +2350,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveTypeHolder = new NumberHolder(move.type); applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder); - applyPreAttackAbAttrs("MoveTypeChangeAbAttr", this, null, move, simulated, moveTypeHolder); + + const power = new NumberHolder(move.power); + applyAbAttrs("MoveTypeChangeAbAttr", { + pokemon: this, + move, + simulated, + moveType: moveTypeHolder, + power, + opponent: this, + }); // If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type, // then bypass the check for ion deluge and electrify @@ -2351,17 +2423,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const cancelledHolder = cancelled ?? new BooleanHolder(false); + // TypeMultiplierAbAttrParams is shared amongst the type of AbAttrs we will be invoking + const commonAbAttrParams: TypeMultiplierAbAttrParams = { + pokemon: this, + opponent: source, + move, + cancelled: cancelledHolder, + simulated, + typeMultiplier, + }; if (!ignoreAbility) { - applyPreDefendAbAttrs("TypeImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("TypeImmunityAbAttr", commonAbAttrParams); if (!cancelledHolder.value) { - applyPreDefendAbAttrs("MoveImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("MoveImmunityAbAttr", commonAbAttrParams); } if (!cancelledHolder.value) { const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); defendingSidePlayField.forEach(p => - applyPreDefendAbAttrs("FieldPriorityMoveImmunityAbAttr", p, source, move, cancelledHolder), + applyAbAttrs("FieldPriorityMoveImmunityAbAttr", { + pokemon: p, + opponent: source, + move, + cancelled: cancelledHolder, + }), ); } } @@ -2376,7 +2462,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Apply Tera Shell's effect to attacks after all immunities are accounted for if (!ignoreAbility && move.category !== MoveCategory.STATUS) { - applyPreDefendAbAttrs("FullHpResistTypeAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("FullHpResistTypeAbAttr", commonAbAttrParams); } if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) { @@ -2420,16 +2506,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } let multiplier = types - .map(defType => { - const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType)); + .map(defenderType => { + const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defenderType)); applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier); if (move) { - applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defType); + applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defenderType); } if (source) { const ignoreImmunity = new BooleanHolder(false); if (source.isActive(true) && source.hasAbilityWithAttr("IgnoreTypeImmunityAbAttr")) { - applyAbAttrs("IgnoreTypeImmunityAbAttr", source, ignoreImmunity, simulated, moveType, defType); + applyAbAttrs("IgnoreTypeImmunityAbAttr", { + pokemon: source, + cancelled: ignoreImmunity, + simulated, + moveType, + defenderType, + }); } if (ignoreImmunity.value) { if (multiplier.value === 0) { @@ -2438,7 +2530,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; - if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { + if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType))) { if (multiplier.value === 0) { return 1; } @@ -2473,41 +2565,78 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns A score value based on how favorable this Pokemon is when fighting the given Pokemon */ getMatchupScore(opponent: Pokemon): number { - const types = this.getTypes(true); - - const enemyTypes = opponent.getTypes(true, true, false, true); + const enemyTypes = opponent.getTypes(true, false, false, true); /** Is this Pokemon faster than the opponent? */ const outspeed = (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false)) >= opponent.getEffectiveStat(Stat.SPD, this); - /** - * Based on how effective this Pokemon's types are offensively against the opponent's types. - * This score is increased by 25 percent if this Pokemon is faster than the opponent. - */ - let atkScore = - opponent.getAttackTypeEffectiveness(types[0], this, false, true, undefined, true) * (outspeed ? 1.25 : 1); + /** * Based on how effectively this Pokemon defends against the opponent's types. * This score cannot be higher than 4. */ let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0], opponent), 0.25); - if (types.length > 1) { - atkScore *= opponent.getAttackTypeEffectiveness(types[1], this); - } if (enemyTypes.length > 1) { defScore *= 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25); } + + const moveset = this.moveset; + let moveAtkScoreLength = 0; + let atkScore = 0; + // TODO: this calculation needs to consider more factors; it's currently very simplistic + for (const move of moveset) { + const resolvedMove = move.getMove(); + // NOTE: Counter and Mirror Coat are considered as attack moves here + if (resolvedMove.category === MoveCategory.STATUS || move.getPpRatio() <= 0) { + continue; + } + const moveType = resolvedMove.type; + let thisScore = opponent.getAttackTypeEffectiveness(moveType, this, false, true, undefined, true); + + // Add STAB multiplier for attack type effectiveness. + // For now, simply don't apply STAB to moves that may change type + if (this.getTypes(true).includes(moveType) && !move.getMove().hasAttr("VariableMoveTypeAttr")) { + thisScore *= 1.5; + } + + atkScore += thisScore; + moveAtkScoreLength++; + } + // Get average attack score of all damaging moves (|| 1 prevents division by zero)) + // TODO: Averaging the attack score is excessively simplistic, and doesn't reflect the AI's move selection logic + // e.g. if the mon has one 4x effective move and three 0.5x effective moves, this score would be ~1.375 + // which does not seem fair, given that if the AI were to switch, in all likelihood it would use the 4x move. + // We could consider a weighted average... + atkScore /= moveAtkScoreLength || 1; /** * Based on this Pokemon's HP ratio compared to that of the opponent. * This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent; * however, the final ratio cannot be higher than 1. */ - let hpDiffRatio = this.getHpRatio() + (1 - opponent.getHpRatio()); - if (outspeed) { - hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1); + const hpRatio = this.getHpRatio(); + const oppHpRatio = opponent.getHpRatio(); + // TODO: use better logic for predicting whether the pokemon "is dying" + // E.g., perhaps check if it would faint if the opponent were to use the same move it just used + // (twice if the user is slower) + const isDying = hpRatio <= 0.2; + let hpDiffRatio = hpRatio + (1 - oppHpRatio); + if (isDying && this.isActive(true)) { + //It might be a sacrifice candidate if hp under 20% + const badMatchup = atkScore < 1.5 && defScore < 1.5; + if (!outspeed && badMatchup) { + //It might not be a worthy sacrifice if it doesn't outspeed or doesn't do enough damage + hpDiffRatio *= 0.85; + } else { + hpDiffRatio = 1 - hpRatio + (outspeed ? 0.2 : 0.1); + } + } else if (outspeed) { + hpDiffRatio = hpDiffRatio * 1.25; + } else if (hpRatio > 0.2 && hpRatio <= 0.4) { + // Might be considered to be switched because it's not in low enough health + hpDiffRatio = hpDiffRatio * 0.5; } - return (atkScore + defScore) * hpDiffRatio; + return (atkScore + defScore) * Math.min(hpDiffRatio, 1); } getEvolution(): SpeciesFormEvolution | null { @@ -2880,7 +3009,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); }; - let fusionOverride: PokemonSpecies | undefined = undefined; + let fusionOverride: PokemonSpecies | undefined; if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); @@ -3292,16 +3421,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param onField - whether to also check if the pokemon is currently on the field (defaults to true) */ getOpponents(onField = true): Pokemon[] { - return ((this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField()) as Pokemon[]).filter(p => + return (this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField()).filter(p => p.isActive(onField), ); } getOpponentDescriptor(): string { - const opponents = this.getOpponents(); - if (opponents.length === 1) { - return opponents[0].name; - } return this.isPlayer() ? i18next.t("arenaTag:opposingTeam") : i18next.t("arenaTag:yourTeam"); } @@ -3323,10 +3448,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * Note that this does not apply to evasion or accuracy * @see {@linkcode getAccuracyMultiplier} - * @param stat the desired {@linkcode EffectiveStat} - * @param opponent the target {@linkcode Pokemon} - * @param move the {@linkcode Move} being used - * @param ignoreOppAbility determines whether the effects of the opponent's abilities (i.e. Unaware) should be ignored (`false` by default) + * @param stat - The {@linkcode EffectiveStat} to calculate + * @param opponent - The {@linkcode Pokemon} being targeted + * @param move - The {@linkcode Move} being used + * @param ignoreOppAbility determines whether the effects of the opponent's abilities (i.e. Unaware) should be ignored (`false` by default) * @param isCritical determines whether a critical hit has occurred or not (`false` by default) * @param simulated determines whether effects are applied without altering game state (`true` by default) * @param ignoreHeldItems determines whether this Pokemon's held items should be ignored during the stat calculation, default `false` @@ -3358,7 +3483,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } if (!ignoreOppAbility) { - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", opponent, null, simulated, stat, ignoreStatStage); + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { + pokemon: opponent, + ignored: ignoreStatStage, + stat, + simulated, + }); } if (move) { applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage); @@ -3397,8 +3527,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const ignoreAccStatStage = new BooleanHolder(false); const ignoreEvaStatStage = new BooleanHolder(false); - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", target, null, false, Stat.ACC, ignoreAccStatStage); - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", this, null, false, Stat.EVA, ignoreEvaStatStage); + // TODO: consider refactoring this method to accept `simulated` and then pass simulated to these applyAbAttrs + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: target, stat: Stat.ACC, ignored: ignoreAccStatStage }); + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: this, stat: Stat.EVA, ignored: ignoreEvaStatStage }); applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage); globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage); @@ -3418,33 +3549,40 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6)); } - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, Stat.ACC, accuracyMultiplier, false, sourceMove); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: this, + stat: Stat.ACC, + statVal: accuracyMultiplier, + move: sourceMove, + }); const evasionMultiplier = new NumberHolder(1); - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", target, Stat.EVA, evasionMultiplier); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: target, + stat: Stat.EVA, + statVal: evasionMultiplier, + move: sourceMove, + }); const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { const ignore = this.hasAbilityWithAttr("MoveAbilityBypassAbAttr") || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES); - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, - Stat.ACC, - accuracyMultiplier, - false, - this, - ignore, - ); - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, - Stat.EVA, - evasionMultiplier, - false, - this, - ignore, - ); + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, + stat: Stat.ACC, + statVal: accuracyMultiplier, + ignoreAbility: ignore, + move: sourceMove, + }); + + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, + stat: Stat.EVA, + statVal: evasionMultiplier, + ignoreAbility: ignore, + move: sourceMove, + }); } return accuracyMultiplier.value / evasionMultiplier.value; @@ -3559,7 +3697,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier); if (!ignoreSourceAbility) { - applyAbAttrs("StabBoostAbAttr", source, null, simulated, stabMultiplier); + applyAbAttrs("StabBoostAbAttr", { pokemon: source, simulated, multiplier: stabMultiplier }); } if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) { @@ -3706,16 +3844,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { null, multiStrikeEnhancementMultiplier, ); + if (!ignoreSourceAbility) { - applyPreAttackAbAttrs( - "AddSecondStrikeAbAttr", - source, - this, + applyAbAttrs("AddSecondStrikeAbAttr", { + pokemon: source, move, simulated, - null, - multiStrikeEnhancementMultiplier, - ); + multiplier: multiStrikeEnhancementMultiplier, + }); } /** Doubles damage if this Pokemon's last move was Glaive Rush */ @@ -3726,7 +3862,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** The damage multiplier when the given move critically hits */ const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1); - applyAbAttrs("MultCritAbAttr", source, null, simulated, criticalMultiplier); + applyAbAttrs("MultCritAbAttr", { pokemon: source, simulated, critMult: criticalMultiplier }); /** * A multiplier for random damage spread in the range [0.85, 1] @@ -3747,7 +3883,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { const burnDamageReductionCancelled = new BooleanHolder(false); if (!ignoreSourceAbility) { - applyAbAttrs("BypassBurnDamageReductionAbAttr", source, burnDamageReductionCancelled, simulated); + applyAbAttrs("BypassBurnDamageReductionAbAttr", { + pokemon: source, + cancelled: burnDamageReductionCancelled, + simulated, + }); } if (!burnDamageReductionCancelled.value) { burnMultiplier = 0.5; @@ -3811,7 +3951,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Doubles damage if the attacker has Tinted Lens and is using a resisted move */ if (!ignoreSourceAbility) { - applyPreAttackAbAttrs("DamageBoostAbAttr", source, this, move, simulated, damage); + applyAbAttrs("DamageBoostAbAttr", { + pokemon: source, + opponent: this, + move, + simulated, + damage, + }); } /** Apply the enemy's Damage and Resistance tokens */ @@ -3822,14 +3968,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.applyModifiers(EnemyDamageReducerModifier, false, damage); } + const abAttrParams: PreAttackModifyDamageAbAttrParams = { + pokemon: this, + opponent: source, + move, + simulated, + damage, + }; /** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */ if (!ignoreAbility) { - applyPreDefendAbAttrs("ReceivedMoveDamageMultiplierAbAttr", this, source, move, cancelled, simulated, damage); + applyAbAttrs("ReceivedMoveDamageMultiplierAbAttr", abAttrParams); const ally = this.getAlly(); /** Additionally apply friend guard damage reduction if ally has it. */ if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) { - applyPreDefendAbAttrs("AlliedFieldDamageReductionAbAttr", ally, source, move, cancelled, simulated, damage); + applyAbAttrs("AlliedFieldDamageReductionAbAttr", { + ...abAttrParams, + // Same parameters as before, except we are applying the ally's ability + pokemon: ally, + }); } } @@ -3837,7 +3994,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage); if (this.isFullHp() && !ignoreAbility) { - applyPreDefendAbAttrs("PreDefendFullHpEndureAbAttr", this, source, move, cancelled, false, damage); + applyAbAttrs("PreDefendFullHpEndureAbAttr", abAttrParams); } // debug message for when damage is applied (i.e. not simulated) @@ -3875,7 +4032,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const alwaysCrit = new BooleanHolder(false); applyMoveAttrs("CritOnlyAttr", source, this, move, alwaysCrit); - applyAbAttrs("ConditionalCritAbAttr", source, null, false, alwaysCrit, this, move); + applyAbAttrs("ConditionalCritAbAttr", { pokemon: source, isCritical: alwaysCrit, target: this, move }); const alwaysCritTag = !!source.getTag(BattlerTagType.ALWAYS_CRIT); const critChance = [24, 8, 2, 1][Phaser.Math.Clamp(this.getCritStage(source, move), 0, 3)]; @@ -3886,7 +4043,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // apply crit block effects from lucky chant & co., overriding previous effects const blockCrit = new BooleanHolder(false); - applyAbAttrs("BlockCritAbAttr", this, null, false, blockCrit); + applyAbAttrs("BlockCritAbAttr", { pokemon: this, blockCrit }); const blockCritTag = globalScene.arena.getTagOnSide( NoCritTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, @@ -3998,7 +4155,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr */ if (!source || source.turnData.hitCount <= 1) { - applyPostDamageAbAttrs("PostDamageAbAttr", this, damage, this.hasPassive(), false, [], source); + applyAbAttrs("PostDamageAbAttr", { pokemon: this, damage, source }); } return damage; } @@ -4046,11 +4203,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const stubTag = new BattlerTag(tagType, 0, 0); const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, stubTag, cancelled, true); + applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: stubTag, cancelled, simulated: true }); const userField = this.getAlliedField(); userField.forEach(pokemon => - applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, stubTag, cancelled, true, this), + applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { + pokemon, + tag: stubTag, + cancelled, + simulated: true, + target: this, + }), ); return !cancelled.value; @@ -4066,13 +4229,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, newTag, cancelled); + applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: newTag, cancelled }); if (cancelled.value) { return false; } for (const pokemon of this.getAlliedField()) { - applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, newTag, cancelled, false, this); + applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { pokemon, tag: newTag, cancelled, target: this }); if (cancelled.value) { return false; } @@ -4100,7 +4263,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getTag(tagType: Constructor): T | undefined; getTag(tagType: BattlerTagType | Constructor): BattlerTag | undefined { - return tagType instanceof Function + return typeof tagType === "function" ? this.summonData.tags.find(t => t instanceof tagType) : this.summonData.tags.find(t => t.tagType === tagType); } @@ -4277,6 +4440,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return null; } + /** + * Return this Pokemon's move history. + * Entries are sorted in order of OLDEST to NEWEST + * @returns An array of {@linkcode TurnMove}, as described above. + * @see {@linkcode getLastXMoves} + */ public getMoveHistory(): TurnMove[] { return this.summonData.moveHistory; } @@ -4290,19 +4459,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Returns a list of the most recent move entries in this Pokemon's move history. - * The retrieved move entries are sorted in order from NEWEST to OLDEST. - * @param moveCount The number of move entries to retrieve. - * If negative, retrieve the Pokemon's entire move history (equivalent to reversing the output of {@linkcode getMoveHistory()}). - * Default is `1`. - * @returns A list of {@linkcode TurnMove}, as specified above. + * Return a list of the most recent move entries in this {@linkcode Pokemon}'s move history. + * The retrieved move entries are sorted in order from **NEWEST** to **OLDEST**. + * @param moveCount - The maximum number of move entries to retrieve. + * If negative, retrieves the Pokemon's entire move history (equivalent to reversing the output of {@linkcode getMoveHistory()}). + * Default is `1`. + * @returns An array of {@linkcode TurnMove}, as specified above. */ + // TODO: Update documentation in dancer PR to mention "getLastNonVirtualMove" getLastXMoves(moveCount = 1): TurnMove[] { const moveHistory = this.getMoveHistory(); - if (moveCount >= 0) { + if (moveCount > 0) { return moveHistory.slice(Math.max(moveHistory.length - moveCount, 0)).reverse(); } - return moveHistory.slice(0).reverse(); + return moveHistory.slice().reverse(); } /** @@ -4373,9 +4543,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { try { SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2))); - fusionCry = this.getFusionSpeciesForm(undefined, true).cry( - Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig), - ); + fusionCry = this.getFusionSpeciesForm(undefined, true).cry({ + seek: Math.max(fusionCry.totalDuration * 0.4, 0), + ...soundConfig, + }); SoundFade.fadeIn( scene, fusionCry, @@ -4517,13 +4688,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (i === transitionIndex && fusionCryKey) { SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2))); - fusionCry = globalScene.playSound( - fusionCryKey, - Object.assign({ - seek: Math.max(fusionCry.totalDuration * 0.4, 0), - rate: rate, - }), - ); + fusionCry = globalScene.playSound(fusionCryKey, { + seek: Math.max(fusionCry.totalDuration * 0.4, 0), + rate: rate, + }); SoundFade.fadeIn( globalScene, fusionCry, @@ -4574,16 +4742,37 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } - queueImmuneMessage(quiet: boolean, effect?: StatusEffect): void { - if (!effect || quiet) { + /** + * Display an immunity message for a failed status application. + * @param quiet - Whether to suppress message and return early + * @param reason - The reason for the status application failure - + * can be "overlap" (already has same status), "other" (generic fail message) + * or a {@linkcode TerrainType} for terrain-based blockages. + * Defaults to "other". + */ + queueStatusImmuneMessage( + quiet: boolean, + reason: "overlap" | "other" | Exclude = "other", + ): void { + if (quiet) { return; } - const message = - effect && this.status?.effect === effect - ? getStatusEffectOverlapText(effect ?? StatusEffect.NONE, getPokemonNameWithAffix(this)) - : i18next.t("abilityTriggers:moveImmunity", { - pokemonNameWithAffix: getPokemonNameWithAffix(this), - }); + + let message: string; + if (reason === "overlap") { + // "XYZ is already XXX!" + message = getStatusEffectOverlapText(this.status?.effect ?? StatusEffect.NONE, getPokemonNameWithAffix(this)); + } else if (typeof reason === "number") { + // "XYZ was protected by the XXX terrain!" / + // "XYZ surrounds itself with a protective mist!" + message = getTerrainBlockMessage(this, reason); + } else { + // "It doesn't affect XXX!" + message = i18next.t("abilityTriggers:moveImmunity", { + pokemonNameWithAffix: getPokemonNameWithAffix(this), + }); + } + globalScene.phaseManager.queueMessage(message); } @@ -4597,7 +4786,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreField Whether any field effects (weather, terrain, etc.) should be considered */ canSetStatus( - effect: StatusEffect | undefined, + effect: StatusEffect, quiet = false, overrideStatus = false, sourcePokemon: Pokemon | null = null, @@ -4605,11 +4794,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ): boolean { if (effect !== StatusEffect.FAINT) { if (overrideStatus ? this.status?.effect === effect : this.status) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet, overrideStatus ? "overlap" : "other"); // having different status displays generic fail message return false; } if (this.isGrounded() && !ignoreField && globalScene.arena.terrain?.terrainType === TerrainType.MISTY) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet, TerrainType.MISTY); return false; } } @@ -4628,8 +4817,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity const cancelImmunity = new BooleanHolder(false); + // TODO: Determine if we need to pass `quiet` as the value for simulated in this call if (sourcePokemon) { - applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", sourcePokemon, cancelImmunity, false, effect, defType); + applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", { + pokemon: sourcePokemon, + cancelled: cancelImmunity, + statusEffect: effect, + defenderType: defType, + }); if (cancelImmunity.value) { return false; } @@ -4640,7 +4835,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.isOfType(PokemonType.POISON) || this.isOfType(PokemonType.STEEL)) { if (poisonImmunity.includes(true)) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet); return false; } } @@ -4648,13 +4843,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } case StatusEffect.PARALYSIS: if (this.isOfType(PokemonType.ELECTRIC)) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet); return false; } break; case StatusEffect.SLEEP: if (this.isGrounded() && globalScene.arena.terrain?.terrainType === TerrainType.ELECTRIC) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet, TerrainType.ELECTRIC); return false; } break; @@ -4665,34 +4860,33 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene?.arena?.weather?.weatherType && [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(globalScene.arena.weather.weatherType)) ) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet); return false; } break; case StatusEffect.BURN: if (this.isOfType(PokemonType.FIRE)) { - this.queueImmuneMessage(quiet, effect); + this.queueStatusImmuneMessage(quiet); return false; } break; } const cancelled = new BooleanHolder(false); - applyPreSetStatusAbAttrs("StatusEffectImmunityAbAttr", this, effect, cancelled, quiet); + applyAbAttrs("StatusEffectImmunityAbAttr", { pokemon: this, effect, cancelled, simulated: quiet }); if (cancelled.value) { return false; } for (const pokemon of this.getAlliedField()) { - applyPreSetStatusAbAttrs( - "UserFieldStatusEffectImmunityAbAttr", + applyAbAttrs("UserFieldStatusEffectImmunityAbAttr", { pokemon, effect, cancelled, - quiet, - this, - sourcePokemon, - ); + simulated: quiet, + target: this, + source: sourcePokemon, + }); if (cancelled.value) { break; } @@ -4723,6 +4917,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { overrideStatus?: boolean, quiet = true, ): boolean { + if (!effect) { + return false; + } if (!this.canSetStatus(effect, quiet, overrideStatus, sourcePokemon)) { return false; } @@ -4781,7 +4978,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } sleepTurnsRemaining = sleepTurnsRemaining!; // tell TS compiler it's defined - effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call this.status = new Status(effect, 0, sleepTurnsRemaining?.value); return true; @@ -4842,7 +5038,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { const bypassed = new BooleanHolder(false); if (attacker) { - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed }); } return !bypassed.value; } @@ -5316,10 +5512,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { for (let sc = 0; sc < spriteColors.length; sc++) { const delta = Math.min(...paletteDeltas[sc]); - const paletteIndex = Math.min( - paletteDeltas[sc].findIndex(pd => pd === delta), - fusionPalette.length - 1, - ); + const paletteIndex = Math.min(paletteDeltas[sc].indexOf(delta), fusionPalette.length - 1); if (delta < 255) { const ratio = easeFunc(delta / 255); const color = [0, 0, 0, fusionSpriteColors[sc][3]]; @@ -5391,7 +5584,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.hideInfo(); } // Trigger abilities that activate upon leaving the field - applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", this); + applyAbAttrs("PreLeaveFieldAbAttr", { pokemon: this }); this.setSwitchOutStatus(true); globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); globalScene.field.remove(this, destroy); @@ -5435,13 +5628,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Reduces one of this Pokemon's held item stacks by 1, and removes the item if applicable. + * Reduces one of this Pokemon's held item stacks by 1, removing it if applicable. * Does nothing if this Pokemon is somehow not the owner of the held item. - * @param heldItem The item stack to be reduced by 1. - * @param forBattle If `false`, do not trigger in-battle effects (such as Unburden) from losing the item. For example, set this to `false` if the Pokemon is giving away the held item for a Mystery Encounter. Default is `true`. - * @returns `true` if the item was removed successfully, `false` otherwise. + * @param heldItem - The item stack to be reduced. + * @param forBattle - Whether to trigger in-battle effects (such as Unburden) after losing the item. Default: `true` + * Should be `false` for all item loss occurring outside of battle (MEs, etc.). + * @returns Whether the item was removed successfully. */ public loseHeldItem(heldItem: PokemonHeldItemModifier, forBattle = true): boolean { + // TODO: What does a -1 pokemon id mean? if (heldItem.pokemonId !== -1 && heldItem.pokemonId !== this.id) { return false; } @@ -5451,7 +5646,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.removeModifier(heldItem, this.isEnemy()); } if (forBattle) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", this, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: this }); } return true; @@ -6113,22 +6308,23 @@ export class EnemyPokemon extends Pokemon { } /** - * Sets the pokemons boss status. If true initializes the boss segments either from the arguments - * or through the the Scene.getEncounterBossSegments function + * Set this {@linkcode EnemyPokemon}'s boss status. * - * @param boss if the pokemon is a boss - * @param bossSegments amount of boss segments (health-bar segments) + * @param boss - Whether this pokemon should be a boss; default `true` + * @param bossSegments - Optional amount amount of health bar segments to give; + * will be generated by {@linkcode BattleScene.getEncounterBossSegments} if omitted */ - setBoss(boss = true, bossSegments = 0): void { - if (boss) { - this.bossSegments = - bossSegments || - globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, this.level, this.species, true); - this.bossSegmentIndex = this.bossSegments - 1; - } else { + setBoss(boss = true, bossSegments?: number): void { + if (!boss) { this.bossSegments = 0; this.bossSegmentIndex = 0; + return; } + + this.bossSegments = + bossSegments ?? + globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, this.level, this.species, true); + this.bossSegmentIndex = this.bossSegments - 1; } generateAndPopulateMoveset(formIndex?: number): void { @@ -6706,241 +6902,3 @@ export class EnemyPokemon extends Pokemon { this.battleInfo.toggleFlyout(visible); } } - -/** - * Illusion property - */ -interface IllusionData { - basePokemon: { - /** The actual name of the Pokemon */ - name: string; - /** The actual nickname of the Pokemon */ - nickname: string; - /** Whether the base pokemon is shiny or not */ - shiny: boolean; - /** The shiny variant of the base pokemon */ - variant: Variant; - /** Whether the fusion species of the base pokemon is shiny or not */ - fusionShiny: boolean; - /** The variant of the fusion species of the base pokemon */ - fusionVariant: Variant; - }; - /** The species of the illusion */ - species: SpeciesId; - /** The formIndex of the illusion */ - formIndex: number; - /** The gender of the illusion */ - gender: Gender; - /** The pokeball of the illusion */ - pokeball: PokeballType; - /** The fusion species of the illusion if it's a fusion */ - fusionSpecies?: PokemonSpecies; - /** The fusionFormIndex of the illusion */ - fusionFormIndex?: number; - /** The fusionGender of the illusion if it's a fusion */ - fusionGender?: Gender; - /** The level of the illusion (not used currently) */ - level?: number; -} - -export interface TurnMove { - move: MoveId; - targets: BattlerIndex[]; - useMode: MoveUseMode; - result?: MoveResult; - turn?: number; -} - -export interface AttackMoveResult { - move: MoveId; - result: DamageResult; - damage: number; - critical: boolean; - sourceId: number; - sourceBattlerIndex: BattlerIndex; -} - -/** - * Persistent in-battle data for a {@linkcode Pokemon}. - * Resets on switch or new battle. - */ -export class PokemonSummonData { - /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ - public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; - /** - * A queue of moves yet to be executed, used by charging, recharging and frenzy moves. - * So long as this array is nonempty, this Pokemon's corresponding `CommandPhase` will be skipped over entirely - * in favor of using the queued move. - * TODO: Clean up a lot of the code surrounding the move queue. - */ - public moveQueue: TurnMove[] = []; - public tags: BattlerTag[] = []; - public abilitySuppressed = false; - - // Overrides for transform. - // TODO: Move these into a separate class & add rage fist hit count - public speciesForm: PokemonSpeciesForm | null = null; - public fusionSpeciesForm: PokemonSpeciesForm | null = null; - public ability: AbilityId | undefined; - public passiveAbility: AbilityId | undefined; - public gender: Gender | undefined; - public fusionGender: Gender | undefined; - public stats: number[] = [0, 0, 0, 0, 0, 0]; - public moveset: PokemonMove[] | null; - - // If not initialized this value will not be populated from save data. - public types: PokemonType[] = []; - public addedType: PokemonType | null = null; - - /** Data pertaining to this pokemon's illusion. */ - public illusion: IllusionData | null = null; - public illusionBroken = false; - - /** Array containing all berries eaten in the last turn; used by {@linkcode AbilityId.CUD_CHEW} */ - public berriesEatenLast: BerryType[] = []; - - /** - * An array of all moves this pokemon has used since entering the battle. - * Used for most moves and abilities that check prior move usage or copy already-used moves. - */ - public moveHistory: TurnMove[] = []; - - constructor(source?: PokemonSummonData | Partial) { - if (isNullOrUndefined(source)) { - return; - } - - // TODO: Rework this into an actual generic function for use elsewhere - for (const [key, value] of Object.entries(source)) { - if (isNullOrUndefined(value) && this.hasOwnProperty(key)) { - continue; - } - - if (key === "moveset") { - this.moveset = value?.map((m: any) => PokemonMove.loadMove(m)); - continue; - } - - if (key === "tags") { - // load battler tags - this.tags = value.map((t: BattlerTag) => loadBattlerTag(t)); - continue; - } - this[key] = value; - } - } -} - -// TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added -export class PokemonTempSummonData { - /** - * The number of turns this pokemon has spent without switching out. - * Only currently used for positioning the battle cursor. - */ - turnCount = 1; - - /** - * The number of turns this pokemon has spent in the active position since the start of the wave - * without switching out. - * Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file. - - * Used to evaluate "first turn only" conditions such as - * {@linkcode MoveId.FAKE_OUT | Fake Out} and {@linkcode MoveId.FIRST_IMPRESSION | First Impression}). - */ - waveTurnCount = 1; -} - -/** - * Persistent data for a {@linkcode Pokemon}. - * Resets at the start of a new battle (but not on switch). - */ -export class PokemonBattleData { - /** Counter tracking direct hits this Pokemon has received during this battle; used for {@linkcode MoveId.RAGE_FIST} */ - public hitCount = 0; - /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode MoveId.BELCH} */ - public hasEatenBerry = false; - /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode AbilityId.HARVEST} */ - public berriesEaten: BerryType[] = []; - - constructor(source?: PokemonBattleData | Partial) { - if (!isNullOrUndefined(source)) { - this.hitCount = source.hitCount ?? 0; - this.hasEatenBerry = source.hasEatenBerry ?? false; - this.berriesEaten = source.berriesEaten ?? []; - } - } -} - -/** - * Temporary data for a {@linkcode Pokemon}. - * Resets on new wave/battle start (but not on switch). - */ -export class PokemonWaveData { - /** Whether the pokemon has endured due to a {@linkcode BattlerTagType.ENDURE_TOKEN} */ - public endured = false; - /** - * A set of all the abilities this {@linkcode Pokemon} has used in this wave. - * Used to track once per battle conditions, as well as (hopefully) by the updated AI for move effectiveness. - */ - public abilitiesApplied: Set = new Set(); - /** Whether the pokemon's ability has been revealed or not */ - public abilityRevealed = false; -} - -/** - * Temporary data for a {@linkcode Pokemon}. - * Resets at the start of a new turn, as well as on switch. - */ -export class PokemonTurnData { - public acted = false; - /** How many times the current move should hit the target(s) */ - public hitCount = 0; - /** - * - `-1` = Calculate how many hits are left - * - `0` = Move is finished - */ - public hitsLeft = -1; - public totalDamageDealt = 0; - public singleHitDamageDealt = 0; - public damageTaken = 0; - public attacksReceived: AttackMoveResult[] = []; - public order: number; - public statStagesIncreased = false; - public statStagesDecreased = false; - public moveEffectiveness: TypeDamageMultiplier | null = null; - public combiningPledge?: MoveId; - public switchedInThisTurn = false; - public failedRunAway = false; - public joinedRound = false; - /** - * The amount of times this Pokemon has acted again and used a move in the current turn. - * Used to make sure multi-hits occur properly when the user is - * forced to act again in the same turn, and **must be incremented** by any effects that grant extra actions. - */ - public extraTurns = 0; - /** - * All berries eaten by this pokemon in this turn. - * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode AbilityId.CUD_CHEW} on turn end. - * @see {@linkcode PokemonSummonData.berriesEatenLast} - */ - public berriesEaten: BerryType[] = []; -} - -export type DamageResult = - | HitResult.EFFECTIVE - | HitResult.SUPER_EFFECTIVE - | HitResult.NOT_VERY_EFFECTIVE - | HitResult.ONE_HIT_KO - | HitResult.CONFUSION - | HitResult.INDIRECT_KO - | HitResult.INDIRECT; - -/** Interface containing the results of a damage calculation for a given move */ -export interface DamageCalculationResult { - /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ - cancelled: boolean; - /** The effectiveness of the move */ - result: HitResult; - /** The damage dealt by the move */ - damage: number; -} diff --git a/src/field/trainer.ts b/src/field/trainer.ts index b64821d259a..7186cc4e928 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -1,29 +1,31 @@ import { globalScene } from "#app/global-scene"; -import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import type { TrainerConfig } from "#app/data/trainers/trainer-config"; -import type { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { TrainerPoolTier } from "#enums/trainer-pool-tier"; -import { TeraAIMode } from "#enums/tera-ai-mode"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils/common"; -import type { PersistentModifier } from "#app/modifier/modifier"; -import { ArenaTrapTag } from "#app/data/arena-tag"; +import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import { signatureSpecies } from "#balance/signature-species"; +import { ArenaTrapTag } from "#data/arena-tag"; +import type { PokemonSpecies } from "#data/pokemon-species"; import { ArenaTagSide } from "#enums/arena-tag-side"; -import { getIsInitialized, initI18n } from "#app/plugins/i18n"; -import i18next from "i18next"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { SpeciesId } from "#enums/species-id"; +import { TeraAIMode } from "#enums/tera-ai-mode"; +import { TrainerPoolTier } from "#enums/trainer-pool-tier"; +import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerType } from "#enums/trainer-type"; -import { signatureSpecies } from "#app/data/balance/signature-species"; import { TrainerVariant } from "#enums/trainer-variant"; +import type { EnemyPokemon } from "#field/pokemon"; +import type { PersistentModifier } from "#modifiers/modifier"; +import { getIsInitialized, initI18n } from "#plugins/i18n"; +import type { TrainerConfig } from "#trainers/trainer-config"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { + TrainerPartyCompoundTemplate, + type TrainerPartyTemplate, + trainerPartyTemplates, +} from "#trainers/trainer-party-template"; +import { randSeedInt, randSeedItem, randSeedWeightedItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; -export default class Trainer extends Phaser.GameObjects.Container { +export class Trainer extends Phaser.GameObjects.Container { public config: TrainerConfig; public variant: TrainerVariant; public partyTemplateIndex: number; @@ -158,7 +160,7 @@ export default class Trainer extends Phaser.GameObjects.Container { * @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE. * @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false. * @returns {string} - The formatted name of the trainer. - **/ + */ getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle = false): string { // Get the base title based on the trainer slot and variant. let name = this.config.getTitle(trainerSlot, this.variant); @@ -421,7 +423,8 @@ export default class Trainer extends Phaser.GameObjects.Container { // If useNewSpeciesPool is true, we need to generate a new species from the new species pool, otherwise we generate a random species let species = useNewSpeciesPool - ? getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))]) + ? // TODO: should this use `randSeedItem`? + getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))]) : template.isSameSpecies(index) && index > offset ? getPokemonSpecies( battle.enemyParty[offset].species.getTrainerSpeciesForLevel( @@ -619,6 +622,8 @@ export default class Trainer extends Phaser.GameObjects.Container { if (maxScorePartyMemberIndexes.length > 1) { let rand: number; + // TODO: should this use `randSeedItem`? + globalScene.executeWithSeedOffset( () => (rand = randSeedInt(maxScorePartyMemberIndexes.length)), globalScene.currentBattle.turn << 2, diff --git a/src/game-mode.ts b/src/game-mode.ts index da6ef62e33c..c5ab120e218 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -1,21 +1,21 @@ -import i18next from "i18next"; -import type { FixedBattleConfigs } from "./battle"; -import { classicFixedBattles, FixedBattleConfig } from "./battle"; -import type { Challenge } from "./data/challenge"; -import { allChallenges, applyChallenges, copyChallenge } from "./data/challenge"; -import { ChallengeType } from "#enums/challenge-type"; -import type PokemonSpecies from "./data/pokemon-species"; -import { allSpecies } from "#app/data/data-lists"; -import type { Arena } from "./field/arena"; -import Overrides from "#app/overrides"; -import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common"; -import { BiomeId } from "#enums/biome-id"; -import { SpeciesId } from "#enums/species-id"; -import { Challenges } from "./enums/challenges"; +import { FixedBattleConfig } from "#app/battle"; +import { CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { getDailyStartingBiome } from "./data/daily-run"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES } from "./constants"; +import Overrides from "#app/overrides"; +import type { Challenge } from "#data/challenge"; +import { allChallenges, applyChallenges, copyChallenge } from "#data/challenge"; +import { getDailyStartingBiome } from "#data/daily-run"; +import { allSpecies } from "#data/data-lists"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { BiomeId } from "#enums/biome-id"; +import { ChallengeType } from "#enums/challenge-type"; +import { Challenges } from "#enums/challenges"; import { GameModes } from "#enums/game-modes"; +import { SpeciesId } from "#enums/species-id"; +import type { Arena } from "#field/arena"; +import { classicFixedBattles, type FixedBattleConfigs } from "#trainers/fixed-battle-configs"; +import { isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common"; +import i18next from "i18next"; interface GameModeConfig { isClassic?: boolean; @@ -164,14 +164,14 @@ export class GameMode implements GameModeConfig { if (waveIndex % 10 !== 1 && waveIndex % 10) { /** * Do not check X1 floors since there's a bug that stops trainer sprites from appearing - * after a X0 full party heal + * after a X0 full party heal, this also allows for a smoother biome transition for general gameplay feel */ const trainerChance = arena.getTrainerChance(); let allowTrainerBattle = true; if (trainerChance) { const waveBase = Math.floor(waveIndex / 10) * 10; - // Stop generic trainers from spawning in within 3 waves of a trainer battle - for (let w = Math.max(waveIndex - 3, waveBase + 2); w <= Math.min(waveIndex + 3, waveBase + 9); w++) { + // Stop generic trainers from spawning in within 2 waves of a fixed trainer battle + for (let w = Math.max(waveIndex - 2, waveBase + 2); w <= Math.min(waveIndex + 2, waveBase + 9); w++) { if (w === waveIndex) { continue; } diff --git a/src/global-event-manager.ts b/src/global-event-manager.ts index 3df3d17b5e9..5b67b4a6cc6 100644 --- a/src/global-event-manager.ts +++ b/src/global-event-manager.ts @@ -1,3 +1,3 @@ -import { TimedEventManager } from "./timed-event-manager"; +import { TimedEventManager } from "#app/timed-event-manager"; export const timedEventManager = new TimedEventManager(); diff --git a/src/global-scene.ts b/src/global-scene.ts index 76071bd7fac..3cc5a904db7 100644 --- a/src/global-scene.ts +++ b/src/global-scene.ts @@ -1,4 +1,4 @@ -import type BattleScene from "#app/battle-scene"; +import type { BattleScene } from "#app/battle-scene"; export let globalScene: BattleScene; diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 02a95f71ac4..1607e4ee74f 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -1,23 +1,23 @@ -import Phaser from "phaser"; -import { getEnumValues } from "#app/utils/common"; -import { deepCopy } from "#app/utils/data"; -import pad_generic from "./configs/inputs/pad_generic"; -import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES"; -import pad_xbox360 from "./configs/inputs/pad_xbox360"; -import pad_dualshock from "./configs/inputs/pad_dualshock"; -import pad_procon from "./configs/inputs/pad_procon"; -import { UiMode } from "#enums/ui-mode"; -import type SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler"; -import type SettingsKeyboardUiHandler from "./ui/settings/settings-keyboard-ui-handler"; -import cfg_keyboard_qwerty from "./configs/inputs/cfg_keyboard_qwerty"; -import { assign, getButtonWithKeycode, getIconForLatestInput, swap } from "#app/configs/inputs/configHandler"; import { globalScene } from "#app/global-scene"; -import type { SettingGamepad } from "#app/system/settings/settings-gamepad"; -import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import TouchControl from "#app/touch-controls"; +import { TouchControl } from "#app/touch-controls"; import { Button } from "#enums/buttons"; import { Device } from "#enums/devices"; -import MoveTouchControlsHandler from "./ui/settings/move-touch-controls-handler"; +import { UiMode } from "#enums/ui-mode"; +import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty"; +import { assign, getButtonWithKeycode, getIconForLatestInput, swap } from "#inputs/configHandler"; +import pad_dualshock from "#inputs/pad_dualshock"; +import pad_generic from "#inputs/pad_generic"; +import pad_procon from "#inputs/pad_procon"; +import pad_unlicensedSNES from "#inputs/pad_unlicensedSNES"; +import pad_xbox360 from "#inputs/pad_xbox360"; +import type { SettingGamepad } from "#system/settings-gamepad"; +import type { SettingKeyboard } from "#system/settings-keyboard"; +import { MoveTouchControlsHandler } from "#ui/move-touch-controls-handler"; +import type { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler"; +import type { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler"; +import { deepCopy } from "#utils/data"; +import { getEnumValues } from "#utils/enums"; +import Phaser from "phaser"; export interface DeviceMapping { [key: string]: number; @@ -70,20 +70,20 @@ const repeatInputDelayMillis = 250; * providing a unified interface for all input-related interactions. */ export class InputsController { - private gamepads: Array = new Array(); + private gamepads: Array = []; public events: Phaser.Events.EventEmitter; - private buttonLock: Button[] = new Array(); + private buttonLock: Button[] = []; private interactions: Map> = new Map(); private configs: Map = new Map(); public gamepadSupport = true; public selectedDevice; - private disconnectedGamepads: Array = new Array(); + private disconnectedGamepads: Array = []; public lastSource = "keyboard"; - private inputInterval: NodeJS.Timeout[] = new Array(); + private inputInterval: NodeJS.Timeout[] = []; private touchControls: TouchControl; public moveTouchControlsHandler: MoveTouchControlsHandler; diff --git a/src/loading-scene.ts b/src/loading-scene.ts index f67d19e1027..eb6883e0c68 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -1,28 +1,29 @@ -import { GachaType } from "#enums/gacha-types"; -import { getBiomeHasProps } from "#app/field/arena"; -import CacheBustedLoaderPlugin from "#app/plugins/cache-busted-loader-plugin"; +import { initAbilities } from "#abilities/ability"; +import { timedEventManager } from "#app/global-event-manager"; import { SceneBase } from "#app/scene-base"; -import { WindowVariant, getWindowVariantSuffix } from "#app/ui/ui-theme"; import { isMobile } from "#app/touch-controls"; -import { localPing, getEnumValues, hasAllLocalizedSprites, getEnumKeys } from "#app/utils/common"; -import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions"; -import { initBiomes } from "#app/data/balance/biomes"; -import { initEggMoves } from "#app/data/balance/egg-moves"; -import { initPokemonForms } from "#app/data/pokemon-forms"; -import { initSpecies } from "#app/data/pokemon-species"; -import { initMoves } from "#app/data/moves/move"; -import { initAbilities } from "#app/data/abilities/ability"; -import { initAchievements } from "#app/system/achv"; -import { initTrainerTypeDialogue } from "#app/data/dialogue"; -import { initChallenges } from "#app/data/challenge"; -import i18next from "i18next"; -import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; -import { initVouchers } from "#app/system/voucher"; +import { initBiomes } from "#balance/biomes"; +import { initEggMoves } from "#balance/egg-moves"; +import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions"; +import { initChallenges } from "#data/challenge"; +import { initTrainerTypeDialogue } from "#data/dialogue"; +import { initPokemonForms } from "#data/pokemon-forms"; +import { initSpecies } from "#data/pokemon-species"; import { BiomeId } from "#enums/biome-id"; -import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; -import { timedEventManager } from "./global-event-manager"; -import { initModifierPools } from "./modifier/init-modifier-pools"; -import { initModifierTypes } from "./modifier/modifier-type"; +import { GachaType } from "#enums/gacha-types"; +import { getBiomeHasProps } from "#field/arena"; +import { initModifierPools } from "#modifiers/init-modifier-pools"; +import { initModifierTypes } from "#modifiers/modifier-type"; +import { initMoves } from "#moves/move"; +import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters"; +import { CacheBustedLoaderPlugin } from "#plugins/cache-busted-loader-plugin"; +import { initAchievements } from "#system/achv"; +import { initVouchers } from "#system/voucher"; +import { initStatsKeys } from "#ui/game-stats-ui-handler"; +import { getWindowVariantSuffix, WindowVariant } from "#ui/ui-theme"; +import { hasAllLocalizedSprites, localPing } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import i18next from "i18next"; export class LoadingScene extends SceneBase { public static readonly KEY = "loading"; @@ -270,7 +271,7 @@ export class LoadingScene extends SceneBase { this.loadAtlas("egg_icons", "egg"); this.loadAtlas("egg_shard", "egg"); this.loadAtlas("egg_lightrays", "egg"); - for (const gt of getEnumKeys(GachaType)) { + for (const gt of Object.keys(GachaType)) { const key = gt.toLowerCase(); this.loadImage(`gacha_${key}`, "egg"); this.loadAtlas(`gacha_underlay_${key}`, "egg"); diff --git a/src/main.ts b/src/main.ts index 38bfcbe5636..7e4943bdca5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,14 @@ +import "#app/polyfills"; +// All polyfills MUST be loaded first for side effects + +import { InvertPostFX } from "#app/pipelines/invert"; +import { initI18n } from "#app/plugins/i18n"; +import { version } from "#package.json"; import Phaser from "phaser"; -import InvertPostFX from "./pipelines/invert"; -import { version } from "../package.json"; -import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin"; import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin"; import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin"; -import { initI18n } from "./plugins/i18n"; +import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; // Catch global errors and display them in an alert so users can report the issue. window.onerror = (_message, _source, _lineno, _colno, error) => { @@ -42,11 +45,13 @@ Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems")); // biome-ignore lint/suspicious/noImplicitAnyLet: TODO let game; +// biome-ignore lint/suspicious/noImplicitAnyLet: TODO +let manifest; -const startGame = async (manifest?: any) => { +const startGame = async () => { await initI18n(); const LoadingScene = (await import("./loading-scene")).LoadingScene; - const BattleScene = (await import("./battle-scene")).default; + const BattleScene = (await import("./battle-scene")).BattleScene; game = new Phaser.Game({ type: Phaser.WEBGL, parent: "app", @@ -107,10 +112,13 @@ const startGame = async (manifest?: any) => { fetch("/manifest.json") .then(res => res.json()) .then(jsonResponse => { - startGame(jsonResponse.manifest); + manifest = jsonResponse.manifest; }) - .catch(() => { - // Manifest not found (likely local build) + .catch(err => { + // Manifest not found (likely local build or path error on live) + console.log(`Manifest not found. ${err}`); + }) + .finally(() => { startGame(); }); diff --git a/src/messages.ts b/src/messages.ts index 21473eb8361..177b4cc9b05 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { BattleSpec } from "#enums/battle-spec"; -import type Pokemon from "./field/pokemon"; +import type { Pokemon } from "#field/pokemon"; import i18next from "i18next"; /** diff --git a/src/modifier/init-modifier-pools.ts b/src/modifier/init-modifier-pools.ts index 60697333600..316d4dae741 100644 --- a/src/modifier/init-modifier-pools.ts +++ b/src/modifier/init-modifier-pools.ts @@ -1,31 +1,37 @@ -import type Pokemon from "#app/field/pokemon"; +/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ +import type { initModifierTypes } from "#modifiers/modifier-type"; +/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */ + +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { modifierTypes } from "#data/data-lists"; +import { MAX_PER_TYPE_POKEBALLS } from "#data/pokeball"; +import { AbilityId } from "#enums/ability-id"; +import { BerryType } from "#enums/berry-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { Unlockables } from "#enums/unlockables"; +import type { Pokemon } from "#field/pokemon"; +import { + BerryModifier, + DoubleBattleChanceBoosterModifier, + SpeciesCritBoosterModifier, + TurnStatusEffectModifier, +} from "#modifiers/modifier"; import { dailyStarterModifierPool, enemyBuffModifierPool, modifierPool, trainerModifierPool, wildModifierPool, -} from "#app/modifier/modifier-pools"; -import { globalScene } from "#app/global-scene"; -import { DoubleBattleChanceBoosterModifier, SpeciesCritBoosterModifier, TurnStatusEffectModifier } from "./modifier"; -import { WeightedModifierType } from "./modifier-type"; -import { ModifierTier } from "../enums/modifier-tier"; -import type { WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types"; -import { modifierTypes } from "#app/data/data-lists"; -import { PokeballType } from "#enums/pokeball"; -import { BerryModifier } from "./modifier"; -import { BerryType } from "#enums/berry-type"; -import { SpeciesId } from "#enums/species-id"; -import { timedEventManager } from "#app/global-event-manager"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { Unlockables } from "#enums/unlockables"; -import { isNullOrUndefined } from "#app/utils/common"; -import { MoveId } from "#enums/move-id"; -import { StatusEffect } from "#enums/status-effect"; -import { AbilityId } from "#enums/ability-id"; -import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; -// biome-ignore lint/correctness/noUnusedImports: This is used in a tsdoc comment -import type { initModifierTypes } from "./modifier-type"; +} from "#modifiers/modifier-pools"; +import { WeightedModifierType } from "#modifiers/modifier-type"; +import type { WeightedModifierTypeWeightFunc } from "#types/modifier-types"; +import { isNullOrUndefined } from "#utils/common"; /** * Initialize the wild modifier pool diff --git a/src/modifier/modifier-pools.ts b/src/modifier/modifier-pools.ts index 3396dca1f93..d66511e3239 100644 --- a/src/modifier/modifier-pools.ts +++ b/src/modifier/modifier-pools.ts @@ -1,9 +1,9 @@ -/** +/* * Contains modifier pools for different contexts in the game. * Can be safely imported without worrying about circular dependencies. */ -import type { ModifierPool } from "#app/@types/modifier-types"; +import type { ModifierPool } from "#types/modifier-types"; export const modifierPool: ModifierPool = {}; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index a04a5e2be47..b359ec756e6 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1,19 +1,32 @@ +import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; +import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms"; -import { getBerryEffectDescription, getBerryName } from "#app/data/berry"; -import { allMoves, modifierTypes } from "#app/data/data-lists"; -import { getNatureName, getNatureStatMultiplier } from "#app/data/nature"; -import { getPokeballCatchMultiplier, getPokeballName } from "#app/data/pokeball"; -import { pokemonFormChanges, SpeciesFormChangeCondition } from "#app/data/pokemon-forms"; -import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { FormChangeItem } from "#enums/form-change-item"; -import { getStatusEffectDescriptor } from "#app/data/status-effect"; -import { PokemonType } from "#enums/pokemon-type"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; +import Overrides from "#app/overrides"; +import { EvolutionItem, pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { tmPoolTiers, tmSpecies } from "#balance/tms"; +import { getBerryEffectDescription, getBerryName } from "#data/berry"; +import { allMoves, modifierTypes } from "#data/data-lists"; +import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; +import { getNatureName, getNatureStatMultiplier } from "#data/nature"; +import { getPokeballCatchMultiplier, getPokeballName } from "#data/pokeball"; +import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-forms"; +import { getStatusEffectDescriptor } from "#data/status-effect"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { FormChangeItem } from "#enums/form-change-item"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { Nature } from "#enums/nature"; +import { PokeballType } from "#enums/pokeball"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesFormKey } from "#enums/species-form-key"; +import { SpeciesId } from "#enums/species-id"; +import type { PermanentStat, TempBattleStat } from "#enums/stat"; +import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { AddPokeballModifier, AddVoucherModifier, @@ -24,6 +37,7 @@ import { BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, + CriticalCatchChanceBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, @@ -31,6 +45,7 @@ import { EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, + type EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, @@ -40,6 +55,7 @@ import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, + FieldEffectModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, @@ -52,10 +68,12 @@ import { LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, + type Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, + type PersistentModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, @@ -83,6 +101,7 @@ import { SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, + TempExtraModifierModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerrastalizeModifier, @@ -90,44 +109,17 @@ import { TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, - type EnemyPersistentModifier, - type Modifier, - type PersistentModifier, - TempExtraModifierModifier, - CriticalCatchChanceBoosterModifier, - FieldEffectModifier, -} from "#app/modifier/modifier"; -import { ModifierTier } from "#enums/modifier-tier"; -import Overrides from "#app/overrides"; -import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher"; -import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler"; -import PartyUiHandler from "#app/ui/party-ui-handler"; -import { getModifierTierTextTint } from "#app/ui/text"; -import { - formatMoney, - getEnumKeys, - getEnumValues, - isNullOrUndefined, - NumberHolder, - padInt, - randSeedInt, -} from "#app/utils/common"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BerryType } from "#enums/berry-type"; -import { MoveId } from "#enums/move-id"; -import { Nature } from "#enums/nature"; -import { PokeballType } from "#enums/pokeball"; -import { SpeciesId } from "#enums/species-id"; -import { SpeciesFormKey } from "#enums/species-form-key"; -import type { PermanentStat, TempBattleStat } from "#enums/stat"; -import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; -import { StatusEffect } from "#enums/status-effect"; +} from "#modifiers/modifier"; +import type { PokemonMove } from "#moves/pokemon-move"; +import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#system/voucher"; +import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#types/modifier-types"; +import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler"; +import { PartyUiHandler } from "#ui/party-ui-handler"; +import { getModifierTierTextTint } from "#ui/text"; +import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#utils/common"; +import { getEnumKeys, getEnumValues } from "#utils/enums"; +import { getModifierPoolForType, getModifierType } from "#utils/modifier-utils"; import i18next from "i18next"; -import { timedEventManager } from "#app/global-event-manager"; -import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { getModifierPoolForType, getModifierType } from "#app/utils/modifier-utils"; -import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -967,31 +959,23 @@ export class PokemonBaseStatTotalModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType { - private readonly statModifier: number; + private readonly statModifier: 10 | -15; - constructor(statModifier: number) { + constructor(statModifier: 10 | -15) { super( - "modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE", - "berry_juice", - (_type, args) => new PokemonBaseStatTotalModifier(this, (args[0] as Pokemon).id, this.statModifier), + statModifier > 0 + ? "modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE_GOOD" + : "modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE_BAD", + statModifier > 0 ? "berry_juice_good" : "berry_juice_bad", + (_type, args) => new PokemonBaseStatTotalModifier(this, (args[0] as Pokemon).id, statModifier), ); this.statModifier = statModifier; } override getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonBaseStatTotalModifierType.description", { - increaseDecrease: i18next.t( - this.statModifier >= 0 - ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.increase" - : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.decrease", - ), - blessCurse: i18next.t( - this.statModifier >= 0 - ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.blessed" - : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.cursed", - ), - statValue: this.statModifier, - }); + return this.statModifier > 0 + ? i18next.t("modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE_GOOD.description") + : i18next.t("modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE_BAD.description"); } public getPregenArgs(): any[] { @@ -999,38 +983,6 @@ export class PokemonBaseStatTotalModifierType } } -/** - * Old Gateau item - */ -export class PokemonBaseStatFlatModifierType - extends PokemonHeldItemModifierType - implements GeneratedPersistentModifierType -{ - private readonly statModifier: number; - private readonly stats: Stat[]; - - constructor(statModifier: number, stats: Stat[]) { - super( - "modifierType:ModifierType.MYSTERY_ENCOUNTER_OLD_GATEAU", - "old_gateau", - (_type, args) => new PokemonBaseStatFlatModifier(this, (args[0] as Pokemon).id, this.statModifier, this.stats), - ); - this.statModifier = statModifier; - this.stats = stats; - } - - override getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonBaseStatFlatModifierType.description", { - stats: this.stats.map(stat => i18next.t(getStatKey(stat))).join("/"), - statValue: this.statModifier, - }); - } - - public getPregenArgs(): any[] { - return [this.statModifier, this.stats]; - } -} - class AllPokemonFullHpRestoreModifierType extends ModifierType { private descriptionKey: string; @@ -1565,6 +1517,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator { if (!tierUniqueCompatibleTms.length) { return null; } + // TODO: should this use `randSeedItem`? const randTmIndex = randSeedInt(tierUniqueCompatibleTms.length); return new TmModifierType(tierUniqueCompatibleTms[randTmIndex]); }); @@ -1585,7 +1538,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { pokemonEvolutions.hasOwnProperty(p.species.speciesId) && (!p.pauseEvolutions || p.species.speciesId === SpeciesId.SLOWPOKE || - p.species.speciesId === SpeciesId.EEVEE), + p.species.speciesId === SpeciesId.EEVEE || + p.species.speciesId === SpeciesId.KIRLIA || + p.species.speciesId === SpeciesId.SNORUNT), ) .flatMap(p => { const evolutions = pokemonEvolutions[p.species.speciesId]; @@ -1599,7 +1554,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && (!p.pauseEvolutions || p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE || - p.fusionSpecies.speciesId === SpeciesId.EEVEE), + p.fusionSpecies.speciesId === SpeciesId.EEVEE || + p.fusionSpecies.speciesId === SpeciesId.KIRLIA || + p.fusionSpecies.speciesId === SpeciesId.SNORUNT), ) .flatMap(p => { const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; @@ -1614,6 +1571,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { return null; } + // TODO: should this use `randSeedItem`? return new EvolutionItemModifierType(evolutionItemPool[randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? }); } @@ -1699,6 +1657,7 @@ export class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { return null; } + // TODO: should this use `randSeedItem`? return new FormChangeItemModifierType(formChangeItemPool[randSeedInt(formChangeItemPool.length)]); }); } @@ -1969,7 +1928,7 @@ const modifierTypeInitObj = Object.freeze({ if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) { return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature); } - return new PokemonNatureChangeModifierType(randSeedInt(getEnumValues(Nature).length) as Nature); + return new PokemonNatureChangeModifierType(randSeedItem(getEnumValues(Nature))); }), MYSTICAL_ROCK: () => @@ -2327,17 +2286,16 @@ const modifierTypeInitObj = Object.freeze({ MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => { if (pregenArgs) { - return new PokemonBaseStatTotalModifierType(pregenArgs[0] as number); + return new PokemonBaseStatTotalModifierType(pregenArgs[0] as 10 | -15); } - return new PokemonBaseStatTotalModifierType(randSeedInt(20, 1)); + return new PokemonBaseStatTotalModifierType(10); }), MYSTERY_ENCOUNTER_OLD_GATEAU: () => - new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs) { - return new PokemonBaseStatFlatModifierType(pregenArgs[0] as number, pregenArgs[1] as Stat[]); - } - return new PokemonBaseStatFlatModifierType(randSeedInt(20, 1), [Stat.HP, Stat.ATK, Stat.DEF]); - }), + new PokemonHeldItemModifierType( + "modifierType:ModifierType.MYSTERY_ENCOUNTER_OLD_GATEAU", + "old_gateau", + (type, args) => new PokemonBaseStatFlatModifier(type, (args[0] as Pokemon).id), + ), MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => { if (pregenArgs) { @@ -2494,12 +2452,31 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod } export interface CustomModifierSettings { + /** If specified, will override the next X items to be the specified tier. These can upgrade with luck. */ guaranteedModifierTiers?: ModifierTier[]; + /** If specified, will override the first X items to be specific modifier options (these should be pre-genned). */ guaranteedModifierTypeOptions?: ModifierTypeOption[]; + /** If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned). */ guaranteedModifierTypeFuncs?: ModifierTypeFunc[]; + /** + * If set to `true`, will fill the remainder of shop items that were not overridden by the 3 options above, up to the `count` param value. + * @example + * ```ts + * count = 4; + * customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true }; + * ``` + * The first item in the shop will be `GREAT` tier, and the remaining `3` items will be generated normally. + * + * If `fillRemaining: false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of the value of `count`). + * @defaultValue `false` + */ fillRemaining?: boolean; - /** Set to negative value to disable rerolls completely in shop */ + /** If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all. */ rerollMultiplier?: number; + /** + * If `false`, will prevent set item tiers from upgrading via luck. + * @defaultValue `true` + */ allowLuckUpgrades?: boolean; } @@ -2509,19 +2486,10 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc { /** * Generates modifier options for a {@linkcode SelectModifierPhase} - * @param count Determines the number of items to generate - * @param party Party is required for generating proper modifier pools - * @param modifierTiers (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule. - * @param customModifierSettings (Optional) If specified, can customize the item shop rewards further. - * - `guaranteedModifierTypeOptions?: ModifierTypeOption[]` If specified, will override the first X items to be specific modifier options (these should be pre-genned). - * - `guaranteedModifierTypeFuncs?: ModifierTypeFunc[]` If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned). - * - `guaranteedModifierTiers?: ModifierTier[]` If specified, will override the next X items to be the specified tier. These can upgrade with luck. - * - `fillRemaining?: boolean` Default 'false'. If set to true, will fill the remainder of shop items that were not overridden by the 3 options above, up to the 'count' param value. - * - Example: `count = 4`, `customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true }`, - * - The first item in the shop will be `GREAT` tier, and the remaining 3 items will be generated normally. - * - If `fillRemaining = false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of `count` value). - * - `rerollMultiplier?: number` If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all. - * - `allowLuckUpgrades?: boolean` Default `true`, if `false` will prevent set item tiers from upgrading via luck + * @param count - Determines the number of items to generate + * @param party - Party is required for generating proper modifier pools + * @param modifierTiers - (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule. + * @param customModifierSettings - See {@linkcode CustomModifierSettings} */ export function getPlayerModifierTypeOptions( count: number, @@ -2532,16 +2500,10 @@ export function getPlayerModifierTypeOptions( const options: ModifierTypeOption[] = []; const retryCount = Math.min(count * 5, 50); if (!customModifierSettings) { - new Array(count).fill(0).map((_, i) => { - options.push( - getModifierTypeOptionWithRetry( - options, - retryCount, - party, - modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined, - ), - ); - }); + for (let i = 0; i < count; i++) { + const tier = modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined; + options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier)); + } } else { // Guaranteed mod options first if ( diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 54b7323569a..f8c35b3e8f9 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,30 +1,29 @@ -import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; -import { getLevelTotalExp } from "#app/data/exp"; -import { allMoves, modifierTypes } from "#app/data/data-lists"; -import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; -import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import type { FormChangeItem } from "#enums/form-change-item"; -import { getStatusEffectHealText } from "#app/data/status-effect"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; -import { LearnMoveType } from "#enums/learn-move-type"; -import type { VoucherType } from "#app/system/voucher"; -import { Command } from "#enums/command"; -import { addTextObject, TextStyle } from "#app/ui/text"; -import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common"; +import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters"; +import { getBerryEffectFunc, getBerryPredicate } from "#data/berry"; +import { allMoves, modifierTypes } from "#data/data-lists"; +import { getLevelTotalExp } from "#data/exp"; +import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; +import { MAX_PER_TYPE_POKEBALLS } from "#data/pokeball"; +import { getStatusEffectHealText } from "#data/status-effect"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; +import { Color, ShadowColor } from "#enums/color"; +import { Command } from "#enums/command"; +import type { FormChangeItem } from "#enums/form-change-item"; +import { LearnMoveType } from "#enums/learn-move-type"; import type { MoveId } from "#enums/move-id"; import type { Nature } from "#enums/nature"; import type { PokeballType } from "#enums/pokeball"; -import { SpeciesId } from "#enums/species-id"; -import { type PermanentStat, type TempBattleStat, BATTLE_STATS, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; -import { StatusEffect } from "#enums/status-effect"; import type { PokemonType } from "#enums/pokemon-type"; -import i18next from "i18next"; +import { SpeciesId } from "#enums/species-id"; +import { BATTLE_STATS, type PermanentStat, Stat, TEMP_BATTLE_STATS, type TempBattleStat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import type { DoubleBattleChanceBoosterModifierType, EvolutionItemModifierType, @@ -38,13 +37,13 @@ import type { PokemonMultiHitModifierType, TerastallizeModifierType, TmModifierType, -} from "./modifier-type"; -import { getModifierType } from "#app/utils/modifier-utils"; -import { Color, ShadowColor } from "#enums/color"; -import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters"; -import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { globalScene } from "#app/global-scene"; -import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types"; +} from "#modifiers/modifier-type"; +import type { VoucherType } from "#system/voucher"; +import type { ModifierInstanceMap, ModifierString } from "#types/modifier-types"; +import { addTextObject, TextStyle } from "#ui/text"; +import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#utils/common"; +import { getModifierType } from "#utils/modifier-utils"; +import i18next from "i18next"; export type ModifierPredicate = (modifier: Modifier) => boolean; @@ -53,25 +52,11 @@ const iconOverflowIndex = 24; export const modifierSortFunc = (a: Modifier, b: Modifier): number => { const itemNameMatch = a.type.name.localeCompare(b.type.name); const typeNameMatch = a.constructor.name.localeCompare(b.constructor.name); - const aId = a instanceof PokemonHeldItemModifier && a.pokemonId ? a.pokemonId : 4294967295; - const bId = b instanceof PokemonHeldItemModifier && b.pokemonId ? b.pokemonId : 4294967295; + const aId = a instanceof PokemonHeldItemModifier ? a.pokemonId : -1; + const bId = b instanceof PokemonHeldItemModifier ? b.pokemonId : -1; - //First sort by pokemonID - if (aId < bId) { - return 1; - } - if (aId > bId) { - return -1; - } - if (aId === bId) { - //Then sort by item type - if (typeNameMatch === 0) { - return itemNameMatch; - //Finally sort by item name - } - return typeNameMatch; - } - return 0; + // First sort by pokemon ID, then by item type and then name + return aId - bId || typeNameMatch || itemNameMatch; }; export class ModifierBar extends Phaser.GameObjects.Container { @@ -751,14 +736,14 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { } getPokemon(): Pokemon | undefined { - return this.pokemonId ? (globalScene.getPokemonById(this.pokemonId) ?? undefined) : undefined; + return globalScene.getPokemonById(this.pokemonId) ?? undefined; } getScoreMultiplier(): number { return 1; } - getMaxStackCount(forThreshold?: boolean): number { + getMaxStackCount(forThreshold = false): number { const pokemon = this.getPokemon(); if (!pokemon) { return 0; @@ -952,10 +937,9 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier { export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { public override type: PokemonBaseStatTotalModifierType; public isTransferable = false; + public statModifier: 10 | -15; - private statModifier: number; - - constructor(type: PokemonBaseStatTotalModifierType, pokemonId: number, statModifier: number, stackCount?: number) { + constructor(type: PokemonBaseStatTotalModifierType, pokemonId: number, statModifier: 10 | -15, stackCount?: number) { super(type, pokemonId, stackCount); this.statModifier = statModifier; } @@ -1012,31 +996,14 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { * Currently used by Old Gateau item */ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { - private statModifier: number; - private stats: Stat[]; public isTransferable = false; - constructor(type: ModifierType, pokemonId: number, statModifier: number, stats: Stat[], stackCount?: number) { - super(type, pokemonId, stackCount); - - this.statModifier = statModifier; - this.stats = stats; - } - override matchType(modifier: Modifier): boolean { - return ( - modifier instanceof PokemonBaseStatFlatModifier && - modifier.statModifier === this.statModifier && - this.stats.every(s => modifier.stats.some(stat => s === stat)) - ); + return modifier instanceof PokemonBaseStatFlatModifier; } override clone(): PersistentModifier { - return new PokemonBaseStatFlatModifier(this.type, this.pokemonId, this.statModifier, this.stats, this.stackCount); - } - - override getArgs(): any[] { - return [...super.getArgs(), this.statModifier, this.stats]; + return new PokemonBaseStatFlatModifier(this.type, this.pokemonId, this.stackCount); } /** @@ -1055,11 +1022,13 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { * @param baseStats The base stats of the {@linkcode Pokemon} * @returns always `true` */ - override apply(_pokemon: Pokemon, baseStats: number[]): boolean { + override apply(pokemon: Pokemon, baseStats: number[]): boolean { // Modifies the passed in baseStats[] array by a flat value, only if the stat is specified in this.stats + const stats = this.getStats(pokemon); + const statModifier = 20; baseStats.forEach((v, i) => { - if (this.stats.includes(i)) { - const newVal = Math.floor(v + this.statModifier); + if (stats.includes(i)) { + const newVal = Math.floor(v + statModifier); baseStats[i] = Math.min(Math.max(newVal, 1), 999999); } }); @@ -1067,6 +1036,22 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { return true; } + /** + * Get the lowest of HP/Spd, lowest of Atk/SpAtk, and lowest of Def/SpDef + * @returns Array of 3 {@linkcode Stat}s to boost + */ + getStats(pokemon: Pokemon): Stat[] { + const stats: Stat[] = []; + const baseStats = pokemon.getSpeciesForm().baseStats.slice(0); + // HP or Speed + stats.push(baseStats[Stat.HP] < baseStats[Stat.SPD] ? Stat.HP : Stat.SPD); + // 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); + return stats; + } + override getScoreMultiplier(): number { return 1.1; } @@ -1879,7 +1864,7 @@ export class BerryModifier extends PokemonHeldItemModifier { // munch the berry and trigger unburden-like effects getBerryEffectFunc(this.berryType)(pokemon); - applyPostItemLostAbAttrs("PostItemLostAbAttr", pokemon, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon }); // Update berry eaten trackers for Belch, Harvest, Cud Chew, etc. // Don't recover it if we proc berry pouch (no item duplication) @@ -1967,7 +1952,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { // Reapply Commander on the Pokemon's side of the field, if applicable const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { - applyAbAttrs("CommanderAbAttr", p, null, false); + applyAbAttrs("CommanderAbAttr", { pokemon: p }); } return true; } @@ -2815,6 +2800,7 @@ export class PokemonMultiHitModifier extends PokemonHeldItemModifier { damageMultiplier.value *= 1 - 0.25 * this.getStackCount(); return true; } + if (pokemon.turnData.hitCount - pokemon.turnData.hitsLeft !== this.getStackCount() + 1) { // Deal 25% damage for each remaining Multi Lens hit damageMultiplier.value *= 0.25; diff --git a/src/overrides.ts b/src/overrides.ts index b390b9fa70f..de0d1d3f30a 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -1,18 +1,16 @@ import { type PokeballCounts } from "#app/battle-scene"; -import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; -import { Gender } from "#app/data/gender"; -import { FormChangeItem } from "#enums/form-change-item"; -import { type ModifierOverride } from "#app/modifier/modifier-type"; -import { Variant } from "#app/sprites/variant"; -import { Unlockables } from "#enums/unlockables"; +import { EvolutionItem } from "#balance/pokemon-evolutions"; +import { Gender } from "#data/gender"; import { AbilityId } from "#enums/ability-id"; import { BattleType } from "#enums/battle-type"; import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; import { EggTier } from "#enums/egg-type"; +import { FormChangeItem } from "#enums/form-change-item"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; import { PokeballType } from "#enums/pokeball"; import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; @@ -20,8 +18,11 @@ import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { TimeOfDay } from "#enums/time-of-day"; import { TrainerType } from "#enums/trainer-type"; +import { Unlockables } from "#enums/unlockables"; import { VariantTier } from "#enums/variant-tier"; import { WeatherType } from "#enums/weather-type"; +import { type ModifierOverride } from "#modifiers/modifier-type"; +import { Variant } from "#sprites/variant"; /** * This comment block exists to prevent IDEs from automatically removing unused imports @@ -118,7 +119,13 @@ class DefaultOverrides { * or `false` to force it to never trigger. */ readonly CONFUSION_ACTIVATION_OVERRIDE: boolean | null = null; - + /** + * If non-null, will override random flee attempts to always or never succeed by forcing {@linkcode calculateEscapeChance} to return 100% or 0%. + * Set to `null` to disable. + * + * Is overridden if either player Pokemon has {@linkcode AbilityId.RUN_AWAY | Run Away}. + */ + readonly RUN_SUCCESS_OVERRIDE: boolean | null = null; // ---------------- // PLAYER OVERRIDES // ---------------- @@ -159,10 +166,20 @@ class DefaultOverrides { readonly MOVESET_OVERRIDE: MoveId | Array = []; readonly SHINY_OVERRIDE: boolean | null = null; readonly VARIANT_OVERRIDE: Variant | null = null; + /** + * Overrides the IVs of player pokemon. Values must never be outside the range `0` to `31`! + * - If set to a number between `0` and `31`, set all IVs of all player pokemon to that number. + * - If set to an array, set the IVs of all player pokemon to that array. Array length must be exactly `6`! + * - If set to `null`, disable the override. + */ + readonly IVS_OVERRIDE: number | number[] | null = null; + /** Override the nature of all player pokemon to the specified nature. Disabled if `null`. */ + readonly NATURE_OVERRIDE: Nature | null = null; // -------------------------- // OPPONENT / ENEMY OVERRIDES // -------------------------- + // TODO: rename `OPP_` to `ENEMY_` readonly OPP_SPECIES_OVERRIDE: SpeciesId | number = 0; /** * This will make all opponents fused Pokemon @@ -181,7 +198,15 @@ class DefaultOverrides { readonly OPP_MOVESET_OVERRIDE: MoveId | Array = []; readonly OPP_SHINY_OVERRIDE: boolean | null = null; readonly OPP_VARIANT_OVERRIDE: Variant | null = null; - readonly OPP_IVS_OVERRIDE: number | number[] = []; + /** + * Overrides the IVs of enemy pokemon. Values must never be outside the range `0` to `31`! + * - If set to a number between `0` and `31`, set all IVs of all enemy pokemon to that number. + * - If set to an array, set the IVs of all enemy pokemon to that array. Array length must be exactly `6`! + * - If set to `null`, disable the override. + */ + readonly ENEMY_IVS_OVERRIDE: number | number[] | null = null; + /** Override the nature of all enemy pokemon to the specified nature. Disabled if `null`. */ + readonly ENEMY_NATURE_OVERRIDE: Nature | null = null; readonly OPP_FORM_OVERRIDES: Partial> = {}; /** * Override to give the enemy Pokemon a given amount of health segments diff --git a/src/phase-manager.ts b/src/phase-manager.ts index 9390e6dd75d..73ea373904f 100644 --- a/src/phase-manager.ts +++ b/src/phase-manager.ts @@ -1,106 +1,106 @@ -import type { Phase } from "#app/phase"; -import type { default as Pokemon } from "#app/field/pokemon"; -import type { PhaseMap, PhaseString } from "./@types/phase-types"; import { globalScene } from "#app/global-scene"; -import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase"; -import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase"; -import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase"; -import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; -import { coerceArray, type Constructor } from "#app/utils/common"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; +import type { Phase } from "#app/phase"; +import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#data/phase-priority-queue"; import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; -import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; -import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; -import { EggSummaryPhase } from "#app/phases/egg-summary-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EndCardPhase } from "#app/phases/end-card-phase"; -import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { EvolutionPhase } from "#app/phases/evolution-phase"; -import { ExpPhase } from "#app/phases/exp-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { FormChangePhase } from "#app/phases/form-change-phase"; -import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; -import { GameOverPhase } from "#app/phases/game-over-phase"; -import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; -import { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { LevelCapPhase } from "#app/phases/level-cap-phase"; -import { LevelUpPhase } from "#app/phases/level-up-phase"; -import { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase"; -import { LoginPhase } from "#app/phases/login-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; -import { MoneyRewardPhase } from "#app/phases/money-reward-phase"; -import { MoveAnimPhase } from "#app/phases/move-anim-phase"; -import { MoveChargePhase } from "#app/phases/move-charge-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { MoveHeaderPhase } from "#app/phases/move-header-phase"; -import { MovePhase } from "#app/phases/move-phase"; +import type { Pokemon } from "#field/pokemon"; +import { ActivatePriorityQueuePhase } from "#phases/activate-priority-queue-phase"; +import { AddEnemyBuffModifierPhase } from "#phases/add-enemy-buff-modifier-phase"; +import { AttemptCapturePhase } from "#phases/attempt-capture-phase"; +import { AttemptRunPhase } from "#phases/attempt-run-phase"; +import { BattleEndPhase } from "#phases/battle-end-phase"; +import { BerryPhase } from "#phases/berry-phase"; +import { CheckStatusEffectPhase } from "#phases/check-status-effect-phase"; +import { CheckSwitchPhase } from "#phases/check-switch-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { CommonAnimPhase } from "#phases/common-anim-phase"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { EggHatchPhase } from "#phases/egg-hatch-phase"; +import { EggLapsePhase } from "#phases/egg-lapse-phase"; +import { EggSummaryPhase } from "#phases/egg-summary-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { EndCardPhase } from "#phases/end-card-phase"; +import { EndEvolutionPhase } from "#phases/end-evolution-phase"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { EvolutionPhase } from "#phases/evolution-phase"; +import { ExpPhase } from "#phases/exp-phase"; +import { FaintPhase } from "#phases/faint-phase"; +import { FormChangePhase } from "#phases/form-change-phase"; +import { GameOverModifierRewardPhase } from "#phases/game-over-modifier-reward-phase"; +import { GameOverPhase } from "#phases/game-over-phase"; +import { HideAbilityPhase } from "#phases/hide-ability-phase"; +import { HidePartyExpBarPhase } from "#phases/hide-party-exp-bar-phase"; +import { LearnMovePhase } from "#phases/learn-move-phase"; +import { LevelCapPhase } from "#phases/level-cap-phase"; +import { LevelUpPhase } from "#phases/level-up-phase"; +import { LoadMoveAnimPhase } from "#phases/load-move-anim-phase"; +import { LoginPhase } from "#phases/login-phase"; +import { MessagePhase } from "#phases/message-phase"; +import { ModifierRewardPhase } from "#phases/modifier-reward-phase"; +import { MoneyRewardPhase } from "#phases/money-reward-phase"; +import { MoveAnimPhase } from "#phases/move-anim-phase"; +import { MoveChargePhase } from "#phases/move-charge-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { MoveHeaderPhase } from "#phases/move-header-phase"; +import { MovePhase } from "#phases/move-phase"; import { - MysteryEncounterPhase, - MysteryEncounterOptionSelectedPhase, MysteryEncounterBattlePhase, + MysteryEncounterBattleStartCleanupPhase, + MysteryEncounterOptionSelectedPhase, + MysteryEncounterPhase, MysteryEncounterRewardsPhase, PostMysteryEncounterPhase, - MysteryEncounterBattleStartCleanupPhase, -} from "#app/phases/mystery-encounter-phases"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; -import { PartyExpPhase } from "#app/phases/party-exp-phase"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#app/data/phase-priority-queue"; -import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; -import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { ReloadSessionPhase } from "#app/phases/reload-session-phase"; -import { ResetStatusPhase } from "#app/phases/reset-status-phase"; -import { ReturnPhase } from "#app/phases/return-phase"; -import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; -import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; -import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; -import { SelectBiomePhase } from "#app/phases/select-biome-phase"; -import { SelectChallengePhase } from "#app/phases/select-challenge-phase"; -import { SelectGenderPhase } from "#app/phases/select-gender-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { SelectTargetPhase } from "#app/phases/select-target-phase"; -import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; -import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import { SummonMissingPhase } from "#app/phases/summon-missing-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { SwitchBiomePhase } from "#app/phases/switch-biome-phase"; -import { SwitchPhase } from "#app/phases/switch-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { TeraPhase } from "#app/phases/tera-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; -import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; -import { UnavailablePhase } from "#app/phases/unavailable-phase"; -import { UnlockPhase } from "#app/phases/unlock-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; -import { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; +} from "#phases/mystery-encounter-phases"; +import { NewBattlePhase } from "#phases/new-battle-phase"; +import { NewBiomeEncounterPhase } from "#phases/new-biome-encounter-phase"; +import { NextEncounterPhase } from "#phases/next-encounter-phase"; +import { ObtainStatusEffectPhase } from "#phases/obtain-status-effect-phase"; +import { PartyExpPhase } from "#phases/party-exp-phase"; +import { PartyHealPhase } from "#phases/party-heal-phase"; +import { PokemonAnimPhase } from "#phases/pokemon-anim-phase"; +import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; +import { PokemonTransformPhase } from "#phases/pokemon-transform-phase"; +import { PostGameOverPhase } from "#phases/post-game-over-phase"; +import { PostSummonPhase } from "#phases/post-summon-phase"; +import { PostTurnStatusEffectPhase } from "#phases/post-turn-status-effect-phase"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { ReloadSessionPhase } from "#phases/reload-session-phase"; +import { ResetStatusPhase } from "#phases/reset-status-phase"; +import { ReturnPhase } from "#phases/return-phase"; +import { RevivalBlessingPhase } from "#phases/revival-blessing-phase"; +import { RibbonModifierRewardPhase } from "#phases/ribbon-modifier-reward-phase"; +import { ScanIvsPhase } from "#phases/scan-ivs-phase"; +import { SelectBiomePhase } from "#phases/select-biome-phase"; +import { SelectChallengePhase } from "#phases/select-challenge-phase"; +import { SelectGenderPhase } from "#phases/select-gender-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import { SelectTargetPhase } from "#phases/select-target-phase"; +import { ShinySparklePhase } from "#phases/shiny-sparkle-phase"; +import { ShowAbilityPhase } from "#phases/show-ability-phase"; +import { ShowPartyExpBarPhase } from "#phases/show-party-exp-bar-phase"; +import { ShowTrainerPhase } from "#phases/show-trainer-phase"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; +import { SummonMissingPhase } from "#phases/summon-missing-phase"; +import { SummonPhase } from "#phases/summon-phase"; +import { SwitchBiomePhase } from "#phases/switch-biome-phase"; +import { SwitchPhase } from "#phases/switch-phase"; +import { SwitchSummonPhase } from "#phases/switch-summon-phase"; +import { TeraPhase } from "#phases/tera-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { ToggleDoublePositionPhase } from "#phases/toggle-double-position-phase"; +import { TrainerVictoryPhase } from "#phases/trainer-victory-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { UnavailablePhase } from "#phases/unavailable-phase"; +import { UnlockPhase } from "#phases/unlock-phase"; +import { VictoryPhase } from "#phases/victory-phase"; +import { WeatherEffectPhase } from "#phases/weather-effect-phase"; +import type { PhaseMap, PhaseString } from "#types/phase-types"; +import { type Constructor, coerceArray } from "#utils/common"; -/** +/* * Manager for phases used by battle scene. * * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.* @@ -560,14 +560,13 @@ export class PhaseManager { } /** - * Queues an ability bar flyout phase - * @param pokemon The pokemon who has the ability - * @param passive Whether the ability is a passive - * @param show Whether to show or hide the bar + * Queue a phase to show or hide the ability flyout bar. + * @param pokemon - The {@linkcode Pokemon} whose ability is being activated + * @param passive - Whether the ability is a passive + * @param show - Whether to show or hide the bar */ public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase()); - this.clearPhaseQueueSplice(); } /** diff --git a/src/phase.ts b/src/phase.ts index 5e81679d29e..46a81dddb6f 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import type { PhaseMap, PhaseString } from "./@types/phase-types"; +import type { PhaseMap, PhaseString } from "#types/phase-types"; export abstract class Phase { start() {} diff --git a/src/phases/activate-priority-queue-phase.ts b/src/phases/activate-priority-queue-phase.ts index df42c491676..a31d3291a60 100644 --- a/src/phases/activate-priority-queue-phase.ts +++ b/src/phases/activate-priority-queue-phase.ts @@ -1,6 +1,6 @@ -import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; +import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; export class ActivatePriorityQueuePhase extends Phase { public readonly phaseName = "ActivatePriorityQueuePhase"; diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-buff-modifier-phase.ts index 218a3a653ff..5c9a56796d4 100644 --- a/src/phases/add-enemy-buff-modifier-phase.ts +++ b/src/phases/add-enemy-buff-modifier-phase.ts @@ -1,9 +1,9 @@ -import { ModifierTier } from "#enums/modifier-tier"; -import { regenerateModifierPoolThresholds, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { EnemyPersistentModifier } from "#app/modifier/modifier"; -import { Phase } from "#app/phase"; import { globalScene } from "#app/global-scene"; +import { Phase } from "#app/phase"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { EnemyPersistentModifier } from "#modifiers/modifier"; +import { getEnemyBuffModifierForWave, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; export class AddEnemyBuffModifierPhase extends Phase { public readonly phaseName = "AddEnemyBuffModifierPhase"; diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index f4e6725935a..604d4fd8384 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -1,30 +1,31 @@ -import { BattlerIndex } from "#enums/battler-index"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { SubstituteTag } from "#app/data/battler-tags"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { SubstituteTag } from "#data/battler-tags"; +import { Gender } from "#data/gender"; import { doPokeballBounceAnim, + getCriticalCaptureChance, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, - getCriticalCaptureChance, -} from "#app/data/pokeball"; -import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; -import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { PokemonPhase } from "#app/phases/pokemon-phase"; -import { achvs } from "#app/system/achv"; -import type { PartyOption } from "#app/ui/party-ui-handler"; -import { PartyUiMode } from "#app/ui/party-ui-handler"; -import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { UiMode } from "#enums/ui-mode"; +} from "#data/pokeball"; +import { getStatusEffectCatchRateMultiplier } from "#data/status-effect"; +import { BattlerIndex } from "#enums/battler-index"; import type { PokeballType } from "#enums/pokeball"; import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import { addPokeballCaptureStars, addPokeballOpenParticles } from "#field/anims"; +import type { EnemyPokemon } from "#field/pokemon"; +import { PokemonHeldItemModifier } from "#modifiers/modifier"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { achvs } from "#system/achv"; +import type { PartyOption } from "#ui/party-ui-handler"; +import { PartyUiMode } from "#ui/party-ui-handler"; +import { SummaryUiMode } from "#ui/summary-ui-handler"; import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; -import { Gender } from "#app/data/gender"; +// TODO: Refactor and split up to allow for overriding capture chance export class AttemptCapturePhase extends PokemonPhase { public readonly phaseName = "AttemptCapturePhase"; private pokeballType: PokeballType; @@ -119,7 +120,7 @@ export class AttemptCapturePhase extends PokemonPhase { repeatDelay: 500, onUpdate: t => { if (shakeCount && shakeCount < (isCritical ? 2 : 4)) { - const value = t.getValue(); + const value = t.getValue() ?? 0; const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1; this.pokeball.setX(pbX + value * 4 * directionMultiplier); this.pokeball.setAngle(value * 27.5 * directionMultiplier); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 5e24f3474a6..a59667bdd4e 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -1,34 +1,33 @@ -import { applyAbAttrs, applyPreLeaveFieldAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { globalScene } from "#app/global-scene"; +import Overrides from "#app/overrides"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; +import { FieldPhase } from "#phases/field-phase"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils/common"; -import { PokemonPhase } from "./pokemon-phase"; -import { globalScene } from "#app/global-scene"; -export class AttemptRunPhase extends PokemonPhase { +export class AttemptRunPhase extends FieldPhase { public readonly phaseName = "AttemptRunPhase"; - /** For testing purposes: this is to force the pokemon to fail and escape */ - public forceFailEscape = false; - start() { + public start() { super.start(); - const playerField = globalScene.getPlayerField(); + // Increment escape attempts count on entry + const currentAttempts = globalScene.currentBattle.escapeAttempts++; + + const activePlayerField = globalScene.getPlayerField(true); const enemyField = globalScene.getEnemyField(); - const playerPokemon = this.getPokemon(); + const escapeRoll = globalScene.randBattleSeedInt(100); + const escapeChance = new NumberHolder(this.calculateEscapeChance(currentAttempts)); - const escapeChance = new NumberHolder(0); + activePlayerField.forEach(pokemon => { + applyAbAttrs("RunSuccessAbAttr", { pokemon, chance: escapeChance }); + }); - this.attemptRunAway(playerField, enemyField, escapeChance); - - applyAbAttrs("RunSuccessAbAttr", playerPokemon, null, false, escapeChance); - - if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) { - enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", enemyPokemon)); + if (escapeRoll < escapeChance.value) { + enemyField.forEach(pokemon => applyAbAttrs("PreLeaveFieldAbAttr", { pokemon })); globalScene.playSound("se/flee"); globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); @@ -38,14 +37,11 @@ export class AttemptRunPhase extends PokemonPhase { alpha: 0, duration: 250, ease: "Sine.easeIn", - onComplete: () => - // biome-ignore lint/complexity/noForEach: TODO - enemyField.forEach(enemyPokemon => enemyPokemon.destroy()), + onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()), }); globalScene.clearEnemyHeldItemModifiers(); - // biome-ignore lint/complexity/noForEach: TODO enemyField.forEach(enemyPokemon => { enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); enemyPokemon.hp = 0; @@ -60,24 +56,35 @@ export class AttemptRunPhase extends PokemonPhase { globalScene.phaseManager.pushNew("NewBattlePhase"); } else { - playerPokemon.turnData.failedRunAway = true; + activePlayerField.forEach(p => { + p.turnData.failedRunAway = true; + }); + globalScene.phaseManager.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); } this.end(); } - attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: NumberHolder) { - /** Sum of the speed of all enemy pokemon on the field */ - const enemySpeed = enemyField.reduce( - (total: number, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), - 0, - ); - /** Sum of the speed of all player pokemon on the field */ - const playerSpeed = playerField.reduce( - (total: number, playerPokemon: Pokemon) => total + playerPokemon.getStat(Stat.SPD), - 0, - ); + /** + * Calculate the chance for the player's team to successfully run away from battle. + * + * @param escapeAttempts - The number of prior failed escape attempts in the current battle + * @returns The final escape chance, as percentage out of 100. + */ + public calculateEscapeChance(escapeAttempts: number): number { + // Check for override, guaranteeing or forbidding random flee attempts as applicable. + if (Overrides.RUN_SUCCESS_OVERRIDE !== null) { + return Overrides.RUN_SUCCESS_OVERRIDE ? 100 : 0; + } + + const enemyField = globalScene.getEnemyField(); + const activePlayerField = globalScene.getPlayerField(true); + + // Cf https://bulbapedia.bulbagarden.net/wiki/Escape#Generation_V_onwards + // From gen 5 onwards, running takes the _base_ speed totals of both party sides. + const enemySpeed = enemyField.reduce((total, enemy) => total + enemy.getStat(Stat.SPD), 0); + const playerSpeed = activePlayerField.reduce((total, player) => total + player.getStat(Stat.SPD), 0); /* The way the escape chance works is by looking at the difference between your speed and the enemy field's average speed as a ratio. The higher this ratio, the higher your chance of success. * However, there is a cap for the ratio of your speed vs enemy speed which beyond that point, you won't gain any advantage. It also looks at how many times you've tried to escape. @@ -95,10 +102,8 @@ export class AttemptRunPhase extends PokemonPhase { * From the above, we can calculate the below values */ - let isBoss = false; - for (let e = 0; e < enemyField.length; e++) { - isBoss = isBoss || enemyField[e].isBoss(); // this line checks if any of the enemy pokemon on the field are bosses; if so, the calculation for escaping is different - } + /** Whether at least 1 pokemon on the enemy field is a boss. */ + const isBoss = enemyField.some(e => e.isBoss()); /** The ratio between the speed of your active pokemon and the speed of the enemy field */ const speedRatio = playerSpeed / enemySpeed; @@ -114,8 +119,8 @@ export class AttemptRunPhase extends PokemonPhase { const escapeSlope = (maxChance - minChance) / speedCap; // This will calculate the escape chance given all of the above and clamp it to the range of [`minChance`, `maxChance`] - escapeChance.value = Phaser.Math.Clamp( - Math.round(escapeSlope * speedRatio + minChance + escapeBonus * globalScene.currentBattle.escapeAttempts++), + return Phaser.Math.Clamp( + Math.round(escapeSlope * speedRatio + minChance + escapeBonus * escapeAttempts), minChance, maxChance, ); diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index e1bf4c2296c..8d199915385 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -1,7 +1,7 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier"; -import { BattlePhase } from "./battle-phase"; +import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#modifiers/modifier"; +import { BattlePhase } from "#phases/battle-phase"; export class BattleEndPhase extends BattlePhase { public readonly phaseName = "BattleEndPhase"; @@ -65,7 +65,7 @@ export class BattleEndPhase extends BattlePhase { } for (const pokemon of globalScene.getPokemonAllowedInBattle()) { - applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory); + applyAbAttrs("PostBattleAbAttr", { pokemon, victory: this.isVictory }); } if (globalScene.currentBattle.moneyScattered) { diff --git a/src/phases/battle-phase.ts b/src/phases/battle-phase.ts index 7cefd0369d9..26794ed9bc5 100644 --- a/src/phases/battle-phase.ts +++ b/src/phases/battle-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import { TrainerSlot } from "#enums/trainer-slot"; import { Phase } from "#app/phase"; +import { TrainerSlot } from "#enums/trainer-slot"; export abstract class BattlePhase extends Phase { showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index c126f3306b9..941406d0b96 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -1,13 +1,13 @@ -import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { CommonAnim } from "#enums/move-anims-common"; -import { BerryUsedEvent } from "#app/events/battle-scene"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { BerryModifier } from "#app/modifier/modifier"; -import i18next from "i18next"; -import { BooleanHolder } from "#app/utils/common"; -import { FieldPhase } from "./field-phase"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { CommonAnim } from "#enums/move-anims-common"; +import { BerryUsedEvent } from "#events/battle-scene"; +import type { Pokemon } from "#field/pokemon"; +import { BerryModifier } from "#modifiers/modifier"; +import { FieldPhase } from "#phases/field-phase"; +import { BooleanHolder } from "#utils/common"; +import i18next from "i18next"; /** * The phase after attacks where the pokemon eat berries. @@ -20,7 +20,7 @@ export class BerryPhase extends FieldPhase { this.executeForAll(pokemon => { this.eatBerries(pokemon); - applyAbAttrs("RepeatBerryNextTurnAbAttr", pokemon, null); + applyAbAttrs("CudChewConsumeBerryAbAttr", { pokemon }); }); this.end(); @@ -42,7 +42,7 @@ export class BerryPhase extends FieldPhase { // TODO: If both opponents on field have unnerve, which one displays its message? const cancelled = new BooleanHolder(false); - pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", opp, cancelled)); + pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", { pokemon: opp, cancelled })); if (cancelled.value) { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:preventBerryUse", { @@ -70,6 +70,6 @@ export class BerryPhase extends FieldPhase { globalScene.updateModifiers(pokemon.isPlayer()); // AbilityId.CHEEK_POUCH only works once per round of nom noms - applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false)); + applyAbAttrs("HealFromBerryUseAbAttr", { pokemon }); } } diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts index 43495e038e9..bdaa536986a 100644 --- a/src/phases/check-status-effect-phase.ts +++ b/src/phases/check-status-effect-phase.ts @@ -1,6 +1,6 @@ +import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import type { BattlerIndex } from "#enums/battler-index"; -import { globalScene } from "#app/global-scene"; export class CheckStatusEffectPhase extends Phase { public readonly phaseName = "CheckStatusEffectPhase"; diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index 97f4092096f..f4e8ee56c55 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; -import { BattleStyle } from "#app/enums/battle-style"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; import { getPokemonNameWithAffix } from "#app/messages"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; -import { BattlePhase } from "./battle-phase"; +import { BattleStyle } from "#enums/battle-style"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { SwitchType } from "#enums/switch-type"; +import { UiMode } from "#enums/ui-mode"; +import { BattlePhase } from "#phases/battle-phase"; +import i18next from "i18next"; export class CheckSwitchPhase extends BattlePhase { public readonly phaseName = "CheckSwitchPhase"; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 754d54de70a..14674037fbe 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -1,28 +1,29 @@ -import { globalScene } from "#app/global-scene"; import type { TurnCommand } from "#app/battle"; -import { BattleType } from "#enums/battle-type"; -import type { EncoreTag } from "#app/data/battler-tags"; -import { TrappedTag } from "#app/data/battler-tags"; -import type { MoveTargetSet } from "#app/data/moves/move"; -import { getMoveTargets } from "#app/data/moves/move-utils"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BiomeId } from "#enums/biome-id"; -import { MoveId } from "#enums/move-id"; -import { PokeballType } from "#enums/pokeball"; -import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; +import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; -import { Command } from "#enums/command"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; -import { FieldPhase } from "./field-phase"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { isNullOrUndefined } from "#app/utils/common"; +import { speciesStarterCosts } from "#balance/starters"; +import type { EncoreTag } from "#data/battler-tags"; +import { TrappedTag } from "#data/battler-tags"; +import { AbilityId } from "#enums/ability-id"; import { ArenaTagSide } from "#enums/arena-tag-side"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { isVirtual, isIgnorePP, MoveUseMode } from "#enums/move-use-mode"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattleType } from "#enums/battle-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BiomeId } from "#enums/biome-id"; +import { Command } from "#enums/command"; +import { FieldPosition } from "#enums/field-position"; +import { MoveId } from "#enums/move-id"; +import { isIgnorePP, isVirtual, MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { PokeballType } from "#enums/pokeball"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { MoveTargetSet } from "#moves/move"; +import { getMoveTargets } from "#moves/move-utils"; +import { FieldPhase } from "#phases/field-phase"; +import type { TurnMove } from "#types/turn-move"; +import { isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; export class CommandPhase extends FieldPhase { public readonly phaseName = "CommandPhase"; @@ -87,7 +88,7 @@ export class CommandPhase extends FieldPhase { } // Checks if the Pokemon is under the effects of Encore. If so, Encore can end early if the encored move has no more PP. - const encoreTag = this.getPokemon().getTag(BattlerTagType.ENCORE) as EncoreTag; + const encoreTag = this.getPokemon().getTag(BattlerTagType.ENCORE) as EncoreTag | undefined; if (encoreTag) { this.getPokemon().lapseTag(BattlerTagType.ENCORE); } diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts index abfe8ed99f0..78da1dcfa2f 100644 --- a/src/phases/common-anim-phase.ts +++ b/src/phases/common-anim-phase.ts @@ -1,8 +1,8 @@ -import type { BattlerIndex } from "#enums/battler-index"; import { globalScene } from "#app/global-scene"; +import { CommonBattleAnim } from "#data/battle-anims"; +import type { BattlerIndex } from "#enums/battler-index"; import type { CommonAnim } from "#enums/move-anims-common"; -import { CommonBattleAnim } from "#app/data/battle-anims"; -import { PokemonPhase } from "./pokemon-phase"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class CommonAnimPhase extends PokemonPhase { // PokemonHealPhase extends CommonAnimPhase, and to make typescript happy, diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index aa5a0a6c3e6..a651255baff 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; import { BattleSpec } from "#enums/battle-spec"; -import type { DamageResult } from "#app/field/pokemon"; +import type { BattlerIndex } from "#enums/battler-index"; import { HitResult } from "#enums/hit-result"; -import { fixedInt } from "#app/utils/common"; -import { PokemonPhase } from "#app/phases/pokemon-phase"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import type { DamageResult } from "#types/damage-result"; +import { fixedInt } from "#utils/common"; export class DamageAnimPhase extends PokemonPhase { public readonly phaseName = "DamageAnimPhase"; diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index d6c40a1510e..94923ae8c1f 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -1,20 +1,20 @@ import type { AnySound } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type { Egg } from "#app/data/egg"; -import { EggCountChangedEvent } from "#app/events/egg"; -import type { PlayerPokemon } from "#app/field/pokemon"; import { Phase } from "#app/phase"; -import { achvs } from "#app/system/achv"; -import EggCounterContainer from "#app/ui/egg-counter-container"; -import type EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; -import PokemonInfoContainer from "#app/ui/pokemon-info-container"; +import type { Egg } from "#data/egg"; +import type { EggHatchData } from "#data/egg-hatch-data"; import { UiMode } from "#enums/ui-mode"; +import { EggCountChangedEvent } from "#events/egg"; +import { doShinySparkleAnim } from "#field/anims"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { EggLapsePhase } from "#phases/egg-lapse-phase"; +import { achvs } from "#system/achv"; +import { EggCounterContainer } from "#ui/egg-counter-container"; +import type { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler"; +import { PokemonInfoContainer } from "#ui/pokemon-info-container"; +import { fixedInt, getFrameMs, randInt } from "#utils/common"; import i18next from "i18next"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { fixedInt, getFrameMs, randInt } from "#app/utils/common"; -import type { EggLapsePhase } from "./egg-lapse-phase"; -import type { EggHatchData } from "#app/data/egg-hatch-data"; -import { doShinySparkleAnim } from "#app/field/anims"; /** * Class that represents egg hatching diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 206bef1a33c..4f393bddd36 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -1,13 +1,13 @@ import { globalScene } from "#app/global-scene"; -import type { Egg } from "#app/data/egg"; -import { EGG_SEED } from "#app/data/egg"; -import { Phase } from "#app/phase"; -import i18next from "i18next"; import Overrides from "#app/overrides"; +import { Phase } from "#app/phase"; +import type { Egg } from "#data/egg"; +import { EGG_SEED } from "#data/egg"; +import { EggHatchData } from "#data/egg-hatch-data"; import { UiMode } from "#enums/ui-mode"; -import { achvs } from "#app/system/achv"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { EggHatchData } from "#app/data/egg-hatch-data"; +import type { PlayerPokemon } from "#field/pokemon"; +import { achvs } from "#system/achv"; +import i18next from "i18next"; /** * Phase that handles updating eggs, and hatching any ready eggs diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts index cc7857426bc..c236c5c3abc 100644 --- a/src/phases/egg-summary-phase.ts +++ b/src/phases/egg-summary-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; +import type { EggHatchData } from "#data/egg-hatch-data"; import { UiMode } from "#enums/ui-mode"; -import type { EggHatchData } from "#app/data/egg-hatch-data"; /** * Class that represents the egg summary phase diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index f2c23384627..79da7134e9a 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -1,37 +1,41 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { BattleType } from "#enums/battle-type"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; +import { PLAYER_PARTY_MAX_SIZE, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { applyAbAttrs, applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims"; -import { getCharVariantFromDialogue } from "#app/data/dialogue"; -import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { getRandomWeatherType } from "#app/data/weather"; -import { EncounterPhaseEvent } from "#app/events/battle-scene"; -import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; import { getPokemonNameWithAffix } from "#app/messages"; -import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; -import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import Overrides from "#app/overrides"; -import { BattlePhase } from "#app/phases/battle-phase"; -import { achvs } from "#app/system/achv"; import { handleTutorial, Tutorial } from "#app/tutorial"; -import { UiMode } from "#enums/ui-mode"; -import { randSeedInt, randSeedItem } from "#app/utils/common"; +import { initEncounterAnims, loadEncounterAnimAssets } from "#data/battle-anims"; +import { getCharVariantFromDialogue } from "#data/dialogue"; +import { getNatureName } from "#data/nature"; +import { getRandomWeatherType } from "#data/weather"; import { BattleSpec } from "#enums/battle-spec"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BiomeId } from "#enums/biome-id"; +import { FieldPosition } from "#enums/field-position"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { PlayerGender } from "#enums/player-gender"; import { SpeciesId } from "#enums/species-id"; -import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { UiMode } from "#enums/ui-mode"; +import { EncounterPhaseEvent } from "#events/battle-scene"; +import type { Pokemon } from "#field/pokemon"; +import { + BoostBugSpawnModifier, + IvScannerModifier, + overrideHeldItems, + overrideModifiers, + TurnHeldItemTransferModifier, +} from "#modifiers/modifier"; +import { regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import { doTrainerExclamation } from "#mystery-encounters/encounter-phase-utils"; +import { getGoldenBugNetSpecies } from "#mystery-encounters/encounter-pokemon-utils"; +import { BattlePhase } from "#phases/battle-phase"; +import { achvs } from "#system/achv"; +import { randSeedInt, randSeedItem } from "#utils/common"; import i18next from "i18next"; -import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants"; -import { getNatureName } from "#app/data/nature"; export class EncounterPhase extends BattlePhase { // Union type is necessary as this is subclassed, and typescript will otherwise complain @@ -128,7 +132,7 @@ export class EncounterPhase extends BattlePhase { .slice(0, !battle.double ? 1 : 2) .reverse() .forEach(playerPokemon => { - applyAbAttrs("SyncEncounterNatureAbAttr", playerPokemon, null, false, battle.enemyParty[e]); + applyAbAttrs("SyncEncounterNatureAbAttr", { pokemon: playerPokemon, target: battle.enemyParty[e] }); }); } } @@ -249,7 +253,7 @@ export class EncounterPhase extends BattlePhase { if (e < (battle.double ? 2 : 1)) { if (battle.battleType === BattleType.WILD) { for (const pokemon of globalScene.getField()) { - applyPreSummonAbAttrs("PreSummonAbAttr", pokemon, []); + applyAbAttrs("PreSummonAbAttr", { pokemon }); } globalScene.field.add(enemyPokemon); battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); diff --git a/src/phases/end-card-phase.ts b/src/phases/end-card-phase.ts index bb64969514f..5c3f6e1bf9b 100644 --- a/src/phases/end-card-phase.ts +++ b/src/phases/end-card-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import { PlayerGender } from "#app/enums/player-gender"; import { Phase } from "#app/phase"; -import { addTextObject, TextStyle } from "#app/ui/text"; +import { PlayerGender } from "#enums/player-gender"; +import { addTextObject, TextStyle } from "#ui/text"; import i18next from "i18next"; export class EndCardPhase extends Phase { diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 0dc41a592e0..7e4dff37b0f 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { BattlerIndex } from "#enums/battler-index"; -import { Command } from "#enums/command"; -import { FieldPhase } from "./field-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { Command } from "#enums/command"; +import { FieldPhase } from "#phases/field-phase"; /** * Phase for determining an enemy AI's action for the next turn. diff --git a/src/phases/enemy-party-member-pokemon-phase.ts b/src/phases/enemy-party-member-pokemon-phase.ts index 5c701f7d73a..2f0ace608ff 100644 --- a/src/phases/enemy-party-member-pokemon-phase.ts +++ b/src/phases/enemy-party-member-pokemon-phase.ts @@ -1,5 +1,5 @@ -import type { EnemyPokemon } from "#app/field/pokemon"; -import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; +import type { EnemyPokemon } from "#field/pokemon"; +import { PartyMemberPokemonPhase } from "#phases/party-member-pokemon-phase"; export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPhase { constructor(partyMemberIndex: number) { diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index bcc93b028bd..f8bee8371f2 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -1,20 +1,19 @@ -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { Phase } from "#app/phase"; import type { AnySound } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; -import { FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; -import type EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; -import { fixedInt, getFrameMs, randInt } from "#app/utils/common"; -import { UiMode } from "#enums/ui-mode"; -import { cos, sin } from "#app/field/anims"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { LearnMoveSituation } from "#enums/learn-move-situation"; -import { getTypeRgb } from "#app/data/type"; -import i18next from "i18next"; import { getPokemonNameWithAffix } from "#app/messages"; -import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves"; +import { Phase } from "#app/phase"; +import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; +import { FusionSpeciesFormEvolution } from "#balance/pokemon-evolutions"; +import { EVOLVE_MOVE } from "#balance/pokemon-level-moves"; +import { getTypeRgb } from "#data/type"; +import { LearnMoveSituation } from "#enums/learn-move-situation"; +import { UiMode } from "#enums/ui-mode"; +import { cos, sin } from "#field/anims"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { EvolutionSceneHandler } from "#ui/evolution-scene-handler"; +import { fixedInt, getFrameMs, randInt } from "#utils/common"; +import i18next from "i18next"; +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; export class EvolutionPhase extends Phase { // FormChangePhase inherits from this, but EvolutionPhase is not abstract. @@ -23,6 +22,8 @@ export class EvolutionPhase extends Phase { protected pokemon: PlayerPokemon; protected lastLevel: number; + protected evoChain: Phaser.Tweens.TweenChain | null = null; + private preEvolvedPokemonName: string; private evolution: SpeciesFormEvolution | null; @@ -40,13 +41,23 @@ export class EvolutionPhase extends Phase { protected pokemonEvoSprite: Phaser.GameObjects.Sprite; protected pokemonEvoTintSprite: Phaser.GameObjects.Sprite; - constructor(pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: number) { + /** Whether the evolution can be cancelled by the player */ + protected canCancel: boolean; + + /** + * @param pokemon - The Pokemon that is evolving + * @param evolution - The form being evolved into + * @param lastLevel - The level at which the Pokemon is evolving + * @param canCancel - Whether the evolution can be cancelled by the player + */ + constructor(pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: number, canCancel = true) { super(); this.pokemon = pokemon; this.evolution = evolution; this.lastLevel = lastLevel; this.fusionSpeciesEvolved = evolution instanceof FusionSpeciesFormEvolution; + this.canCancel = canCancel; } validate(): boolean { @@ -57,198 +68,227 @@ export class EvolutionPhase extends Phase { return globalScene.ui.setModeForceTransition(UiMode.EVOLUTION_SCENE); } - start() { - super.start(); + /** + * Set up the following evolution assets + * - {@linkcode evolutionContainer} + * - {@linkcode evolutionBaseBg} + * - {@linkcode evolutionBg} + * - {@linkcode evolutionBgOverlay} + * - {@linkcode evolutionOverlay} + * + */ + private setupEvolutionAssets(): void { + this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneHandler; + this.evolutionContainer = this.evolutionHandler.evolutionContainer; + this.evolutionBaseBg = globalScene.add.image(0, 0, "default_bg").setOrigin(0); - this.setMode().then(() => { - if (!this.validate()) { - return this.end(); - } + this.evolutionBg = globalScene.add + .video(0, 0, "evo_bg") + .stop() + .setOrigin(0) + .setScale(0.4359673025) + .setVisible(false); - globalScene.fadeOutBgm(undefined, false); + this.evolutionBgOverlay = globalScene.add + .rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0x262626) + .setOrigin(0) + .setAlpha(0); + this.evolutionContainer.add([this.evolutionBaseBg, this.evolutionBgOverlay, this.evolutionBg]); - this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneHandler; + this.evolutionOverlay = globalScene.add.rectangle( + 0, + -globalScene.game.canvas.height / 6, + globalScene.game.canvas.width / 6, + globalScene.game.canvas.height / 6 - 48, + 0xffffff, + ); + this.evolutionOverlay.setOrigin(0).setAlpha(0); + globalScene.ui.add(this.evolutionOverlay); + } - this.evolutionContainer = this.evolutionHandler.evolutionContainer; + /** + * Configure the sprite, setting its pipeline data + * @param pokemon - The pokemon object that the sprite information is configured from + * @param sprite - The sprite object to configure + * @param setPipeline - Whether to also set the pipeline; should be false + * if the sprite is only being updated with new sprite assets + * + * + * @returns The sprite object that was passed in + */ + protected configureSprite(pokemon: Pokemon, sprite: Phaser.GameObjects.Sprite, setPipeline = true): typeof sprite { + const spriteKey = pokemon.getSpriteKey(true); + try { + sprite.play(spriteKey); + } catch (err: unknown) { + console.error(`Failed to play animation for ${spriteKey}`, err); + } - this.evolutionBaseBg = globalScene.add.image(0, 0, "default_bg"); - this.evolutionBaseBg.setOrigin(0, 0); - this.evolutionContainer.add(this.evolutionBaseBg); - - this.evolutionBg = globalScene.add.video(0, 0, "evo_bg").stop(); - this.evolutionBg.setOrigin(0, 0); - this.evolutionBg.setScale(0.4359673025); - this.evolutionBg.setVisible(false); - this.evolutionContainer.add(this.evolutionBg); - - this.evolutionBgOverlay = globalScene.add.rectangle( - 0, - 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, - 0x262626, - ); - this.evolutionBgOverlay.setOrigin(0, 0); - this.evolutionBgOverlay.setAlpha(0); - this.evolutionContainer.add(this.evolutionBgOverlay); - - const getPokemonSprite = () => { - const ret = globalScene.addPokemonSprite( - this.pokemon, - this.evolutionBaseBg.displayWidth / 2, - this.evolutionBaseBg.displayHeight / 2, - "pkmn__sub", - ); - ret.setPipeline(globalScene.spritePipeline, { - tone: [0.0, 0.0, 0.0, 0.0], - ignoreTimeTint: true, - }); - return ret; - }; - - this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonEvoSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonEvoTintSprite = getPokemonSprite())); - - this.pokemonTintSprite.setAlpha(0); - this.pokemonTintSprite.setTintFill(0xffffff); - this.pokemonEvoSprite.setVisible(false); - this.pokemonEvoTintSprite.setVisible(false); - this.pokemonEvoTintSprite.setTintFill(0xffffff); - - this.evolutionOverlay = globalScene.add.rectangle( - 0, - -globalScene.game.canvas.height / 6, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6 - 48, - 0xffffff, - ); - this.evolutionOverlay.setOrigin(0, 0); - this.evolutionOverlay.setAlpha(0); - globalScene.ui.add(this.evolutionOverlay); - - [this.pokemonSprite, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = this.pokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); - } - - sprite.setPipeline(globalScene.spritePipeline, { - tone: [0.0, 0.0, 0.0, 0.0], - hasShadow: false, - teraColor: getTypeRgb(this.pokemon.getTeraType()), - isTerastallized: this.pokemon.isTerastallized, - }); - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - sprite.setPipelineData("shiny", this.pokemon.shiny); - sprite.setPipelineData("variant", this.pokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (this.pokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; - }); + if (setPipeline) { + sprite.setPipeline(globalScene.spritePipeline, { + tone: [0.0, 0.0, 0.0, 0.0], + hasShadow: false, + teraColor: getTypeRgb(pokemon.getTeraType()), + isTerastallized: pokemon.isTerastallized, + }); + } + + sprite + .setPipelineData("ignoreTimeTint", true) + .setPipelineData("spriteKey", pokemon.getSpriteKey()) + .setPipelineData("shiny", pokemon.shiny) + .setPipelineData("variant", pokemon.variant); + + for (let k of ["spriteColors", "fusionSpriteColors"]) { + if (pokemon.summonData.speciesForm) { + k += "Base"; + } + sprite.pipelineData[k] = pokemon.getSprite().pipelineData[k]; + } + + return sprite; + } + + private getPokemonSprite(): Phaser.GameObjects.Sprite { + const sprite = globalScene.addPokemonSprite( + this.pokemon, + this.evolutionBaseBg.displayWidth / 2, + this.evolutionBaseBg.displayHeight / 2, + "pkmn__sub", + ); + sprite.setPipeline(globalScene.spritePipeline, { + tone: [0.0, 0.0, 0.0, 0.0], + ignoreTimeTint: true, + }); + return sprite; + } + + /** + * Initialize {@linkcode pokemonSprite}, {@linkcode pokemonTintSprite}, {@linkcode pokemonEvoSprite}, and {@linkcode pokemonEvoTintSprite} + * and add them to the {@linkcode evolutionContainer} + */ + private setupPokemonSprites(): void { + this.pokemonSprite = this.configureSprite(this.pokemon, this.getPokemonSprite()); + this.pokemonTintSprite = this.configureSprite( + this.pokemon, + this.getPokemonSprite().setAlpha(0).setTintFill(0xffffff), + ); + this.pokemonEvoSprite = this.configureSprite(this.pokemon, this.getPokemonSprite().setVisible(false)); + this.pokemonEvoTintSprite = this.configureSprite( + this.pokemon, + this.getPokemonSprite().setVisible(false).setTintFill(0xffffff), + ); + + this.evolutionContainer.add([ + this.pokemonSprite, + this.pokemonTintSprite, + this.pokemonEvoSprite, + this.pokemonEvoTintSprite, + ]); + } + + async start() { + super.start(); + await this.setMode(); + + if (!this.validate()) { + return this.end(); + } + this.setupEvolutionAssets(); + this.setupPokemonSprites(); + this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon); + this.doEvolution(); + } + + /** + * Update the sprites depicting the evolved Pokemon + * @param evolvedPokemon - The evolved Pokemon + */ + private updateEvolvedPokemonSprites(evolvedPokemon: Pokemon): void { + this.configureSprite(evolvedPokemon, this.pokemonEvoSprite, false); + this.configureSprite(evolvedPokemon, this.pokemonEvoTintSprite, false); + } + + /** + * Adds the evolution tween and begins playing it + */ + private playEvolutionAnimation(evolvedPokemon: Pokemon): void { + globalScene.time.delayedCall(1000, () => { + this.evolutionBgm = globalScene.playSoundWithoutBgm("evolution"); + globalScene.tweens.add({ + targets: this.evolutionBgOverlay, + alpha: 1, + delay: 500, + duration: 1500, + ease: "Sine.easeOut", + onComplete: () => { + globalScene.time.delayedCall(1000, () => { + this.evolutionBg.setVisible(true).play(); + }); + globalScene.playSound("se/charge"); + this.doSpiralUpward(); + this.fadeOutPokemonSprite(evolvedPokemon); + }, }); - this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon); - this.doEvolution(); }); } + private fadeOutPokemonSprite(evolvedPokemon: Pokemon): void { + globalScene.tweens.addCounter({ + from: 0, + to: 1, + duration: 2000, + onUpdate: t => { + this.pokemonTintSprite.setAlpha(t.getValue() ?? 1); + }, + onComplete: () => { + this.pokemonSprite.setVisible(false); + globalScene.time.delayedCall(1100, () => { + globalScene.playSound("se/beam"); + this.doArcDownward(); + this.prepareForCycle(evolvedPokemon); + }); + }, + }); + } + + /** + * Prepares the evolution cycle by setting up the tint sprites and starting the cycle + */ + private prepareForCycle(evolvedPokemon: Pokemon): void { + globalScene.time.delayedCall(1500, () => { + this.pokemonEvoTintSprite.setScale(0.25).setVisible(true); + this.evolutionHandler.canCancel = this.canCancel; + this.doCycle(1, undefined, () => { + if (this.evolutionHandler.cancelled) { + this.handleFailedEvolution(evolvedPokemon); + } else { + this.handleSuccessEvolution(evolvedPokemon); + } + }); + }); + } + + /** + * Show the evolution text and then commence the evolution animation + */ doEvolution(): void { globalScene.ui.showText( i18next.t("menu:evolving", { pokemonName: this.preEvolvedPokemonName }), null, () => { this.pokemon.cry(); - this.pokemon.getPossibleEvolution(this.evolution).then(evolvedPokemon => { - [this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = evolvedPokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); - } - - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", evolvedPokemon.getSpriteKey()); - sprite.setPipelineData("shiny", evolvedPokemon.shiny); - sprite.setPipelineData("variant", evolvedPokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (evolvedPokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = evolvedPokemon.getSprite().pipelineData[k]; - }); - }); - - globalScene.time.delayedCall(1000, () => { - this.evolutionBgm = globalScene.playSoundWithoutBgm("evolution"); - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 1, - delay: 500, - duration: 1500, - ease: "Sine.easeOut", - onComplete: () => { - globalScene.time.delayedCall(1000, () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - }); - this.evolutionBg.setVisible(true); - this.evolutionBg.play(); - }); - globalScene.playSound("se/charge"); - this.doSpiralUpward(); - globalScene.tweens.addCounter({ - from: 0, - to: 1, - duration: 2000, - onUpdate: t => { - this.pokemonTintSprite.setAlpha(t.getValue()); - }, - onComplete: () => { - this.pokemonSprite.setVisible(false); - globalScene.time.delayedCall(1100, () => { - globalScene.playSound("se/beam"); - this.doArcDownward(); - globalScene.time.delayedCall(1500, () => { - this.pokemonEvoTintSprite.setScale(0.25); - this.pokemonEvoTintSprite.setVisible(true); - this.evolutionHandler.canCancel = true; - this.doCycle(1).then(success => { - if (success) { - this.handleSuccessEvolution(evolvedPokemon); - } else { - this.handleFailedEvolution(evolvedPokemon); - } - }); - }); - }); - }, - }); - }, - }); - }); + this.updateEvolvedPokemonSprites(evolvedPokemon); + this.playEvolutionAnimation(evolvedPokemon); }); }, 1000, ); } - /** - * Handles a failed/stopped evolution - * @param evolvedPokemon - The evolved Pokemon - */ - private handleFailedEvolution(evolvedPokemon: Pokemon): void { - this.pokemonSprite.setVisible(true); - this.pokemonTintSprite.setScale(1); + /** Used exclusively by {@linkcode handleFailedEvolution} to fade out the evolution sprites and music */ + private fadeOutEvolutionAssets(): void { globalScene.tweens.add({ targets: [this.evolutionBg, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite], alpha: 0, @@ -257,9 +297,40 @@ export class EvolutionPhase extends Phase { this.evolutionBg.setVisible(false); }, }); - SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); + } + /** + * Show the confirmation prompt for pausing evolutions + * @param endCallback - The callback to call after either option is selected. + * This should end the evolution phase + */ + private showPauseEvolutionConfirmation(endCallback: () => void): void { + globalScene.ui.setOverlayMode( + UiMode.CONFIRM, + () => { + globalScene.ui.revertMode(); + this.pokemon.pauseEvolutions = true; + globalScene.ui.showText( + i18next.t("menu:evolutionsPaused", { + pokemonName: this.preEvolvedPokemonName, + }), + null, + endCallback, + 3000, + ); + }, + () => { + globalScene.ui.revertMode(); + globalScene.time.delayedCall(3000, endCallback); + }, + ); + } + + /** + * Used exclusively by {@linkcode handleFailedEvolution} to show the failed evolution UI messages + */ + private showFailedEvolutionUI(evolvedPokemon: Pokemon): void { globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); globalScene.ui.showText( @@ -280,25 +351,7 @@ export class EvolutionPhase extends Phase { evolvedPokemon.destroy(); this.end(); }; - globalScene.ui.setOverlayMode( - UiMode.CONFIRM, - () => { - globalScene.ui.revertMode(); - this.pokemon.pauseEvolutions = true; - globalScene.ui.showText( - i18next.t("menu:evolutionsPaused", { - pokemonName: this.preEvolvedPokemonName, - }), - null, - end, - 3000, - ); - }, - () => { - globalScene.ui.revertMode(); - globalScene.time.delayedCall(3000, end); - }, - ); + this.showPauseEvolutionConfirmation(end); }, ); }, @@ -307,6 +360,93 @@ export class EvolutionPhase extends Phase { ); } + /** + * Fade out the evolution assets, show the failed evolution UI messages, and enqueue the EndEvolutionPhase + * @param evolvedPokemon - The evolved Pokemon + */ + private handleFailedEvolution(evolvedPokemon: Pokemon): void { + this.pokemonSprite.setVisible(true); + this.pokemonTintSprite.setScale(1); + this.fadeOutEvolutionAssets(); + + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + this.showFailedEvolutionUI(evolvedPokemon); + } + + /** + * Fadeout evolution music, play the cry, show the evolution completed text, and end the phase + */ + private onEvolutionComplete(evolvedPokemon: Pokemon) { + SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); + globalScene.time.delayedCall(250, () => { + this.pokemon.cry(); + globalScene.time.delayedCall(1250, () => { + globalScene.playSoundWithoutBgm("evolution_fanfare"); + + evolvedPokemon.destroy(); + globalScene.ui.showText( + i18next.t("menu:evolutionDone", { + pokemonName: this.preEvolvedPokemonName, + evolvedPokemonName: this.pokemon.name, + }), + null, + () => this.end(), + null, + true, + fixedInt(4000), + ); + globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm()); + }); + }); + } + + private postEvolve(evolvedPokemon: Pokemon): void { + const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved + ? LearnMoveSituation.EVOLUTION_FUSED + : this.pokemon.fusionSpecies + ? LearnMoveSituation.EVOLUTION_FUSED_BASE + : LearnMoveSituation.EVOLUTION; + const levelMoves = this.pokemon + .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) + .filter(lm => lm[0] === EVOLVE_MOVE); + for (const lm of levelMoves) { + globalScene.phaseManager.unshiftNew("LearnMovePhase", globalScene.getPlayerParty().indexOf(this.pokemon), lm[1]); + } + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + + globalScene.playSound("se/shine"); + this.doSpray(); + + globalScene.tweens.chain({ + targets: null, + tweens: [ + { + targets: this.evolutionOverlay, + alpha: 1, + duration: 250, + easing: "Sine.easeIn", + onComplete: () => { + this.evolutionBgOverlay.setAlpha(1); + this.evolutionBg.setVisible(false); + }, + }, + { + targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], + alpha: 0, + duration: 2000, + delay: 150, + easing: "Sine.easeIn", + }, + { + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + onComplete: () => this.onEvolutionComplete(evolvedPokemon), + }, + ], + }); + } + /** * Handles a successful evolution * @param evolvedPokemon - The evolved Pokemon @@ -316,85 +456,15 @@ export class EvolutionPhase extends Phase { this.pokemonEvoSprite.setVisible(true); this.doCircleInward(); - const onEvolutionComplete = () => { - SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); - globalScene.time.delayedCall(250, () => { - this.pokemon.cry(); - globalScene.time.delayedCall(1250, () => { - globalScene.playSoundWithoutBgm("evolution_fanfare"); - - evolvedPokemon.destroy(); - globalScene.ui.showText( - i18next.t("menu:evolutionDone", { - pokemonName: this.preEvolvedPokemonName, - evolvedPokemonName: this.pokemon.species.getExpandedSpeciesName(), - }), - null, - () => this.end(), - null, - true, - fixedInt(4000), - ); - globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm()); - }); - }); - }; - globalScene.time.delayedCall(900, () => { - this.evolutionHandler.canCancel = false; + this.evolutionHandler.canCancel = this.canCancel; - this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => { - const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved - ? LearnMoveSituation.EVOLUTION_FUSED - : this.pokemon.fusionSpecies - ? LearnMoveSituation.EVOLUTION_FUSED_BASE - : LearnMoveSituation.EVOLUTION; - const levelMoves = this.pokemon - .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) - .filter(lm => lm[0] === EVOLVE_MOVE); - for (const lm of levelMoves) { - globalScene.phaseManager.unshiftNew( - "LearnMovePhase", - globalScene.getPlayerParty().indexOf(this.pokemon), - lm[1], - ); - } - globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); - - globalScene.playSound("se/shine"); - this.doSpray(); - globalScene.tweens.add({ - targets: this.evolutionOverlay, - alpha: 1, - duration: 250, - easing: "Sine.easeIn", - onComplete: () => { - this.evolutionBgOverlay.setAlpha(1); - this.evolutionBg.setVisible(false); - globalScene.tweens.add({ - targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], - alpha: 0, - duration: 2000, - delay: 150, - easing: "Sine.easeIn", - onComplete: () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - onComplete: onEvolutionComplete, - }); - }, - }); - }, - }); - }); + this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => this.postEvolve(evolvedPokemon)); }); } doSpiralUpward() { let f = 0; - globalScene.tweens.addCounter({ repeat: 64, duration: getFrameMs(1), @@ -430,34 +500,41 @@ export class EvolutionPhase extends Phase { }); } - doCycle(l: number, lastCycle = 15): Promise { - return new Promise(resolve => { - const isLastCycle = l === lastCycle; - globalScene.tweens.add({ - targets: this.pokemonTintSprite, - scale: 0.25, + /** + * Return a tween chain that cycles the evolution sprites + */ + doCycle(cycles: number, lastCycle = 15, onComplete = () => {}): void { + // Make our tween start both at the same time + const tweens: Phaser.Types.Tweens.TweenBuilderConfig[] = []; + for (let i = cycles; i <= lastCycle; i += 0.5) { + tweens.push({ + targets: [this.pokemonTintSprite, this.pokemonEvoTintSprite], + scale: (_target, _key, _value, targetIndex: number, _totalTargets, _tween) => (targetIndex === 0 ? 0.25 : 1), ease: "Cubic.easeInOut", - duration: 500 / l, - yoyo: !isLastCycle, - }); - globalScene.tweens.add({ - targets: this.pokemonEvoTintSprite, - scale: 1, - ease: "Cubic.easeInOut", - duration: 500 / l, - yoyo: !isLastCycle, + duration: 500 / i, + yoyo: i !== lastCycle, onComplete: () => { if (this.evolutionHandler.cancelled) { - return resolve(false); + // cause the tween chain to complete instantly, skipping the remaining tweens. + this.pokemonEvoTintSprite.setScale(1); + this.pokemonEvoTintSprite.setVisible(false); + this.evoChain?.complete?.(); + return; } - if (l < lastCycle) { - this.doCycle(l + 0.5, lastCycle).then(success => resolve(success)); - } else { - this.pokemonTintSprite.setVisible(false); - resolve(true); + if (i === lastCycle) { + this.pokemonEvoTintSprite.setScale(1); } }, }); + } + + this.evoChain = globalScene.tweens.chain({ + targets: null, + tweens, + onComplete: () => { + this.evoChain = null; + onComplete(); + }, }); } diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 74768e86186..7ab162c2ac6 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; -import { ExpBoosterModifier } from "#app/modifier/modifier"; +import { ExpBoosterModifier } from "#modifiers/modifier"; +import { PlayerPartyMemberPokemonPhase } from "#phases/player-party-member-pokemon-phase"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils/common"; -import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; export class ExpPhase extends PlayerPartyMemberPokemonPhase { public readonly phaseName = "ExpPhase"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 675a198d096..d1bd0ed0804 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -1,30 +1,24 @@ -import type { BattlerIndex } from "#enums/battler-index"; -import { BattleType } from "#enums/battle-type"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { - applyPostFaintAbAttrs, - applyPostKnockOutAbAttrs, - applyPostVictoryAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { battleSpecDialogue } from "#app/data/dialogue"; -import { allMoves } from "#app/data/data-lists"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { BattleSpec } from "#app/enums/battle-spec"; -import { StatusEffect } from "#app/enums/status-effect"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { HitResult } from "#enums/hit-result"; -import type { PlayerPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; -import { SwitchType } from "#enums/switch-type"; -import i18next from "i18next"; -import { PokemonPhase } from "./pokemon-phase"; -import { isNullOrUndefined } from "#app/utils/common"; -import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters"; +import { FRIENDSHIP_LOSS_FROM_FAINT } from "#balance/starters"; +import { allMoves } from "#data/data-lists"; +import { battleSpecDialogue } from "#data/dialogue"; +import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; +import { BattleSpec } from "#enums/battle-spec"; +import { BattleType } from "#enums/battle-type"; +import type { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { HitResult } from "#enums/hit-result"; +import { StatusEffect } from "#enums/status-effect"; +import { SwitchType } from "#enums/switch-type"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; +import { PokemonInstantReviveModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; export class FaintPhase extends PokemonPhase { public readonly phaseName = "FaintPhase"; @@ -117,29 +111,31 @@ export class FaintPhase extends PokemonPhase { pokemon.resetTera(); + // TODO: this can be simplified by just checking whether lastAttack is defined if (pokemon.turnData.attacksReceived?.length) { const lastAttack = pokemon.turnData.attacksReceived[0]; - applyPostFaintAbAttrs( - "PostFaintAbAttr", - pokemon, - globalScene.getPokemonById(lastAttack.sourceId)!, - new PokemonMove(lastAttack.move).getMove(), - lastAttack.result, - ); // TODO: is this bang correct? + applyAbAttrs("PostFaintAbAttr", { + pokemon: pokemon, + // TODO: We should refactor lastAttack's sourceId to forbid null and just use undefined + attacker: globalScene.getPokemonById(lastAttack.sourceId) ?? undefined, + // TODO: improve the way that we provide the move that knocked out the pokemon... + move: new PokemonMove(lastAttack.move).getMove(), + hitResult: lastAttack.result, + }); // TODO: is this bang correct? } else { //If killed by indirect damage, apply post-faint abilities without providing a last move - applyPostFaintAbAttrs("PostFaintAbAttr", pokemon); + applyAbAttrs("PostFaintAbAttr", { pokemon }); } const alivePlayField = globalScene.getField(true); for (const p of alivePlayField) { - applyPostKnockOutAbAttrs("PostKnockOutAbAttr", p, pokemon); + applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon }); } if (pokemon.turnData.attacksReceived?.length) { const defeatSource = this.source; if (defeatSource?.isOnField()) { - applyPostVictoryAbAttrs("PostVictoryAbAttr", defeatSource); + applyAbAttrs("PostVictoryAbAttr", { pokemon: defeatSource }); const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr"); if (pvattrs.length) { diff --git a/src/phases/field-phase.ts b/src/phases/field-phase.ts index c37f0e960e7..99de3d9fcf5 100644 --- a/src/phases/field-phase.ts +++ b/src/phases/field-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import type Pokemon from "#app/field/pokemon"; -import { BattlePhase } from "./battle-phase"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; type PokemonFunc = (pokemon: Pokemon) => void; diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index 13cd410ef87..7b7f5a6dac6 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils/common"; -import { achvs } from "../system/achv"; -import type { SpeciesFormChange } from "../data/pokemon-forms"; -import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms/form-change-triggers"; -import type { PlayerPokemon } from "../field/pokemon"; -import { UiMode } from "#enums/ui-mode"; -import type PartyUiHandler from "../ui/party-ui-handler"; -import { getPokemonNameWithAffix } from "../messages"; -import { EvolutionPhase } from "./evolution-phase"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { getSpeciesFormChangeMessage } from "#data/form-change-triggers"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; import { BattlerTagType } from "#enums/battler-tag-type"; import { SpeciesFormKey } from "#enums/species-form-key"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { EvolutionPhase } from "#phases/evolution-phase"; +import { achvs } from "#system/achv"; +import type { PartyUiHandler } from "#ui/party-ui-handler"; +import { fixedInt } from "#utils/common"; export class FormChangePhase extends EvolutionPhase { public readonly phaseName = "FormChangePhase"; @@ -34,146 +34,158 @@ export class FormChangePhase extends EvolutionPhase { return globalScene.ui.setOverlayMode(UiMode.EVOLUTION_SCENE); } - doEvolution(): void { - const preName = getPokemonNameWithAffix(this.pokemon); - - this.pokemon.getPossibleForm(this.formChange).then(transformedPokemon => { - [this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = transformedPokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); + /** + * Commence the tweens that play after the form change animation finishes + * @param transformedPokemon - The Pokemon after the evolution + * @param preName - The name of the Pokemon before the evolution + */ + private postFormChangeTweens(transformedPokemon: Pokemon, preName: string): void { + globalScene.tweens.chain({ + targets: null, + tweens: [ + { + targets: this.evolutionOverlay, + alpha: 1, + duration: 250, + easing: "Sine.easeIn", + onComplete: () => { + this.evolutionBgOverlay.setAlpha(1); + this.evolutionBg.setVisible(false); + }, + }, + { + targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], + alpha: 0, + duration: 2000, + delay: 150, + easing: "Sine.easeIn", + }, + { + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + completeDelay: 250, + onComplete: () => this.pokemon.cry(), + }, + ], + // 1.25 seconds after the pokemon cry + completeDelay: 1250, + onComplete: () => { + let playEvolutionFanfare = false; + if (this.formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1) { + globalScene.validateAchv(achvs.MEGA_EVOLVE); + playEvolutionFanfare = true; + } else if ( + this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || + this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1 + ) { + globalScene.validateAchv(achvs.GIGANTAMAX); + playEvolutionFanfare = true; } - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", transformedPokemon.getSpriteKey()); - sprite.setPipelineData("shiny", transformedPokemon.shiny); - sprite.setPipelineData("variant", transformedPokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (transformedPokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = transformedPokemon.getSprite().pipelineData[k]; - }); - }); + const delay = playEvolutionFanfare ? 4000 : 1750; + globalScene.playSoundWithoutBgm(playEvolutionFanfare ? "evolution_fanfare" : "minor_fanfare"); + transformedPokemon.destroy(); + globalScene.ui.showText( + getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), + null, + () => this.end(), + null, + true, + fixedInt(delay), + ); + globalScene.time.delayedCall(fixedInt(delay + 250), () => globalScene.playBgm()); + }, + }); + } - globalScene.time.delayedCall(250, () => { - globalScene.tweens.add({ + /** + * Commence the animations that occur once the form change evolution cycle ({@linkcode doCycle}) is complete + * + * @privateRemarks + * This would prefer {@linkcode doCycle} to be refactored and de-promisified so this can be moved into {@linkcode beginTweens} + * @param preName - The name of the Pokemon before the evolution + * @param transformedPokemon - The Pokemon being transformed into + */ + private afterCycle(preName: string, transformedPokemon: Pokemon): void { + globalScene.playSound("se/sparkle"); + this.pokemonEvoSprite.setVisible(true); + this.doCircleInward(); + globalScene.time.delayedCall(900, () => { + this.pokemon.changeForm(this.formChange).then(() => { + if (!this.modal) { + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + } + globalScene.playSound("se/shine"); + this.doSpray(); + this.postFormChangeTweens(transformedPokemon, preName); + }); + }); + } + + /** + * Commence the sequence of tweens and events that occur during the evolution animation + * @param preName The name of the Pokemon before the evolution + * @param transformedPokemon The Pokemon after the evolution + */ + private beginTweens(preName: string, transformedPokemon: Pokemon): void { + globalScene.tweens.chain({ + // Starts 250ms after sprites have been configured + targets: null, + tweens: [ + // Step 1: Fade in the background overlay + { + delay: 250, targets: this.evolutionBgOverlay, alpha: 1, - delay: 500, duration: 1500, ease: "Sine.easeOut", + // We want the backkground overlay to fade out after it fades in onComplete: () => { - globalScene.time.delayedCall(1000, () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - }); - this.evolutionBg.setVisible(true); - this.evolutionBg.play(); + globalScene.tweens.add({ + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + delay: 1000, }); + this.evolutionBg.setVisible(true).play(); + }, + }, + // Step 2: Play the sounds and fade in the tint sprite + { + targets: this.pokemonTintSprite, + alpha: { from: 0, to: 1 }, + duration: 2000, + onStart: () => { globalScene.playSound("se/charge"); this.doSpiralUpward(); - globalScene.tweens.addCounter({ - from: 0, - to: 1, - duration: 2000, - onUpdate: t => { - this.pokemonTintSprite.setAlpha(t.getValue()); - }, - onComplete: () => { - this.pokemonSprite.setVisible(false); - globalScene.time.delayedCall(1100, () => { - globalScene.playSound("se/beam"); - this.doArcDownward(); - globalScene.time.delayedCall(1000, () => { - this.pokemonEvoTintSprite.setScale(0.25); - this.pokemonEvoTintSprite.setVisible(true); - this.doCycle(1, 1).then(_success => { - globalScene.playSound("se/sparkle"); - this.pokemonEvoSprite.setVisible(true); - this.doCircleInward(); - globalScene.time.delayedCall(900, () => { - this.pokemon.changeForm(this.formChange).then(() => { - if (!this.modal) { - globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); - } - - globalScene.playSound("se/shine"); - this.doSpray(); - globalScene.tweens.add({ - targets: this.evolutionOverlay, - alpha: 1, - duration: 250, - easing: "Sine.easeIn", - onComplete: () => { - this.evolutionBgOverlay.setAlpha(1); - this.evolutionBg.setVisible(false); - globalScene.tweens.add({ - targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], - alpha: 0, - duration: 2000, - delay: 150, - easing: "Sine.easeIn", - onComplete: () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - onComplete: () => { - globalScene.time.delayedCall(250, () => { - this.pokemon.cry(); - globalScene.time.delayedCall(1250, () => { - let playEvolutionFanfare = false; - if (this.formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1) { - globalScene.validateAchv(achvs.MEGA_EVOLVE); - playEvolutionFanfare = true; - } else if ( - this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || - this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1 - ) { - globalScene.validateAchv(achvs.GIGANTAMAX); - playEvolutionFanfare = true; - } - - const delay = playEvolutionFanfare ? 4000 : 1750; - globalScene.playSoundWithoutBgm( - playEvolutionFanfare ? "evolution_fanfare" : "minor_fanfare", - ); - - transformedPokemon.destroy(); - globalScene.ui.showText( - getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), - null, - () => this.end(), - null, - true, - fixedInt(delay), - ); - globalScene.time.delayedCall(fixedInt(delay + 250), () => - globalScene.playBgm(), - ); - }); - }); - }, - }); - }, - }); - }, - }); - }); - }); - }); - }); - }); - }, - }); }, + onComplete: () => { + this.pokemonSprite.setVisible(false); + }, + }, + ], + + // Step 3: Commence the form change animation via doCycle then continue the animation chain with afterCycle + completeDelay: 1100, + onComplete: () => { + globalScene.playSound("se/beam"); + this.doArcDownward(); + globalScene.time.delayedCall(1000, () => { + this.pokemonEvoTintSprite.setScale(0.25).setVisible(true); + this.doCycle(1, 1, () => this.afterCycle(preName, transformedPokemon)); }); - }); + }, + }); + } + + doEvolution(): void { + const preName = getPokemonNameWithAffix(this.pokemon, false); + + this.pokemon.getPossibleForm(this.formChange).then(transformedPokemon => { + this.configureSprite(transformedPokemon, this.pokemonEvoSprite, false); + this.configureSprite(transformedPokemon, this.pokemonEvoTintSprite, false); + this.beginTweens(preName, transformedPokemon); }); } diff --git a/src/phases/game-over-modifier-reward-phase.ts b/src/phases/game-over-modifier-reward-phase.ts index 13c8f48abad..f07c7d2adba 100644 --- a/src/phases/game-over-modifier-reward-phase.ts +++ b/src/phases/game-over-modifier-reward-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { UiMode } from "#enums/ui-mode"; +import { ModifierRewardPhase } from "#phases/modifier-reward-phase"; import i18next from "i18next"; -import { ModifierRewardPhase } from "./modifier-reward-phase"; export class GameOverModifierRewardPhase extends ModifierRewardPhase { public readonly phaseName = "GameOverModifierRewardPhase"; diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index eaf1e4f58d7..d4562b5a237 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -1,29 +1,29 @@ +import { pokerogueApi } from "#api/pokerogue-api"; import { clientSessionId } from "#app/account"; -import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { getCharVariantFromDialogue } from "#app/data/dialogue"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import type Pokemon from "#app/field/pokemon"; -import { modifierTypes } from "#app/data/data-lists"; -import { BattlePhase } from "#app/phases/battle-phase"; -import type { EndCardPhase } from "#app/phases/end-card-phase"; -import { achvs, ChallengeAchv } from "#app/system/achv"; -import { Unlockables } from "#enums/unlockables"; -import { UiMode } from "#enums/ui-mode"; -import { isLocal, isLocalServerConnected } from "#app/utils/common"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { modifierTypes } from "#data/data-lists"; +import { getCharVariantFromDialogue } from "#data/dialogue"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { BattleType } from "#enums/battle-type"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; +import { UiMode } from "#enums/ui-mode"; +import { Unlockables } from "#enums/unlockables"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; +import type { EndCardPhase } from "#phases/end-card-phase"; +import { achvs, ChallengeAchv } from "#system/achv"; +import { ArenaData } from "#system/arena-data"; +import { ChallengeData } from "#system/challenge-data"; +import type { SessionSaveData } from "#system/game-data"; +import { ModifierData as PersistentModifierData } from "#system/modifier-data"; +import { PokemonData } from "#system/pokemon-data"; +import { TrainerData } from "#system/trainer-data"; +import { trainerConfigs } from "#trainers/trainer-config"; +import { isLocal, isLocalServerConnected } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import type { SessionSaveData } from "#app/system/game-data"; -import PersistentModifierData from "#app/system/modifier-data"; -import PokemonData from "#app/system/pokemon-data"; -import ChallengeData from "#app/system/challenge-data"; -import TrainerData from "#app/system/trainer-data"; -import ArenaData from "#app/system/arena-data"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; export class GameOverPhase extends BattlePhase { public readonly phaseName = "GameOverPhase"; @@ -299,7 +299,7 @@ export class GameOverPhase extends BattlePhase { battleType: globalScene.currentBattle.battleType, trainer: globalScene.currentBattle.trainer ? new TrainerData(globalScene.currentBattle.trainer) : null, gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, diff --git a/src/phases/hide-party-exp-bar-phase.ts b/src/phases/hide-party-exp-bar-phase.ts index 9ee08280cd4..e59bd02ce60 100644 --- a/src/phases/hide-party-exp-bar-phase.ts +++ b/src/phases/hide-party-exp-bar-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { BattlePhase } from "./battle-phase"; +import { BattlePhase } from "#phases/battle-phase"; export class HidePartyExpBarPhase extends BattlePhase { public readonly phaseName = "HidePartyExpBarPhase"; diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index e24efa63b5a..a714d247730 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -1,18 +1,18 @@ import { globalScene } from "#app/global-scene"; -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; -import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { MoveId } from "#enums/move-id"; import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; -import EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; -import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; -import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; -import type Pokemon from "#app/field/pokemon"; +import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; +import { allMoves } from "#data/data-lists"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#data/form-change-triggers"; import { LearnMoveType } from "#enums/learn-move-type"; +import { MoveId } from "#enums/move-id"; +import { UiMode } from "#enums/ui-mode"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { PlayerPartyMemberPokemonPhase } from "#phases/player-party-member-pokemon-phase"; +import { EvolutionSceneHandler } from "#ui/evolution-scene-handler"; +import { SummaryUiMode } from "#ui/summary-ui-handler"; +import i18next from "i18next"; export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { public readonly phaseName = "LearnMovePhase"; diff --git a/src/phases/level-cap-phase.ts b/src/phases/level-cap-phase.ts index 12d4d64e8e2..a1f1e07f58d 100644 --- a/src/phases/level-cap-phase.ts +++ b/src/phases/level-cap-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { UiMode } from "#enums/ui-mode"; +import { FieldPhase } from "#phases/field-phase"; import i18next from "i18next"; -import { FieldPhase } from "./field-phase"; export class LevelCapPhase extends FieldPhase { public readonly phaseName = "LevelCapPhase"; diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index c78a1798304..07a87d73411 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; -import { ExpNotification } from "#app/enums/exp-notification"; -import type { PlayerPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; -import { LevelAchv } from "#app/system/achv"; -import { NumberHolder } from "#app/utils/common"; +import { ExpNotification } from "#enums/exp-notification"; +import type { PlayerPokemon } from "#field/pokemon"; +import { PlayerPartyMemberPokemonPhase } from "#phases/player-party-member-pokemon-phase"; +import { LevelAchv } from "#system/achv"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { diff --git a/src/phases/load-move-anim-phase.ts b/src/phases/load-move-anim-phase.ts index c9b78797407..3209e395399 100644 --- a/src/phases/load-move-anim-phase.ts +++ b/src/phases/load-move-anim-phase.ts @@ -1,6 +1,6 @@ -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; -import type { MoveId } from "#enums/move-id"; import { Phase } from "#app/phase"; +import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; +import type { MoveId } from "#enums/move-id"; /** * Phase for synchronous move animation loading. diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index de426866baa..f310c60b0d4 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -1,12 +1,12 @@ import { updateUserInfo } from "#app/account"; -import { bypassLogin } from "#app/global-vars/bypass-login"; import { globalScene } from "#app/global-scene"; +import { bypassLogin } from "#app/global-vars/bypass-login"; import { Phase } from "#app/phase"; import { handleTutorial, Tutorial } from "#app/tutorial"; import { UiMode } from "#enums/ui-mode"; +import { executeIf, sessionIdKey } from "#utils/common"; +import { getCookie, removeCookie } from "#utils/cookies"; import i18next, { t } from "i18next"; -import { sessionIdKey, executeIf } from "#app/utils/common"; -import { getCookie, removeCookie } from "#app/utils/cookies"; export class LoginPhase extends Phase { public readonly phaseName = "LoginPhase"; diff --git a/src/phases/modifier-reward-phase.ts b/src/phases/modifier-reward-phase.ts index 29bbe215fc0..b84985b7311 100644 --- a/src/phases/modifier-reward-phase.ts +++ b/src/phases/modifier-reward-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import type { ModifierType } from "#app/modifier/modifier-type"; -import type { ModifierTypeFunc } from "#app/@types/modifier-types"; -import { getModifierType } from "#app/utils/modifier-utils"; +import type { ModifierType } from "#modifiers/modifier-type"; +import { BattlePhase } from "#phases/battle-phase"; +import type { ModifierTypeFunc } from "#types/modifier-types"; +import { getModifierType } from "#utils/modifier-utils"; import i18next from "i18next"; -import { BattlePhase } from "./battle-phase"; export class ModifierRewardPhase extends BattlePhase { // RibbonModifierRewardPhase extends ModifierRewardPhase and to make typescript happy diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts index 52cb9ecb3ff..61153de8565 100644 --- a/src/phases/money-reward-phase.ts +++ b/src/phases/money-reward-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { MoneyMultiplierModifier } from "#app/modifier/modifier"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { MoneyMultiplierModifier } from "#modifiers/modifier"; +import { BattlePhase } from "#phases/battle-phase"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils/common"; -import { BattlePhase } from "./battle-phase"; export class MoneyRewardPhase extends BattlePhase { public readonly phaseName = "MoneyRewardPhase"; diff --git a/src/phases/move-anim-phase.ts b/src/phases/move-anim-phase.ts index 383841a0146..b8563fbb997 100644 --- a/src/phases/move-anim-phase.ts +++ b/src/phases/move-anim-phase.ts @@ -1,5 +1,5 @@ -import type { MoveAnim } from "#app/data/battle-anims"; import { Phase } from "#app/phase"; +import type { MoveAnim } from "#data/battle-anims"; /** * Plays the given {@linkcode MoveAnim} sequentially. diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index 51b8fe96ff6..0c83db10511 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; +import { MoveChargeAnim } from "#data/battle-anims"; import type { BattlerIndex } from "#enums/battler-index"; -import { MoveChargeAnim } from "#app/data/battle-anims"; -import { applyMoveChargeAttrs } from "#app/data/moves/apply-attrs"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; -import { BooleanHolder } from "#app/utils/common"; -import { PokemonPhase } from "#app/phases/pokemon-phase"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveResult } from "#enums/move-result"; import type { MoveUseMode } from "#enums/move-use-mode"; -import type { ChargingMove } from "#app/@types/move-types"; +import type { Pokemon } from "#field/pokemon"; +import { applyMoveChargeAttrs } from "#moves/apply-attrs"; +import type { PokemonMove } from "#moves/pokemon-move"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import type { ChargingMove } from "#types/move-types"; +import { BooleanHolder } from "#utils/common"; /** * Phase for the "charging turn" of two-turn moves (e.g. Dig). diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index d7da1ab996c..4446b2071d0 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -1,38 +1,27 @@ -import { BattlerIndex } from "#enums/battler-index"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { - applyExecutedMoveAbAttrs, - applyPostAttackAbAttrs, - applyPostDamageAbAttrs, - applyPostDefendAbAttrs, - applyPreAttackAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import { ConditionalProtectTag } from "#app/data/arena-tag"; +import { getPokemonNameWithAffix } from "#app/messages"; +import type { Phase } from "#app/phase"; +import { ConditionalProtectTag } from "#data/arena-tag"; +import { MoveAnim } from "#data/battle-anims"; +import { DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag, TypeBoostTag } from "#data/battler-tags"; +import { SpeciesFormChangePostMoveTrigger } from "#data/form-change-triggers"; +import type { TypeDamageMultiplier } from "#data/type"; import { ArenaTagSide } from "#enums/arena-tag-side"; -import { MoveAnim } from "#app/data/battle-anims"; -import { - DamageProtectedTag, - ProtectedTag, - SemiInvulnerableTag, - SubstituteTag, - TypeBoostTag, -} from "#app/data/battler-tags"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import type { MoveAttr } from "#app/data/moves/move"; -import { getMoveTargets } from "#app/data/moves/move-utils"; -import { applyFilteredMoveAttrs, applyMoveAttrs } from "#app/data/moves/apply-attrs"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { HitCheckResult } from "#enums/hit-check-result"; +import { HitResult } from "#enums/hit-result"; +import { MoveCategory } from "#enums/MoveCategory"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; -import { MoveCategory } from "#enums/MoveCategory"; -import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { PokemonType } from "#enums/pokemon-type"; -import type { DamageResult, TurnMove } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; +import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; -import { HitResult } from "#enums/hit-result"; -import { getPokemonNameWithAffix } from "#app/messages"; +import { isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode"; +import { PokemonType } from "#enums/pokemon-type"; +import type { Pokemon } from "#field/pokemon"; import { ContactHeldItemTransferChanceModifier, DamageMoneyRewardModifier, @@ -41,20 +30,18 @@ import { FlinchChanceModifier, HitHealModifier, PokemonMultiHitModifier, -} from "#app/modifier/modifier"; -import { PokemonPhase } from "#app/phases/pokemon-phase"; -import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils/common"; -import type { nil } from "#app/utils/common"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { MoveId } from "#enums/move-id"; +} from "#modifiers/modifier"; +import { applyFilteredMoveAttrs, applyMoveAttrs } from "#moves/apply-attrs"; +import type { Move, MoveAttr } from "#moves/move"; +import { getMoveTargets, isFieldTargeted } from "#moves/move-utils"; +import { PokemonMove } from "#moves/pokemon-move"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { DamageAchv } from "#system/achv"; +import type { DamageResult } from "#types/damage-result"; +import type { TurnMove } from "#types/turn-move"; +import type { nil } from "#utils/common"; +import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#utils/common"; import i18next from "i18next"; -import type { Phase } from "#app/phase"; -import type { TypeDamageMultiplier } from "#app/data/type"; -import { HitCheckResult } from "#enums/hit-check-result"; -import type Move from "#app/data/moves/move"; -import { isFieldTargeted } from "#app/data/moves/move-utils"; -import { DamageAchv } from "#app/system/achv"; -import { isVirtual, isReflected, MoveUseMode } from "#enums/move-use-mode"; export type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier]; @@ -322,7 +309,7 @@ export class MoveEffectPhase extends PokemonPhase { // Assume single target for multi hit applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount); // If Parental Bond is applicable, add another hit - applyPreAttackAbAttrs("AddSecondStrikeAbAttr", user, null, move, false, hitCount, null); + applyAbAttrs("AddSecondStrikeAbAttr", { pokemon: user, move, hitCount }); // If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount); // Set the user's relevant turnData fields to reflect the final hit count @@ -370,7 +357,7 @@ export class MoveEffectPhase extends PokemonPhase { // Add to the move history entry if (this.firstHit) { user.pushMoveHistory(this.moveHistoryEntry); - applyExecutedMoveAbAttrs("ExecutedMoveAbAttr", user); + applyAbAttrs("ExecutedMoveAbAttr", { pokemon: user }); } try { @@ -437,9 +424,15 @@ export class MoveEffectPhase extends PokemonPhase { * @param user - The {@linkcode Pokemon} using this phase's invoked move * @param target - {@linkcode Pokemon} the current target of this phase's invoked move * @param hitResult - The {@linkcode HitResult} of the attempted move + * @param wasCritical - `true` if the move was a critical hit */ - protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void { - applyPostDefendAbAttrs("PostDefendAbAttr", target, user, this.move, hitResult); + protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, wasCritical = false): void { + const params = { pokemon: target, opponent: user, move: this.move, hitResult }; + applyAbAttrs("PostDefendAbAttr", params); + + if (wasCritical) { + applyAbAttrs("PostReceiveCritStatStageChangeAbAttr", params); + } target.lapseTags(BattlerTagLapseType.AFTER_HIT); } @@ -696,12 +689,9 @@ export class MoveEffectPhase extends PokemonPhase { * @param target - The {@linkcode Pokemon} to be removed */ protected removeTarget(target: Pokemon): void { - const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); + const targetIndex = this.targets.indexOf(target.getBattlerIndex()); if (targetIndex !== -1) { - this.targets.splice( - this.targets.findIndex(ind => ind === target.getBattlerIndex()), - 1, - ); + this.targets.splice(this.targets.indexOf(target.getBattlerIndex()), 1); } } @@ -796,19 +786,21 @@ export class MoveEffectPhase extends PokemonPhase { this.triggerMoveEffects(MoveEffectTrigger.PRE_APPLY, user, target); - const hitResult = this.applyMove(user, target, effectiveness); + const [hitResult, wasCritical] = this.applyMove(user, target, effectiveness); // Apply effects to the user (always) and the target (if not blocked by substitute). this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, true); if (!this.move.hitsSubstitute(user, target)) { - this.applyOnTargetEffects(user, target, hitResult, firstTarget); + this.applyOnTargetEffects(user, target, hitResult, firstTarget, wasCritical); } if (this.lastHit) { globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); // Multi-hit check for Wimp Out/Emergency Exit if (user.turnData.hitCount > 1) { - applyPostDamageAbAttrs("PostDamageAbAttr", target, 0, target.hasPassive(), false, [], user); + // TODO: Investigate why 0 is being passed for damage amount here + // and then determing if refactoring `applyMove` to return the damage dealt is appropriate. + applyAbAttrs("PostDamageAbAttr", { pokemon: target, damage: 0, source: user }); } } } @@ -819,8 +811,9 @@ export class MoveEffectPhase extends PokemonPhase { * @param user - The {@linkcode Pokemon} using this phase's invoked move * @param target - The {@linkcode Pokemon} targeted by the move * @param effectiveness - The effectiveness of the move against the target + * @returns The {@linkcode HitResult} of the move against the target and a boolean indicating whether the target was crit */ - protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult { + protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] { const isCritical = target.getCriticalHitResult(user, this.move); /* @@ -851,7 +844,7 @@ export class MoveEffectPhase extends PokemonPhase { const isOneHitKo = result === HitResult.ONE_HIT_KO; if (!dmg) { - return result; + return [result, false]; } target.lapseTags(BattlerTagLapseType.HIT); @@ -879,7 +872,7 @@ export class MoveEffectPhase extends PokemonPhase { } if (damage <= 0) { - return result; + return [result, isCritical]; } if (user.isPlayer()) { @@ -908,7 +901,7 @@ export class MoveEffectPhase extends PokemonPhase { globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage)); } - return result; + return [result, isCritical]; } /** @@ -962,17 +955,17 @@ export class MoveEffectPhase extends PokemonPhase { * @param target - The {@linkcode Pokemon} struck by the move * @param effectiveness - The effectiveness of the move against the target */ - protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult { + protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] { const moveCategory = user.getMoveCategory(target, this.move); if (moveCategory === MoveCategory.STATUS) { - return HitResult.STATUS; + return [HitResult.STATUS, false]; } const result = this.applyMoveDamage(user, target, effectiveness); if (user.turnData.hitsLeft === 1 || target.isFainted()) { - this.queueHitResultMessage(result); + this.queueHitResultMessage(result[0]); } if (target.isFainted()) { @@ -989,8 +982,15 @@ export class MoveEffectPhase extends PokemonPhase { * @param target - The {@linkcode Pokemon} targeted by the move * @param hitResult - The {@linkcode HitResult} obtained from applying the move * @param firstTarget - `true` if the target is the first Pokemon hit by the attack + * @param wasCritical - `true` if the move was a critical hit */ - protected applyOnTargetEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, firstTarget: boolean): void { + protected applyOnTargetEffects( + user: Pokemon, + target: Pokemon, + hitResult: HitResult, + firstTarget: boolean, + wasCritical = false, + ): void { /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ const dealsDamage = [ HitResult.EFFECTIVE, @@ -1001,8 +1001,8 @@ export class MoveEffectPhase extends PokemonPhase { this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false); this.applyHeldItemFlinchCheck(user, target, dealsDamage); - this.applyOnGetHitAbEffects(user, target, hitResult); - applyPostAttackAbAttrs("PostAttackAbAttr", user, target, this.move, hitResult); + this.applyOnGetHitAbEffects(user, target, hitResult, wasCritical); + applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult }); // We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens if (!user.isPlayer() && this.move.is("AttackMove")) { diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 8c8f2ac5239..fd893c445ff 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -1,9 +1,9 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { PokemonPhase } from "./pokemon-phase"; import type { BattlerIndex } from "#enums/battler-index"; -import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import type Pokemon from "#app/field/pokemon"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; +import type { Pokemon } from "#field/pokemon"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class MoveEndPhase extends PokemonPhase { public readonly phaseName = "MoveEndPhase"; @@ -30,7 +30,7 @@ export class MoveEndPhase extends PokemonPhase { globalScene.arena.setIgnoreAbilities(false); for (const target of this.targets) { if (target) { - applyPostSummonAbAttrs("PostSummonRemoveEffectAbAttr", target); + applyAbAttrs("PostSummonRemoveEffectAbAttr", { pokemon: target }); } } diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts index 8c2d184c3f5..5c69dcd1217 100644 --- a/src/phases/move-header-phase.ts +++ b/src/phases/move-header-phase.ts @@ -1,7 +1,7 @@ -import { applyMoveAttrs } from "#app/data/moves/apply-attrs"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import { BattlePhase } from "./battle-phase"; +import type { Pokemon } from "#field/pokemon"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import type { PokemonMove } from "#moves/pokemon-move"; +import { BattlePhase } from "#phases/battle-phase"; export class MoveHeaderPhase extends BattlePhase { public readonly phaseName = "MoveHeaderPhase"; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 2e94b085948..09a542861be 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -1,33 +1,34 @@ -import { BattlerIndex } from "#enums/battler-index"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import type { DelayedAttackTag } from "#app/data/arena-tag"; -import { CommonAnim } from "#enums/move-anims-common"; -import { CenterOfAttentionTag } from "#app/data/battler-tags"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { applyMoveAttrs } from "#app/data/moves/apply-attrs"; -import { allMoves } from "#app/data/data-lists"; -import { MoveFlags } from "#enums/MoveFlags"; -import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; -import { PokemonType } from "#enums/pokemon-type"; -import { getTerrainBlockMessage, getWeatherBlockMessage } from "#app/data/weather"; -import { MoveUsedEvent } from "#app/events/battle-scene"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; -import { BattlePhase } from "#app/phases/battle-phase"; -import { enumValueToKey, NumberHolder } from "#app/utils/common"; +import type { DelayedAttackTag } from "#data/arena-tag"; +import { CenterOfAttentionTag } from "#data/battler-tags"; +import { SpeciesFormChangePreMoveTrigger } from "#data/form-change-triggers"; +import { getStatusEffectActivationText, getStatusEffectHealText } from "#data/status-effect"; +import { getTerrainBlockMessage } from "#data/terrain"; +import { getWeatherBlockMessage } from "#data/weather"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveFlags } from "#enums/MoveFlags"; +import { CommonAnim } from "#enums/move-anims-common"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { isIgnorePP, isIgnoreStatus, isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode"; +import { PokemonType } from "#enums/pokemon-type"; import { StatusEffect } from "#enums/status-effect"; +import { MoveUsedEvent } from "#events/battle-scene"; +import type { Pokemon } from "#field/pokemon"; +import { applyMoveAttrs } from "#moves/apply-attrs"; +import { frenzyMissFunc } from "#moves/move-utils"; +import type { PokemonMove } from "#moves/pokemon-move"; +import { BattlePhase } from "#phases/battle-phase"; +import { NumberHolder } from "#utils/common"; +import { enumValueToKey } from "#utils/enums"; import i18next from "i18next"; -import { isVirtual, isIgnorePP, isReflected, MoveUseMode, isIgnoreStatus } from "#enums/move-use-mode"; -import { frenzyMissFunc } from "#app/data/moves/move-utils"; export class MovePhase extends BattlePhase { public readonly phaseName = "MovePhase"; @@ -35,11 +36,11 @@ export class MovePhase extends BattlePhase { protected _move: PokemonMove; protected _targets: BattlerIndex[]; public readonly useMode: MoveUseMode; // Made public for quash + /** Whether the current move is forced last (used for Quash). */ protected forcedLast: boolean; - - /** Whether the current move should fail but still use PP */ + /** Whether the current move should fail but still use PP. */ protected failed = false; - /** Whether the current move should cancel and retain PP */ + /** Whether the current move should fail and retain PP. */ protected cancelled = false; public get pokemon(): Pokemon { @@ -164,6 +165,7 @@ export class MovePhase extends BattlePhase { this.resolveFinalPreMoveCancellationChecks(); } + // Cancel, charge or use the move as applicable. if (this.cancelled || this.failed) { this.handlePreMoveFailures(); } else if (this.move.getMove().isChargingMove() && !this.pokemon.getTag(BattlerTagType.CHARGING)) { @@ -228,14 +230,11 @@ export class MovePhase extends BattlePhase { case StatusEffect.SLEEP: { applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove()); const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0); - applyAbAttrs( - "ReduceStatusEffectDurationAbAttr", - this.pokemon, - null, - false, - this.pokemon.status.effect, - turnsRemaining, - ); + applyAbAttrs("ReduceStatusEffectDurationAbAttr", { + pokemon: this.pokemon, + statusEffect: this.pokemon.status.effect, + duration: turnsRemaining, + }); this.pokemon.status.sleepTurnsRemaining = turnsRemaining.value; healed = this.pokemon.status.sleepTurnsRemaining <= 0; activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP); @@ -284,8 +283,7 @@ export class MovePhase extends BattlePhase { protected lapsePreMoveAndMoveTags(): void { this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); - // TODO: does this intentionally happen before the no targets/MoveId.NONE on queue cancellation case is checked? - // (In other words, check if truant can proc on a move w/o targets) + // This intentionally happens before moves without targets are cancelled (Truant takes priority over lack of targets) if (!isIgnoreStatus(this.useMode) && this.canMove() && !this.cancelled) { this.pokemon.lapseTags(BattlerTagLapseType.MOVE); } @@ -299,44 +297,27 @@ export class MovePhase extends BattlePhase { // form changes happen even before we know that the move wll execute. globalScene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger); - const isDelayedAttack = move.hasAttr("DelayedAttackAttr"); - if (isDelayedAttack) { - // Check the player side arena if future sight is active - const futureSightTags = globalScene.arena.findTags(t => t.tagType === ArenaTagType.FUTURE_SIGHT); - const doomDesireTags = globalScene.arena.findTags(t => t.tagType === ArenaTagType.DOOM_DESIRE); - let fail = false; + // Check the player side arena if another delayed attack is active and hitting the same slot. + if (move.hasAttr("DelayedAttackAttr")) { const currentTargetIndex = targets[0].getBattlerIndex(); - for (const tag of futureSightTags) { - if ((tag as DelayedAttackTag).targetIndex === currentTargetIndex) { - fail = true; - break; - } - } - for (const tag of doomDesireTags) { - if ((tag as DelayedAttackTag).targetIndex === currentTargetIndex) { - fail = true; - break; - } - } - if (fail) { - this.showMoveText(); - this.showFailedText(); - this.end(); + const delayedAttackHittingSameSlot = globalScene.arena.tags.some( + tag => + (tag.tagType === ArenaTagType.FUTURE_SIGHT || tag.tagType === ArenaTagType.DOOM_DESIRE) && + (tag as DelayedAttackTag).targetIndex === currentTargetIndex, + ); + + if (delayedAttackHittingSameSlot) { + this.failMove(true); return; } } - let success = true; - // Check if there are any attributes that can interrupt the move, overriding the fail message. - for (const move of this.move.getMove().getAttrs("PreUseInterruptAttr")) { - if (move.apply(this.pokemon, targets[0], this.move.getMove())) { - success = false; - break; - } - } - - if (success) { - this.showMoveText(); + // Check if the move has any attributes that can interrupt its own use **before** displaying text. + // TODO: This should not rely on direct return values + let failed = move.getAttrs("PreUseInterruptAttr").some(attr => attr.apply(this.pokemon, targets[0], move)); + if (failed) { + this.failMove(false); + return; } // Clear out any two turn moves once they've been used. @@ -344,6 +325,7 @@ export class MovePhase extends BattlePhase { // Move queues should be handled by the calling `CommandPhase` or a manager for it // @ts-expect-error - useMode is readonly and shouldn't normally be assigned to this.useMode = moveQueue.shift()?.useMode ?? this.useMode; + if (this.pokemon.getTag(BattlerTagType.CHARGING)?.sourceMove === this.move.moveId) { this.pokemon.lapseTag(BattlerTagType.CHARGING); } @@ -351,127 +333,145 @@ export class MovePhase extends BattlePhase { if (!isIgnorePP(this.useMode)) { // "commit" to using the move, deducting PP. const ppUsed = 1 + this.getPpIncreaseFromPressure(targets); - this.move.usePp(ppUsed); - globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, move, this.move.ppUsed)); + globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon.id, move, this.move.ppUsed)); } /** * Determine if the move is successful (meaning that its damage/effects can be attempted) * by checking that all of the following are true: * - Conditional attributes of the move are all met - * - The target's `ForceSwitchOutImmunityAbAttr` is not triggered (see {@linkcode Move.prototype.applyConditions}) * - Weather does not block the move * - Terrain does not block the move - * - * TODO: These steps are straightforward, but the implementation below is extremely convoluted. */ /** * Move conditions assume the move has a single target * TODO: is this sustainable? */ - let failedDueToTerrain = false; - let failedDueToWeather = false; - if (success) { - const passesConditions = move.applyConditions(this.pokemon, targets[0], move); - failedDueToWeather = globalScene.arena.isMoveWeatherCancelled(this.pokemon, move); - failedDueToTerrain = globalScene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, move); - success = passesConditions && !failedDueToWeather && !failedDueToTerrain; + const failsConditions = !move.applyConditions(this.pokemon, targets[0], move); + const failedDueToWeather = globalScene.arena.isMoveWeatherCancelled(this.pokemon, move); + const failedDueToTerrain = globalScene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, move); + failed ||= failsConditions || failedDueToWeather || failedDueToTerrain; + + if (failed) { + this.failMove(true, failedDueToWeather, failedDueToTerrain); + return; } - // Update the battle's "last move" pointer, unless we're currently mimicking a move. - if (!allMoves[this.move.moveId].hasAttr("CopyMoveAttr")) { - // The last move used is unaffected by moves that fail - if (success) { - globalScene.currentBattle.lastMove = this.move.moveId; - } + this.executeMove(); + } + + /** Execute the current move and apply its effects. */ + private executeMove() { + const move = this.move.getMove(); + const targets = this.getActiveTargetPokemon(); + + // Update the battle's "last move" pointer unless we're currently mimicking a move or triggering Dancer. + if (!move.hasAttr("CopyMoveAttr") && !isReflected(this.useMode)) { + globalScene.currentBattle.lastMove = move.id; } - /** - * If the move has not failed, trigger ability-based user type changes and then execute it. - * - * Notably, Roar, Whirlwind, Trick-or-Treat, and Forest's Curse will trigger these type changes even - * if the move fails. - */ - if (success) { - const move = this.move.getMove(); - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, move); - globalScene.phaseManager.unshiftNew( - "MoveEffectPhase", - this.pokemon.getBattlerIndex(), - this.targets, - move, - this.useMode, - ); - } else { - if ([MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)) { - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove()); - } - - this.pokemon.pushMoveHistory({ - move: this.move.moveId, - targets: this.targets, - result: MoveResult.FAIL, - useMode: this.useMode, - }); - - const failureMessage = move.getFailedText(this.pokemon, targets[0], move); - let failedText: string | undefined; - if (failureMessage) { - failedText = failureMessage; - } else if (failedDueToTerrain) { - failedText = getTerrainBlockMessage(targets[0], globalScene.arena.getTerrainType()); - } else if (failedDueToWeather) { - failedText = getWeatherBlockMessage(globalScene.arena.getWeatherType()); - } - - this.showFailedText(failedText); - - // Remove the user from its semi-invulnerable state (if applicable) - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); - } + // Trigger ability-based user type changes, display move text and then execute move effects. + // TODO: Investigate whether PokemonTypeChangeAbAttr can drop the "opponent" parameter + applyAbAttrs("PokemonTypeChangeAbAttr", { pokemon: this.pokemon, move, opponent: targets[0] }); + this.showMoveText(); + globalScene.phaseManager.unshiftNew( + "MoveEffectPhase", + this.pokemon.getBattlerIndex(), + this.targets, + move, + this.useMode, + ); // Handle Dancer, which triggers immediately after a move is used (rather than waiting on `this.end()`). // Note the MoveUseMode check here prevents an infinite Dancer loop. + // TODO: This needs to go at the end of `MoveEffectPhase` to check move results const dancerModes: MoveUseMode[] = [MoveUseMode.INDIRECT, MoveUseMode.REFLECTED] as const; if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !dancerModes.includes(this.useMode)) { - // TODO: Fix in dancer PR to move to MEP for hit checks globalScene.getField(true).forEach(pokemon => { - applyPostMoveUsedAbAttrs("PostMoveUsedAbAttr", pokemon, this.move, this.pokemon, this.targets); + applyAbAttrs("PostMoveUsedAbAttr", { pokemon, move: this.move, source: this.pokemon, targets: this.targets }); }); } } - /** Queues a {@linkcode MoveChargePhase} for this phase's invoked move. */ + /** + * Fail the move currently being used. + * Handles failure messages, pushing to move history, etc. + * @param showText - Whether to show move text when failing the move. + * @param failedDueToWeather - Whether the move failed due to weather (default `false`) + * @param failedDueToTerrain - Whether the move failed due to terrain (default `false`) + */ + protected failMove(showText: boolean, failedDueToWeather = false, failedDueToTerrain = false) { + const move = this.move.getMove(); + const targets = this.getActiveTargetPokemon(); + + // DO NOT CHANGE THE ORDER OF OPERATIONS HERE! + // Protean is supposed to trigger its effects first, _then_ move text is displayed, + // _then_ any blockage messages are shown. + + // Roar, Whirlwind, Trick-or-Treat, and Forest's Curse will trigger Protean/Libero + // even on failure, as will all moves blocked by terrain. + // TODO: Verify if this also applies to primal weather failures + if ( + failedDueToTerrain || + [MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId) + ) { + applyAbAttrs("PokemonTypeChangeAbAttr", { + pokemon: this.pokemon, + move, + opponent: targets[0], + }); + } + + if (showText) { + this.showMoveText(); + } + + this.pokemon.pushMoveHistory({ + move: this.move.moveId, + targets: this.targets, + result: MoveResult.FAIL, + useMode: this.useMode, + }); + + // Use move-specific failure messages if present before checking terrain/weather blockage + // and falling back to the classic "But it failed!". + const failureMessage = + move.getFailedText(this.pokemon, targets[0], move) || + (failedDueToTerrain + ? getTerrainBlockMessage(targets[0], globalScene.arena.getTerrainType()) + : failedDueToWeather + ? getWeatherBlockMessage(globalScene.arena.getWeatherType()) + : i18next.t("battle:attackFailed")); + + this.showFailedText(failureMessage); + + // Remove the user from its semi-invulnerable state (if applicable) + this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + } + + /** + * Queue a {@linkcode MoveChargePhase} for this phase's invoked move. + * Does NOT consume PP (occurs on the 2nd strike of the move) + */ protected chargeMove() { const move = this.move.getMove(); const targets = this.getActiveTargetPokemon(); - this.showMoveText(); - - // Conditions currently assume single target - // TODO: Is this sustainable? if (!move.applyConditions(this.pokemon, targets[0], move)) { - this.pokemon.pushMoveHistory({ - move: this.move.moveId, - targets: this.targets, - result: MoveResult.FAIL, - useMode: this.useMode, - }); - - const failureMessage = move.getFailedText(this.pokemon, targets[0], move); - this.showMoveText(); - this.showFailedText(failureMessage ?? undefined); - - // Remove the user from its semi-invulnerable state (if applicable) - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + this.failMove(true); return; } - // Protean and Libero apply on the charging turn of charge moves - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove()); + // Protean and Libero apply on the charging turn of charge moves, even before showing usage text + applyAbAttrs("PokemonTypeChangeAbAttr", { + pokemon: this.pokemon, + move, + opponent: targets[0], + }); + this.showMoveText(); globalScene.phaseManager.unshiftNew( "MoveChargePhase", this.pokemon.getBattlerIndex(), @@ -482,7 +482,7 @@ export class MovePhase extends BattlePhase { } /** - * Queues a {@linkcode MoveEndPhase} and then ends the phase + * Queue a {@linkcode MoveEndPhase} and then end this phase. */ public end(): void { globalScene.phaseManager.unshiftNew( @@ -496,15 +496,15 @@ export class MovePhase extends BattlePhase { } /** - * Applies PP increasing abilities (currently only {@link AbilityId.PRESSURE Pressure}) if they exist on the target pokemon. + * Applies PP increasing abilities (currently only {@linkcode AbilityId.PRESSURE | Pressure}) if they exist on the target pokemon. * Note that targets must include only active pokemon. * * TODO: This hardcodes the PP increase at 1 per opponent, rather than deferring to the ability. */ public getPpIncreaseFromPressure(targets: Pokemon[]): number { const foesWithPressure = this.pokemon - .getOpponents() - .filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr("IncreasePpAbAttr")); + .getOpponents(true) + .filter(o => targets.includes(o) && o.hasAbilityWithAttr("IncreasePpAbAttr")); return foesWithPressure.length; } @@ -514,100 +514,113 @@ export class MovePhase extends BattlePhase { * - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`). */ protected resolveRedirectTarget(): void { - if (this.targets.length === 1) { - const currentTarget = this.targets[0]; - const redirectTarget = new NumberHolder(currentTarget); + if (this.targets.length !== 1) { + // Spread moves cannot be redirected + return; + } - // check move redirection abilities of every pokemon *except* the user. - globalScene - .getField(true) - .filter(p => p !== this.pokemon) - .forEach(p => - applyAbAttrs("RedirectMoveAbAttr", p, null, false, this.move.moveId, redirectTarget, this.pokemon), - ); + const currentTarget = this.targets[0]; + const redirectTarget = new NumberHolder(currentTarget); - /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ - let redirectedByAbility = currentTarget !== redirectTarget.value; + // check move redirection abilities of every pokemon *except* the user. + globalScene + .getField(true) + .filter(p => p !== this.pokemon) + .forEach(pokemon => { + applyAbAttrs("RedirectMoveAbAttr", { + pokemon, + moveId: this.move.moveId, + targetIndex: redirectTarget, + sourcePokemon: this.pokemon, + }); + }); - // check for center-of-attention tags (note that this will override redirect abilities) - this.pokemon.getOpponents().forEach(p => { - const redirectTag = p.getTag(CenterOfAttentionTag); + /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ + let redirectedByAbility = currentTarget !== redirectTarget.value; - // TODO: don't hardcode this interaction. - // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) - if ( - redirectTag && - (!redirectTag.powder || - (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT))) - ) { - redirectTarget.value = p.getBattlerIndex(); - redirectedByAbility = false; + // check for center-of-attention tags (note that this will override redirect abilities) + this.pokemon.getOpponents(true).forEach(p => { + const redirectTag = p.getTag(CenterOfAttentionTag); + + // TODO: don't hardcode this interaction. + // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) + if ( + redirectTag && + (!redirectTag.powder || + (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT))) + ) { + redirectTarget.value = p.getBattlerIndex(); + redirectedByAbility = false; + } + }); + + // TODO: Don't hardcode these ability interactions + if (currentTarget !== redirectTarget.value) { + const bypassRedirectAttrs = this.move.getMove().getAttrs("BypassRedirectAttr"); + bypassRedirectAttrs.forEach(attr => { + if (!attr.abilitiesOnly || redirectedByAbility) { + redirectTarget.value = currentTarget; } }); - if (currentTarget !== redirectTarget.value) { - const bypassRedirectAttrs = this.move.getMove().getAttrs("BypassRedirectAttr"); - bypassRedirectAttrs.forEach(attr => { - if (!attr.abilitiesOnly || redirectedByAbility) { - redirectTarget.value = currentTarget; - } - }); - - if (this.pokemon.hasAbilityWithAttr("BlockRedirectAbAttr")) { - redirectTarget.value = currentTarget; - // TODO: Ability displays should be handled by the ability - globalScene.phaseManager.queueAbilityDisplay( - this.pokemon, - this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), - true, - ); - globalScene.phaseManager.queueAbilityDisplay( - this.pokemon, - this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), - false, - ); - } - - this.targets[0] = redirectTarget.value; + if (this.pokemon.hasAbilityWithAttr("BlockRedirectAbAttr")) { + redirectTarget.value = currentTarget; + // TODO: Ability displays should be handled by the ability + globalScene.phaseManager.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), + true, + ); + globalScene.phaseManager.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), + false, + ); } + + this.targets[0] = redirectTarget.value; } } /** - * Counter-attacking moves pass in `[`{@linkcode BattlerIndex.ATTACKER}`]` into the constructor's `targets` param. - * This function modifies `this.targets` to reflect the actual battler index of the user's last - * attacker. + * Update the targets of any counter-attacking moves with `[`{@linkcode BattlerIndex.ATTACKER}`]` set + * to reflect the actual battler index of the user's last attacker. * - * If there is no last attacker, or they are no longer on the field, a message is displayed and the + * If there is no last attacker or they are no longer on the field, a message is displayed and the * move is marked for failure. + * @todo Make this a feature of the move rather than basing logic on {@linkcode BattlerIndex.ATTACKER} */ protected resolveCounterAttackTarget(): void { - if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { - if (this.pokemon.turnData.attacksReceived.length) { - this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; + if (this.targets.length !== 1 || this.targets[0] !== BattlerIndex.ATTACKER) { + return; + } - // account for metal burst and comeuppance hitting remaining targets in double battles - // counterattack will redirect to remaining ally if original attacker faints - if (globalScene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { - if (globalScene.getField()[this.targets[0]].hp === 0) { - const opposingField = this.pokemon.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField(); - this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER; - } - } - } + // TODO: This should be covered in move conditions + if (this.pokemon.turnData.attacksReceived.length === 0) { + this.fail(); + this.showMoveText(); + this.showFailedText(); + return; + } - if (this.targets[0] === BattlerIndex.ATTACKER) { - this.fail(); - this.showMoveText(); - this.showFailedText(); - } + this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; + + // account for metal burst and comeuppance hitting remaining targets in double battles + // counterattack will redirect to remaining ally if original attacker faints + if ( + globalScene.currentBattle.double && + this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER) && + globalScene.getField()[this.targets[0]].hp === 0 + ) { + const opposingField = this.pokemon.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField(); + this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER; } } /** * Handles the case where the move was cancelled or failed: - * - Uses PP if the move failed (not cancelled) and should use PP (failed moves are not affected by {@link AbilityId.PRESSURE Pressure}) - * - Records a cancelled OR failed move in move history, so abilities like {@link AbilityId.TRUANT Truant} don't trigger on the + * - Uses PP if the move failed (not cancelled) and should use PP (failed moves are not affected by {@linkcode AbilityId.PRESSURE | Pressure}) + * - Records a cancelled OR failed move in move history, so abilities like {@linkcode AbilityId.TRUANT | Truant} don't trigger on the * next turn and soft-lock. * - Lapses `MOVE_EFFECT` tags: * - Semi-invulnerable battler tags (Fly/Dive/etc.) are intended to lapse on move effects, but also need @@ -615,52 +628,55 @@ export class MovePhase extends BattlePhase { * * TODO: ...this seems weird. * - Lapses `AFTER_MOVE` tags: - * - This handles the effects of {@link MoveId.SUBSTITUTE Substitute} + * - This handles the effects of {@linkcode MoveId.SUBSTITUTE | Substitute} * - Removes the second turn of charge moves */ protected handlePreMoveFailures(): void { - if (this.cancelled || this.failed) { - if (this.failed) { - const ppUsed = isIgnorePP(this.useMode) ? 0 : 1; - - if (ppUsed) { - this.move.usePp(); - } - - globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); - } - - if (this.cancelled && this.pokemon.summonData.tags?.find(t => t.tagType === BattlerTagType.FRENZY)) { - frenzyMissFunc(this.pokemon, this.move.getMove()); - } - - this.pokemon.pushMoveHistory({ - move: MoveId.NONE, - result: MoveResult.FAIL, - targets: this.targets, - useMode: this.useMode, - }); - - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); - this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); - - this.pokemon.getMoveQueue().shift(); + if (!this.cancelled && !this.failed) { + return; } + + if (this.failed) { + // TODO: should this consider struggle? + const ppUsed = isIgnorePP(this.useMode) ? 0 : 1; + this.move.usePp(ppUsed); + globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + } + + if (this.cancelled && this.pokemon.summonData.tags.some(t => t.tagType === BattlerTagType.FRENZY)) { + frenzyMissFunc(this.pokemon, this.move.getMove()); + } + + this.pokemon.pushMoveHistory({ + move: MoveId.NONE, + result: MoveResult.FAIL, + targets: this.targets, + useMode: this.useMode, + }); + + this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); + + // This clears out 2 turn moves after they've been used + // TODO: Remove post move queue refactor + this.pokemon.getMoveQueue().shift(); } /** - * Displays the move's usage text to the player, unless it's a charge turn (ie: {@link MoveId.SOLAR_BEAM Solar Beam}), - * the pokemon is on a recharge turn (ie: {@link MoveId.HYPER_BEAM Hyper Beam}), or a 2-turn move was interrupted (ie: {@link MoveId.FLY Fly}). + * Displays the move's usage text to the player as applicable for the move being used. */ public showMoveText(): void { - if (this.move.moveId === MoveId.NONE) { - return; - } - - if (this.pokemon.getTag(BattlerTagType.RECHARGING) || this.pokemon.getTag(BattlerTagType.INTERRUPTED)) { + // No text for Moves.NONE, recharging/2-turn moves or interrupted moves + if ( + this.move.moveId === MoveId.NONE || + this.pokemon.getTag(BattlerTagType.RECHARGING) || + this.pokemon.getTag(BattlerTagType.INTERRUPTED) + ) { return; } + // Play message for magic coat reflection + // TODO: This should be done by the move... globalScene.phaseManager.queueMessage( i18next.t(isReflected(this.useMode) ? "battle:magicCoatActivated" : "battle:useMove", { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), @@ -674,7 +690,12 @@ export class MovePhase extends BattlePhase { applyMoveAttrs("PreMoveMessageAttr", this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove()); } - public showFailedText(failedText: string = i18next.t("battle:attackFailed")): void { + /** + * Display the text for a move failing to execute. + * @param failedText - The failure text to display; defaults to `"battle:attackFailed"` locale key + * ("But it failed!" in english) + */ + public showFailedText(failedText = i18next.t("battle:attackFailed")): void { globalScene.phaseManager.queueMessage(failedText); } } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 9aae796211f..9363efcb460 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -1,22 +1,21 @@ -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import type { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option"; -import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; -import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { globalScene } from "#app/global-scene"; +import { Phase } from "#app/phase"; +import { getCharVariantFromDialogue } from "#data/dialogue"; import { BattleSpec } from "#enums/battle-spec"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { SwitchType } from "#enums/switch-type"; -import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; -import { getCharVariantFromDialogue } from "../data/dialogue"; -import type { OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; -import { transitionMysteryEncounterIntroVisuals } from "../data/mystery-encounters/utils/encounter-phase-utils"; import { TrainerSlot } from "#enums/trainer-slot"; -import { IvScannerModifier } from "../modifier/modifier"; -import { Phase } from "../phase"; import { UiMode } from "#enums/ui-mode"; -import { isNullOrUndefined, randSeedItem } from "#app/utils/common"; +import { IvScannerModifier } from "#modifiers/modifier"; +import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { OptionSelectSettings } from "#mystery-encounters/encounter-phase-utils"; +import { transitionMysteryEncounterIntroVisuals } from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounterOption, OptionPhaseCallback } from "#mystery-encounters/mystery-encounter-option"; +import { SeenEncounterData } from "#mystery-encounters/mystery-encounter-save-data"; +import { isNullOrUndefined, randSeedItem } from "#utils/common"; +import i18next from "i18next"; /** * Will handle (in order): diff --git a/src/phases/new-battle-phase.ts b/src/phases/new-battle-phase.ts index 65ecc81df2d..b9a57161bd0 100644 --- a/src/phases/new-battle-phase.ts +++ b/src/phases/new-battle-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { BattlePhase } from "./battle-phase"; +import { BattlePhase } from "#phases/battle-phase"; export class NewBattlePhase extends BattlePhase { public readonly phaseName = "NewBattlePhase"; diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index 5aad607764f..db57671c4e3 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -1,7 +1,7 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { getRandomWeatherType } from "#app/data/weather"; -import { NextEncounterPhase } from "./next-encounter-phase"; +import { getRandomWeatherType } from "#data/weather"; +import { NextEncounterPhase } from "#phases/next-encounter-phase"; export class NewBiomeEncounterPhase extends NextEncounterPhase { public readonly phaseName = "NewBiomeEncounterPhase"; @@ -14,7 +14,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { if (pokemon) { pokemon.resetBattleAndWaveData(); if (pokemon.isOnField()) { - applyAbAttrs("PostBiomeChangeAbAttr", pokemon, null); + applyAbAttrs("PostBiomeChangeAbAttr", { pokemon }); } } } diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts index c31b4b5bbc3..02b9dd7ed69 100644 --- a/src/phases/next-encounter-phase.ts +++ b/src/phases/next-encounter-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { EncounterPhase } from "./encounter-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; /** * The phase between defeating an encounter and starting another wild wave. diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index dc26d070029..fbadbed205b 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -1,15 +1,15 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { CommonBattleAnim } from "#app/data/battle-anims"; -import { CommonAnim } from "#enums/move-anims-common"; -import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect"; -import { StatusEffect } from "#app/enums/status-effect"; -import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { PokemonPhase } from "./pokemon-phase"; -import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { applyPostSetStatusAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { isNullOrUndefined } from "#app/utils/common"; +import { CommonBattleAnim } from "#data/battle-anims"; +import { SpeciesFormChangeStatusEffectTrigger } from "#data/form-change-triggers"; +import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#data/status-effect"; +import type { BattlerIndex } from "#enums/battler-index"; +import { CommonAnim } from "#enums/move-anims-common"; +import { StatusEffect } from "#enums/status-effect"; +import type { Pokemon } from "#field/pokemon"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { isNullOrUndefined } from "#utils/common"; export class ObtainStatusEffectPhase extends PokemonPhase { public readonly phaseName = "ObtainStatusEffectPhase"; @@ -53,7 +53,11 @@ export class ObtainStatusEffectPhase extends PokemonPhase { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true); // If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards globalScene.arena.setIgnoreAbilities(false); - applyPostSetStatusAbAttrs("PostSetStatusAbAttr", pokemon, this.statusEffect, this.sourcePokemon); + applyAbAttrs("PostSetStatusAbAttr", { + pokemon, + effect: this.statusEffect, + sourcePokemon: this.sourcePokemon ?? undefined, + }); } this.end(); }); diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index 765c7dbad8e..80d8b315102 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils/common"; -import { BattlePhase } from "./battle-phase"; +import { BattlePhase } from "#phases/battle-phase"; +import { fixedInt } from "#utils/common"; export class PartyHealPhase extends BattlePhase { public readonly phaseName = "PartyHealPhase"; diff --git a/src/phases/party-member-pokemon-phase.ts b/src/phases/party-member-pokemon-phase.ts index a782eabda38..9536dafda60 100644 --- a/src/phases/party-member-pokemon-phase.ts +++ b/src/phases/party-member-pokemon-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import type Pokemon from "#app/field/pokemon"; -import { FieldPhase } from "./field-phase"; +import type { Pokemon } from "#field/pokemon"; +import { FieldPhase } from "#phases/field-phase"; export abstract class PartyMemberPokemonPhase extends FieldPhase { protected partyMemberIndex: number; diff --git a/src/phases/player-party-member-pokemon-phase.ts b/src/phases/player-party-member-pokemon-phase.ts index d97376a8614..4538bb42325 100644 --- a/src/phases/player-party-member-pokemon-phase.ts +++ b/src/phases/player-party-member-pokemon-phase.ts @@ -1,5 +1,5 @@ -import type { PlayerPokemon } from "#app/field/pokemon"; -import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; +import type { PlayerPokemon } from "#field/pokemon"; +import { PartyMemberPokemonPhase } from "#phases/party-member-pokemon-phase"; export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase { constructor(partyMemberIndex: number) { diff --git a/src/phases/pokemon-anim-phase.ts b/src/phases/pokemon-anim-phase.ts index 314bddb981d..39e9c609aec 100644 --- a/src/phases/pokemon-anim-phase.ts +++ b/src/phases/pokemon-anim-phase.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; -import { SubstituteTag } from "#app/data/battler-tags"; -import type Pokemon from "#app/field/pokemon"; -import { BattlePhase } from "#app/phases/battle-phase"; -import { isNullOrUndefined } from "#app/utils/common"; +import { SubstituteTag } from "#data/battler-tags"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { SpeciesId } from "#enums/species-id"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; +import { isNullOrUndefined } from "#utils/common"; export class PokemonAnimPhase extends BattlePhase { public readonly phaseName = "PokemonAnimPhase"; diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index cf6cf40a923..fa6a3222466 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -1,17 +1,17 @@ import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { CommonAnim } from "#enums/move-anims-common"; -import { getStatusEffectHealText } from "#app/data/status-effect"; -import { StatusEffect } from "#app/enums/status-effect"; -import { HitResult } from "#enums/hit-result"; import { getPokemonNameWithAffix } from "#app/messages"; -import { HealingBoosterModifier } from "#app/modifier/modifier"; -import { HealAchv } from "#app/system/achv"; +import type { HealBlockTag } from "#data/battler-tags"; +import { getStatusEffectHealText } from "#data/status-effect"; +import type { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { HitResult } from "#enums/hit-result"; +import { CommonAnim } from "#enums/move-anims-common"; +import { StatusEffect } from "#enums/status-effect"; +import { HealingBoosterModifier } from "#modifiers/modifier"; +import { CommonAnimPhase } from "#phases/common-anim-phase"; +import { HealAchv } from "#system/achv"; +import { NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils/common"; -import { CommonAnimPhase } from "./common-anim-phase"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { HealBlockTag } from "#app/data/battler-tags"; export class PokemonHealPhase extends CommonAnimPhase { public readonly phaseName = "PokemonHealPhase"; diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts index cfa30f0d06e..9739c58d667 100644 --- a/src/phases/pokemon-phase.ts +++ b/src/phases/pokemon-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { BattlerIndex } from "#enums/battler-index"; -import type Pokemon from "#app/field/pokemon"; -import { FieldPhase } from "./field-phase"; +import type { Pokemon } from "#field/pokemon"; +import { FieldPhase } from "#phases/field-phase"; export abstract class PokemonPhase extends FieldPhase { /** diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index 938915309d9..143c47886b6 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -1,11 +1,11 @@ +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; import type { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; -import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { globalScene } from "#app/global-scene"; -import { PokemonPhase } from "./pokemon-phase"; -import { getPokemonNameWithAffix } from "#app/messages"; +import { BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; +import { PokemonMove } from "#moves/pokemon-move"; +import { PokemonPhase } from "#phases/pokemon-phase"; import i18next from "i18next"; /** @@ -26,7 +26,7 @@ export class PokemonTransformPhase extends PokemonPhase { public override start(): void { const user = this.getPokemon(); - const target = globalScene.getField(true).find(p => p.getBattlerIndex() === this.targetIndex); + const target = globalScene.getField()[this.targetIndex]; if (!target) { this.end(); @@ -58,6 +58,8 @@ export class PokemonTransformPhase extends PokemonPhase { console.warn(`Transform: somehow iterating over a ${m} value when copying moveset!`); return new PokemonMove(MoveId.NONE); }); + + // TODO: This should fallback to the target's original typing if none are left (from Burn Up, etc.) user.summonData.types = target.getTypes(); const promises = [user.updateInfo()]; diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index 8e19dcd5498..3ac112a8a8b 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import type { EndCardPhase } from "./end-card-phase"; +import type { EndCardPhase } from "#phases/end-card-phase"; export class PostGameOverPhase extends Phase { public readonly phaseName = "PostGameOverPhase"; diff --git a/src/phases/post-summon-activate-ability-phase.ts b/src/phases/post-summon-activate-ability-phase.ts index ba6c80d4ee0..5f790c01ad1 100644 --- a/src/phases/post-summon-activate-ability-phase.ts +++ b/src/phases/post-summon-activate-ability-phase.ts @@ -1,6 +1,6 @@ -import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import type { BattlerIndex } from "#enums/battler-index"; +import { PostSummonPhase } from "#phases/post-summon-phase"; /** * Helper to {@linkcode PostSummonPhase} which applies abilities @@ -16,7 +16,8 @@ export class PostSummonActivateAbilityPhase extends PostSummonPhase { } start() { - applyPostSummonAbAttrs("PostSummonAbAttr", this.getPokemon(), this.passive, false); + // TODO: Check with Dean on whether or not passive must be provided to `this.passive` + applyAbAttrs("PostSummonAbAttr", { pokemon: this.getPokemon(), passive: this.passive }); this.end(); } diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 26fffd1b024..e0811d0ab93 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -1,10 +1,10 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { ArenaTrapTag } from "#app/data/arena-tag"; -import { StatusEffect } from "#app/enums/status-effect"; -import { PokemonPhase } from "./pokemon-phase"; -import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; +import { ArenaTrapTag } from "#data/arena-tag"; +import { MysteryEncounterPostSummonTag } from "#data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { StatusEffect } from "#enums/status-effect"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class PostSummonPhase extends PokemonPhase { public readonly phaseName = "PostSummonPhase"; @@ -28,7 +28,7 @@ export class PostSummonPhase extends PokemonPhase { const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { - applyAbAttrs("CommanderAbAttr", p, null, false); + applyAbAttrs("CommanderAbAttr", { pokemon: p }); } this.end(); diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index e0a3bb5c00b..9d5172180b8 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -1,14 +1,14 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { applyAbAttrs, applyPostDamageAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { CommonBattleAnim } from "#app/data/battle-anims"; -import { CommonAnim } from "#enums/move-anims-common"; -import { getStatusEffectActivationText } from "#app/data/status-effect"; -import { BattleSpec } from "#app/enums/battle-spec"; -import { StatusEffect } from "#app/enums/status-effect"; import { getPokemonNameWithAffix } from "#app/messages"; -import { BooleanHolder, NumberHolder } from "#app/utils/common"; -import { PokemonPhase } from "./pokemon-phase"; +import { CommonBattleAnim } from "#data/battle-anims"; +import { getStatusEffectActivationText } from "#data/status-effect"; +import { BattleSpec } from "#enums/battle-spec"; +import type { BattlerIndex } from "#enums/battler-index"; +import { CommonAnim } from "#enums/move-anims-common"; +import { StatusEffect } from "#enums/status-effect"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { BooleanHolder, NumberHolder } from "#utils/common"; export class PostTurnStatusEffectPhase extends PokemonPhase { public readonly phaseName = "PostTurnStatusEffectPhase"; @@ -22,8 +22,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) { pokemon.status.incrementTurn(); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); - applyAbAttrs("BlockStatusDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); + applyAbAttrs("BlockStatusDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { globalScene.phaseManager.queueMessage( @@ -39,14 +39,14 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { break; case StatusEffect.BURN: damage.value = Math.max(pokemon.getMaxHp() >> 4, 1); - applyAbAttrs("ReduceBurnDamageAbAttr", pokemon, null, false, damage); + applyAbAttrs("ReduceBurnDamageAbAttr", { pokemon, burnDamage: damage }); break; } if (damage.value) { // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ... globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true)); pokemon.updateInfo(); - applyPostDamageAbAttrs("PostDamageAbAttr", pokemon, damage.value, pokemon.hasPassive(), false, []); + applyAbAttrs("PostDamageAbAttr", { pokemon, damage: damage.value }); } new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end()); } else { diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 41b691844bf..ef53b16cc56 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -1,18 +1,15 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { SemiInvulnerableTag } from "#app/data/battler-tags"; -import type { SpeciesFormChange } from "#app/data/pokemon-forms"; -import { - getSpeciesFormChangeMessage, - SpeciesFormChangeTeraTrigger, -} from "#app/data/pokemon-forms/form-change-triggers"; -import { getTypeRgb } from "#app/data/type"; -import { BattleSpec } from "#app/enums/battle-spec"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { BattlePhase } from "./battle-phase"; -import type { MovePhase } from "./move-phase"; -import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { SemiInvulnerableTag } from "#data/battler-tags"; +import { getSpeciesFormChangeMessage, SpeciesFormChangeTeraTrigger } from "#data/form-change-triggers"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; +import { getTypeRgb } from "#data/type"; +import { BattleSpec } from "#enums/battle-spec"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; +import type { MovePhase } from "#phases/move-phase"; export class QuietFormChangePhase extends BattlePhase { public readonly phaseName = "QuietFormChangePhase"; @@ -181,9 +178,10 @@ export class QuietFormChangePhase extends BattlePhase { } } if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) { - applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", this.pokemon, null); - applyAbAttrs("ClearWeatherAbAttr", this.pokemon, null); - applyAbAttrs("ClearTerrainAbAttr", this.pokemon, null); + const params = { pokemon: this.pokemon }; + applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", params); + applyAbAttrs("ClearWeatherAbAttr", params); + applyAbAttrs("ClearTerrainAbAttr", params); } super.end(); diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts index ac9337753c4..ac8a6bc2799 100644 --- a/src/phases/reload-session-phase.ts +++ b/src/phases/reload-session-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import { UiMode } from "#enums/ui-mode"; -import { fixedInt } from "#app/utils/common"; +import { fixedInt } from "#utils/common"; export class ReloadSessionPhase extends Phase { public readonly phaseName = "ReloadSessionPhase"; diff --git a/src/phases/reset-status-phase.ts b/src/phases/reset-status-phase.ts index 779f375d7e2..e15fb7fee85 100644 --- a/src/phases/reset-status-phase.ts +++ b/src/phases/reset-status-phase.ts @@ -1,5 +1,5 @@ -import type Pokemon from "#app/field/pokemon"; -import { BattlePhase } from "#app/phases/battle-phase"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; /** * Phase which handles resetting a Pokemon's status to none diff --git a/src/phases/return-phase.ts b/src/phases/return-phase.ts index a8233f98bd1..a8cce72810e 100644 --- a/src/phases/return-phase.ts +++ b/src/phases/return-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; +import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; import { SwitchType } from "#enums/switch-type"; -import { SwitchSummonPhase } from "./switch-summon-phase"; +import { SwitchSummonPhase } from "#phases/switch-summon-phase"; export class ReturnPhase extends SwitchSummonPhase { public readonly phaseName = "ReturnPhase"; diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index e3e69f7ef25..0235fb51da3 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -1,12 +1,12 @@ -import { SwitchType } from "#enums/switch-type"; import { globalScene } from "#app/global-scene"; -import type { PartyOption } from "#app/ui/party-ui-handler"; -import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; +import { SwitchType } from "#enums/switch-type"; import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; +import type { PartyOption } from "#ui/party-ui-handler"; +import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; +import { isNullOrUndefined, toDmgValue } from "#utils/common"; import i18next from "i18next"; -import { toDmgValue, isNullOrUndefined } from "#app/utils/common"; -import { BattlePhase } from "#app/phases/battle-phase"; -import type { PlayerPokemon } from "#app/field/pokemon"; /** * Sets the Party UI and handles the effect of Revival Blessing diff --git a/src/phases/ribbon-modifier-reward-phase.ts b/src/phases/ribbon-modifier-reward-phase.ts index 10d63ba707f..7cf3f7a8071 100644 --- a/src/phases/ribbon-modifier-reward-phase.ts +++ b/src/phases/ribbon-modifier-reward-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import type { ModifierTypeFunc } from "#app/@types/modifier-types"; +import type { PokemonSpecies } from "#data/pokemon-species"; import { UiMode } from "#enums/ui-mode"; +import { ModifierRewardPhase } from "#phases/modifier-reward-phase"; +import type { ModifierTypeFunc } from "#types/modifier-types"; import i18next from "i18next"; -import { ModifierRewardPhase } from "./modifier-reward-phase"; export class RibbonModifierRewardPhase extends ModifierRewardPhase { public readonly phaseName = "RibbonModifierRewardPhase"; diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index d296d87ca88..e0865feb7ca 100644 --- a/src/phases/scan-ivs-phase.ts +++ b/src/phases/scan-ivs-phase.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { PERMANENT_STATS, Stat } from "#app/enums/stat"; import { getPokemonNameWithAffix } from "#app/messages"; -import { getTextColor, TextStyle } from "#app/ui/text"; +import type { BattlerIndex } from "#enums/battler-index"; +import { PERMANENT_STATS, Stat } from "#enums/stat"; import { UiMode } from "#enums/ui-mode"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import { getTextColor, TextStyle } from "#ui/text"; import i18next from "i18next"; -import { PokemonPhase } from "./pokemon-phase"; export class ScanIvsPhase extends PokemonPhase { public readonly phaseName = "ScanIvsPhase"; diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index e8b4946b6d1..ab96bf5c45e 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; -import { biomeLinks, getBiomeName } from "#app/data/balance/biomes"; +import { biomeLinks, getBiomeName } from "#balance/biomes"; import { BiomeId } from "#enums/biome-id"; -import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { UiMode } from "#enums/ui-mode"; -import { BattlePhase } from "./battle-phase"; -import { randSeedInt } from "#app/utils/common"; +import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier"; +import { BattlePhase } from "#phases/battle-phase"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { randSeedInt } from "#utils/common"; export class SelectBiomePhase extends BattlePhase { public readonly phaseName = "SelectBiomePhase"; @@ -56,6 +56,7 @@ export class SelectBiomePhase extends BattlePhase { delay: 1000, }); } else { + // TODO: should this use `randSeedItem`? setNextBiome(biomes[randSeedInt(biomes.length)]); } } else if (biomeLinks.hasOwnProperty(currentBiome)) { diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts index ad8515e312e..d44c308d08c 100644 --- a/src/phases/select-gender-phase.ts +++ b/src/phases/select-gender-phase.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; -import { PlayerGender } from "#app/enums/player-gender"; import { Phase } from "#app/phase"; -import { SettingKeys } from "#app/system/settings/settings"; +import { PlayerGender } from "#enums/player-gender"; import { UiMode } from "#enums/ui-mode"; +import { SettingKeys } from "#system/settings"; import i18next from "i18next"; export class SelectGenderPhase extends Phase { diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 53e1f5bc282..05c890136ee 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -1,35 +1,34 @@ import { globalScene } from "#app/global-scene"; -import type { ModifierTier } from "#enums/modifier-tier"; -import type { ModifierTypeOption, ModifierType } from "#app/modifier/modifier-type"; -import { - regenerateModifierPoolThresholds, - getPlayerShopModifierTypeOptionsForWave, - PokemonModifierType, - FusePokemonModifierType, - PokemonMoveModifierType, - TmModifierType, - RememberMoveModifierType, - PokemonPpRestoreModifierType, - PokemonPpUpModifierType, - getPlayerModifierTypeOptions, -} from "#app/modifier/modifier-type"; +import Overrides from "#app/overrides"; import { ModifierPoolType } from "#enums/modifier-pool-type"; -import type { Modifier } from "#app/modifier/modifier"; +import type { ModifierTier } from "#enums/modifier-tier"; +import { UiMode } from "#enums/ui-mode"; +import type { Modifier } from "#modifiers/modifier"; import { ExtraModifierModifier, HealShopCostModifier, PokemonHeldItemModifier, TempExtraModifierModifier, -} from "#app/modifier/modifier"; -import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; -import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; -import { UiMode } from "#enums/ui-mode"; +} from "#modifiers/modifier"; +import type { CustomModifierSettings, ModifierType, ModifierTypeOption } from "#modifiers/modifier-type"; +import { + FusePokemonModifierType, + getPlayerModifierTypeOptions, + getPlayerShopModifierTypeOptionsForWave, + PokemonModifierType, + PokemonMoveModifierType, + PokemonPpRestoreModifierType, + PokemonPpUpModifierType, + RememberMoveModifierType, + regenerateModifierPoolThresholds, + TmModifierType, +} from "#modifiers/modifier-type"; +import { BattlePhase } from "#phases/battle-phase"; +import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { SHOP_OPTIONS_ROW_LIMIT } from "#ui/modifier-select-ui-handler"; +import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; +import { isNullOrUndefined, NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { BattlePhase } from "./battle-phase"; -import Overrides from "#app/overrides"; -import type { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { isNullOrUndefined, NumberHolder } from "#app/utils/common"; export type ModifierSelectCallback = (rowCursor: number, cursor: number) => boolean; @@ -180,7 +179,7 @@ export class SelectModifierPhase extends BattlePhase { } else { this.applyModifier(modifierType.newModifier()!); } - return !cost; + return cost === -1; } // Reroll rewards diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 76247c14ce0..af056ebb4ee 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -1,18 +1,18 @@ import { globalScene } from "#app/global-scene"; -import { applyChallenges } from "#app/data/challenge"; -import { ChallengeType } from "#enums/challenge-type"; -import { Gender } from "#app/data/gender"; -import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier"; import Overrides from "#app/overrides"; import { Phase } from "#app/phase"; -import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; -import type { Starter } from "#app/ui/starter-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; +import { applyChallenges } from "#data/challenge"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#data/form-change-triggers"; +import { Gender } from "#data/gender"; +import { ChallengeType } from "#enums/challenge-type"; import type { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { overrideHeldItems, overrideModifiers } from "#modifiers/modifier"; +import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; +import type { Starter } from "#ui/starter-select-ui-handler"; +import { isNullOrUndefined } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { isNullOrUndefined } from "#app/utils/common"; export class SelectStarterPhase extends Phase { public readonly phaseName = "SelectStarterPhase"; @@ -37,7 +37,7 @@ export class SelectStarterPhase extends Phase { /** * Initialize starters before starting the first battle - * @param starters {@linkcode Pokemon} with which to start the first battle + * @param starters - Array of {@linkcode Starter}s with which to start the battle */ initBattle(starters: Starter[]) { const party = globalScene.getPlayerParty(); diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 6d47ac18021..3805cb919b2 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; +import { allMoves } from "#data/data-lists"; import type { BattlerIndex } from "#enums/battler-index"; import { Command } from "#enums/command"; import { UiMode } from "#enums/ui-mode"; -import { PokemonPhase } from "./pokemon-phase"; -import i18next from "#app/plugins/i18n"; -import { allMoves } from "#app/data/data-lists"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import i18next from "#plugins/i18n"; export class SelectTargetPhase extends PokemonPhase { public readonly phaseName = "SelectTargetPhase"; diff --git a/src/phases/shiny-sparkle-phase.ts b/src/phases/shiny-sparkle-phase.ts index 53866af89e6..0930d536267 100644 --- a/src/phases/shiny-sparkle-phase.ts +++ b/src/phases/shiny-sparkle-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#enums/battler-index"; -import { PokemonPhase } from "./pokemon-phase"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class ShinySparklePhase extends PokemonPhase { public readonly phaseName = "ShinySparklePhase"; diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index 0f568819cde..21c3e7341a7 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { PokemonPhase } from "./pokemon-phase"; import { getPokemonNameWithAffix } from "#app/messages"; +import type { BattlerIndex } from "#enums/battler-index"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class ShowAbilityPhase extends PokemonPhase { public readonly phaseName = "ShowAbilityPhase"; diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 4849526b639..f22be5f63e3 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; -import { ExpNotification } from "#app/enums/exp-notification"; -import { ExpBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder } from "#app/utils/common"; -import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; +import { ExpGainsSpeed } from "#enums/exp-gains-speed"; +import { ExpNotification } from "#enums/exp-notification"; +import { ExpBoosterModifier } from "#modifiers/modifier"; +import { PlayerPartyMemberPokemonPhase } from "#phases/player-party-member-pokemon-phase"; +import { NumberHolder } from "#utils/common"; export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { public readonly phaseName = "ShowPartyExpBarPhase"; diff --git a/src/phases/show-trainer-phase.ts b/src/phases/show-trainer-phase.ts index bae6ecd839c..e012c22116a 100644 --- a/src/phases/show-trainer-phase.ts +++ b/src/phases/show-trainer-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import { PlayerGender } from "#app/enums/player-gender"; -import { BattlePhase } from "./battle-phase"; +import { PlayerGender } from "#enums/player-gender"; +import { BattlePhase } from "#phases/battle-phase"; export class ShowTrainerPhase extends BattlePhase { public readonly phaseName = "ShowTrainerPhase"; diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index e73f72f7a63..140ef841929 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -1,23 +1,20 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import type { BattlerIndex } from "#enums/battler-index"; -import { - applyAbAttrs, - applyPostStatStageChangeAbAttrs, - applyPreStatStageChangeAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import { MistTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type { ArenaTag } from "#app/data/arena-tag"; -import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { ResetNegativeStatStageModifier } from "#app/modifier/modifier"; import { handleTutorial, Tutorial } from "#app/tutorial"; -import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils/common"; +import type { ArenaTag } from "#data/arena-tag"; +import { MistTag } from "#data/arena-tag"; +import { OctolockTag } from "#data/battler-tags"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import type { BattlerIndex } from "#enums/battler-index"; +import { type BattleStat, getStatKey, getStatStageChangeDescriptionKey, Stat } from "#enums/stat"; +import type { Pokemon } from "#field/pokemon"; +import { ResetNegativeStatStageModifier } from "#modifiers/modifier"; +import { PokemonPhase } from "#phases/pokemon-phase"; +import type { ConditionalUserFieldProtectStatAbAttrParams, PreStatStageChangeAbAttrParams } from "#types/ability-types"; +import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#utils/common"; import i18next from "i18next"; -import { PokemonPhase } from "./pokemon-phase"; -import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; -import { OctolockTag } from "#app/data/battler-tags"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; export type StatStageChangeCallback = ( target: Pokemon | null, @@ -126,7 +123,7 @@ export class StatStageChangePhase extends PokemonPhase { const stages = new NumberHolder(this.stages); if (!this.ignoreAbilities) { - applyAbAttrs("StatStageChangeMultiplierAbAttr", pokemon, null, false, stages); + applyAbAttrs("StatStageChangeMultiplierAbAttr", { pokemon, numStages: stages }); } let simulate = false; @@ -146,42 +143,38 @@ export class StatStageChangePhase extends PokemonPhase { } if (!cancelled.value && !this.selfTarget && stages.value < 0) { - applyPreStatStageChangeAbAttrs("ProtectStatAbAttr", pokemon, stat, cancelled, simulate); - applyPreStatStageChangeAbAttrs( - "ConditionalUserFieldProtectStatAbAttr", + const abAttrParams: PreStatStageChangeAbAttrParams & ConditionalUserFieldProtectStatAbAttrParams = { pokemon, stat, cancelled, - simulate, - pokemon, - ); + simulated: simulate, + target: pokemon, + stages: this.stages, + }; + applyAbAttrs("ProtectStatAbAttr", abAttrParams); + applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", abAttrParams); + // TODO: Consider skipping this call if `cancelled` is false. const ally = pokemon.getAlly(); if (!isNullOrUndefined(ally)) { - applyPreStatStageChangeAbAttrs( - "ConditionalUserFieldProtectStatAbAttr", - ally, - stat, - cancelled, - simulate, - pokemon, - ); + applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", { ...abAttrParams, pokemon: ally }); } /** Potential stat reflection due to Mirror Armor, does not apply to Octolock end of turn effect */ if ( opponentPokemon !== undefined && + // TODO: investigate whether this is stoping mirror armor from applying to non-octolock + // reasons for stat drops if the user has the Octolock tag !pokemon.findTag(t => t instanceof OctolockTag) && !this.comingFromMirrorArmorUser ) { - applyPreStatStageChangeAbAttrs( - "ReflectStatStageChangeAbAttr", + applyAbAttrs("ReflectStatStageChangeAbAttr", { pokemon, stat, cancelled, - simulate, - opponentPokemon, - this.stages, - ); + simulated: simulate, + source: opponentPokemon, + stages: this.stages, + }); } } @@ -222,17 +215,16 @@ export class StatStageChangePhase extends PokemonPhase { if (stages.value > 0 && this.canBeCopied) { for (const opponent of pokemon.getOpponents()) { - applyAbAttrs("StatStageChangeCopyAbAttr", opponent, null, false, this.stats, stages.value); + applyAbAttrs("StatStageChangeCopyAbAttr", { pokemon: opponent, stats: this.stats, numStages: stages.value }); } } - applyPostStatStageChangeAbAttrs( - "PostStatStageChangeAbAttr", + applyAbAttrs("PostStatStageChangeAbAttr", { pokemon, - filteredStats, - this.stages, - this.selfTarget, - ); + stats: filteredStats, + stages: this.stages, + selfTarget: this.selfTarget, + }); // Look for any other stat change phases; if this is the last one, do White Herb check const existingPhase = globalScene.phaseManager.findPhase( diff --git a/src/phases/summon-missing-phase.ts b/src/phases/summon-missing-phase.ts index ce3e982055e..89d36b13597 100644 --- a/src/phases/summon-missing-phase.ts +++ b/src/phases/summon-missing-phase.ts @@ -1,7 +1,7 @@ -import { getPokemonNameWithAffix } from "#app/messages"; -import i18next from "i18next"; -import { SummonPhase } from "./summon-phase"; import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { SummonPhase } from "#phases/summon-phase"; +import i18next from "i18next"; export class SummonMissingPhase extends SummonPhase { public readonly phaseName = "SummonMissingPhase"; diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index ad93452331f..e4c8aa9af7a 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -1,17 +1,17 @@ -import { BattleType } from "#enums/battle-type"; -import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { PlayerGender } from "#app/enums/player-gender"; -import { addPokeballOpenParticles } from "#app/field/anims"; -import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; -import { getPokemonNameWithAffix } from "#app/messages"; -import i18next from "i18next"; -import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; +import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; +import { BattleType } from "#enums/battle-type"; +import { FieldPosition } from "#enums/field-position"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { PlayerGender } from "#enums/player-gender"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { addPokeballOpenParticles } from "#field/anims"; +import type { Pokemon } from "#field/pokemon"; +import { PartyMemberPokemonPhase } from "#phases/party-member-pokemon-phase"; +import i18next from "i18next"; export class SummonPhase extends PartyMemberPokemonPhase { // The union type is needed to keep typescript happy as these phases extend from SummonPhase @@ -27,7 +27,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { start() { super.start(); - applyPreSummonAbAttrs("PreSummonAbAttr", this.getPokemon()); + applyAbAttrs("PreSummonAbAttr", { pokemon: this.getPokemon() }); this.preSummon(); } diff --git a/src/phases/switch-biome-phase.ts b/src/phases/switch-biome-phase.ts index f84f1d517b4..dc2e0fa64d4 100644 --- a/src/phases/switch-biome-phase.ts +++ b/src/phases/switch-biome-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BiomeId } from "#enums/biome-id"; -import { getBiomeKey } from "#app/field/arena"; -import { BattlePhase } from "./battle-phase"; +import { getBiomeKey } from "#field/arena"; +import { BattlePhase } from "#phases/battle-phase"; export class SwitchBiomePhase extends BattlePhase { public readonly phaseName = "SwitchBiomePhase"; diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 8d18e29e6a6..a431d973a02 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; -import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; -import { UiMode } from "#enums/ui-mode"; import { SwitchType } from "#enums/switch-type"; -import { BattlePhase } from "./battle-phase"; +import { UiMode } from "#enums/ui-mode"; +import { BattlePhase } from "#phases/battle-phase"; +import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; /** * Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase} diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index af03cc42b54..b7460e77569 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -1,17 +1,17 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { applyPreSummonAbAttrs, applyPreSwitchOutAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { allMoves } from "#app/data/data-lists"; -import { getPokeballTintColor } from "#app/data/pokeball"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { TrainerSlot } from "#enums/trainer-slot"; -import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { SwitchEffectTransferModifier } from "#app/modifier/modifier"; +import { SubstituteTag } from "#data/battler-tags"; +import { allMoves } from "#data/data-lists"; +import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; +import { getPokeballTintColor } from "#data/pokeball"; import { Command } from "#enums/command"; -import i18next from "i18next"; -import { SummonPhase } from "./summon-phase"; -import { SubstituteTag } from "#app/data/battler-tags"; import { SwitchType } from "#enums/switch-type"; +import { TrainerSlot } from "#enums/trainer-slot"; +import type { Pokemon } from "#field/pokemon"; +import { SwitchEffectTransferModifier } from "#modifiers/modifier"; +import { SummonPhase } from "#phases/summon-phase"; +import i18next from "i18next"; export class SwitchSummonPhase extends SummonPhase { public readonly phaseName: "SwitchSummonPhase" | "ReturnPhase" = "SwitchSummonPhase"; @@ -44,7 +44,7 @@ export class SwitchSummonPhase extends SummonPhase { preSummon(): void { if (!this.player) { if (this.slotIndex === -1) { - //@ts-ignore + //@ts-expect-error this.slotIndex = globalScene.currentBattle.trainer?.getNextSummonIndex( !this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER, ); // TODO: what would be the default trainer-slot fallback? @@ -124,8 +124,8 @@ export class SwitchSummonPhase extends SummonPhase { switchedInPokemon.resetSummonData(); switchedInPokemon.loadAssets(true); - applyPreSummonAbAttrs("PreSummonAbAttr", switchedInPokemon); - applyPreSwitchOutAbAttrs("PreSwitchOutAbAttr", this.lastPokemon); + applyAbAttrs("PreSummonAbAttr", { pokemon: switchedInPokemon }); + applyAbAttrs("PreSwitchOutAbAttr", { pokemon: this.lastPokemon }); if (!switchedInPokemon) { this.end(); return; diff --git a/src/phases/tera-phase.ts b/src/phases/tera-phase.ts index a6025e20488..84b05d88abe 100644 --- a/src/phases/tera-phase.ts +++ b/src/phases/tera-phase.ts @@ -1,13 +1,13 @@ -import type Pokemon from "#app/field/pokemon"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { BattlePhase } from "./battle-phase"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; -import { PokemonType } from "#enums/pokemon-type"; -import { achvs } from "#app/system/achv"; -import { SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { CommonBattleAnim } from "#app/data/battle-anims"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { CommonBattleAnim } from "#data/battle-anims"; +import { SpeciesFormChangeTeraTrigger } from "#data/form-change-triggers"; import { CommonAnim } from "#enums/move-anims-common"; +import { PokemonType } from "#enums/pokemon-type"; +import type { Pokemon } from "#field/pokemon"; +import { BattlePhase } from "#phases/battle-phase"; +import { achvs } from "#system/achv"; +import i18next from "i18next"; export class TeraPhase extends BattlePhase { public readonly phaseName = "TeraPhase"; diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 5e36081b899..38e3ff3a017 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -1,25 +1,25 @@ import { loggedInUser } from "#app/account"; -import { BattleType } from "#enums/battle-type"; -import { fetchDailyRunSeed, getDailyRunStarters } from "#app/data/daily-run"; -import { Gender } from "#app/data/gender"; -import { getBiomeKey } from "#app/field/arena"; import { GameMode, getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; -import type { Modifier } from "#app/modifier/modifier"; -import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { Phase } from "#app/phase"; -import type { SessionSaveData } from "#app/system/game-data"; -import { Unlockables } from "#enums/unlockables"; -import { vouchers } from "#app/system/voucher"; -import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#app/utils/common"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; +import { Phase } from "#app/phase"; +import { fetchDailyRunSeed, getDailyRunStarters } from "#data/daily-run"; +import { modifierTypes } from "#data/data-lists"; +import { Gender } from "#data/gender"; +import { BattleType } from "#enums/battle-type"; +import { GameModes } from "#enums/game-modes"; +import { ModifierPoolType } from "#enums/modifier-pool-type"; +import { UiMode } from "#enums/ui-mode"; +import { Unlockables } from "#enums/unlockables"; +import { getBiomeKey } from "#field/arena"; +import type { Modifier } from "#modifiers/modifier"; +import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import type { SessionSaveData } from "#system/game-data"; +import { vouchers } from "#system/voucher"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; +import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; export class TitlePhase extends Phase { public readonly phaseName = "TitlePhase"; @@ -204,7 +204,7 @@ export class TitlePhase extends Phase { globalScene.eventManager.startEventChallenges(); globalScene.setSeed(seed); - globalScene.resetSeed(0); + globalScene.resetSeed(); globalScene.money = globalScene.gameMode.getStartingMoney(); @@ -283,6 +283,7 @@ export class TitlePhase extends Phase { console.error("Failed to load daily run:\n", err); }); } else { + // Grab first 10 chars of ISO date format (YYYY-MM-DD) and convert to base64 let seed: string = btoa(new Date().toISOString().substring(0, 10)); if (!isNullOrUndefined(Overrides.DAILY_RUN_SEED_OVERRIDE)) { seed = Overrides.DAILY_RUN_SEED_OVERRIDE; diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts index 596bf87eb5b..2dcaac52d2f 100644 --- a/src/phases/toggle-double-position-phase.ts +++ b/src/phases/toggle-double-position-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { FieldPosition } from "#enums/field-position"; -import { BattlePhase } from "./battle-phase"; +import { BattlePhase } from "#phases/battle-phase"; export class ToggleDoublePositionPhase extends BattlePhase { public readonly phaseName = "ToggleDoublePositionPhase"; diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 554b8109f02..6f92dbe496d 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -1,15 +1,15 @@ -import { getCharVariantFromDialogue } from "#app/data/dialogue"; -import { TrainerType } from "#app/enums/trainer-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { vouchers } from "#app/system/voucher"; -import i18next from "i18next"; -import { randSeedItem } from "#app/utils/common"; -import { BattlePhase } from "./battle-phase"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { globalScene } from "#app/global-scene"; -import { BiomeId } from "#enums/biome-id"; -import { achvs } from "#app/system/achv"; import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { getCharVariantFromDialogue } from "#data/dialogue"; +import { BiomeId } from "#enums/biome-id"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { TrainerType } from "#enums/trainer-type"; +import { BattlePhase } from "#phases/battle-phase"; +import { achvs } from "#system/achv"; +import { vouchers } from "#system/voucher"; +import { randSeedItem } from "#utils/common"; +import i18next from "i18next"; export class TrainerVictoryPhase extends BattlePhase { public readonly phaseName = "TrainerVictoryPhase"; diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index ab46292c1d2..ce3b2958c23 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -1,20 +1,20 @@ -import { applyPostTurnAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { TerrainType } from "#app/data/terrain"; -import { WeatherType } from "#app/enums/weather-type"; -import { TurnEndEvent } from "#app/events/battle-scene"; -import type Pokemon from "#app/field/pokemon"; -import { getPokemonNameWithAffix } from "#app/messages"; -import { - TurnHealModifier, - EnemyTurnHealModifier, - EnemyStatusEffectHealChanceModifier, - TurnStatusEffectModifier, - TurnHeldItemTransferModifier, -} from "#app/modifier/modifier"; -import i18next from "i18next"; -import { FieldPhase } from "./field-phase"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { TerrainType } from "#data/terrain"; +import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; +import { WeatherType } from "#enums/weather-type"; +import { TurnEndEvent } from "#events/battle-scene"; +import type { Pokemon } from "#field/pokemon"; +import { + EnemyStatusEffectHealChanceModifier, + EnemyTurnHealModifier, + TurnHealModifier, + TurnHeldItemTransferModifier, + TurnStatusEffectModifier, +} from "#modifiers/modifier"; +import { FieldPhase } from "#phases/field-phase"; +import i18next from "i18next"; export class TurnEndPhase extends FieldPhase { public readonly phaseName = "TurnEndPhase"; @@ -49,7 +49,7 @@ export class TurnEndPhase extends FieldPhase { globalScene.applyModifier(EnemyStatusEffectHealChanceModifier, false, pokemon); } - applyPostTurnAbAttrs("PostTurnAbAttr", pokemon); + applyAbAttrs("PostTurnAbAttr", { pokemon }); } globalScene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon); diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index 8d0508c5ebb..b2ab096102c 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -1,13 +1,13 @@ +import { globalScene } from "#app/global-scene"; import { BattlerIndex } from "#enums/battler-index"; +import { TurnInitEvent } from "#events/battle-scene"; +import type { PlayerPokemon } from "#field/pokemon"; import { handleMysteryEncounterBattleStartEffects, handleMysteryEncounterTurnStartEffects, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { TurnInitEvent } from "#app/events/battle-scene"; -import type { PlayerPokemon } from "#app/field/pokemon"; +} from "#mystery-encounters/encounter-phase-utils"; +import { FieldPhase } from "#phases/field-phase"; import i18next from "i18next"; -import { FieldPhase } from "./field-phase"; -import { globalScene } from "#app/global-scene"; export class TurnInitPhase extends FieldPhase { public readonly phaseName = "TurnInitPhase"; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 6219907fb68..0fc126801ec 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,17 +1,16 @@ -import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { allMoves } from "#app/data/data-lists"; -import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#app/enums/stat"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; -import { Command } from "#enums/command"; -import { randSeedShuffle, BooleanHolder } from "#app/utils/common"; -import { FieldPhase } from "./field-phase"; -import { BattlerIndex } from "#enums/battler-index"; -import { TrickRoomTag } from "#app/data/arena-tag"; -import { SwitchType } from "#enums/switch-type"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; +import { TrickRoomTag } from "#data/arena-tag"; +import { allMoves } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; +import { Command } from "#enums/command"; +import { Stat } from "#enums/stat"; +import { SwitchType } from "#enums/switch-type"; +import type { Pokemon } from "#field/pokemon"; +import { BypassSpeedChanceModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import { FieldPhase } from "#phases/field-phase"; +import { BooleanHolder, randSeedShuffle } from "#utils/common"; export class TurnStartPhase extends FieldPhase { public readonly phaseName = "TurnStartPhase"; @@ -66,8 +65,12 @@ export class TurnStartPhase extends FieldPhase { globalScene.getField(true).forEach(p => { const bypassSpeed = new BooleanHolder(false); const canCheckHeldItems = new BooleanHolder(true); - applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed); - applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems); + applyAbAttrs("BypassSpeedChanceAbAttr", { pokemon: p, bypass: bypassSpeed }); + applyAbAttrs("PreventBypassSpeedChanceAbAttr", { + pokemon: p, + bypass: bypassSpeed, + canCheckHeldItems: canCheckHeldItems, + }); if (canCheckHeldItems.value) { globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); } @@ -209,27 +212,8 @@ export class TurnStartPhase extends FieldPhase { break; case Command.RUN: { - let runningPokemon = pokemon; - if (globalScene.currentBattle.double) { - const playerActivePokemon = field.filter(pokemon => { - if (pokemon) { - return pokemon.isPlayer() && pokemon.isActive(); - } - return; - }); - // if only one pokemon is alive, use that one - if (playerActivePokemon.length > 1) { - // find which active pokemon has faster speed - const fasterPokemon = - playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD) - ? playerActivePokemon[0] - : playerActivePokemon[1]; - // check if either active pokemon has the ability "Run Away" - const hasRunAway = playerActivePokemon.find(p => p.hasAbility(AbilityId.RUN_AWAY)); - runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; - } - } - phaseManager.unshiftNew("AttemptRunPhase", runningPokemon.getFieldIndex()); + // Running (like ball throwing) is a team action taking up both Pokemon's turns. + phaseManager.unshiftNew("AttemptRunPhase"); } break; } diff --git a/src/phases/unlock-phase.ts b/src/phases/unlock-phase.ts index 76719847f92..ec3be88d4a5 100644 --- a/src/phases/unlock-phase.ts +++ b/src/phases/unlock-phase.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import type { Unlockables } from "#enums/unlockables"; -import { getUnlockableName } from "#app/system/unlockables"; import { UiMode } from "#enums/ui-mode"; +import type { Unlockables } from "#enums/unlockables"; +import { getUnlockableName } from "#system/unlockables"; import i18next from "i18next"; export class UnlockPhase extends Phase { diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index ae5b727c2a6..4b1a79d7443 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,12 +1,12 @@ +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { modifierTypes } from "#data/data-lists"; +import { BattleType } from "#enums/battle-type"; import type { BattlerIndex } from "#enums/battler-index"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; -import { BattleType } from "#enums/battle-type"; -import type { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { PokemonPhase } from "./pokemon-phase"; -import { handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { globalScene } from "#app/global-scene"; -import { timedEventManager } from "#app/global-event-manager"; +import type { CustomModifierSettings } from "#modifiers/modifier-type"; +import { handleMysteryEncounterVictory } from "#mystery-encounters/encounter-phase-utils"; +import { PokemonPhase } from "#phases/pokemon-phase"; export class VictoryPhase extends PokemonPhase { public readonly phaseName = "VictoryPhase"; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index d9239220376..81db543001b 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -1,18 +1,14 @@ +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; -import { - applyPreWeatherEffectAbAttrs, - applyAbAttrs, - applyPostWeatherLapseAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; -import { CommonAnim } from "#enums/move-anims-common"; -import type { Weather } from "#app/data/weather"; -import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { WeatherType } from "#app/enums/weather-type"; -import type Pokemon from "#app/field/pokemon"; +import type { Weather } from "#data/weather"; +import { getWeatherDamageMessage, getWeatherLapseMessage } from "#data/weather"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { HitResult } from "#enums/hit-result"; -import { BooleanHolder, toDmgValue } from "#app/utils/common"; -import { CommonAnimPhase } from "./common-anim-phase"; +import { CommonAnim } from "#enums/move-anims-common"; +import { WeatherType } from "#enums/weather-type"; +import type { Pokemon } from "#field/pokemon"; +import { CommonAnimPhase } from "#phases/common-anim-phase"; +import { BooleanHolder, toDmgValue } from "#utils/common"; export class WeatherEffectPhase extends CommonAnimPhase { public readonly phaseName = "WeatherEffectPhase"; @@ -41,15 +37,15 @@ export class WeatherEffectPhase extends CommonAnimPhase { const cancelled = new BooleanHolder(false); this.executeForAll((pokemon: Pokemon) => - applyPreWeatherEffectAbAttrs("SuppressWeatherEffectAbAttr", pokemon, this.weather, cancelled), + applyAbAttrs("SuppressWeatherEffectAbAttr", { pokemon, weather: this.weather, cancelled }), ); if (!cancelled.value) { const inflictDamage = (pokemon: Pokemon) => { const cancelled = new BooleanHolder(false); - applyPreWeatherEffectAbAttrs("PreWeatherDamageAbAttr", pokemon, this.weather, cancelled); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("PreWeatherDamageAbAttr", { pokemon, weather: this.weather, cancelled }); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if ( cancelled.value || @@ -80,7 +76,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { globalScene.ui.showText(getWeatherLapseMessage(this.weather.weatherType) ?? "", null, () => { this.executeForAll((pokemon: Pokemon) => { if (!pokemon.switchOutStatus) { - applyPostWeatherLapseAbAttrs("PostWeatherLapseAbAttr", pokemon, this.weather); + applyAbAttrs("PostWeatherLapseAbAttr", { pokemon, weather: this.weather }); } }); diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts index a6e248c9998..647483d1524 100644 --- a/src/pipelines/field-sprite.ts +++ b/src/pipelines/field-sprite.ts @@ -1,10 +1,11 @@ import { globalScene } from "#app/global-scene"; -import { TerrainType, getTerrainColor } from "../data/terrain"; -import { getCurrentTime } from "#app/utils/common"; +import { getTerrainColor, TerrainType } from "#data/terrain"; +import { getCurrentTime } from "#utils/common"; +import Phaser from "phaser"; import fieldSpriteFragShader from "./glsl/fieldSpriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; -export default class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline { +export class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline { constructor(game: Phaser.Game, config?: Phaser.Types.Renderer.WebGL.WebGLPipelineConfig) { super( config || { diff --git a/src/pipelines/invert.ts b/src/pipelines/invert.ts index 0ebc3ad865f..2414f2cf6b3 100644 --- a/src/pipelines/invert.ts +++ b/src/pipelines/invert.ts @@ -1,7 +1,8 @@ import type { Game } from "phaser"; +import Phaser from "phaser"; import fragShader from "./glsl/invert.frag?raw"; -export default class InvertPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline { +export class InvertPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline { constructor(game: Game) { super({ game, diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 307c2cee4cc..596a761b05f 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -1,14 +1,14 @@ -import { variantColorCache } from "#app/sprites/variant"; -import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; -import Pokemon from "#app/field/pokemon"; -import Trainer from "#app/field/trainer"; import { globalScene } from "#app/global-scene"; -import { rgbHexToRgba } from "#app/utils/common"; -import FieldSpritePipeline from "./field-sprite"; +import { FieldSpritePipeline } from "#app/pipelines/field-sprite"; +import { MysteryEncounterIntroVisuals } from "#field/mystery-encounter-intro"; +import { Pokemon } from "#field/pokemon"; +import { Trainer } from "#field/trainer"; +import { variantColorCache } from "#sprites/variant"; +import { rgbHexToRgba } from "#utils/common"; import spriteFragShader from "./glsl/spriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; -export default class SpritePipeline extends FieldSpritePipeline { +export class SpritePipeline extends FieldSpritePipeline { private _tone: number[]; constructor(game: Phaser.Game) { diff --git a/src/plugins/api/api-base.ts b/src/plugins/api/api-base.ts index f55ffe2d3fd..63f56c129f3 100644 --- a/src/plugins/api/api-base.ts +++ b/src/plugins/api/api-base.ts @@ -1,5 +1,5 @@ import { SESSION_ID_COOKIE_NAME } from "#app/constants"; -import { getCookie } from "#app/utils/cookies"; +import { getCookie } from "#utils/cookies"; type DataType = "json" | "form-urlencoded"; @@ -33,7 +33,7 @@ export abstract class ApiBase { * @param dataType The data-type of the {@linkcode bodyData}. */ protected async doPost(path: string, bodyData?: D, dataType: DataType = "json") { - let body: string | undefined = undefined; + let body: string | undefined; const headers: HeadersInit = {}; if (bodyData) { diff --git a/src/plugins/api/pokerogue-account-api.ts b/src/plugins/api/pokerogue-account-api.ts index 9cd82c24430..56bdc92b9b9 100644 --- a/src/plugins/api/pokerogue-account-api.ts +++ b/src/plugins/api/pokerogue-account-api.ts @@ -1,12 +1,12 @@ +import { ApiBase } from "#api/api-base"; +import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import type { AccountInfoResponse, AccountLoginRequest, AccountLoginResponse, AccountRegisterRequest, -} from "#app/@types/PokerogueAccountApi"; -import { SESSION_ID_COOKIE_NAME } from "#app/constants"; -import { ApiBase } from "#app/plugins/api/api-base"; -import { removeCookie, setCookie } from "#app/utils/cookies"; +} from "#types/PokerogueAccountApi"; +import { removeCookie, setCookie } from "#utils/cookies"; /** * A wrapper for PokéRogue account API requests. diff --git a/src/plugins/api/pokerogue-admin-api.ts b/src/plugins/api/pokerogue-admin-api.ts index 5923f286430..4e549c7ff53 100644 --- a/src/plugins/api/pokerogue-admin-api.ts +++ b/src/plugins/api/pokerogue-admin-api.ts @@ -1,3 +1,4 @@ +import { ApiBase } from "#api/api-base"; import type { LinkAccountToDiscordIdRequest, LinkAccountToGoogledIdRequest, @@ -5,8 +6,7 @@ import type { SearchAccountResponse, UnlinkAccountFromDiscordIdRequest, UnlinkAccountFromGoogledIdRequest, -} from "#app/@types/PokerogueAdminApi"; -import { ApiBase } from "#app/plugins/api/api-base"; +} from "#types/PokerogueAdminApi"; export class PokerogueAdminApi extends ApiBase { public readonly ERR_USERNAME_NOT_FOUND: string = "Username not found!"; diff --git a/src/plugins/api/pokerogue-api.ts b/src/plugins/api/pokerogue-api.ts index c6dfae019a5..99600a359a2 100644 --- a/src/plugins/api/pokerogue-api.ts +++ b/src/plugins/api/pokerogue-api.ts @@ -1,9 +1,9 @@ -import type { TitleStatsResponse } from "#app/@types/PokerogueApi"; -import { ApiBase } from "#app/plugins/api/api-base"; -import { PokerogueAccountApi } from "#app/plugins/api/pokerogue-account-api"; -import { PokerogueAdminApi } from "#app/plugins/api/pokerogue-admin-api"; -import { PokerogueDailyApi } from "#app/plugins/api/pokerogue-daily-api"; -import { PokerogueSavedataApi } from "#app/plugins/api/pokerogue-savedata-api"; +import { ApiBase } from "#api/api-base"; +import { PokerogueAccountApi } from "#api/pokerogue-account-api"; +import { PokerogueAdminApi } from "#api/pokerogue-admin-api"; +import { PokerogueDailyApi } from "#api/pokerogue-daily-api"; +import { PokerogueSavedataApi } from "#api/pokerogue-savedata-api"; +import type { TitleStatsResponse } from "#types/PokerogueApi"; /** * A wrapper for PokéRogue API requests. diff --git a/src/plugins/api/pokerogue-daily-api.ts b/src/plugins/api/pokerogue-daily-api.ts index c9319ae7fdc..2913c7bd4f2 100644 --- a/src/plugins/api/pokerogue-daily-api.ts +++ b/src/plugins/api/pokerogue-daily-api.ts @@ -1,6 +1,6 @@ -import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#app/@types/PokerogueDailyApi"; -import { ApiBase } from "#app/plugins/api/api-base"; -import type { RankingEntry } from "#app/ui/daily-run-scoreboard"; +import { ApiBase } from "#api/api-base"; +import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/PokerogueDailyApi"; +import type { RankingEntry } from "#ui/daily-run-scoreboard"; /** * A wrapper for daily-run PokéRogue API requests. diff --git a/src/plugins/api/pokerogue-savedata-api.ts b/src/plugins/api/pokerogue-savedata-api.ts index b8531e82d4f..bc77de8060b 100644 --- a/src/plugins/api/pokerogue-savedata-api.ts +++ b/src/plugins/api/pokerogue-savedata-api.ts @@ -1,8 +1,8 @@ -import type { UpdateAllSavedataRequest } from "#app/@types/PokerogueSavedataApi"; +import { ApiBase } from "#api/api-base"; +import { PokerogueSessionSavedataApi } from "#api/pokerogue-session-savedata-api"; +import { PokerogueSystemSavedataApi } from "#api/pokerogue-system-savedata-api"; import { MAX_INT_ATTR_VALUE } from "#app/constants"; -import { ApiBase } from "#app/plugins/api/api-base"; -import { PokerogueSessionSavedataApi } from "#app/plugins/api/pokerogue-session-savedata-api"; -import { PokerogueSystemSavedataApi } from "#app/plugins/api/pokerogue-system-savedata-api"; +import type { UpdateAllSavedataRequest } from "#types/PokerogueSavedataApi"; /** * A wrapper for PokéRogue savedata API requests. diff --git a/src/plugins/api/pokerogue-session-savedata-api.ts b/src/plugins/api/pokerogue-session-savedata-api.ts index aac8b9b93ad..a807d4b712c 100644 --- a/src/plugins/api/pokerogue-session-savedata-api.ts +++ b/src/plugins/api/pokerogue-session-savedata-api.ts @@ -1,3 +1,5 @@ +import { ApiBase } from "#api/api-base"; +import type { SessionSaveData } from "#system/game-data"; import type { ClearSessionSavedataRequest, ClearSessionSavedataResponse, @@ -5,9 +7,7 @@ import type { GetSessionSavedataRequest, NewClearSessionSavedataRequest, UpdateSessionSavedataRequest, -} from "#app/@types/PokerogueSessionSavedataApi"; -import { ApiBase } from "#app/plugins/api/api-base"; -import type { SessionSaveData } from "#app/system/game-data"; +} from "#types/PokerogueSessionSavedataApi"; /** * A wrapper for PokéRogue session savedata API requests. diff --git a/src/plugins/api/pokerogue-system-savedata-api.ts b/src/plugins/api/pokerogue-system-savedata-api.ts index d6fbb39ae0a..f87aba81ee9 100644 --- a/src/plugins/api/pokerogue-system-savedata-api.ts +++ b/src/plugins/api/pokerogue-system-savedata-api.ts @@ -1,10 +1,10 @@ +import { ApiBase } from "#api/api-base"; import type { GetSystemSavedataRequest, UpdateSystemSavedataRequest, VerifySystemSavedataRequest, VerifySystemSavedataResponse, -} from "#app/@types/PokerogueSystemSavedataApi"; -import { ApiBase } from "#app/plugins/api/api-base"; +} from "#types/PokerogueSystemSavedataApi"; /** * A wrapper for PokéRogue system savedata API requests. diff --git a/src/plugins/cache-busted-loader-plugin.ts b/src/plugins/cache-busted-loader-plugin.ts index 4ae9b352ae3..4853265ec65 100644 --- a/src/plugins/cache-busted-loader-plugin.ts +++ b/src/plugins/cache-busted-loader-plugin.ts @@ -1,8 +1,8 @@ -import { coerceArray } from "#app/utils/common"; +import { coerceArray } from "#utils/common"; let manifest: object; -export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin { +export class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin { get manifest() { return manifest; } diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 7d3b30ed5b0..4ee4aa730fb 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -1,9 +1,9 @@ -import { camelCaseToKebabCase } from "#app/utils/common"; +import pkg from "#package.json"; +import { camelCaseToKebabCase } from "#utils/common"; import i18next from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import HttpBackend from "i18next-http-backend"; import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; -import pkg from "../../package.json"; //#region Interfaces/Types @@ -65,28 +65,27 @@ const fonts: Array = [ unicodeRange: rangesByLanguage.chinese, }), extraOptions: { sizeAdjust: "70%", format: "woff2" }, - only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca", "da", "tr", "ro", "ru"], + only: ["zh"], }, { face: new FontFace("pkmnems", "url(./fonts/unifont-15.1.05.subset.woff2)", { unicodeRange: rangesByLanguage.chinese, }), extraOptions: { format: "woff2" }, - only: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca", "da", "tr", "ro", "ru"], + only: ["zh"], }, // japanese { - face: new FontFace("emerald", "url(./fonts/Galmuri11.subset.woff2)", { + face: new FontFace("emerald", "url(./fonts/pokemon-bw.ttf)", { unicodeRange: rangesByLanguage.japanese, }), - extraOptions: { sizeAdjust: "66%" }, - only: ["ja"], + only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru"], }, { - face: new FontFace("pkmnems", "url(./fonts/Galmuri9.subset.woff2)", { + face: new FontFace("pkmnems", "url(./fonts/pokemon-bw.ttf)", { unicodeRange: rangesByLanguage.japanese, }), - only: ["ja"], + only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru"], }, ]; diff --git a/src/plugins/vite/vite-minify-json-plugin.ts b/src/plugins/vite/vite-minify-json-plugin.ts index 38f299eea50..50f5f87fe7c 100644 --- a/src/plugins/vite/vite-minify-json-plugin.ts +++ b/src/plugins/vite/vite-minify-json-plugin.ts @@ -1,5 +1,5 @@ -import path from "path"; import fs from "fs"; +import path from "path"; import type { Plugin as VitePlugin } from "vite"; /** diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 00000000000..8666629fceb --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,21 @@ +/* +Manual rolling of polyfills desired by the project. + +IMPORTANT: When adding / removing polyfills, ensure that typescript becomes +aware of their existence, either by creating `src/typings/polyfills.d.ts` +and defining them there, or or by adding the appropriate field polyfill to the +`lib` property in `tsconfig.json`. +*/ + +if (typeof Promise.withResolvers === "undefined") { + Promise.withResolvers = () => { + // Bangs are OK here; they are guaranteed to be defined when the promise is invoked. + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; + }; +} diff --git a/src/scene-base.ts b/src/scene-base.ts index ccea373fca0..3f2e4409794 100644 --- a/src/scene-base.ts +++ b/src/scene-base.ts @@ -1,4 +1,4 @@ -import { coerceArray } from "#app/utils/common"; +import { coerceArray } from "#utils/common"; export const legacyCompatibleImages: string[] = []; diff --git a/src/sprites/pokemon-asset-loader.ts b/src/sprites/pokemon-asset-loader.ts index 980d242a880..9deb68b7443 100644 --- a/src/sprites/pokemon-asset-loader.ts +++ b/src/sprites/pokemon-asset-loader.ts @@ -1,5 +1,5 @@ +import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; import type { MoveId } from "#enums/move-id"; -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; /** * Asynchronously load the animations and assets for the provided moves. diff --git a/src/sprites/pokemon-sprite.ts b/src/sprites/pokemon-sprite.ts index 66432f5a4ea..5a8d7d53f0c 100644 --- a/src/sprites/pokemon-sprite.ts +++ b/src/sprites/pokemon-sprite.ts @@ -1,15 +1,15 @@ +import type { BattleScene } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import { variantColorCache, variantData } from "#app/sprites/variant"; -import { Gender } from "#app/data/gender"; -import { hasExpSprite } from "./sprite-utils"; -import type { Variant, VariantSet } from "#app/sprites/variant"; -import type Pokemon from "#app/field/pokemon"; -import type BattleScene from "#app/battle-scene"; +import { Gender } from "#data/gender"; +import type { Pokemon } from "#field/pokemon"; +import { hasExpSprite } from "#sprites/sprite-utils"; +import type { Variant, VariantSet } from "#sprites/variant"; +import { variantColorCache, variantData } from "#sprites/variant"; // Regex patterns /** Regex matching double underscores */ -const DUNDER_REGEX = /\_{2}/g; +const DUNDER_REGEX = /_{2}/g; /** * Calculate the sprite ID from a pokemon form. diff --git a/src/sprites/sprite-utils.ts b/src/sprites/sprite-utils.ts index 0f4adf7882f..e5c00d4c914 100644 --- a/src/sprites/sprite-utils.ts +++ b/src/sprites/sprite-utils.ts @@ -1,6 +1,6 @@ -import { expSpriteKeys } from "#app/sprites/sprite-keys"; +import { expSpriteKeys } from "#sprites/sprite-keys"; -const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/; +const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(-.*?)?(?:_[1-3])?$/; export function hasExpSprite(key: string): boolean { const keyMatch = expKeyRegex.exec(key); diff --git a/src/sprites/variant.ts b/src/sprites/variant.ts index 985068015c6..28d7ed13839 100644 --- a/src/sprites/variant.ts +++ b/src/sprites/variant.ts @@ -1,8 +1,8 @@ -import { VariantTier } from "#app/enums/variant-tier"; -import { hasExpSprite } from "#app/sprites/sprite-utils"; import { globalScene } from "#app/global-scene"; -import type Pokemon from "#app/field/pokemon"; -import { isNullOrUndefined } from "#app/utils/common"; +import { VariantTier } from "#enums/variant-tier"; +import type { Pokemon } from "#field/pokemon"; +import { hasExpSprite } from "#sprites/sprite-utils"; +import { isNullOrUndefined } from "#utils/common"; export type Variant = 0 | 1 | 2; diff --git a/src/starting-wave.ts b/src/starting-wave.ts index 3d36dabe652..7dbcffebbfe 100644 --- a/src/starting-wave.ts +++ b/src/starting-wave.ts @@ -1,3 +1,3 @@ -import Overrides from "./overrides"; +import Overrides from "#app/overrides"; export const startingWave = Overrides.STARTING_WAVE_OVERRIDE || 1; diff --git a/src/system/achv.ts b/src/system/achv.ts index 90816ff65c3..abe6f264d20 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -1,21 +1,21 @@ -import type { Modifier } from "typescript"; -import { TurnHeldItemTransferModifier } from "../modifier/modifier"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import i18next from "i18next"; -import { NumberHolder } from "#app/utils/common"; -import { PlayerGender } from "#enums/player-gender"; -import type { Challenge } from "#app/data/challenge"; +import { globalScene } from "#app/global-scene"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import type { Challenge } from "#data/challenge"; import { FlipStatChallenge, FreshStartChallenge, + InverseBattleChallenge, SingleGenerationChallenge, SingleTypeChallenge, - InverseBattleChallenge, -} from "#app/data/challenge"; -import type { ConditionFn } from "#app/@types/common"; -import { Stat, getShortenedStatKey } from "#app/enums/stat"; -import { Challenges } from "#app/enums/challenges"; -import { globalScene } from "#app/global-scene"; +} from "#data/challenge"; +import { Challenges } from "#enums/challenges"; +import { PlayerGender } from "#enums/player-gender"; +import { getShortenedStatKey, Stat } from "#enums/stat"; +import { TurnHeldItemTransferModifier } from "#modifiers/modifier"; +import type { ConditionFn } from "#types/common"; +import { NumberHolder } from "#utils/common"; +import i18next from "i18next"; +import type { Modifier } from "typescript"; export enum AchvTier { COMMON, diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index 29423d10207..c0ad4a25024 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -1,35 +1,47 @@ -import { Arena } from "../field/arena"; -import type { ArenaTag } from "../data/arena-tag"; -import { loadArenaTag } from "../data/arena-tag"; +import type { ArenaTag } from "#data/arena-tag"; +import { loadArenaTag, SerializableArenaTag } from "#data/arena-tag"; +import { Terrain } from "#data/terrain"; +import { Weather } from "#data/weather"; import type { BiomeId } from "#enums/biome-id"; -import { Weather } from "../data/weather"; -import { Terrain } from "#app/data/terrain"; +import { Arena } from "#field/arena"; +import type { ArenaTagTypeData } from "#types/arena-tags"; +import type { NonFunctionProperties } from "#types/type-helpers"; -export default class ArenaData { +export interface SerializedArenaData { + biome: BiomeId; + weather: NonFunctionProperties | null; + terrain: NonFunctionProperties | null; + tags?: ArenaTagTypeData[]; + playerTerasUsed?: number; +} + +export class ArenaData { public biome: BiomeId; public weather: Weather | null; public terrain: Terrain | null; public tags: ArenaTag[]; public playerTerasUsed: number; - constructor(source: Arena | any) { - const sourceArena = source instanceof Arena ? (source as Arena) : null; - this.biome = sourceArena ? sourceArena.biomeType : source.biome; - this.weather = sourceArena - ? sourceArena.weather - : source.weather - ? new Weather(source.weather.weatherType, source.weather.turnsLeft) - : null; - this.terrain = sourceArena - ? sourceArena.terrain - : source.terrain - ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) - : null; - this.playerTerasUsed = (sourceArena ? sourceArena.playerTerasUsed : source.playerTerasUsed) ?? 0; - this.tags = []; + constructor(source: Arena | SerializedArenaData) { + // Exclude any unserializable tags from the serialized data (such as ones only lasting 1 turn). + // NOTE: The filter has to be done _after_ map, data loaded from `ArenaTagTypeData` + // is not yet an instance of `ArenaTag` + this.tags = + source.tags + ?.map((t: ArenaTag | ArenaTagTypeData) => loadArenaTag(t)) + ?.filter((tag): tag is SerializableArenaTag => tag instanceof SerializableArenaTag) ?? []; - if (source.tags) { - this.tags = source.tags.map(t => loadArenaTag(t)); + this.playerTerasUsed = source.playerTerasUsed ?? 0; + + if (source instanceof Arena) { + this.biome = source.biomeType; + this.weather = source.weather; + this.terrain = source.terrain; + return; } + + this.biome = source.biome; + this.weather = source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; + this.terrain = source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; } } diff --git a/src/system/challenge-data.ts b/src/system/challenge-data.ts index 87299ae653a..1d4ab23f523 100644 --- a/src/system/challenge-data.ts +++ b/src/system/challenge-data.ts @@ -1,7 +1,7 @@ -import type { Challenge } from "#app/data/challenge"; -import { copyChallenge } from "#app/data/challenge"; +import type { Challenge } from "#data/challenge"; +import { copyChallenge } from "#data/challenge"; -export default class ChallengeData { +export class ChallengeData { public id: number; public value: number; public severity: number; diff --git a/src/system/egg-data.ts b/src/system/egg-data.ts index 63f9e17d7be..4910d01d5a8 100644 --- a/src/system/egg-data.ts +++ b/src/system/egg-data.ts @@ -1,10 +1,10 @@ +import { EGG_SEED, Egg } from "#data/egg"; +import type { EggSourceType } from "#enums/egg-source-types"; import type { EggTier } from "#enums/egg-type"; import type { SpeciesId } from "#enums/species-id"; import type { VariantTier } from "#enums/variant-tier"; -import { EGG_SEED, Egg } from "../data/egg"; -import type { EggSourceType } from "#app/enums/egg-source-types"; -export default class EggData { +export class EggData { public id: number; public tier: EggTier; public sourceType: EggSourceType; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index e933c5704f9..4f251b212b1 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,74 +1,73 @@ -import i18next from "i18next"; -import type { PokeballCounts } from "#app/battle-scene"; -import { bypassLogin } from "#app/global-vars/bypass-login"; -import { globalScene } from "#app/global-scene"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { randInt, getEnumKeys, isLocal, executeIf, fixedInt, randSeedItem, NumberHolder } from "#app/utils/common"; -import Overrides from "#app/overrides"; -import PokemonData from "#app/system/pokemon-data"; -import PersistentModifierData from "#app/system/modifier-data"; -import ArenaData from "#app/system/arena-data"; -import { Unlockables } from "#enums/unlockables"; -import { getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; -import { BattleType } from "#enums/battle-type"; -import TrainerData from "#app/system/trainer-data"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import { resetSettings, setSetting, SettingKeys } from "#app/system/settings/settings"; -import { achvs } from "#app/system/achv"; -import EggData from "#app/system/egg-data"; -import type { Egg } from "#app/data/egg"; -import { vouchers, VoucherType } from "#app/system/voucher"; -import { AES, enc } from "crypto-js"; -import { UiMode } from "#enums/ui-mode"; +import { pokerogueApi } from "#api/pokerogue-api"; import { clientSessionId, loggedInUser, updateUserInfo } from "#app/account"; -import { Nature } from "#enums/nature"; -import { GameStats } from "#app/system/game-stats"; +import type { PokeballCounts } from "#app/battle-scene"; +import { defaultStarterSpecies, saveKey } from "#app/constants"; +import { getGameMode } from "#app/game-mode"; +import { globalScene } from "#app/global-scene"; +import { bypassLogin } from "#app/global-vars/bypass-login"; +import Overrides from "#app/overrides"; import { Tutorial } from "#app/tutorial"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/data-lists"; -import { TrainerVariant } from "#enums/trainer-variant"; -import type { Variant } from "#app/sprites/variant"; -import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; -import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { setSettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; -// biome-ignore lint/style/noNamespaceImport: Something weird is going on here and I don't want to touch it -import * as Modifier from "#app/modifier/modifier"; -import { StatusEffect } from "#enums/status-effect"; -import ChallengeData from "#app/system/challenge-data"; -import { Device } from "#enums/devices"; -import { GameDataType } from "#enums/game-data-type"; -import type { MoveId } from "#enums/move-id"; -import { PlayerGender } from "#enums/player-gender"; -import { SpeciesId } from "#enums/species-id"; -import { applyChallenges } from "#app/data/challenge"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import { speciesStarterCosts } from "#balance/starters"; +import { ArenaTrapTag } from "#data/arena-tag"; +import { applyChallenges } from "#data/challenge"; +import { allMoves, allSpecies } from "#data/data-lists"; +import type { Egg } from "#data/egg"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { TerrainType } from "#data/terrain"; +import { AbilityAttr } from "#enums/ability-attr"; +import { BattleType } from "#enums/battle-type"; import { ChallengeType } from "#enums/challenge-type"; +import { Device } from "#enums/devices"; +import { DexAttr } from "#enums/dex-attr"; +import { GameDataType } from "#enums/game-data-type"; +import { GameModes } from "#enums/game-modes"; +import type { MoveId } from "#enums/move-id"; +import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; +import { PlayerGender } from "#enums/player-gender"; +import type { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { TrainerVariant } from "#enums/trainer-variant"; +import { UiMode } from "#enums/ui-mode"; +import { Unlockables } from "#enums/unlockables"; import { WeatherType } from "#enums/weather-type"; -import { TerrainType } from "#app/data/terrain"; -import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler"; +import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; +// biome-ignore lint/performance/noNamespaceImport: Something weird is going on here and I don't want to touch it +import * as Modifier from "#modifiers/modifier"; +import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data"; +import type { Variant } from "#sprites/variant"; +import { achvs } from "#system/achv"; +import { ArenaData, type SerializedArenaData } from "#system/arena-data"; +import { ChallengeData } from "#system/challenge-data"; +import { EggData } from "#system/egg-data"; +import { GameStats } from "#system/game-stats"; +import { ModifierData as PersistentModifierData } from "#system/modifier-data"; +import { PokemonData } from "#system/pokemon-data"; +import { resetSettings, SettingKeys, setSetting } from "#system/settings"; +import { SettingGamepad, setSettingGamepad, settingGamepadDefaults } from "#system/settings-gamepad"; +import type { SettingKeyboard } from "#system/settings-keyboard"; +import { setSettingKeyboard } from "#system/settings-keyboard"; +import { TrainerData } from "#system/trainer-data"; import { applySessionVersionMigration, - applySystemVersionMigration, applySettingsVersionMigration, -} from "./version_migration/version_converter"; -import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; -import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { ArenaTrapTag } from "#app/data/arena-tag"; -import { pokemonFormChanges } from "#app/data/pokemon-forms"; -import type { PokemonType } from "#enums/pokemon-type"; -import type { DexData, DexEntry } from "../@types/dex-data"; -import { DexAttr } from "#enums/dex-attr"; -import { AbilityAttr } from "#enums/ability-attr"; -import { defaultStarterSpecies, saveKey } from "#app/constants"; -import { encrypt, decrypt } from "#app/utils/data"; + applySystemVersionMigration, +} from "#system/version_converter"; +import { VoucherType, vouchers } from "#system/voucher"; +import { trainerConfigs } from "#trainers/trainer-config"; +import type { DexData, DexEntry } from "#types/dex-data"; +import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler"; +import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common"; +import { decrypt, encrypt } from "#utils/data"; +import { getEnumKeys } from "#utils/enums"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { AES, enc } from "crypto-js"; +import i18next from "i18next"; function getDataTypeKey(dataType: GameDataType, slotId = 0): string { switch (dataType) { @@ -300,7 +299,7 @@ export class GameData { voucherCounts: this.voucherCounts, eggs: this.eggs.map(e => new EggData(e)), gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), eggPity: this.eggPity.slice(0), unlockPity: this.unlockPity.slice(0), }; @@ -930,7 +929,7 @@ export class GameData { ? new TrainerData(globalScene.currentBattle.trainer) : null, gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, @@ -939,7 +938,7 @@ export class GameData { } getSession(slotId: number): Promise { - // biome-ignore lint/suspicious/noAsyncPromiseExecutor: + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this return new Promise(async (resolve, reject) => { if (slotId < 0) { return resolve(null); @@ -980,7 +979,7 @@ export class GameData { } loadSession(slotId: number, sessionData?: SessionSaveData): Promise { - // biome-ignore lint/suspicious/noAsyncPromiseExecutor: + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this return new Promise(async (resolve, reject) => { try { const initSessionFromData = async (sessionData: SessionSaveData) => { @@ -1248,7 +1247,8 @@ export class GameData { // (or prevent them from being null) // If the value is able to *not exist*, it should say so in the code const sessionData = JSON.parse(dataStr, (k: string, v: any) => { - // TODO: Add pre-parse migrate scripts + // TODO: Move this to occur _after_ migrate scripts (and refactor all non-assignment duties into migrate scripts) + // This should ideally be just a giant assign block switch (k) { case "party": case "enemyParty": { @@ -1286,7 +1286,7 @@ export class GameData { } case "arena": - return new ArenaData(v); + return new ArenaData(v as SerializedArenaData); case "challenges": { const ret: ChallengeData[] = []; @@ -1610,7 +1610,7 @@ export class GameData { } } - this.defaultDexData = Object.assign({}, data); + this.defaultDexData = { ...data }; this.dexData = data; } diff --git a/src/system/game-speed.ts b/src/system/game-speed.ts index 712870dfaf1..283e6461e55 100644 --- a/src/system/game-speed.ts +++ b/src/system/game-speed.ts @@ -1,13 +1,17 @@ -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; +import type { BattleScene } from "#app/battle-scene"; +import { globalScene } from "#app/global-scene"; +import { FixedInt } from "#utils/common"; import type FadeIn from "phaser3-rex-plugins/plugins/audio/fade/FadeIn"; import type FadeOut from "phaser3-rex-plugins/plugins/audio/fade/FadeOut"; -import type BattleScene from "#app/battle-scene"; -import { globalScene } from "#app/global-scene"; -import { FixedInt } from "#app/utils/common"; +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; + +type TweenManager = typeof Phaser.Tweens.TweenManager.prototype; + +/** The set of properties to mutate */ +const PROPERTIES = ["delay", "completeDelay", "loopDelay", "duration", "repeatDelay", "hold", "startDelay"]; type FadeInType = typeof FadeIn; type FadeOutType = typeof FadeOut; - export function initGameSpeed() { const thisArg = this as BattleScene; @@ -18,14 +22,44 @@ export function initGameSpeed() { return thisArg.gameSpeed === 1 ? value : Math.ceil((value /= thisArg.gameSpeed)); }; - const originalAddEvent = this.time.addEvent; + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Complexity is necessary here + const mutateProperties = (obj: any, allowArray = false) => { + // We do not mutate Tweens or TweenChain objects themselves. + if (obj instanceof Phaser.Tweens.Tween || obj instanceof Phaser.Tweens.TweenChain) { + return; + } + // If allowArray is true then check if first obj is an array and if so, mutate the tweens inside + if (allowArray && Array.isArray(obj)) { + for (const tween of obj) { + mutateProperties(tween); + } + return; + } + + for (const prop of PROPERTIES) { + const objProp = obj[prop]; + if (typeof objProp === "number" || objProp instanceof FixedInt) { + obj[prop] = transformValue(objProp); + } + } + // If the object has a 'tweens' property that is an array, then it is a tween chain + // and we need to mutate its properties as well + if (obj.tweens && Array.isArray(obj.tweens)) { + for (const tween of obj.tweens) { + mutateProperties(tween); + } + } + }; + + const originalAddEvent: typeof Phaser.Time.Clock.prototype.addEvent = this.time.addEvent; this.time.addEvent = function (config: Phaser.Time.TimerEvent | Phaser.Types.Time.TimerEventConfig) { if (!(config instanceof Phaser.Time.TimerEvent) && config.delay) { config.delay = transformValue(config.delay); } return originalAddEvent.apply(this, [config]); }; - const originalTweensAdd = this.tweens.add; + const originalTweensAdd: TweenManager["add"] = this.tweens.add; + this.tweens.add = function ( config: | Phaser.Types.Tweens.TweenBuilderConfig @@ -33,71 +67,33 @@ export function initGameSpeed() { | Phaser.Tweens.Tween | Phaser.Tweens.TweenChain, ) { - if (config.loopDelay) { - config.loopDelay = transformValue(config.loopDelay as number); - } - - if (!(config instanceof Phaser.Tweens.TweenChain)) { - if (config.duration) { - config.duration = transformValue(config.duration); - } - - if (!(config instanceof Phaser.Tweens.Tween)) { - if (config.delay) { - config.delay = transformValue(config.delay as number); - } - if (config.repeatDelay) { - config.repeatDelay = transformValue(config.repeatDelay); - } - if (config.hold) { - config.hold = transformValue(config.hold); - } - } - } + mutateProperties(config); return originalTweensAdd.apply(this, [config]); - }; - const originalTweensChain = this.tweens.chain; + } as typeof originalTweensAdd; + + const originalTweensChain: TweenManager["chain"] = this.tweens.chain; this.tweens.chain = function (config: Phaser.Types.Tweens.TweenChainBuilderConfig): Phaser.Tweens.TweenChain { - if (config.tweens) { - for (const t of config.tweens) { - if (t.duration) { - t.duration = transformValue(t.duration); - } - if (t.delay) { - t.delay = transformValue(t.delay as number); - } - if (t.repeatDelay) { - t.repeatDelay = transformValue(t.repeatDelay); - } - if (t.loopDelay) { - t.loopDelay = transformValue(t.loopDelay as number); - } - if (t.hold) { - t.hold = transformValue(t.hold); - } - } - } + mutateProperties(config); return originalTweensChain.apply(this, [config]); - }; - const originalAddCounter = this.tweens.addCounter; + } as typeof originalTweensChain; + const originalAddCounter: TweenManager["addCounter"] = this.tweens.addCounter; + this.tweens.addCounter = function (config: Phaser.Types.Tweens.NumberTweenBuilderConfig) { - if (config.duration) { - config.duration = transformValue(config.duration); - } - if (config.delay) { - config.delay = transformValue(config.delay); - } - if (config.repeatDelay) { - config.repeatDelay = transformValue(config.repeatDelay); - } - if (config.loopDelay) { - config.loopDelay = transformValue(config.loopDelay as number); - } - if (config.hold) { - config.hold = transformValue(config.hold); - } + mutateProperties(config); return originalAddCounter.apply(this, [config]); - }; + } as typeof originalAddCounter; + + const originalCreate: TweenManager["create"] = this.tweens.create; + this.tweens.create = function (config: Phaser.Types.Tweens.TweenBuilderConfig) { + mutateProperties(config, true); + return originalCreate.apply(this, [config]); + } as typeof originalCreate; + + const originalAddMultiple: TweenManager["addMultiple"] = this.tweens.addMultiple; + this.tweens.addMultiple = function (config: Phaser.Types.Tweens.TweenBuilderConfig[]) { + mutateProperties(config, true); + return originalAddMultiple.apply(this, [config]); + } as typeof originalAddMultiple; const originalFadeOut = SoundFade.fadeOut; SoundFade.fadeOut = ((_scene: Phaser.Scene, sound: Phaser.Sound.BaseSound, duration: number, destroy?: boolean) => diff --git a/src/system/modifier-data.ts b/src/system/modifier-data.ts index cbda45572ac..135d1907e08 100644 --- a/src/system/modifier-data.ts +++ b/src/system/modifier-data.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { PersistentModifier } from "#app/modifier/modifier"; -import type { GeneratedPersistentModifierType, ModifierType } from "#app/modifier/modifier-type"; -import { ModifierTypeGenerator, getModifierTypeFuncById } from "#app/modifier/modifier-type"; +import { PersistentModifier } from "#modifiers/modifier"; +import type { GeneratedPersistentModifierType, ModifierType } from "#modifiers/modifier-type"; +import { getModifierTypeFuncById, ModifierTypeGenerator } from "#modifiers/modifier-type"; -export default class ModifierData { +export class ModifierData { public player: boolean; public typeId: string; public typePregenArgs: any[]; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 050da57e0be..9cea08bfb13 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -1,22 +1,22 @@ -import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; -import type { Gender } from "../data/gender"; -import { Nature } from "#enums/nature"; -import { PokeballType } from "#enums/pokeball"; -import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { Status } from "../data/status-effect"; -import Pokemon, { EnemyPokemon, PokemonBattleData, PokemonSummonData } from "../field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { TrainerSlot } from "#enums/trainer-slot"; -import type { Variant } from "#app/sprites/variant"; +import type { Gender } from "#data/gender"; +import { CustomPokemonData, PokemonBattleData, PokemonSummonData } from "#data/pokemon-data"; +import { getPokemonSpeciesForm } from "#data/pokemon-species"; +import { Status } from "#data/status-effect"; +import { BattleType } from "#enums/battle-type"; import type { BiomeId } from "#enums/biome-id"; import type { MoveId } from "#enums/move-id"; -import type { SpeciesId } from "#enums/species-id"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { Nature } from "#enums/nature"; +import { PokeballType } from "#enums/pokeball"; import type { PokemonType } from "#enums/pokemon-type"; +import type { SpeciesId } from "#enums/species-id"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { EnemyPokemon, Pokemon } from "#field/pokemon"; +import { PokemonMove } from "#moves/pokemon-move"; +import type { Variant } from "#sprites/variant"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; -export default class PokemonData { +export class PokemonData { public id: number; public player: boolean; public species: SpeciesId; @@ -105,7 +105,7 @@ export default class PokemonData { // TODO: Can't we move some of this verification stuff to an upgrade script? this.nature = source.nature ?? Nature.HARDY; - this.moveset = source.moveset.map((m: any) => PokemonMove.loadMove(m)); + this.moveset = source.moveset?.map((m: any) => PokemonMove.loadMove(m)) ?? []; this.status = source.status ? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining) : null; diff --git a/src/system/settings/settings-gamepad.ts b/src/system/settings/settings-gamepad.ts index 8a28e9fbf14..2e25eda7300 100644 --- a/src/system/settings/settings-gamepad.ts +++ b/src/system/settings/settings-gamepad.ts @@ -1,9 +1,9 @@ -import type SettingsGamepadUiHandler from "../../ui/settings/settings-gamepad-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { truncateString } from "../../utils/common"; -import { Button } from "#enums/buttons"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { SettingKeyboard } from "#system/settings-keyboard"; +import type { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler"; +import { truncateString } from "#utils/common"; import i18next from "i18next"; export enum SettingGamepad { diff --git a/src/system/settings/settings-keyboard.ts b/src/system/settings/settings-keyboard.ts index ec5c9ad6b0e..07f4f86bdb0 100644 --- a/src/system/settings/settings-keyboard.ts +++ b/src/system/settings/settings-keyboard.ts @@ -1,8 +1,8 @@ +import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import { UiMode } from "#enums/ui-mode"; -import type SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; +import type { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler"; import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; export enum SettingKeyboard { // Default_Layout = "DEFAULT_LAYOUT", diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index ca5395a5af7..19d10baedfd 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -1,35 +1,32 @@ -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; import { hasTouchscreen } from "#app/touch-controls"; -import { updateWindowType } from "#app/ui/ui-theme"; -import { CandyUpgradeNotificationChangedEvent } from "#app/events/battle-scene"; -import type SettingsUiHandler from "#app/ui/settings/settings-ui-handler"; import { EaseType } from "#enums/ease-type"; import { MoneyFormat } from "#enums/money-format"; import { PlayerGender } from "#enums/player-gender"; import { ShopCursorTarget } from "#enums/shop-cursor-target"; -import { isLocal } from "#app/utils/common"; +import { UiMode } from "#enums/ui-mode"; +import { CandyUpgradeNotificationChangedEvent } from "#events/battle-scene"; +import type { SettingsUiHandler } from "#ui/settings-ui-handler"; +import { updateWindowType } from "#ui/ui-theme"; +import { isLocal } from "#utils/common"; +import i18next from "i18next"; -const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => - i - ? { - value: (i * 10).toString(), - label: (i * 10).toString(), - } - : { - value: "Mute", - label: i18next.t("settings:mute"), - }, -); +const VOLUME_OPTIONS: SettingOption[] = [ + { + value: "Mute", + label: i18next.t("settings:mute"), + }, +]; +for (let i = 1; i < 11; i++) { + const value = (i * 10).toString(); + VOLUME_OPTIONS.push({ value, label: value }); +} -const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => { +const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = []; +for (let i = 0; i < 9; i++) { const value = ((i + 1) * 10).toString(); - return { - value, - label: value, - }; -}); + SHOP_OVERLAY_OPACITY_OPTIONS.push({ value, label: value }); +} const OFF_ON: SettingOption[] = [ { @@ -183,6 +180,12 @@ export enum MusicPreference { ALLGENS, } +const windowTypeOptions: SettingOption[] = []; +for (let i = 0; i < 5; i++) { + const value = (i + 1).toString(); + windowTypeOptions.push({ value, label: value }); +} + /** * All Settings not related to controls */ @@ -432,13 +435,7 @@ export const Setting: Array = [ { key: SettingKeys.Window_Type, label: i18next.t("settings:windowType"), - options: new Array(5).fill(null).map((_, i) => { - const windowType = (i + 1).toString(); - return { - value: windowType, - label: windowType, - }; - }), + options: windowTypeOptions, default: 0, type: SettingType.DISPLAY, }, diff --git a/src/system/trainer-data.ts b/src/system/trainer-data.ts index 7c9bffd99ee..ce402ff23bf 100644 --- a/src/system/trainer-data.ts +++ b/src/system/trainer-data.ts @@ -1,8 +1,8 @@ import type { TrainerType } from "#enums/trainer-type"; -import Trainer from "../field/trainer"; import { TrainerVariant } from "#enums/trainer-variant"; +import { Trainer } from "#field/trainer"; -export default class TrainerData { +export class TrainerData { public trainerType: TrainerType; public variant: TrainerVariant; public partyTemplateIndex: number; diff --git a/src/system/unlockables.ts b/src/system/unlockables.ts index 72588858eae..647aad9bcdb 100644 --- a/src/system/unlockables.ts +++ b/src/system/unlockables.ts @@ -1,7 +1,7 @@ -import i18next from "i18next"; -import { GameMode } from "../game-mode"; +import { GameMode } from "#app/game-mode"; import { GameModes } from "#enums/game-modes"; import { Unlockables } from "#enums/unlockables"; +import i18next from "i18next"; export function getUnlockableName(unlockable: Unlockables) { switch (unlockable) { diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index 32e105aec66..c49490da756 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -1,9 +1,11 @@ -import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; -import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; -import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; -import type { SessionSaveData, SystemSaveData } from "#app/system/game-data"; +/** biome-ignore-all lint/performance/noNamespaceImport: Convenience */ + +import { version } from "#package.json"; +import type { SessionSaveData, SystemSaveData } from "#system/game-data"; +import type { SessionSaveMigrator } from "#types/SessionSaveMigrator"; +import type { SettingsSaveMigrator } from "#types/SettingsSaveMigrator"; +import type { SystemSaveMigrator } from "#types/SystemSaveMigrator"; import { compareVersions } from "compare-versions"; -import { version } from "../../../package.json"; /* // template for save migrator creation @@ -22,7 +24,7 @@ const systemMigratorA: SystemSaveMigrator = { }, }; -export const systemMigrators: Readonly = [systemMigratorA] as const; +export const systemMigrators: readonly SystemSaveMigrator[] = [systemMigratorA] as const; const sessionMigratorA: SessionSaveMigrator = { version: "A.B.C", @@ -31,7 +33,7 @@ const sessionMigratorA: SessionSaveMigrator = { }, }; -export const sessionMigrators: Readonly = [sessionMigratorA] as const; +export const sessionMigrators: readonly SessionSaveMigrator[] = [sessionMigratorA] as const; const settingsMigratorA: SettingsSaveMigrator = { version: "A.B.C", @@ -41,31 +43,17 @@ const settingsMigratorA: SettingsSaveMigrator = { }, }; -export const settingsMigrators: Readonly = [settingsMigratorA] as const; +export const settingsMigrators: readonly SettingsSaveMigrator[] = [settingsMigratorA] as const; */ -// --- vA.B.C PATCHES --- // -// import * as vA_B_C from "./versions/vA_B_C"; +// Add migrator imports below: +// Example: import * as vA_B_C from "#system/vA_B_C"; -// --- v1.0.4 (and below) PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience (TODO: make this a file-wide ignore when Biome supports those) -import * as v1_0_4 from "./versions/v1_0_4"; - -// --- v1.7.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience -import * as v1_7_0 from "./versions/v1_7_0"; - -// --- v1.8.3 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience -import * as v1_8_3 from "./versions/v1_8_3"; - -// --- v1.9.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience -import * as v1_9_0 from "./versions/v1_9_0"; - -// --- v1.10.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience -import * as v1_10_0 from "./versions/v1_10_0"; +import * as v1_0_4 from "#system/v1_0_4"; +import * as v1_7_0 from "#system/v1_7_0"; +import * as v1_8_3 from "#system/v1_8_3"; +import * as v1_9_0 from "#system/v1_9_0"; +import * as v1_10_0 from "#system/v1_10_0"; /** Current game version */ const LATEST_VERSION = version; diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index a08327a3b70..3523a5b8826 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -1,14 +1,14 @@ -import { SettingKeys } from "#app/system/settings/settings"; -import type { SystemSaveData, SessionSaveData } from "#app/system/game-data"; import { defaultStarterSpecies } from "#app/constants"; +import { allSpecies } from "#data/data-lists"; +import { CustomPokemonData } from "#data/pokemon-data"; import { AbilityAttr } from "#enums/ability-attr"; import { DexAttr } from "#enums/dex-attr"; -import { allSpecies } from "#app/data/data-lists"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { isNullOrUndefined } from "#app/utils/common"; -import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; -import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; -import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; +import type { SessionSaveData, SystemSaveData } from "#system/game-data"; +import { SettingKeys } from "#system/settings"; +import type { SessionSaveMigrator } from "#types/SessionSaveMigrator"; +import type { SettingsSaveMigrator } from "#types/SettingsSaveMigrator"; +import type { SystemSaveMigrator } from "#types/SystemSaveMigrator"; +import { isNullOrUndefined } from "#utils/common"; /** * Migrate ability starter data if empty for caught species. @@ -95,7 +95,7 @@ const fixStarterData: SystemSaveMigrator = { }, }; -export const systemMigrators: Readonly = [ +export const systemMigrators: readonly SystemSaveMigrator[] = [ migrateAbilityData, fixLegendaryStats, fixStarterData, @@ -118,7 +118,7 @@ const fixRerollTarget: SettingsSaveMigrator = { }, }; -export const settingsMigrators: Readonly = [fixRerollTarget] as const; +export const settingsMigrators: readonly SettingsSaveMigrator[] = [fixRerollTarget] as const; /** * Converts old lapsing modifiers (battle items, lures, and Dire Hit) and @@ -206,4 +206,4 @@ const migrateCustomPokemonData: SessionSaveMigrator = { }, }; -export const sessionMigrators: Readonly = [migrateModifiers, migrateCustomPokemonData] as const; +export const sessionMigrators: readonly SessionSaveMigrator[] = [migrateModifiers, migrateCustomPokemonData] as const; diff --git a/src/system/version_migration/versions/v1_10_0.ts b/src/system/version_migration/versions/v1_10_0.ts index 4d1dedf701e..66436717639 100644 --- a/src/system/version_migration/versions/v1_10_0.ts +++ b/src/system/version_migration/versions/v1_10_0.ts @@ -1,10 +1,10 @@ -import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; import type { BattlerIndex } from "#enums/battler-index"; -import type { TurnMove } from "#app/field/pokemon"; -import type { MoveResult } from "#enums/move-result"; -import type { SessionSaveData } from "#app/system/game-data"; -import { MoveUseMode } from "#enums/move-use-mode"; import type { MoveId } from "#enums/move-id"; +import type { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; +import type { SessionSaveData } from "#system/game-data"; +import type { SessionSaveMigrator } from "#types/SessionSaveMigrator"; +import type { TurnMove } from "#types/turn-move"; /** Prior signature of `TurnMove`; used to ensure parity */ interface OldTurnMove { @@ -45,4 +45,4 @@ const fixMoveHistory: SessionSaveMigrator = { }, }; -export const sessionMigrators: Readonly = [fixMoveHistory] as const; +export const sessionMigrators: readonly SessionSaveMigrator[] = [fixMoveHistory] as const; diff --git a/src/system/version_migration/versions/v1_7_0.ts b/src/system/version_migration/versions/v1_7_0.ts index e3c599bf77b..8f757456d0a 100644 --- a/src/system/version_migration/versions/v1_7_0.ts +++ b/src/system/version_migration/versions/v1_7_0.ts @@ -1,11 +1,11 @@ -import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; -import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; -import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; import { globalScene } from "#app/global-scene"; -import type { SessionSaveData, SystemSaveData } from "#app/system/game-data"; +import { getPokemonSpeciesForm } from "#data/pokemon-species"; import { DexAttr } from "#enums/dex-attr"; -import { isNullOrUndefined } from "#app/utils/common"; +import type { SessionSaveData, SystemSaveData } from "#system/game-data"; +import type { SessionSaveMigrator } from "#types/SessionSaveMigrator"; +import type { SystemSaveMigrator } from "#types/SystemSaveMigrator"; +import { isNullOrUndefined } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** * If a starter is caught, but the only forms registered as caught are not starterSelectable, @@ -37,7 +37,7 @@ const migrateUnselectableForms: SystemSaveMigrator = { }, }; -export const systemMigrators: Readonly = [migrateUnselectableForms] as const; +export const systemMigrators: readonly SystemSaveMigrator[] = [migrateUnselectableForms] as const; const migrateTera: SessionSaveMigrator = { version: "1.7.0", @@ -82,4 +82,4 @@ const migrateTera: SessionSaveMigrator = { }, }; -export const sessionMigrators: Readonly = [migrateTera] as const; +export const sessionMigrators: readonly SessionSaveMigrator[] = [migrateTera] as const; diff --git a/src/system/version_migration/versions/v1_8_3.ts b/src/system/version_migration/versions/v1_8_3.ts index 81430659e0b..e6b5129d9d5 100644 --- a/src/system/version_migration/versions/v1_8_3.ts +++ b/src/system/version_migration/versions/v1_8_3.ts @@ -1,8 +1,8 @@ -import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import type { SystemSaveData } from "#app/system/game-data"; import { DexAttr } from "#enums/dex-attr"; import { SpeciesId } from "#enums/species-id"; +import type { SystemSaveData } from "#system/game-data"; +import type { SystemSaveMigrator } from "#types/SystemSaveMigrator"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** * If a starter is caught, but the only forms registered as caught are not starterSelectable, @@ -28,4 +28,4 @@ const migratePichuForms: SystemSaveMigrator = { }, }; -export const systemMigrators: Readonly = [migratePichuForms] as const; +export const systemMigrators: readonly SystemSaveMigrator[] = [migratePichuForms] as const; diff --git a/src/system/version_migration/versions/v1_9_0.ts b/src/system/version_migration/versions/v1_9_0.ts index 0bd5a422ffb..80da5f2996b 100644 --- a/src/system/version_migration/versions/v1_9_0.ts +++ b/src/system/version_migration/versions/v1_9_0.ts @@ -1,8 +1,8 @@ -import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { SessionSaveData } from "#app/system/game-data"; -import type PokemonData from "#app/system/pokemon-data"; import { MoveId } from "#enums/move-id"; +import { PokemonMove } from "#moves/pokemon-move"; +import type { SessionSaveData } from "#system/game-data"; +import type { PokemonData } from "#system/pokemon-data"; +import type { SessionSaveMigrator } from "#types/SessionSaveMigrator"; /** * Migrate all lingering rage fist data inside `CustomPokemonData`, @@ -38,4 +38,4 @@ const migratePartyData: SessionSaveMigrator = { }, }; -export const sessionMigrators: Readonly = [migratePartyData] as const; +export const sessionMigrators: readonly SessionSaveMigrator[] = [migratePartyData] as const; diff --git a/src/system/voucher.ts b/src/system/voucher.ts index ce10560c3e2..79e95f0b07b 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -1,9 +1,9 @@ -import i18next from "i18next"; -import { AchvTier, achvs, getAchievementDescription } from "./achv"; import type { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; -import type { ConditionFn } from "#app/@types/common"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; +import { AchvTier, achvs, getAchievementDescription } from "#system/achv"; +import { trainerConfigs } from "#trainers/trainer-config"; +import type { ConditionFn } from "#types/common"; +import i18next from "i18next"; export enum VoucherType { REGULAR, diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index f73c6dab1b2..02cb7fe8e0d 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; -import { TextStyle, addTextObject } from "#app/ui/text"; -import type { nil } from "#app/utils/common"; -import { isNullOrUndefined } from "#app/utils/common"; -import i18next from "i18next"; -import { SpeciesId } from "#enums/species-id"; -import type { WeatherPoolEntry } from "#app/data/weather"; -import { WeatherType } from "#enums/weather-type"; -import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "./data/balance/starters"; -import { MysteryEncounterType } from "./enums/mystery-encounter-type"; -import { MysteryEncounterTier } from "./enums/mystery-encounter-tier"; +import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "#balance/starters"; +import type { WeatherPoolEntry } from "#data/weather"; import { Challenges } from "#enums/challenges"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { WeatherType } from "#enums/weather-type"; +import { addTextObject, TextStyle } from "#ui/text"; +import type { nil } from "#utils/common"; +import { isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; export enum EventType { SHINY, diff --git a/src/touch-controls.ts b/src/touch-controls.ts index e2b5ad05baa..faee9ea232e 100644 --- a/src/touch-controls.ts +++ b/src/touch-controls.ts @@ -1,13 +1,13 @@ import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; -import EventEmitter = Phaser.Events.EventEmitter; +import type Phaser from "phaser"; const repeatInputDelayMillis = 250; -export default class TouchControl { - events: EventEmitter; - private buttonLock: string[] = new Array(); - private inputInterval: NodeJS.Timeout[] = new Array(); +export class TouchControl { + events: Phaser.Events.EventEmitter; + private buttonLock: string[] = []; + private inputInterval: NodeJS.Timeout[] = []; /** Whether touch controls are disabled */ private disabled = false; /** Whether the last touch event has finished before disabling */ @@ -42,7 +42,7 @@ export default class TouchControl { document.querySelectorAll(".apad-button").forEach(element => this.preventElementZoom(element as HTMLElement)); // Select all elements with the 'data-key' attribute and bind keys to them for (const button of document.querySelectorAll("[data-key]")) { - // @ts-ignore - Bind the key to the button using the dataset key + // @ts-expect-error - Bind the key to the button using the dataset key this.bindKey(button, button.dataset.key); } } @@ -208,7 +208,7 @@ export function isMobile(): boolean { /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( a, ) || - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( a.substr(0, 4), ) ) { diff --git a/src/tutorial.ts b/src/tutorial.ts index d9ae3a03290..018d0927da0 100644 --- a/src/tutorial.ts +++ b/src/tutorial.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import AwaitableUiHandler from "./ui/awaitable-ui-handler"; -import type UiHandler from "./ui/ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; import Overrides from "#app/overrides"; +import { UiMode } from "#enums/ui-mode"; +import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import type { UiHandler } from "#ui/ui-handler"; +import i18next from "i18next"; export enum Tutorial { Intro = "INTRO", diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index e4f11e1c93c..71c5ac1049e 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -1,19 +1,19 @@ -import type Phaser from "phaser"; -import { UiMode } from "#enums/ui-mode"; -import type { InputsController } from "./inputs-controller"; -import type MessageUiHandler from "./ui/message-ui-handler"; -import StarterSelectUiHandler from "./ui/starter-select-ui-handler"; -import { Setting, SettingKeys, settingIndex } from "./system/settings/settings"; -import SettingsUiHandler from "./ui/settings/settings-ui-handler"; -import { Button } from "#enums/buttons"; -import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler"; -import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; import { globalScene } from "#app/global-scene"; -import SettingsDisplayUiHandler from "./ui/settings/settings-display-ui-handler"; -import SettingsAudioUiHandler from "./ui/settings/settings-audio-ui-handler"; -import RunInfoUiHandler from "./ui/run-info-ui-handler"; -import PokedexUiHandler from "./ui/pokedex-ui-handler"; -import PokedexPageUiHandler from "./ui/pokedex-page-ui-handler"; +import type { InputsController } from "#app/inputs-controller"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { Setting, SettingKeys, settingIndex } from "#system/settings"; +import type { MessageUiHandler } from "#ui/message-ui-handler"; +import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler"; +import { PokedexUiHandler } from "#ui/pokedex-ui-handler"; +import { RunInfoUiHandler } from "#ui/run-info-ui-handler"; +import { SettingsAudioUiHandler } from "#ui/settings-audio-ui-handler"; +import { SettingsDisplayUiHandler } from "#ui/settings-display-ui-handler"; +import { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler"; +import { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler"; +import { SettingsUiHandler } from "#ui/settings-ui-handler"; +import { StarterSelectUiHandler } from "#ui/starter-select-ui-handler"; +import type Phaser from "phaser"; type ActionKeys = Record void>; @@ -176,12 +176,12 @@ export class UiInputs { return; } switch (globalScene.ui?.getMode()) { + // biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay case UiMode.MESSAGE: { const messageHandler = globalScene.ui.getHandler(); if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) { return; } - // biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay } case UiMode.TITLE: case UiMode.COMMAND: diff --git a/src/ui/ability-bar.ts b/src/ui/ability-bar.ts index 5481791de64..79a68e9dce7 100644 --- a/src/ui/ability-bar.ts +++ b/src/ui/ability-bar.ts @@ -1,12 +1,12 @@ import { globalScene } from "#app/global-scene"; -import { TextStyle, addTextObject } from "./text"; +import { addTextObject, TextStyle } from "#ui/text"; import i18next from "i18next"; const barWidth = 118; const screenLeft = 0; const baseY = -116; -export default class AbilityBar extends Phaser.GameObjects.Container { +export class AbilityBar extends Phaser.GameObjects.Container { private abilityBars: Phaser.GameObjects.Image[]; private abilityBarText: Phaser.GameObjects.Text; private player: boolean; diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index e1263bc088c..d93ad8b7665 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; -import { TextStyle, addBBCodeTextObject, getTextColor, getTextStyleOptions } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { addWindow } from "./ui-theme"; -import { rgbHexToRgba, fixedInt } from "#app/utils/common"; -import { argbFromRgba } from "@material/material-color-utilities"; import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { addBBCodeTextObject, getTextColor, getTextStyleOptions, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, rgbHexToRgba } from "#utils/common"; +import { argbFromRgba } from "@material/material-color-utilities"; import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; export interface OptionSelectConfig { @@ -33,7 +33,7 @@ export interface OptionSelectItem { const scrollUpLabel = "↑"; const scrollDownLabel = "↓"; -export default abstract class AbstractOptionSelectUiHandler extends UiHandler { +export abstract class AbstractOptionSelectUiHandler extends UiHandler { protected optionSelectContainer: Phaser.GameObjects.Container; protected optionSelectTextContainer: Phaser.GameObjects.Container; protected optionSelectBg: Phaser.GameObjects.NineSlice; diff --git a/src/ui/achv-bar.ts b/src/ui/achv-bar.ts index 1e068157afa..8e0f2a9404b 100644 --- a/src/ui/achv-bar.ts +++ b/src/ui/achv-bar.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; -import { Achv, getAchievementDescription } from "../system/achv"; -import { Voucher } from "../system/voucher"; -import { TextStyle, addTextObject } from "./text"; import type { PlayerGender } from "#enums/player-gender"; +import { Achv, getAchievementDescription } from "#system/achv"; +import { Voucher } from "#system/voucher"; +import { addTextObject, TextStyle } from "#ui/text"; -export default class AchvBar extends Phaser.GameObjects.Container { +export class AchvBar extends Phaser.GameObjects.Container { private defaultWidth: number; private defaultHeight: number; diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index 8588530d370..6b247f6da96 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -1,17 +1,17 @@ -import { Button } from "#enums/buttons"; -import i18next from "i18next"; -import type { Achv } from "#app/system/achv"; -import { achvs, getAchievementDescription } from "#app/system/achv"; -import type { Voucher } from "#app/system/voucher"; -import { getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#app/system/voucher"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import { addTextObject, TextStyle } from "#app/ui/text"; -import type { UiMode } from "#enums/ui-mode"; -import { addWindow } from "#app/ui/ui-theme"; -import { ScrollBar } from "#app/ui/scroll-bar"; -import { PlayerGender } from "#enums/player-gender"; import { globalScene } from "#app/global-scene"; -import type { AchvUnlocks, VoucherUnlocks } from "#app/system/game-data"; +import { Button } from "#enums/buttons"; +import { PlayerGender } from "#enums/player-gender"; +import type { UiMode } from "#enums/ui-mode"; +import type { Achv } from "#system/achv"; +import { achvs, getAchievementDescription } from "#system/achv"; +import type { AchvUnlocks, VoucherUnlocks } from "#system/game-data"; +import type { Voucher } from "#system/voucher"; +import { getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#system/voucher"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { ScrollBar } from "#ui/scroll-bar"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import i18next from "i18next"; const Page = { ACHIEVEMENTS: 0, @@ -27,7 +27,7 @@ const languageSettings: { [key: string]: LanguageSetting } = { de: { TextSize: "80px" }, }; -export default class AchvsUiHandler extends MessageUiHandler { +export class AchvsUiHandler extends MessageUiHandler { private readonly ROWS = 4; private readonly COLS = 17; @@ -81,7 +81,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.headerBg = addWindow(0, 0, WIDTH - 2, 24); - this.headerText = addTextObject(0, 0, "", TextStyle.SETTINGS_LABEL) + this.headerText = addTextObject(0, 0, "", TextStyle.HEADER_LABEL) .setOrigin(0) .setPositionRelative(this.headerBg, 8, 4); this.headerActionButton = new Phaser.GameObjects.Sprite(globalScene, 0, 0, "keyboard", "ACTION.png") diff --git a/src/ui/admin-ui-handler.ts b/src/ui/admin-ui-handler.ts index c8c8e43802b..3d0a1153127 100644 --- a/src/ui/admin-ui-handler.ts +++ b/src/ui/admin-ui-handler.ts @@ -1,17 +1,17 @@ -import { Button } from "#app/enums/buttons"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { formatText } from "#app/utils/common"; -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; -import { TextStyle } from "./text"; -import { UiMode } from "#enums/ui-mode"; +import { pokerogueApi } from "#api/pokerogue-api"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { TextStyle } from "#ui/text"; +import { formatText } from "#utils/common"; type AdminUiHandlerService = "discord" | "google"; type AdminUiHandlerServiceMode = "Link" | "Unlink"; -export default class AdminUiHandler extends FormModalUiHandler { +export class AdminUiHandler extends FormModalUiHandler { private adminMode: AdminMode; private adminResult: AdminSearchInfo; private config: ModalConfig; diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index fec02ffb660..43cc553d936 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -1,23 +1,23 @@ -import { addTextObject, TextStyle } from "./text"; import { globalScene } from "#app/global-scene"; -import { ArenaTrapTag } from "#app/data/arena-tag"; +import { ArenaTrapTag } from "#data/arena-tag"; +import { TerrainType } from "#data/terrain"; import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { WeatherType } from "#enums/weather-type"; -import { TerrainType } from "#app/data/terrain"; -import { addWindow, WindowVariant } from "./ui-theme"; -import type { ArenaEvent } from "#app/events/arena"; +import type { ArenaEvent } from "#events/arena"; import { ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent, -} from "#app/events/arena"; -import type { TurnEndEvent } from "../events/battle-scene"; -import { BattleSceneEventType } from "../events/battle-scene"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import TimeOfDayWidget from "./time-of-day-widget"; -import { toCamelCaseString, formatText, fixedInt } from "#app/utils/common"; +} from "#events/arena"; +import type { TurnEndEvent } from "#events/battle-scene"; +import { BattleSceneEventType } from "#events/battle-scene"; +import { addTextObject, TextStyle } from "#ui/text"; +import { TimeOfDayWidget } from "#ui/time-of-day-widget"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import { fixedInt, formatText, toCamelCaseString } from "#utils/common"; import type { ParseKeys } from "i18next"; import i18next from "i18next"; diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/autocomplete-ui-handler.ts index ba1802c8582..6016245c38d 100644 --- a/src/ui/autocomplete-ui-handler.ts +++ b/src/ui/autocomplete-ui-handler.ts @@ -1,8 +1,8 @@ import { Button } from "#enums/buttons"; -import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler"; import { UiMode } from "#enums/ui-mode"; +import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; -export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { +export class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { modalContainer: Phaser.GameObjects.Container; constructor(mode: UiMode = UiMode.OPTION_SELECT) { super(mode); diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 3c577fd4411..e8513b4acc1 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -1,9 +1,9 @@ -import type { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import type { UiMode } from "#enums/ui-mode"; +import { UiHandler } from "#ui/ui-handler"; -export default abstract class AwaitableUiHandler extends UiHandler { +export abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; protected onActionInput: Function | null; public tutorialActive = false; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 11fb485164a..bde340e3cf7 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,15 +1,15 @@ -import { getPokeballName } from "../data/pokeball"; -import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; +import { globalScene } from "#app/global-scene"; +import { getPokeballName } from "#data/pokeball"; +import { Button } from "#enums/buttons"; import { Command } from "#enums/command"; import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { addWindow } from "./ui-theme"; -import { Button } from "#enums/buttons"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { globalScene } from "#app/global-scene"; +import type { CommandPhase } from "#phases/command-phase"; +import { addTextObject, getTextStyleOptions, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; import i18next from "i18next"; -export default class BallUiHandler extends UiHandler { +export class BallUiHandler extends UiHandler { private pokeballSelectContainer: Phaser.GameObjects.Container; private pokeballSelectBg: Phaser.GameObjects.NineSlice; private countsText: Phaser.GameObjects.Text; @@ -115,7 +115,7 @@ export default class BallUiHandler extends UiHandler { updateCounts() { this.countsText.setText( Object.values(globalScene.pokeballCounts) - .map(c => `x${c}`) + .map(c => `×${c}`) .join("\n"), ); } diff --git a/src/ui/base-stats-overlay.ts b/src/ui/base-stats-overlay.ts index 0541ae766e5..888b87a8d11 100644 --- a/src/ui/base-stats-overlay.ts +++ b/src/ui/base-stats-overlay.ts @@ -1,9 +1,9 @@ -import type { InfoToggle } from "../battle-scene"; -import { TextStyle, addTextObject } from "./text"; -import { addWindow } from "./ui-theme"; -import { fixedInt } from "#app/utils/common"; -import i18next from "i18next"; +import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt } from "#utils/common"; +import i18next from "i18next"; interface BaseStatsOverlaySettings { scale?: number; // scale the box? A scale of 0.5 is recommended diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index 9a2180eccee..083dc7bbf19 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -1,14 +1,14 @@ -import type { EnemyPokemon, default as Pokemon } from "../field/pokemon"; -import { addTextObject, TextStyle } from "./text"; -import { fixedInt } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; -import type Move from "#app/data/moves/move"; -import type { BerryUsedEvent, MoveUsedEvent } from "../events/battle-scene"; -import { BattleSceneEventType } from "../events/battle-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; import { BerryType } from "#enums/berry-type"; import { MoveId } from "#enums/move-id"; import { UiTheme } from "#enums/ui-theme"; -import { getPokemonNameWithAffix } from "#app/messages"; +import type { BerryUsedEvent, MoveUsedEvent } from "#events/battle-scene"; +import { BattleSceneEventType } from "#events/battle-scene"; +import type { EnemyPokemon, Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { addTextObject, TextStyle } from "#ui/text"; +import { fixedInt } from "#utils/common"; /** Container for info about a {@linkcode Move} */ interface MoveInfo { @@ -22,7 +22,7 @@ interface MoveInfo { } /** A Flyout Menu attached to each {@linkcode BattleInfo} object on the field UI */ -export default class BattleFlyout extends Phaser.GameObjects.Container { +export class BattleFlyout extends Phaser.GameObjects.Container { /** Is this object linked to a player's Pokemon? */ private player: boolean; @@ -52,7 +52,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { /** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */ private flyoutText: Phaser.GameObjects.Text[] = new Array(4); /** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */ - private moveInfo: MoveInfo[] = new Array(); + private moveInfo: MoveInfo[] = []; /** Current state of the flyout's visibility */ public flyoutVisible = false; diff --git a/src/ui/battle-info/battle-info.ts b/src/ui/battle-info/battle-info.ts index e67000bb243..4a2a6d1804d 100644 --- a/src/ui/battle-info/battle-info.ts +++ b/src/ui/battle-info/battle-info.ts @@ -1,13 +1,13 @@ -import type { default as Pokemon } from "../../field/pokemon"; -import { getLocalizedSpriteKey, fixedInt, getShinyDescriptor } from "#app/utils/common"; -import { addTextObject, TextStyle } from "../text"; -import { getGenderSymbol, getGenderColor, Gender } from "../../data/gender"; -import { StatusEffect } from "#enums/status-effect"; import { globalScene } from "#app/global-scene"; -import { getTypeRgb } from "#app/data/type"; +import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; +import { getTypeRgb } from "#data/type"; import { PokemonType } from "#enums/pokemon-type"; -import { getVariantTint } from "#app/sprites/variant"; import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import type { Pokemon } from "#field/pokemon"; +import { getVariantTint } from "#sprites/variant"; +import { addTextObject, TextStyle } from "#ui/text"; +import { fixedInt, getLocalizedSpriteKey, getShinyDescriptor } from "#utils/common"; import i18next from "i18next"; /** @@ -37,7 +37,7 @@ export type BattleInfoParamList = { }; }; -export default abstract class BattleInfo extends Phaser.GameObjects.Container { +export abstract class BattleInfo extends Phaser.GameObjects.Container { public static readonly EXP_GAINS_DURATION_BASE = 1650; protected baseY: number; diff --git a/src/ui/battle-info/enemy-battle-info.ts b/src/ui/battle-info/enemy-battle-info.ts index 7c16f01ac38..5799fb476ef 100644 --- a/src/ui/battle-info/enemy-battle-info.ts +++ b/src/ui/battle-info/enemy-battle-info.ts @@ -1,13 +1,13 @@ import { globalScene } from "#app/global-scene"; -import BattleFlyout from "../battle-flyout"; -import { addTextObject, TextStyle } from "#app/ui/text"; -import { addWindow, WindowVariant } from "#app/ui/ui-theme"; import { Stat } from "#enums/stat"; +import type { EnemyPokemon } from "#field/pokemon"; +import { BattleFlyout } from "#ui/battle-flyout"; +import type { BattleInfoParamList } from "#ui/battle-info"; +import { BattleInfo } from "#ui/battle-info"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; import i18next from "i18next"; -import type { EnemyPokemon } from "#app/field/pokemon"; import type { GameObjects } from "phaser"; -import BattleInfo from "./battle-info"; -import type { BattleInfoParamList } from "./battle-info"; export class EnemyBattleInfo extends BattleInfo { protected player: false = false; diff --git a/src/ui/battle-info/player-battle-info.ts b/src/ui/battle-info/player-battle-info.ts index 634f89b7922..62a2eddecb9 100644 --- a/src/ui/battle-info/player-battle-info.ts +++ b/src/ui/battle-info/player-battle-info.ts @@ -1,10 +1,10 @@ -import { getLevelRelExp, getLevelTotalExp } from "#app/data/exp"; -import type { PlayerPokemon } from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; +import { getLevelRelExp, getLevelTotalExp } from "#data/exp"; import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { Stat } from "#enums/stat"; -import BattleInfo from "./battle-info"; -import type { BattleInfoParamList } from "./battle-info"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { BattleInfoParamList } from "#ui/battle-info"; +import { BattleInfo } from "#ui/battle-info"; export class PlayerBattleInfo extends BattleInfo { protected player: true = true; diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index d1102bbe53e..bd524f0bb43 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -1,14 +1,14 @@ import { globalScene } from "#app/global-scene"; -import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import MessageUiHandler from "./message-ui-handler"; -import { addWindow } from "./ui-theme"; -import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Button } from "#enums/buttons"; +import { getStatKey, PERMANENT_STATS } from "#enums/stat"; +import { UiMode } from "#enums/ui-mode"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; import i18next from "i18next"; -import { PERMANENT_STATS, getStatKey } from "#app/enums/stat"; +import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -export default class BattleMessageUiHandler extends MessageUiHandler { +export class BattleMessageUiHandler extends MessageUiHandler { private levelUpStatsContainer: Phaser.GameObjects.Container; private levelUpStatsIncrContent: Phaser.GameObjects.Text; private levelUpStatsValuesContent: BBCodeText; @@ -96,7 +96,6 @@ export default class BattleMessageUiHandler extends MessageUiHandler { const levelUpStatsLabelsContent = addTextObject(globalScene.game.canvas.width / 6 - 73, -94, "", TextStyle.WINDOW, { maxLines: 6, }); - levelUpStatsLabelsContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); let levelUpStatsLabelText = ""; for (const s of PERMANENT_STATS) { @@ -123,7 +122,6 @@ export default class BattleMessageUiHandler extends MessageUiHandler { TextStyle.WINDOW, { maxLines: 6 }, ); - levelUpStatsIncrContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); levelUpStatsContainer.add(levelUpStatsIncrContent); this.levelUpStatsIncrContent = levelUpStatsIncrContent; @@ -135,7 +133,6 @@ export default class BattleMessageUiHandler extends MessageUiHandler { TextStyle.WINDOW, { maxLines: 6, lineSpacing: 5 }, ); - levelUpStatsValuesContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); levelUpStatsValuesContent.setOrigin(1, 0); levelUpStatsValuesContent.setAlign("right"); levelUpStatsContainer.add(levelUpStatsValuesContent); diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index e331d82f6d9..d8b6bbe8b8a 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -1,13 +1,13 @@ -import { addTextObject, TextStyle } from "./text"; -import i18next from "i18next"; -import { formatText } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; +import { addTextObject, TextStyle } from "#ui/text"; +import { formatText } from "#utils/common"; +import i18next from "i18next"; const hiddenX = -150; const shownX = 0; const baseY = 0; -export default class BgmBar extends Phaser.GameObjects.Container { +export class BgmBar extends Phaser.GameObjects.Container { private defaultWidth: number; private defaultHeight: number; diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 189a418eec8..ea3500d6c4c 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -1,11 +1,11 @@ -import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; -import { TextStyle, addTextObject } from "./text"; -import { argbFromRgba } from "@material/material-color-utilities"; -import { rgbHexToRgba } from "#app/utils/common"; +import { starterColors } from "#app/global-vars/starter-colors"; import type { SpeciesId } from "#enums/species-id"; +import { addTextObject, TextStyle } from "#ui/text"; +import { rgbHexToRgba } from "#utils/common"; +import { argbFromRgba } from "@material/material-color-utilities"; -export default class CandyBar extends Phaser.GameObjects.Container { +export class CandyBar extends Phaser.GameObjects.Container { private bg: Phaser.GameObjects.NineSlice; private candyIcon: Phaser.GameObjects.Sprite; private candyOverlayIcon: Phaser.GameObjects.Sprite; diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index b02bf4abaef..a827cddc9a7 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -1,20 +1,20 @@ -import { TextStyle, addTextObject } from "./text"; -import type { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { addWindow } from "./ui-theme"; -import { Button } from "#enums/buttons"; -import i18next from "i18next"; -import type { Challenge } from "#app/data/challenge"; -import { getLocalizedSpriteKey } from "#app/utils/common"; -import { Challenges } from "#app/enums/challenges"; -import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { Color, ShadowColor } from "#app/enums/color"; import { globalScene } from "#app/global-scene"; +import type { Challenge } from "#data/challenge"; +import { Button } from "#enums/buttons"; +import { Challenges } from "#enums/challenges"; +import { Color, ShadowColor } from "#enums/color"; +import type { UiMode } from "#enums/ui-mode"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import { getLocalizedSpriteKey } from "#utils/common"; +import i18next from "i18next"; +import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; /** * Handles all the UI for choosing optional challenges. */ -export default class GameChallengesUiHandler extends UiHandler { +export class GameChallengesUiHandler extends UiHandler { private challengesContainer: Phaser.GameObjects.Container; private valuesContainer: Phaser.GameObjects.Container; @@ -82,7 +82,7 @@ export default class GameChallengesUiHandler extends UiHandler { headerBg.setName("window-header-bg"); headerBg.setOrigin(0, 0); - const headerText = addTextObject(0, 0, i18next.t("challenges:title"), TextStyle.SETTINGS_LABEL); + const headerText = addTextObject(0, 0, i18next.t("challenges:title"), TextStyle.HEADER_LABEL); headerText.setName("text-header"); headerText.setOrigin(0, 0); headerText.setPositionRelative(headerBg, 8, 4); diff --git a/src/ui/char-sprite.ts b/src/ui/char-sprite.ts index a8451f4bb9c..381421086ff 100644 --- a/src/ui/char-sprite.ts +++ b/src/ui/char-sprite.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import { MissingTextureKey } from "#app/utils/common"; +import { MissingTextureKey } from "#utils/common"; -export default class CharSprite extends Phaser.GameObjects.Container { +export class CharSprite extends Phaser.GameObjects.Container { private sprite: Phaser.GameObjects.Sprite; private transitionSprite: Phaser.GameObjects.Sprite; diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 8df399b6d9b..2e4acfb7c42 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -1,19 +1,19 @@ -import { addTextObject, TextStyle } from "./text"; -import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import i18next from "i18next"; -import { Button } from "#enums/buttons"; -import { getPokemonNameWithAffix } from "#app/messages"; -import type { CommandPhase } from "#app/phases/command-phase"; import { globalScene } from "#app/global-scene"; -import { TerastallizeAccessModifier } from "#app/modifier/modifier"; -import { PokemonType } from "#enums/pokemon-type"; -import { getTypeRgb } from "#app/data/type"; -import { SpeciesId } from "#enums/species-id"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { getTypeRgb } from "#data/type"; +import { Button } from "#enums/buttons"; import { Command } from "#enums/command"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { TerastallizeAccessModifier } from "#modifiers/modifier"; +import type { CommandPhase } from "#phases/command-phase"; +import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import i18next from "i18next"; -export default class CommandUiHandler extends UiHandler { +export class CommandUiHandler extends UiHandler { private commandsContainer: Phaser.GameObjects.Container; private cursorObj: Phaser.GameObjects.Image | null; @@ -53,7 +53,12 @@ export default class CommandUiHandler extends UiHandler { this.commandsContainer.add(this.teraButton); for (let c = 0; c < commands.length; c++) { - const commandText = addTextObject(c % 2 === 0 ? 0 : 55.8, c < 2 ? 0 : 16, commands[c], TextStyle.WINDOW); + const commandText = addTextObject( + c % 2 === 0 ? 0 : 55.8, + c < 2 ? 0 : 16, + commands[c], + TextStyle.WINDOW_BATTLE_COMMAND, + ); commandText.setName(commands[c]); this.commandsContainer.add(commandText); } diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index 7b5ca3d7e63..b2f35931278 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -1,11 +1,11 @@ -import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; -import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; -import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; +import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; +import i18next from "i18next"; -export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { +export class ConfirmUiHandler extends AbstractOptionSelectUiHandler { public static readonly windowWidth: number = 48; private switchCheck: boolean; diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index e19882b09cc..dcd45b40390 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -1,9 +1,10 @@ -import i18next from "i18next"; +import { pokerogueApi } from "#api/pokerogue-api"; import { globalScene } from "#app/global-scene"; -import { getEnumKeys, executeIf } from "#app/utils/common"; -import { TextStyle, addTextObject } from "./text"; -import { WindowVariant, addWindow } from "./ui-theme"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import { executeIf } from "#utils/common"; +import { getEnumKeys } from "#utils/enums"; +import i18next from "i18next"; export interface RankingEntry { rank: number; @@ -41,24 +42,15 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.setup(); } - /** - * Sets the updating state and updates button states accordingly. - * If value is true (updating), disables the buttons; if false, enables the buttons. - * @param {boolean} value - The new updating state. - */ - set isUpdating(value) { + /** When set to `true`, disables the buttons; when set to `false`, enables the buttons. */ + get isUpdating(): boolean { + return this._isUpdating; + } + set isUpdating(value: boolean) { this._isUpdating = value; this.setButtonsState(!value); } - /** - * Gets the current updating state. - * @returns {boolean} - The current updating state. - */ - get isUpdating() { - return this._isUpdating; - } - setup() { const titleWindow = addWindow(0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN); this.add(titleWindow); diff --git a/src/ui/dropdown.ts b/src/ui/dropdown.ts index 2cbd1f0dfa9..2a100ddbe59 100644 --- a/src/ui/dropdown.ts +++ b/src/ui/dropdown.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import { addTextObject, TextStyle } from "./text"; -import { addWindow, WindowVariant } from "./ui-theme"; -import { ScrollBar } from "#app/ui/scroll-bar"; +import { ScrollBar } from "#ui/scroll-bar"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; import i18next from "i18next"; export enum DropDownState { diff --git a/src/ui/egg-counter-container.ts b/src/ui/egg-counter-container.ts index 7bb32189681..ff536228fde 100644 --- a/src/ui/egg-counter-container.ts +++ b/src/ui/egg-counter-container.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; -import { addWindow } from "./ui-theme"; -import { addTextObject, TextStyle } from "./text"; -import type { EggCountChangedEvent } from "#app/events/egg"; -import { EggEventType } from "#app/events/egg"; -import type EggHatchSceneHandler from "./egg-hatch-scene-handler"; +import type { EggCountChangedEvent } from "#events/egg"; +import { EggEventType } from "#events/egg"; +import type { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; /** * A container that displays the count of hatching eggs. * @extends Phaser.GameObjects.Container */ -export default class EggCounterContainer extends Phaser.GameObjects.Container { +export class EggCounterContainer extends Phaser.GameObjects.Container { private readonly WINDOW_DEFAULT_WIDTH = 37; private readonly WINDOW_MEDIUM_WIDTH = 42; private readonly WINDOW_HEIGHT = 26; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 0c8d90fa138..19d1efa75dd 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,21 +1,22 @@ -import { UiMode } from "#enums/ui-mode"; -import { TextStyle, addTextObject, getEggTierTextTint, getTextStyleOptions } from "./text"; -import MessageUiHandler from "./message-ui-handler"; -import { getEnumValues, getEnumKeys, fixedInt, randSeedShuffle } from "#app/utils/common"; -import type { IEggOptions } from "../data/egg"; -import { Egg, getLegendaryGachaSpeciesForTimestamp } from "../data/egg"; -import { VoucherType, getVoucherTypeIcon } from "../system/voucher"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { addWindow } from "./ui-theme"; -import { Tutorial, handleTutorial } from "../tutorial"; -import { Button } from "#enums/buttons"; -import Overrides from "#app/overrides"; -import { GachaType } from "#app/enums/gacha-types"; -import i18next from "i18next"; -import { EggTier } from "#enums/egg-type"; import { globalScene } from "#app/global-scene"; +import Overrides from "#app/overrides"; +import { handleTutorial, Tutorial } from "#app/tutorial"; +import type { IEggOptions } from "#data/egg"; +import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#data/egg"; +import { Button } from "#enums/buttons"; +import { EggTier } from "#enums/egg-type"; +import { GachaType } from "#enums/gacha-types"; +import { UiMode } from "#enums/ui-mode"; +import { getVoucherTypeIcon, VoucherType } from "#system/voucher"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { addTextObject, getEggTierTextTint, getTextStyleOptions, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, randSeedShuffle } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; -export default class EggGachaUiHandler extends MessageUiHandler { +export class EggGachaUiHandler extends MessageUiHandler { private eggGachaContainer: Phaser.GameObjects.Container; private eggGachaMessageBox: Phaser.GameObjects.NineSlice; private eggGachaOptionsContainer: Phaser.GameObjects.Container; @@ -38,6 +39,9 @@ export default class EggGachaUiHandler extends MessageUiHandler { private summaryFinished: boolean; private defaultText: string; + /** The tween chain playing the egg drop animation sequence */ + private eggDropTweenChain?: Phaser.Tweens.TweenChain; + private scale = 0.1666666667; private legendaryExpiration = addTextObject(0, 0, "", TextStyle.WINDOW_ALT); @@ -55,18 +59,124 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.defaultText = i18next.t("egg:selectMachine"); } + private setupGachaType(key: keyof typeof GachaType, gachaType: GachaType): void { + const gachaTypeKey = key.toLowerCase(); + const gachaContainer = globalScene.add.container(180 * gachaType, 18); + + const gacha = globalScene.add.sprite(0, 0, `gacha_${gachaTypeKey}`).setOrigin(0); + + const gachaUnderlay = globalScene.add.sprite(115, 80, `gacha_underlay_${gachaTypeKey}`).setOrigin(0); + + const gachaEggs = globalScene.add.sprite(0, 0, "gacha_eggs").setOrigin(0); + + const gachaGlass = globalScene.add.sprite(0, 0, "gacha_glass").setOrigin(0); + + const gachaInfoContainer = globalScene.add.container(160, 46); + + const currentLanguage = i18next.resolvedLanguage ?? "en"; + let gachaTextStyle = TextStyle.WINDOW_ALT; + let gachaX = 4; + let gachaY = 0; + let pokemonIconX = -20; + let pokemonIconY = 6; + + if (["de", "es-ES", "es-MX", "fr", "ko", "pt-BR", "ja", "ru"].includes(currentLanguage)) { + gachaTextStyle = TextStyle.SMALLER_WINDOW_ALT; + gachaX = 2; + gachaY = 2; + } + + let legendaryLabelX = gachaX; + let legendaryLabelY = gachaY; + if (["de", "es-ES", "es-MX"].includes(currentLanguage)) { + pokemonIconX = -25; + pokemonIconY = 10; + legendaryLabelX = -6; + legendaryLabelY = 0; + } + + const gachaUpLabel = addTextObject(gachaX, gachaY, i18next.t("egg:legendaryUPGacha"), gachaTextStyle).setOrigin(0); + gachaInfoContainer.add(gachaUpLabel); + + switch (gachaType as GachaType) { + case GachaType.LEGENDARY: + { + if (["de", "es-ES"].includes(currentLanguage)) { + gachaUpLabel.setAlign("center"); + } + let xOffset = 0; + const pokemonIcon = globalScene.add.sprite(pokemonIconX, pokemonIconY, "pokemon_icons_0"); + + // Intentionally left as "array includes" instead of an equality check to allow for future languages to reuse + if (["pt-BR"].includes(currentLanguage)) { + xOffset = 2; + pokemonIcon.setX(pokemonIconX - 2); + } + + gachaUpLabel.setX(legendaryLabelX - xOffset).setY(legendaryLabelY); + pokemonIcon.setScale(0.5).setOrigin(0, 0.5); + gachaInfoContainer.add(pokemonIcon); + } + break; + case GachaType.MOVE: + if (["de", "es-ES", "fr", "pt-BR", "ru"].includes(currentLanguage)) { + gachaUpLabel.setAlign("center").setY(0); + } + + gachaUpLabel.setText(i18next.t("egg:moveUPGacha")).setX(0).setOrigin(0.5, 0); + break; + case GachaType.SHINY: + if (["de", "fr", "ko", "ru"].includes(currentLanguage)) { + gachaUpLabel.setAlign("center").setY(0); + } + + gachaUpLabel.setText(i18next.t("egg:shinyUPGacha")).setX(0).setOrigin(0.5, 0); + break; + } + + const gachaKnob = globalScene.add.sprite(191, 89, "gacha_knob"); + + const gachaHatch = globalScene.add.sprite(115, 73, "gacha_hatch"); + gachaHatch.setOrigin(0).setAlpha(0.9); + gachaGlass.setAlpha(0.5); + gachaContainer.add([gachaEggs, gachaUnderlay, gacha, gachaGlass, gachaKnob, gachaHatch, gachaInfoContainer]); + + gachaHatch.on("animationupdate", (_anim, frame) => + gachaUnderlay.setFrame(frame.textureFrame === "4.png" ? "open_hatch" : "default"), + ); + + this.gachaContainers.push(gachaContainer); + this.gachaKnobs.push(gachaKnob); + this.gachaHatches.push(gachaHatch); + this.gachaInfoContainers.push(gachaInfoContainer); + + this.eggGachaContainer.add(gachaContainer); + + if (gachaType === GachaType.LEGENDARY) { + // Expiration timer for the legendary gacha + this.legendaryExpiration + .setText(this.getLegendaryGachaTimeLeft()) + .setFontSize("64px") + .setPositionRelative( + gacha, + gacha.width / 2 - this.legendaryExpiration.displayWidth / 2 + 0.3, + gacha.height / 2 + 12.5, + ); + gachaContainer.add(this.legendaryExpiration); + this.updateLegendaryGacha(); + } + } + setup() { this.gachaCursor = 0; this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale; const ui = this.getUi(); - this.eggGachaContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); - this.eggGachaContainer.setVisible(false); + this.eggGachaContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6).setVisible(false); ui.add(this.eggGachaContainer); - const bg = globalScene.add.nineslice(0, 0, "default_bg", undefined, 320, 180, 0, 0, 16, 0); - bg.setOrigin(0, 0); + const bg = globalScene.add.nineslice(0, 0, "default_bg", undefined, 320, 180, 0, 0, 16, 0).setOrigin(0); this.eggGachaContainer.add(bg); @@ -86,144 +196,10 @@ export default class EggGachaUiHandler extends MessageUiHandler { }); } - getEnumValues(GachaType).forEach((gachaType, g) => { - const gachaTypeKey = GachaType[gachaType].toString().toLowerCase(); - const gachaContainer = globalScene.add.container(180 * g, 18); + for (const [gachaTypeKey, gachaType] of Object.entries(GachaType)) { + this.setupGachaType(gachaTypeKey as keyof typeof GachaType, gachaType); + } - const gacha = globalScene.add.sprite(0, 0, `gacha_${gachaTypeKey}`); - gacha.setOrigin(0, 0); - - const gachaUnderlay = globalScene.add.sprite(115, 80, `gacha_underlay_${gachaTypeKey}`); - gachaUnderlay.setOrigin(0, 0); - - const gachaEggs = globalScene.add.sprite(0, 0, "gacha_eggs"); - gachaEggs.setOrigin(0, 0); - - const gachaGlass = globalScene.add.sprite(0, 0, "gacha_glass"); - gachaGlass.setOrigin(0, 0); - - const gachaInfoContainer = globalScene.add.container(160, 46); - - const currentLanguage = i18next.resolvedLanguage ?? "en"; - let gachaTextStyle = TextStyle.WINDOW_ALT; - let gachaX = 4; - let gachaY = 0; - let pokemonIconX = -20; - let pokemonIconY = 6; - - if (["de", "es-ES", "es-MX", "fr", "ko", "pt-BR", "ru"].includes(currentLanguage)) { - gachaTextStyle = TextStyle.SMALLER_WINDOW_ALT; - gachaX = 2; - gachaY = 2; - } - - let legendaryLabelX = gachaX; - let legendaryLabelY = gachaY; - if (["de", "es-ES", "es-MX"].includes(currentLanguage)) { - pokemonIconX = -25; - pokemonIconY = 10; - legendaryLabelX = -6; - legendaryLabelY = 0; - } - - const gachaUpLabel = addTextObject(gachaX, gachaY, i18next.t("egg:legendaryUPGacha"), gachaTextStyle); - gachaUpLabel.setOrigin(0, 0); - gachaInfoContainer.add(gachaUpLabel); - - switch (gachaType as GachaType) { - case GachaType.LEGENDARY: { - if (["de", "es-ES"].includes(currentLanguage)) { - gachaUpLabel.setAlign("center"); - gachaUpLabel.setY(0); - } - if (["pt-BR"].includes(currentLanguage)) { - gachaUpLabel.setX(legendaryLabelX - 2); - } else { - gachaUpLabel.setX(legendaryLabelX); - } - gachaUpLabel.setY(legendaryLabelY); - - const pokemonIcon = globalScene.add.sprite(pokemonIconX, pokemonIconY, "pokemon_icons_0"); - if (["pt-BR"].includes(currentLanguage)) { - pokemonIcon.setX(pokemonIconX - 2); - } - pokemonIcon.setScale(0.5); - pokemonIcon.setOrigin(0, 0.5); - - gachaInfoContainer.add(pokemonIcon); - break; - } - case GachaType.MOVE: - if (["de", "es-ES", "fr", "pt-BR", "ru"].includes(currentLanguage)) { - gachaUpLabel.setAlign("center"); - gachaUpLabel.setY(0); - } - - gachaUpLabel.setText(i18next.t("egg:moveUPGacha")); - gachaUpLabel.setX(0); - gachaUpLabel.setOrigin(0.5, 0); - break; - case GachaType.SHINY: - if (["de", "fr", "ko", "ru"].includes(currentLanguage)) { - gachaUpLabel.setAlign("center"); - gachaUpLabel.setY(0); - } - - gachaUpLabel.setText(i18next.t("egg:shinyUPGacha")); - gachaUpLabel.setX(0); - gachaUpLabel.setOrigin(0.5, 0); - break; - } - - const gachaKnob = globalScene.add.sprite(191, 89, "gacha_knob"); - - const gachaHatch = globalScene.add.sprite(115, 73, "gacha_hatch"); - gachaHatch.setOrigin(0, 0); - - gachaContainer.add(gachaEggs); - gachaContainer.add(gachaUnderlay); - gachaContainer.add(gacha); - gachaContainer.add(gachaGlass); - gachaContainer.add(gachaKnob); - gachaContainer.add(gachaHatch); - gachaContainer.add(gachaInfoContainer); - - gachaGlass.setAlpha(0.5); - gachaHatch.setAlpha(0.9); - - gachaHatch.on("animationupdate", (_anim, frame) => - gachaUnderlay.setFrame(frame.textureFrame === "4.png" ? "open_hatch" : "default"), - ); - - this.gachaContainers.push(gachaContainer); - this.gachaKnobs.push(gachaKnob); - this.gachaHatches.push(gachaHatch); - this.gachaInfoContainers.push(gachaInfoContainer); - - this.eggGachaContainer.add(gachaContainer); - - // Expiration timer for the legendary gacha - if (gachaType === GachaType.LEGENDARY) { - this.legendaryExpiration - .setText(this.getLegendaryGachaTimeLeft()) - .setFontSize("64px") - .setPositionRelative( - gacha, - gacha.width / 2 - this.legendaryExpiration.displayWidth / 2 + 0.3, - gacha.height / 2 + 12.5, - ); - gachaContainer.add(this.legendaryExpiration); - } - - this.updateGachaInfo(g); - }); - - this.eggGachaOptionsContainer = globalScene.add.container(); - - this.eggGachaOptionsContainer = globalScene.add.container(globalScene.game.canvas.width / 6, 148); - this.eggGachaContainer.add(this.eggGachaOptionsContainer); - - // Increase egg box width on certain languages let eggGachaOptionSelectWidth = 0; switch (i18next.resolvedLanguage) { case "ru": @@ -233,9 +209,11 @@ export default class EggGachaUiHandler extends MessageUiHandler { eggGachaOptionSelectWidth = 96; } - this.eggGachaOptionSelectBg = addWindow(0, 0, eggGachaOptionSelectWidth, 16 + 576 * this.scale); - this.eggGachaOptionSelectBg.setOrigin(1, 1); - this.eggGachaOptionsContainer.add(this.eggGachaOptionSelectBg); + this.eggGachaOptionSelectBg = addWindow(0, 0, eggGachaOptionSelectWidth, 16 + 576 * this.scale).setOrigin(1); + this.eggGachaOptionsContainer = globalScene.add + .container(globalScene.game.canvas.width / 6, 148) + .add(this.eggGachaOptionSelectBg); + this.eggGachaContainer.add(this.eggGachaOptionsContainer); const multiplierOne = "x1"; const multiplierTen = "x10"; @@ -275,75 +253,66 @@ export default class EggGachaUiHandler extends MessageUiHandler { desc[0] += ["zh", "ko"].includes(resolvedLanguage.substring(0, 2)) ? " " : " "; } if (option.multiplier === multiplierOne) { - desc[0] = " " + desc[0]; + desc[0] += " "; } return ` ${option.multiplier.padEnd(5)}${desc.join(" ")}`; }) .join("\n"); - const optionText = addTextObject(0, 0, `${pullOptionsText}\n${i18next.t("menu:cancel")}`, TextStyle.WINDOW); - - optionText.setLineSpacing(28); - optionText.setFontSize("80px"); + const optionText = addTextObject(0, 0, `${pullOptionsText}\n${i18next.t("menu:cancel")}`, TextStyle.WINDOW) + .setLineSpacing(28) + .setFontSize("80px") + .setPositionRelative(this.eggGachaOptionSelectBg, 16, 9); this.eggGachaOptionsContainer.add(optionText); - optionText.setPositionRelative(this.eggGachaOptionSelectBg, 16, 9); - pullOptions.forEach((option, i) => { - const icon = globalScene.add.sprite(0, 0, "items", option.icon); - icon.setScale(3 * this.scale); - icon.setPositionRelative(this.eggGachaOptionSelectBg, 20, 9 + (48 + i * 96) * this.scale); + const icon = globalScene.add + .sprite(0, 0, "items", option.icon) + .setScale(3 * this.scale) + .setPositionRelative(this.eggGachaOptionSelectBg, 20, 9 + (48 + i * 96) * this.scale); this.eggGachaOptionsContainer.add(icon); }); this.eggGachaContainer.add(this.eggGachaOptionsContainer); - new Array(getEnumKeys(VoucherType).length).fill(null).map((_, i) => { - const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * i, 0); + for (const voucher of getEnumValues(VoucherType)) { + const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * voucher, 0); - const bg = addWindow(0, 0, 56, 22); - bg.setOrigin(1, 0); + const bg = addWindow(0, 0, 56, 22).setOrigin(1, 0); container.add(bg); - const countLabel = addTextObject(-48, 3, "0", TextStyle.WINDOW); - countLabel.setOrigin(0, 0); + const countLabel = addTextObject(-48, 3, "0", TextStyle.WINDOW).setOrigin(0); container.add(countLabel); this.voucherCountLabels.push(countLabel); - const iconImage = getVoucherTypeIcon(i as VoucherType); + const iconImage = getVoucherTypeIcon(voucher); - const icon = globalScene.add.sprite(-19, 2, "items", iconImage); - icon.setOrigin(0, 0); - icon.setScale(0.5); + const icon = globalScene.add.sprite(-19, 2, "items", iconImage).setOrigin(0).setScale(0.5); container.add(icon); this.eggGachaContainer.add(container); - }); + } - this.eggGachaOverlay = globalScene.add.rectangle(0, 0, bg.displayWidth, bg.displayHeight, 0x000000); - this.eggGachaOverlay.setOrigin(0, 0); - this.eggGachaOverlay.setAlpha(0); + this.eggGachaOverlay = globalScene.add + .rectangle(0, 0, bg.displayWidth, bg.displayHeight, 0x000000) + .setOrigin(0) + .setAlpha(0); this.eggGachaContainer.add(this.eggGachaOverlay); - this.eggGachaSummaryContainer = globalScene.add.container(0, 0); - this.eggGachaSummaryContainer.setVisible(false); + this.eggGachaSummaryContainer = globalScene.add.container().setVisible(false); this.eggGachaContainer.add(this.eggGachaSummaryContainer); - const gachaMessageBoxContainer = globalScene.add.container(0, 148); - - const gachaMessageBox = addWindow(0, 0, 320, 32); - gachaMessageBox.setOrigin(0, 0); - gachaMessageBoxContainer.add(gachaMessageBox); + const gachaMessageBox = addWindow(0, 0, 320, 32).setOrigin(0); + const gachaMessageBoxContainer = globalScene.add.container(0, 148).add(gachaMessageBox); this.eggGachaMessageBox = gachaMessageBox; const gachaMessageText = addTextObject(8, 8, "", TextStyle.WINDOW, { maxLines: 2, - }); - gachaMessageText.setOrigin(0, 0); + }).setOrigin(0); gachaMessageBoxContainer.add(gachaMessageText); this.message = gachaMessageText; @@ -363,18 +332,17 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.setGachaCursor(1); - for (let g = 0; g < this.gachaContainers.length; g++) { - this.updateGachaInfo(g); - } + this.updateLegendaryGacha(); this.updateVoucherCounts(); this.getUi().bringToTop(this.eggGachaContainer); - this.eggGachaContainer.setVisible(true); + this.eggGachaContainer.setActive(true).setVisible(true); handleTutorial(Tutorial.Egg_Gacha); + this.legendaryExpiration.setText(this.getLegendaryGachaTimeLeft()); this.legendaryGachaTimer(); return true; @@ -387,142 +355,188 @@ export default class EggGachaUiHandler extends MessageUiHandler { return fixedInt(delay); } - pull(pullCount = 0, count = 0, eggs?: Egg[]): void { - if (Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE && !count) { + private firstDropAnims(): Phaser.Types.Tweens.TweenBuilderConfig[] { + globalScene.playSound("se/gacha_dial"); + return [ + // Tween 1 animates the gacha knob turning left + { + targets: this.gachaKnobs[this.gachaCursor], + duration: this.getDelayValue(350), + angle: 90, + ease: "Cubic.easeInOut", + }, + // Tween 2 animates the gacha knob turning back + { + targets: this.gachaKnobs[this.gachaCursor], + duration: this.getDelayValue(350), + angle: 0, + ease: "Sine.easeInOut", + }, + // Tween 3 is a dummy tween, used to force a delay, that commences the gacha running sound + { + targets: { dummy: 0 }, + dummy: 1, + duration: this.getDelayValue(350), + onStart: () => { + globalScene.playSound("se/gacha_running", { loop: true }); + }, + }, + // Tween 4 is another dummy tween that plays the gacha dispense sound + { + delay: this.getDelayValue(1250), + onStart: () => { + globalScene.playSound("se/gacha_dispense"); + }, + targets: { dummy: 0 }, + dummy: 1, + duration: this.getDelayValue(750), + onComplete: () => { + globalScene.sound.stopByKey("se/gacha_running"); + }, + }, + ]; + } + + private async doPullAnim(egg: Phaser.GameObjects.Sprite, count: number): Promise { + let resolve: (value: void | PromiseLike) => void; + const hatch = this.gachaHatches[this.gachaCursor]; + + /** The rate of animations and tweens that play for drops after the first */ + const rate = count ? 1.25 : 1.0; + if (count) { + hatch.anims.timeScale = rate; + } + const promise: Promise = new Promise(res => { + resolve = res; + }); + + const tweens: Phaser.Types.Tweens.TweenBuilderConfig[] = count ? [] : this.firstDropAnims(); + + tweens.push( + // Tween 1 is responsible for animating the egg dropping from the gacha + { + targets: egg, + duration: this.getDelayValue(350 / rate), + y: 95, + ease: "Bounce.easeOut", + }, + // Tween 2 plays the catch sound and moves the egg up a bit + { + onStart: () => { + globalScene.playSound("se/pb_catch"); + this.gachaHatches[this.gachaCursor].play("open"); + }, + targets: egg, + delay: this.getDelayValue(125 / rate), + duration: this.getDelayValue(350 / rate), + props: { + scale: { value: 0.75, ease: "Sine.easeIn" }, + y: { value: 110, ease: "Back.easeOut" }, + }, + }, + // Tween 3 "closes" the gacha hatch and moves the egg up while enlarging it + { + onStart: () => { + this.gachaHatches[this.gachaCursor].play("close"); + }, + targets: egg, + y: 200, + duration: this.getDelayValue(350 / rate), + ease: "Cubic.easeIn", + }, + ); + + this.eggDropTweenChain = globalScene.tweens.chain({ + onComplete: () => { + this.eggDropTweenChain = undefined; + hatch.anims.timeScale = 1; // Reset the hatch animation time scale + resolve(); + }, + tweens, + }); + + return promise; + } + + /** + * Pulls the specified number of eggs and returns them + * @param pullCount - The number of eggs to pull + * @returns An array of the pulled eggs + */ + private pullEggs(pullCount: number): Egg[] { + const eggs: Egg[] = []; + for (let i = 1; i <= pullCount; i++) { + const eggOptions: IEggOptions = { + pulled: true, + sourceType: this.gachaCursor, + }; + + // Before creating the last egg, check if the guaranteed egg tier was already generated + // if not, override the egg tier + if (i === pullCount) { + const guaranteedEggTier = this.getGuaranteedEggTierFromPullCount(pullCount); + if (guaranteedEggTier !== EggTier.COMMON && !eggs.some(egg => egg.tier >= guaranteedEggTier)) { + eggOptions.tier = guaranteedEggTier; + } + } + + const egg = new Egg(eggOptions); + eggs.push(egg); + } + // Shuffle the eggs in case the guaranteed one got added as last egg + return randSeedShuffle(eggs); + } + + /** + * Handle pulling eggs from the gacha machine; plays the animations, adds the eggs, and saves game data + * @param pullCount - The number of eggs to pull + */ + async pull(pullCount = 0): Promise { + if (Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE) { pullCount = Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE; } + // Set the eggs + const eggs = this.pullEggs(pullCount); + this.eggGachaOptionsContainer.setVisible(false); this.setTransitioning(true); - const doPull = () => { - if (this.transitionCancelled) { - return this.showSummary(eggs!); + const saveSuccess = await (globalScene.currentBattle + ? globalScene.gameData.saveAll(true, true, true) + : globalScene.gameData.saveSystem() + ).then(success => { + if (!success) { + globalScene.reset(true); + return false; } + return true; + }); - const egg = globalScene.add.sprite(127, 75, "egg", `egg_${eggs![count].getKey()}`); - egg.setScale(0.5); - - this.gachaContainers[this.gachaCursor].add(egg); - this.gachaContainers[this.gachaCursor].moveTo(egg, 2); - - const doPullAnim = () => { - globalScene.playSound("se/gacha_running", { loop: true }); - globalScene.time.delayedCall(this.getDelayValue(count ? 500 : 1250), () => { - globalScene.playSound("se/gacha_dispense"); - globalScene.time.delayedCall(this.getDelayValue(750), () => { - globalScene.sound.stopByKey("se/gacha_running"); - globalScene.tweens.add({ - targets: egg, - duration: this.getDelayValue(350), - y: 95, - ease: "Bounce.easeOut", - onComplete: () => { - globalScene.time.delayedCall(this.getDelayValue(125), () => { - globalScene.playSound("se/pb_catch"); - this.gachaHatches[this.gachaCursor].play("open"); - globalScene.tweens.add({ - targets: egg, - duration: this.getDelayValue(350), - scale: 0.75, - ease: "Sine.easeIn", - }); - globalScene.tweens.add({ - targets: egg, - y: 110, - duration: this.getDelayValue(350), - ease: "Back.easeOut", - onComplete: () => { - this.gachaHatches[this.gachaCursor].play("close"); - globalScene.tweens.add({ - targets: egg, - y: 200, - duration: this.getDelayValue(350), - ease: "Cubic.easeIn", - onComplete: () => { - if (++count < pullCount) { - this.pull(pullCount, count, eggs); - } else { - this.showSummary(eggs!); - } - }, - }); - }, - }); - }); - }, - }); - }); - }); - }; - - if (!count) { - globalScene.playSound("se/gacha_dial"); - globalScene.tweens.add({ - targets: this.gachaKnobs[this.gachaCursor], - duration: this.getDelayValue(350), - angle: 90, - ease: "Cubic.easeInOut", - onComplete: () => { - globalScene.tweens.add({ - targets: this.gachaKnobs[this.gachaCursor], - duration: this.getDelayValue(350), - angle: 0, - ease: "Sine.easeInOut", - }); - globalScene.time.delayedCall(this.getDelayValue(350), doPullAnim); - }, - }); - } else { - doPullAnim(); - } - }; - - if (!pullCount) { - pullCount = 1; - } - if (!count) { - count = 0; - } - if (!eggs) { - eggs = []; - for (let i = 1; i <= pullCount; i++) { - const eggOptions: IEggOptions = { - pulled: true, - sourceType: this.gachaCursor, - }; - - // Before creating the last egg, check if the guaranteed egg tier was already generated - // if not, override the egg tier - if (i === pullCount) { - const guaranteedEggTier = this.getGuaranteedEggTierFromPullCount(pullCount); - if (!eggs.some(egg => egg.tier >= guaranteedEggTier) && guaranteedEggTier !== EggTier.COMMON) { - eggOptions.tier = guaranteedEggTier; - } - } - - const egg = new Egg(eggOptions); - eggs.push(egg); - } - // Shuffle the eggs in case the guaranteed one got added as last egg - eggs = randSeedShuffle(eggs); - - (globalScene.currentBattle - ? globalScene.gameData.saveAll(true, true, true) - : globalScene.gameData.saveSystem() - ).then(success => { - if (!success) { - return globalScene.reset(true); - } - doPull(); - }); + if (!saveSuccess) { return; } - doPull(); + const gachaContainer = this.gachaContainers[this.gachaCursor]; + for (let i = 0; i < pullCount; ++i) { + if (this.transitionCancelled) { + break; + } + const eggSprite = globalScene.add.sprite(127, 75, "egg", `egg_${eggs[i].getKey()}`).setScale(0.5); + gachaContainer.addAt(eggSprite, 2); + // biome-ignore lint/nursery/noAwaitInLoop: The point of this loop is to play the animations, one after another + await this.doPullAnim(eggSprite, i).finally(() => gachaContainer.remove(eggSprite, true)); + } + + this.showSummary(eggs); } - getGuaranteedEggTierFromPullCount(pullCount: number): EggTier { + /** + * Get the guaranteed egg tier based on the pull count + * @param pullCount - The number of pulls made + * @returns The guaranteed egg tier for the given pull count + */ + private getGuaranteedEggTierFromPullCount(pullCount: number): EggTier { switch (pullCount) { case 10: return EggTier.RARE; @@ -611,9 +625,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { duration: this.getDelayValue(250), ease: "Cubic.easeIn", onComplete: () => { - this.eggGachaSummaryContainer.setVisible(false); - this.eggGachaSummaryContainer.setAlpha(1); - this.eggGachaSummaryContainer.removeAll(true); + this.eggGachaSummaryContainer.setVisible(false).setAlpha(1).removeAll(true); this.setTransitioning(false); this.summaryFinished = false; this.eggGachaOptionsContainer.setVisible(true); @@ -621,16 +633,14 @@ export default class EggGachaUiHandler extends MessageUiHandler { }); } - updateGachaInfo(gachaType: GachaType): void { - const infoContainer = this.gachaInfoContainers[gachaType]; - switch (gachaType as GachaType) { - case GachaType.LEGENDARY: { - const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(new Date().getTime())); - const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite; - pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false)); - break; - } - } + /** + * Update the legendary gacha icon based on the current timestamp. + */ + private updateLegendaryGacha(): void { + const infoContainer = this.gachaInfoContainers[GachaType.LEGENDARY]; + const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(Date.now())); + const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite; + pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false)); } consumeVouchers(voucherType: VoucherType, count: number): void { @@ -684,150 +694,165 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.transitionCancelled = false; } - processInput(button: Button): boolean { + /** + * Convert a cursor index to a voucher type and count + * @param cursor - The cursor index corresponding to the voucher type + * @returns The voucher type, vouchers used, and pulls given, or an empty array if the cursor is not on a voucher + */ + private static cursorToVoucher(cursor: number): [VoucherType, number, number] | undefined { + switch (cursor) { + case 0: + return [VoucherType.REGULAR, 1, 1]; + case 1: + return [VoucherType.REGULAR, 10, 10]; + case 2: + return [VoucherType.PLUS, 1, 5]; + case 3: + return [VoucherType.PREMIUM, 1, 10]; + case 4: + return [VoucherType.GOLDEN, 1, 25]; + } + } + + /** + * Process an action input received during voucher selection. + * + * @remarks + * + * Handles playing the error sound and showing the error message, but does not handle playing the success sound. + * + * @param cursor - The index of the voucher menu option + * @returns True if the success sound should be played, false if the error sound should be played, or undefined if the cursor is out of range. + */ + private handleVoucherSelectAction(cursor: number): boolean | undefined { + // Cursors that are out of range should not be processed + if (cursor < 0 || cursor > 5) { + return; + } const ui = this.getUi(); + const voucher = EggGachaUiHandler.cursorToVoucher(cursor); + if (!voucher) { + ui.revertMode(); + return true; + } + const [voucherType, vouchersConsumed, pulls] = voucher; - let success = false; - let error = false; + let errorKey: string | undefined; + const freePulls = Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE; - if (this.transitioning) { - if (!this.transitionCancelled && (button === Button.ACTION || button === Button.CANCEL)) { - this.transitionCancelled = true; + if (!freePulls && globalScene.gameData.eggs.length + pulls > 99) { + errorKey = "egg:tooManyEggs"; + } else if (!freePulls && !globalScene.gameData.voucherCounts[voucherType]) { + errorKey = "egg:notEnoughVouchers"; + } + + if (errorKey) { + this.showError(i18next.t(errorKey)); + return false; + } + + if (!freePulls) { + this.consumeVouchers(voucherType, vouchersConsumed); + } + + // TODO: Remove this dangling proimse if necessary when the UI's input event handling supports async functions + void this.pull(pulls); + return true; + } + + /** + * Process an input received while the egg gacha UI is transitioning + * + * @param button - The button that was pressed + * @returns - `true` if the success sound should be played, otherwise `undefined` + */ + private processTransitionInput(button: Button): true | undefined { + if (!this.transitionCancelled && (button === Button.ACTION || button === Button.CANCEL)) { + this.transitionCancelled = true; + // When transition is cancelled, ensure the active chain playing the egg drop animation is sped up + // We cannot cancel it, as this would leave sprite positions at their current position in the animation + this.eggDropTweenChain?.setTimeScale(50); + return true; + } + } + + /** + * Process an input received in the normal mode of the egg gacha UI (not transitoning, not summary) + * @param button - The button that was pressed + * @returns `true` if the success sound should be played, `false` if the error sound should be played, or `undefined` no input event occurred. + */ + private processNormalInput(button: Button): boolean | undefined { + const ui = this.getUi(); + let success: boolean | undefined; + switch (button) { + case Button.ACTION: + return this.handleVoucherSelectAction(this.cursor); + case Button.CANCEL: + ui.revertMode(); success = true; - } else { - return false; - } + break; + case Button.UP: + if (this.cursor) { + success = this.setCursor(this.cursor - 1); + } + break; + case Button.DOWN: + if (this.cursor < 5) { + success = this.setCursor(this.cursor + 1); + } + break; + case Button.LEFT: + if (this.gachaCursor) { + success = this.setGachaCursor(this.gachaCursor - 1); + } + break; + case Button.RIGHT: + if (this.gachaCursor < Object.keys(GachaType).length - 1) { + success = this.setGachaCursor(this.gachaCursor + 1); + } + break; + } + + // Return undefined here because we do not play error sound in case of failed directional movements + return success || undefined; + } + + /** + * Handles an input event that occurs while the egg gacha summary is visible + * @param button - The button that was pressed + * @returns `true` if an input event occurred and the select sound should be played, otherwise `undefined` + */ + private processSummaryInput(button: Button): true | undefined { + if (this.summaryFinished && (button === Button.ACTION || button === Button.CANCEL)) { + this.hideSummary(); + return true; + } + } + + /** + * + * @param button - The button that was pressed + * @returns - Whether an input event occured. + */ + processInput(button: Button): boolean { + let success: boolean | undefined; + if (this.transitioning) { + success = this.processTransitionInput(button); + } else if (this.eggGachaSummaryContainer.visible) { + success = this.processSummaryInput(button); } else { - if (this.eggGachaSummaryContainer.visible) { - if (this.summaryFinished && (button === Button.ACTION || button === Button.CANCEL)) { - this.hideSummary(); - success = true; - } - } else { - switch (button) { - case Button.ACTION: - switch (this.cursor) { - case 0: - if ( - !globalScene.gameData.voucherCounts[VoucherType.REGULAR] && - !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE - ) { - error = true; - this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (globalScene.gameData.eggs.length < 99 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { - if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - this.consumeVouchers(VoucherType.REGULAR, 1); - } - this.pull(); - success = true; - } else { - error = true; - this.showError(i18next.t("egg:tooManyEggs")); - } - break; - case 2: - if (!globalScene.gameData.voucherCounts[VoucherType.PLUS] && !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - error = true; - this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (globalScene.gameData.eggs.length < 95 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { - if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - this.consumeVouchers(VoucherType.PLUS, 1); - } - this.pull(5); - success = true; - } else { - error = true; - this.showError(i18next.t("egg:tooManyEggs")); - } - break; - case 1: - case 3: - if ( - (this.cursor === 1 && - globalScene.gameData.voucherCounts[VoucherType.REGULAR] < 10 && - !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) || - (this.cursor === 3 && - !globalScene.gameData.voucherCounts[VoucherType.PREMIUM] && - !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) - ) { - error = true; - this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (globalScene.gameData.eggs.length < 90 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { - if (this.cursor === 3) { - if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - this.consumeVouchers(VoucherType.PREMIUM, 1); - } - } else { - if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - this.consumeVouchers(VoucherType.REGULAR, 10); - } - } - this.pull(10); - success = true; - } else { - error = true; - this.showError(i18next.t("egg:tooManyEggs")); - } - break; - case 4: - if ( - !globalScene.gameData.voucherCounts[VoucherType.GOLDEN] && - !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE - ) { - error = true; - this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (globalScene.gameData.eggs.length < 75 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { - if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { - this.consumeVouchers(VoucherType.GOLDEN, 1); - } - this.pull(25); - success = true; - } else { - error = true; - this.showError(i18next.t("egg:tooManyEggs")); - } - break; - case 5: - ui.revertMode(); - success = true; - break; - } - break; - case Button.CANCEL: - this.getUi().revertMode(); - success = true; - break; - case Button.UP: - if (this.cursor) { - success = this.setCursor(this.cursor - 1); - } - break; - case Button.DOWN: - if (this.cursor < 5) { - success = this.setCursor(this.cursor + 1); - } - break; - case Button.LEFT: - if (this.gachaCursor) { - success = this.setGachaCursor(this.gachaCursor - 1); - } - break; - case Button.RIGHT: - if (this.gachaCursor < getEnumKeys(GachaType).length - 1) { - success = this.setGachaCursor(this.gachaCursor + 1); - } - break; - } - } + success = this.processNormalInput(button); } + if (success === undefined) { + return false; + } if (success) { - ui.playSelect(); - } else if (error) { - ui.playError(); + this.getUi().playSelect(); + } else { + this.getUi().playError(); } - - return success || error; + return true; } setCursor(cursor: number): boolean { @@ -898,5 +923,6 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.playTimeTimer.destroy(); this.playTimeTimer = null; } + this.eggGachaContainer.setActive(false); } } diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 85c4199ff1d..5b2c9d40cfa 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,9 +1,9 @@ -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { UiHandler } from "#ui/ui-handler"; -export default class EggHatchSceneHandler extends UiHandler { +export class EggHatchSceneHandler extends UiHandler { public eggHatchContainer: Phaser.GameObjects.Container; /** diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index 9a1b1f51e25..94d6889ed48 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -1,15 +1,15 @@ -import { UiMode } from "#enums/ui-mode"; -import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; -import { TextStyle, addTextObject } from "#app/ui/text"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import { addWindow } from "#app/ui/ui-theme"; -import { Button } from "#enums/buttons"; -import i18next from "i18next"; -import ScrollableGridUiHandler from "#app/ui/scrollable-grid-handler"; -import { ScrollBar } from "#app/ui/scroll-bar"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; +import { ScrollBar } from "#ui/scroll-bar"; +import { ScrollableGridUiHandler } from "#ui/scrollable-grid-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import i18next from "i18next"; -export default class EggListUiHandler extends MessageUiHandler { +export class EggListUiHandler extends MessageUiHandler { private readonly ROWS = 9; private readonly COLUMNS = 11; @@ -49,11 +49,11 @@ export default class EggListUiHandler extends MessageUiHandler { this.eggNameText = addTextObject(8, 68, "", TextStyle.SUMMARY).setOrigin(0); - this.eggDateText = addTextObject(8, 91, "", TextStyle.TOOLTIP_CONTENT); + this.eggDateText = addTextObject(8, 91, "", TextStyle.EGG_LIST); - this.eggHatchWavesText = addTextObject(8, 108, "", TextStyle.TOOLTIP_CONTENT).setWordWrapWidth(540); + this.eggHatchWavesText = addTextObject(8, 108, "", TextStyle.EGG_LIST).setWordWrapWidth(540); - this.eggGachaInfoText = addTextObject(8, 152, "", TextStyle.TOOLTIP_CONTENT).setWordWrapWidth(540); + this.eggGachaInfoText = addTextObject(8, 152, "", TextStyle.EGG_LIST).setWordWrapWidth(540); this.eggListIconContainer = globalScene.add.container(113, 5); diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index 90dc60e16b7..aa4e8974318 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -1,14 +1,14 @@ -import { UiMode } from "#enums/ui-mode"; -import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; -import MessageUiHandler from "./message-ui-handler"; -import { getEggTierForSpecies } from "../data/egg"; -import { Button } from "#enums/buttons"; -import PokemonHatchInfoContainer from "./pokemon-hatch-info-container"; -import type { EggHatchData } from "#app/data/egg-hatch-data"; -import ScrollableGridUiHandler from "./scrollable-grid-handler"; -import { HatchedPokemonContainer } from "./hatched-pokemon-container"; -import { ScrollBar } from "#app/ui/scroll-bar"; import { globalScene } from "#app/global-scene"; +import { getEggTierForSpecies } from "#data/egg"; +import type { EggHatchData } from "#data/egg-hatch-data"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { HatchedPokemonContainer } from "#ui/hatched-pokemon-container"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { PokemonHatchInfoContainer } from "#ui/pokemon-hatch-info-container"; +import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; +import { ScrollBar } from "#ui/scroll-bar"; +import { ScrollableGridUiHandler } from "#ui/scrollable-grid-handler"; const iconContainerX = 112; const iconContainerY = 9; @@ -21,7 +21,7 @@ const iconSize = 18; * Handles navigation and display of each pokemon as a list * Also handles display of the pokemon-hatch-info-container */ -export default class EggSummaryUiHandler extends MessageUiHandler { +export class EggSummaryUiHandler extends MessageUiHandler { /** holds all elements in the scene */ private eggHatchContainer: Phaser.GameObjects.Container; /** holds the icon containers and info container */ diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 7372fc6f2b5..5ad4fc6fdf5 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -1,10 +1,10 @@ -import MessageUiHandler from "./message-ui-handler"; -import { TextStyle, addTextObject } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; -export default class EvolutionSceneHandler extends MessageUiHandler { +export class EvolutionSceneHandler extends MessageUiHandler { public evolutionContainer: Phaser.GameObjects.Container; public messageBg: Phaser.GameObjects.Image; public messageContainer: Phaser.GameObjects.Container; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 14cd10d0d6f..9c08991e063 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -1,24 +1,23 @@ import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import { addTextObject, TextStyle } from "./text"; -import { getTypeDamageMultiplierColor } from "#app/data/type"; -import { PokemonType } from "#enums/pokemon-type"; -import { Command } from "#enums/command"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils/common"; -import { MoveCategory } from "#enums/MoveCategory"; -import i18next from "i18next"; -import { Button } from "#enums/buttons"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import type { CommandPhase } from "#app/phases/command-phase"; -import MoveInfoOverlay from "./move-info-overlay"; +import { getTypeDamageMultiplierColor } from "#data/type"; import { BattleType } from "#enums/battle-type"; +import { Button } from "#enums/buttons"; +import { Command } from "#enums/command"; +import { MoveCategory } from "#enums/MoveCategory"; import { MoveUseMode } from "#enums/move-use-mode"; +import { PokemonType } from "#enums/pokemon-type"; +import { UiMode } from "#enums/ui-mode"; +import type { EnemyPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonMove } from "#moves/pokemon-move"; +import type { CommandPhase } from "#phases/command-phase"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { fixedInt, getLocalizedSpriteKey, padInt } from "#utils/common"; +import i18next from "i18next"; -export default class FightUiHandler extends UiHandler implements InfoToggle { +export class FightUiHandler extends UiHandler implements InfoToggle { public static readonly MOVES_CONTAINER_NAME = "moves"; private movesContainer: Phaser.GameObjects.Container; @@ -42,62 +41,67 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { super(UiMode.FIGHT); } + /** + * Set the visibility of the objects in the move info container. + */ + private setInfoVis(visibility: boolean): void { + this.moveInfoContainer.iterate((o: Phaser.GameObjects.Components.Visible) => o.setVisible(visibility)); + } + setup() { const ui = this.getUi(); - this.movesContainer = globalScene.add.container(18, -38.7); - this.movesContainer.setName(FightUiHandler.MOVES_CONTAINER_NAME); + this.movesContainer = globalScene.add.container(18, -38.7).setName(FightUiHandler.MOVES_CONTAINER_NAME); ui.add(this.movesContainer); - this.moveInfoContainer = globalScene.add.container(1, 0); - this.moveInfoContainer.setName("move-info"); + this.moveInfoContainer = globalScene.add.container(1, 0).setName("move-info"); ui.add(this.moveInfoContainer); - this.typeIcon = globalScene.add.sprite( - globalScene.scaledCanvas.width - 57, - -36, - getLocalizedSpriteKey("types"), - "unknown", - ); - this.typeIcon.setVisible(false); - this.moveInfoContainer.add(this.typeIcon); + this.typeIcon = globalScene.add + .sprite(globalScene.scaledCanvas.width - 57, -36, getLocalizedSpriteKey("types"), "unknown") + .setVisible(false); - this.moveCategoryIcon = globalScene.add.sprite(globalScene.scaledCanvas.width - 25, -36, "categories", "physical"); - this.moveCategoryIcon.setVisible(false); - this.moveInfoContainer.add(this.moveCategoryIcon); + this.moveCategoryIcon = globalScene.add + .sprite(globalScene.scaledCanvas.width - 25, -36, "categories", "physical") + .setVisible(false); - this.ppLabel = addTextObject(globalScene.scaledCanvas.width - 70, -26, "PP", TextStyle.MOVE_INFO_CONTENT); - this.ppLabel.setOrigin(0.0, 0.5); - this.ppLabel.setVisible(false); - this.ppLabel.setText(i18next.t("fightUiHandler:pp")); - this.moveInfoContainer.add(this.ppLabel); + this.ppLabel = addTextObject(globalScene.scaledCanvas.width - 70, -26, "PP", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(0.0, 0.5) + .setVisible(false) + .setText(i18next.t("fightUiHandler:pp")); - this.ppText = addTextObject(globalScene.scaledCanvas.width - 12, -26, "--/--", TextStyle.MOVE_INFO_CONTENT); - this.ppText.setOrigin(1, 0.5); - this.ppText.setVisible(false); - this.moveInfoContainer.add(this.ppText); + this.ppText = addTextObject(globalScene.scaledCanvas.width - 12, -26, "--/--", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(1, 0.5) + .setVisible(false); - this.powerLabel = addTextObject(globalScene.scaledCanvas.width - 70, -18, "POWER", TextStyle.MOVE_INFO_CONTENT); - this.powerLabel.setOrigin(0.0, 0.5); - this.powerLabel.setVisible(false); - this.powerLabel.setText(i18next.t("fightUiHandler:power")); - this.moveInfoContainer.add(this.powerLabel); + this.powerLabel = addTextObject(globalScene.scaledCanvas.width - 70, -18, "POWER", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(0.0, 0.5) + .setVisible(false) + .setText(i18next.t("fightUiHandler:power")); - this.powerText = addTextObject(globalScene.scaledCanvas.width - 12, -18, "---", TextStyle.MOVE_INFO_CONTENT); - this.powerText.setOrigin(1, 0.5); - this.powerText.setVisible(false); - this.moveInfoContainer.add(this.powerText); + this.powerText = addTextObject(globalScene.scaledCanvas.width - 12, -18, "---", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(1, 0.5) + .setVisible(false); - this.accuracyLabel = addTextObject(globalScene.scaledCanvas.width - 70, -10, "ACC", TextStyle.MOVE_INFO_CONTENT); - this.accuracyLabel.setOrigin(0.0, 0.5); - this.accuracyLabel.setVisible(false); - this.accuracyLabel.setText(i18next.t("fightUiHandler:accuracy")); - this.moveInfoContainer.add(this.accuracyLabel); + this.accuracyLabel = addTextObject(globalScene.scaledCanvas.width - 70, -10, "ACC", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(0.0, 0.5) + .setVisible(false) + .setText(i18next.t("fightUiHandler:accuracy")); - this.accuracyText = addTextObject(globalScene.scaledCanvas.width - 12, -10, "---", TextStyle.MOVE_INFO_CONTENT); - this.accuracyText.setOrigin(1, 0.5); - this.accuracyText.setVisible(false); - this.moveInfoContainer.add(this.accuracyText); + this.accuracyText = addTextObject(globalScene.scaledCanvas.width - 12, -10, "---", TextStyle.MOVE_INFO_CONTENT) + .setOrigin(1, 0.5) + .setVisible(false); + + this.moveInfoContainer.add([ + this.typeIcon, + this.moveCategoryIcon, + this.ppLabel, + this.ppText, + this.powerLabel, + this.powerText, + this.accuracyLabel, + this.accuracyText, + ]); // prepare move overlay const overlayScale = 1; @@ -114,15 +118,14 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { }); ui.add(this.moveInfoOverlay); // register the overlay to receive toggle events - globalScene.addInfoToggle(this.moveInfoOverlay); - globalScene.addInfoToggle(this); + globalScene.addInfoToggle(this.moveInfoOverlay, this); } - show(args: any[]): boolean { + override show(args: [number?, Command?]): boolean { super.show(args); - this.fieldIndex = args.length ? (args[0] as number) : 0; - this.fromCommand = args.length > 1 ? (args[1] as Command) : Command.FIGHT; + this.fieldIndex = args[0] ?? 0; + this.fromCommand = args[1] ?? Command.FIGHT; const messageHandler = this.getUi().getMessageHandler(); messageHandler.bg.setVisible(false); @@ -132,7 +135,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { if (pokemon.tempSummonData.turnCount <= 1) { this.setCursor(0); } else { - this.setCursor(this.getCursor()); + this.setCursor(this.fieldIndex ? this.cursor2 : this.cursor); } this.displayMoves(); this.toggleInfo(false); // in case cancel was pressed while info toggle is active @@ -147,21 +150,10 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { */ processInput(button: Button): boolean { const ui = this.getUi(); - const cursor = this.getCursor(); let success = false; + const cursor = this.getCursor(); switch (button) { - case Button.CANCEL: - { - // Attempts to back out of the move selection pane are blocked in certain MEs - // TODO: Should we allow showing the summary menu at least? - const { battleType, mysteryEncounter } = globalScene.currentBattle; - if (battleType !== BattleType.MYSTERY_ENCOUNTER || !mysteryEncounter?.skipToFightInput) { - ui.setMode(UiMode.COMMAND, this.fieldIndex); - success = true; - } - } - break; case Button.ACTION: if ( (globalScene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand( @@ -175,6 +167,15 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { ui.playError(); } break; + case Button.CANCEL: { + // Cannot back out of fight menu if skipToFightInput is enabled + const { battleType, mysteryEncounter } = globalScene.currentBattle; + if (battleType !== BattleType.MYSTERY_ENCOUNTER || !mysteryEncounter?.skipToFightInput) { + ui.setMode(UiMode.COMMAND, this.fieldIndex); + success = true; + } + break; + } case Button.UP: if (cursor >= 2) { success = this.setCursor(cursor - 2); @@ -195,8 +196,6 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { success = this.setCursor(cursor + 1); } break; - default: - // other inputs do nothing while in fight menu } if (success) { @@ -206,21 +205,26 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { return success; } + /** + * Adjust the visibility of move names and the cursor icon when the info overlay is toggled + * @param visible - The visibility of the info overlay; the move names and cursor's visibility will be set to the opposite + */ toggleInfo(visible: boolean): void { + // The info overlay will already fade in, so we should hide the move name text and cursor immediately + // rather than adjusting alpha via a tween. if (visible) { - this.movesContainer.setVisible(false); - this.cursorObj?.setVisible(false); + this.movesContainer.setVisible(false).setAlpha(0); + this.cursorObj?.setVisible(false).setAlpha(0); + return; } globalScene.tweens.add({ targets: [this.movesContainer, this.cursorObj], duration: fixedInt(125), ease: "Sine.easeInOut", - alpha: visible ? 0 : 1, + alpha: 1, }); - if (!visible) { - this.movesContainer.setVisible(true); - this.cursorObj?.setVisible(true); - } + this.movesContainer.setVisible(true); + this.cursorObj?.setVisible(true); } isActive(): boolean { @@ -231,6 +235,64 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { return !this.fieldIndex ? this.cursor : this.cursor2; } + /** @returns TextStyle according to percentage of PP remaining */ + private static ppRatioToColor(ppRatio: number): TextStyle { + if (ppRatio > 0.25 && ppRatio <= 0.5) { + return TextStyle.MOVE_PP_HALF_FULL; + } + if (ppRatio > 0 && ppRatio <= 0.25) { + return TextStyle.MOVE_PP_NEAR_EMPTY; + } + if (ppRatio === 0) { + return TextStyle.MOVE_PP_EMPTY; + } + return TextStyle.MOVE_PP_FULL; // default to full if ppRatio is invalid + } + + /** + * Populate the move info overlay with the information of the move at the given cursor index + * @param cursor - The cursor position to set the move info for + */ + private setMoveInfo(cursor: number): void { + const pokemon = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon(); + const moveset = pokemon.getMoveset(); + + const hasMove = cursor < moveset.length; + this.setInfoVis(hasMove); + + if (!hasMove) { + return; + } + + const pokemonMove = moveset[cursor]; + const moveType = pokemon.getMoveType(pokemonMove.getMove()); + const textureKey = getLocalizedSpriteKey("types"); + this.typeIcon.setTexture(textureKey, PokemonType[moveType].toLowerCase()).setScale(0.8); + + const moveCategory = pokemonMove.getMove().category; + this.moveCategoryIcon.setTexture("categories", MoveCategory[moveCategory].toLowerCase()).setScale(1.0); + const power = pokemonMove.getMove().power; + const accuracy = pokemonMove.getMove().accuracy; + const maxPP = pokemonMove.getMovePp(); + const pp = maxPP - pokemonMove.ppUsed; + + const ppLeftStr = padInt(pp, 2, " "); + const ppMaxStr = padInt(maxPP, 2, " "); + this.ppText.setText(`${ppLeftStr}/${ppMaxStr}`); + this.powerText.setText(`${power >= 0 ? power : "---"}`); + this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`); + + const ppColorStyle = FightUiHandler.ppRatioToColor(pp / maxPP); + + //** Changes the text color and shadow according to the determined TextStyle */ + this.ppText.setColor(this.getTextColor(ppColorStyle, false)).setShadowColor(this.getTextColor(ppColorStyle, true)); + this.moveInfoOverlay.show(pokemonMove.getMove()); + + pokemon.getOpponents().forEach(opponent => { + (opponent as EnemyPokemon).updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove)); + }); + } + setCursor(cursor: number): boolean { const ui = this.getUi(); @@ -244,6 +306,8 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { } } + this.setMoveInfo(cursor); + if (!this.cursorObj) { const isTera = this.fromCommand === Command.TERA; this.cursorObj = globalScene.add.image(0, 0, isTera ? "cursor_tera" : "cursor"); @@ -251,61 +315,6 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { ui.add(this.cursorObj); } - const pokemon = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon(); - const moveset = pokemon.getMoveset(); - - const hasMove = cursor < moveset.length; - - if (hasMove) { - const pokemonMove = moveset[cursor]; - const moveType = pokemon.getMoveType(pokemonMove.getMove()); - const textureKey = getLocalizedSpriteKey("types"); - this.typeIcon.setTexture(textureKey, PokemonType[moveType].toLowerCase()).setScale(0.8); - - const moveCategory = pokemonMove.getMove().category; - this.moveCategoryIcon.setTexture("categories", MoveCategory[moveCategory].toLowerCase()).setScale(1.0); - const power = pokemonMove.getMove().power; - const accuracy = pokemonMove.getMove().accuracy; - const maxPP = pokemonMove.getMovePp(); - const pp = maxPP - pokemonMove.ppUsed; - - const ppLeftStr = padInt(pp, 2, " "); - const ppMaxStr = padInt(maxPP, 2, " "); - this.ppText.setText(`${ppLeftStr}/${ppMaxStr}`); - this.powerText.setText(`${power >= 0 ? power : "---"}`); - this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`); - - const ppPercentLeft = pp / maxPP; - - //** Determines TextStyle according to percentage of PP remaining */ - let ppColorStyle = TextStyle.MOVE_PP_FULL; - if (ppPercentLeft > 0.25 && ppPercentLeft <= 0.5) { - ppColorStyle = TextStyle.MOVE_PP_HALF_FULL; - } else if (ppPercentLeft > 0 && ppPercentLeft <= 0.25) { - ppColorStyle = TextStyle.MOVE_PP_NEAR_EMPTY; - } else if (ppPercentLeft === 0) { - ppColorStyle = TextStyle.MOVE_PP_EMPTY; - } - - //** Changes the text color and shadow according to the determined TextStyle */ - this.ppText.setColor(this.getTextColor(ppColorStyle, false)); - this.ppText.setShadowColor(this.getTextColor(ppColorStyle, true)); - this.moveInfoOverlay.show(pokemonMove.getMove()); - - pokemon.getOpponents().forEach(opponent => { - (opponent as EnemyPokemon).updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove)); - }); - } - - this.typeIcon.setVisible(hasMove); - this.ppLabel.setVisible(hasMove); - this.ppText.setVisible(hasMove); - this.powerLabel.setVisible(hasMove); - this.powerText.setVisible(hasMove); - this.accuracyLabel.setVisible(hasMove); - this.accuracyText.setVisible(hasMove); - this.moveCategoryIcon.setVisible(hasMove); - this.cursorObj.setPosition(13 + (cursor % 2 === 1 ? 114 : 0), -31 + (cursor >= 2 ? 15 : 0)); return changed; @@ -313,7 +322,6 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { /** * Gets multiplier text for a pokemon's move against a specific opponent - * Returns undefined if it's a status move */ private getEffectivenessText(pokemon: Pokemon, opponent: Pokemon, pokemonMove: PokemonMove): string | undefined { const effectiveness = opponent.getMoveEffectiveness( @@ -324,8 +332,11 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { undefined, true, ); - if (effectiveness === undefined) { - return undefined; + if (pokemonMove.getMove().category === MoveCategory.STATUS) { + if (effectiveness === 0) { + return "0x"; + } + return "1x"; } return `${effectiveness}x`; @@ -336,14 +347,19 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { const moveset = pokemon.getMoveset(); for (let moveIndex = 0; moveIndex < 4; moveIndex++) { - const moveText = addTextObject(moveIndex % 2 === 0 ? 0 : 114, moveIndex < 2 ? 0 : 16, "-", TextStyle.WINDOW); - moveText.setName("text-empty-move"); + const moveText = addTextObject( + moveIndex % 2 === 0 ? 0 : 114, + moveIndex < 2 ? 0 : 16, + "-", + TextStyle.WINDOW, + ).setName("text-empty-move"); if (moveIndex < moveset.length) { const pokemonMove = moveset[moveIndex]!; // TODO is the bang correct? - moveText.setText(pokemonMove.getName()); - moveText.setName(pokemonMove.getName()); - moveText.setColor(this.getMoveColor(pokemon, pokemonMove) ?? moveText.style.color); + moveText + .setText(pokemonMove.getName()) + .setName(pokemonMove.getName()) + .setColor(this.getMoveColor(pokemon, pokemonMove) ?? moveText.style.color); } this.movesContainer.add(moveText); @@ -377,7 +393,12 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { ), ) .sort((a, b) => b - a) - .map(effectiveness => getTypeDamageMultiplierColor(effectiveness ?? 0, "offense")); + .map(effectiveness => { + if (pokemonMove.getMove().category === MoveCategory.STATUS && effectiveness !== 0) { + return undefined; + } + return getTypeDamageMultiplierColor(effectiveness ?? 0, "offense"); + }); return moveColors[0]; } @@ -386,14 +407,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { super.clear(); const messageHandler = this.getUi().getMessageHandler(); this.clearMoves(); - this.typeIcon.setVisible(false); - this.ppLabel.setVisible(false); - this.ppText.setVisible(false); - this.powerLabel.setVisible(false); - this.powerText.setVisible(false); - this.accuracyLabel.setVisible(false); - this.accuracyText.setVisible(false); - this.moveCategoryIcon.setVisible(false); + this.setInfoVis(false); this.moveInfoOverlay.clear(); messageHandler.bg.setVisible(true); this.eraseCursor(); diff --git a/src/ui/filter-bar.ts b/src/ui/filter-bar.ts index 622488c04cd..3961ae3415c 100644 --- a/src/ui/filter-bar.ts +++ b/src/ui/filter-bar.ts @@ -1,11 +1,11 @@ -import type { DropDown } from "./dropdown"; -import { DropDownType } from "./dropdown"; -import type { StarterContainer } from "./starter-container"; -import { addTextObject, getTextColor, TextStyle } from "./text"; -import type { UiTheme } from "#enums/ui-theme"; -import { addWindow, WindowVariant } from "./ui-theme"; import { globalScene } from "#app/global-scene"; import type { DropDownColumn } from "#enums/drop-down-column"; +import type { UiTheme } from "#enums/ui-theme"; +import type { DropDown } from "#ui/dropdown"; +import { DropDownType } from "#ui/dropdown"; +import type { StarterContainer } from "#ui/starter-container"; +import { addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; export class FilterBar extends Phaser.GameObjects.Container { private window: Phaser.GameObjects.NineSlice; @@ -60,7 +60,7 @@ export class FilterBar extends Phaser.GameObjects.Container { this.columns.push(column); - const filterTypesLabel = addTextObject(0, 3, title, TextStyle.TOOLTIP_CONTENT); + const filterTypesLabel = addTextObject(0, 3, title, TextStyle.FILTER_BAR_MAIN); this.labels.push(filterTypesLabel); this.add(filterTypesLabel); this.dropDowns.push(dropDown); diff --git a/src/ui/filter-text.ts b/src/ui/filter-text.ts index 7e27a806478..4a9012e44fc 100644 --- a/src/ui/filter-text.ts +++ b/src/ui/filter-text.ts @@ -1,12 +1,12 @@ -import type { StarterContainer } from "./starter-container"; -import { addTextObject, getTextColor, TextStyle } from "./text"; -import type { UiTheme } from "#enums/ui-theme"; -import { addWindow, WindowVariant } from "./ui-theme"; -import i18next from "i18next"; -import type AwaitableUiHandler from "./awaitable-ui-handler"; -import type UI from "./ui"; -import { UiMode } from "#enums/ui-mode"; import { globalScene } from "#app/global-scene"; +import { UiMode } from "#enums/ui-mode"; +import type { UiTheme } from "#enums/ui-theme"; +import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import type { StarterContainer } from "#ui/starter-container"; +import { addTextObject, getTextColor, TextStyle } from "#ui/text"; +import type { UI } from "#ui/ui"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import i18next from "i18next"; export enum FilterTextRow { NAME, diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 8c30b4e0bc4..35965b09b80 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -1,12 +1,12 @@ -import type { ModalConfig } from "./modal-ui-handler"; -import { ModalUiHandler } from "./modal-ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import { TextStyle, addTextInputObject, addTextObject } from "./text"; -import { WindowVariant, addWindow } from "./ui-theme"; -import type InputText from "phaser3-rex-plugins/plugins/inputtext"; -import { fixedInt } from "#app/utils/common"; -import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import type { UiMode } from "#enums/ui-mode"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { ModalUiHandler } from "#ui/modal-ui-handler"; +import { addTextInputObject, addTextObject, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import { fixedInt } from "#utils/common"; +import type InputText from "phaser3-rex-plugins/plugins/inputtext"; export interface FormModalConfig extends ModalConfig { errorMessage?: string; diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 4213a244fdb..f9087ba7dfa 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -1,16 +1,16 @@ -import Phaser from "phaser"; -import { TextStyle, addTextObject } from "#app/ui/text"; -import type { UiMode } from "#enums/ui-mode"; -import UiHandler from "#app/ui/ui-handler"; -import { addWindow } from "#app/ui/ui-theme"; -import { getPlayTimeString, formatFancyLargeNumber, toReadableString } from "#app/utils/common"; -import type { GameData } from "#app/system/game-data"; -import { DexAttr } from "#enums/dex-attr"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import { Button } from "#enums/buttons"; -import i18next from "i18next"; -import { UiTheme } from "#enums/ui-theme"; import { globalScene } from "#app/global-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import { Button } from "#enums/buttons"; +import { DexAttr } from "#enums/dex-attr"; +import type { UiMode } from "#enums/ui-mode"; +import { UiTheme } from "#enums/ui-theme"; +import type { GameData } from "#system/game-data"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import { formatFancyLargeNumber, getPlayTimeString, toReadableString } from "#utils/common"; +import i18next from "i18next"; +import Phaser from "phaser"; interface DisplayStat { label_key?: string; @@ -213,7 +213,7 @@ const displayStats: DisplayStats = { }, }; -export default class GameStatsUiHandler extends UiHandler { +export class GameStatsUiHandler extends UiHandler { private gameStatsContainer: Phaser.GameObjects.Container; private statsContainer: Phaser.GameObjects.Container; @@ -243,7 +243,7 @@ export default class GameStatsUiHandler extends UiHandler { const headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24); headerBg.setOrigin(0, 0); - const headerText = addTextObject(0, 0, i18next.t("gameStatsUiHandler:stats"), TextStyle.SETTINGS_LABEL); + const headerText = addTextObject(0, 0, i18next.t("gameStatsUiHandler:stats"), TextStyle.HEADER_LABEL); headerText.setOrigin(0, 0); headerText.setPositionRelative(headerBg, 8, 4); @@ -267,10 +267,10 @@ export default class GameStatsUiHandler extends UiHandler { this.statsContainer = globalScene.add.container(0, 0); - new Array(18).fill(null).map((_, s) => { + for (let i = 0; i < 18; i++) { const statLabel = addTextObject( - 8 + (s % 2 === 1 ? statsBgWidth : 0), - 28 + Math.floor(s / 2) * 16, + 8 + (i % 2 === 1 ? statsBgWidth : 0), + 28 + Math.floor(i / 2) * 16, "", TextStyle.STATS_LABEL, ); @@ -278,11 +278,11 @@ export default class GameStatsUiHandler extends UiHandler { this.statsContainer.add(statLabel); this.statLabels.push(statLabel); - const statValue = addTextObject(statsBgWidth * ((s % 2) + 1) - 8, statLabel.y, "", TextStyle.STATS_VALUE); + const statValue = addTextObject(statsBgWidth * ((i % 2) + 1) - 8, statLabel.y, "", TextStyle.STATS_VALUE); statValue.setOrigin(1, 0); this.statsContainer.add(statValue); this.statValues.push(statValue); - }); + } this.gameStatsContainer.add(headerBg); this.gameStatsContainer.add(headerText); diff --git a/src/ui/hatched-pokemon-container.ts b/src/ui/hatched-pokemon-container.ts index 8b9a5309a9c..a5919348a94 100644 --- a/src/ui/hatched-pokemon-container.ts +++ b/src/ui/hatched-pokemon-container.ts @@ -1,11 +1,11 @@ -import type { EggHatchData } from "#app/data/egg-hatch-data"; -import { Gender } from "#app/data/gender"; -import { getVariantTint } from "#app/sprites/variant"; -import { DexAttr } from "#enums/dex-attr"; import { globalScene } from "#app/global-scene"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import type PokemonIconAnimHandler from "./pokemon-icon-anim-handler"; -import { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; +import type { EggHatchData } from "#data/egg-hatch-data"; +import { Gender } from "#data/gender"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { DexAttr } from "#enums/dex-attr"; +import { getVariantTint } from "#sprites/variant"; +import type { PokemonIconAnimHandler } from "#ui/pokemon-icon-anim-handler"; +import { PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; /** * A container for a Pokemon's sprite and icons to get displayed in the egg summary screen diff --git a/src/ui/loading-modal-ui-handler.ts b/src/ui/loading-modal-ui-handler.ts index 13dffe5614c..585d70d51db 100644 --- a/src/ui/loading-modal-ui-handler.ts +++ b/src/ui/loading-modal-ui-handler.ts @@ -1,9 +1,9 @@ -import i18next from "i18next"; -import { ModalUiHandler } from "./modal-ui-handler"; -import { addTextObject, TextStyle } from "./text"; import type { UiMode } from "#enums/ui-mode"; +import { ModalUiHandler } from "#ui/modal-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import i18next from "i18next"; -export default class LoadingModalUiHandler extends ModalUiHandler { +export class LoadingModalUiHandler extends ModalUiHandler { constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 5c48cf55753..417a9031bf7 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -1,14 +1,14 @@ -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; -import { fixedInt } from "#app/utils/common"; -import { UiMode } from "#enums/ui-mode"; -import i18next from "i18next"; -import { addTextObject, TextStyle } from "./text"; -import { addWindow } from "./ui-theme"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { pokerogueApi } from "#api/pokerogue-api"; import { globalScene } from "#app/global-scene"; +import { UiMode } from "#enums/ui-mode"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt } from "#utils/common"; +import i18next from "i18next"; import JSZip from "jszip"; interface BuildInteractableImageOpts { @@ -18,7 +18,7 @@ interface BuildInteractableImageOpts { origin?: { x: number; y: number }; } -export default class LoginFormUiHandler extends FormModalUiHandler { +export class LoginFormUiHandler extends FormModalUiHandler { private readonly ERR_USERNAME: string = "invalid username"; private readonly ERR_PASSWORD: string = "invalid password"; private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist"; diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 5ab1d4f9e96..4e45dfedcb3 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -1,22 +1,23 @@ -import { bypassLogin } from "#app/global-vars/bypass-login"; +import { pokerogueApi } from "#api/pokerogue-api"; +import { loggedInUser, updateUserInfo } from "#app/account"; import { globalScene } from "#app/global-scene"; -import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import { getEnumKeys, isLocal, fixedInt, sessionIdKey } from "#app/utils/common"; -import { isBeta } from "#app/utils/utility-vars"; -import { getCookie } from "#app/utils/cookies"; -import { addWindow, WindowVariant } from "./ui-theme"; -import MessageUiHandler from "./message-ui-handler"; -import type { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; -import { Tutorial, handleTutorial } from "../tutorial"; -import { loggedInUser, updateUserInfo } from "../account"; -import i18next from "i18next"; +import { bypassLogin } from "#app/global-vars/bypass-login"; +import { handleTutorial, Tutorial } from "#app/tutorial"; import { Button } from "#enums/buttons"; import { GameDataType } from "#enums/game-data-type"; -import BgmBar from "#app/ui/bgm-bar"; -import type AwaitableUiHandler from "./awaitable-ui-handler"; -import { AdminMode, getAdminModeName } from "./admin-ui-handler"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { UiMode } from "#enums/ui-mode"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { AdminMode, getAdminModeName } from "#ui/admin-ui-handler"; +import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import { BgmBar } from "#ui/bgm-bar"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { addTextObject, getTextStyleOptions, TextStyle } from "#ui/text"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import { fixedInt, isLocal, sessionIdKey } from "#utils/common"; +import { getCookie } from "#utils/cookies"; +import { getEnumValues } from "#utils/enums"; +import { isBeta } from "#utils/utility-vars"; +import i18next from "i18next"; enum MenuOptions { GAME_SETTINGS, @@ -37,7 +38,7 @@ const githubUrl = "https://github.com/pagefaultgames/pokerogue"; const redditUrl = "https://www.reddit.com/r/pokerogue"; const donateUrl = "https://github.com/sponsors/pagefaultgames"; -export default class MenuUiHandler extends MessageUiHandler { +export class MenuUiHandler extends MessageUiHandler { private readonly textPadding = 8; private readonly defaultMessageBoxWidth = 220; private readonly defaultWordWrapWidth = 1224; @@ -76,11 +77,9 @@ export default class MenuUiHandler extends MessageUiHandler { { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, ]; - this.menuOptions = getEnumKeys(MenuOptions) - .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) - .filter(m => { - return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); - }); + this.menuOptions = getEnumValues(MenuOptions).filter(m => { + return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); + }); } setup(): void { @@ -131,11 +130,9 @@ export default class MenuUiHandler extends MessageUiHandler { { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, ]; - this.menuOptions = getEnumKeys(MenuOptions) - .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) - .filter(m => { - return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); - }); + this.menuOptions = getEnumValues(MenuOptions).filter(m => { + return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); + }); this.optionSelectText = addTextObject( 0, @@ -511,11 +508,9 @@ export default class MenuUiHandler extends MessageUiHandler { this.render(); super.show(args); - this.menuOptions = getEnumKeys(MenuOptions) - .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) - .filter(m => { - return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); - }); + this.menuOptions = getEnumValues(MenuOptions).filter(m => { + return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); + }); this.menuContainer.setVisible(true); this.setCursor(0); diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index efa53b63808..57f054c9bf9 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -1,9 +1,9 @@ -import AwaitableUiHandler from "./awaitable-ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import { getFrameMs } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; +import type { UiMode } from "#enums/ui-mode"; +import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import { getFrameMs } from "#utils/common"; -export default abstract class MessageUiHandler extends AwaitableUiHandler { +export abstract class MessageUiHandler extends AwaitableUiHandler { protected textTimer: Phaser.Time.TimerEvent | null; protected textCallbackTimer: Phaser.Time.TimerEvent | null; public pendingPrompt: boolean; diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index 56c1c2c3fcf..844f7f43930 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -1,9 +1,9 @@ -import { TextStyle, addTextObject } from "./text"; -import type { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { WindowVariant, addWindow } from "./ui-theme"; -import type { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; +import type { Button } from "#enums/buttons"; +import type { UiMode } from "#enums/ui-mode"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; export interface ModalConfig { buttonActions: Function[]; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 7f5bf997f88..264804eb627 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -1,28 +1,28 @@ import { globalScene } from "#app/global-scene"; -import type { ModifierTypeOption } from "../modifier/modifier-type"; -import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "../modifier/modifier-type"; -import { getPokeballAtlasKey } from "#app/data/pokeball"; -import { addTextObject, getTextStyleOptions, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; -import AwaitableUiHandler from "./awaitable-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { LockModifierTiersModifier, PokemonHeldItemModifier, HealShopCostModifier } from "../modifier/modifier"; -import { handleTutorial, Tutorial } from "../tutorial"; -import { Button } from "#enums/buttons"; -import MoveInfoOverlay from "./move-info-overlay"; -import { allMoves } from "#app/data/data-lists"; -import { formatMoney, NumberHolder } from "#app/utils/common"; import Overrides from "#app/overrides"; -import i18next from "i18next"; -import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import Phaser from "phaser"; +import { handleTutorial, Tutorial } from "#app/tutorial"; +import { allMoves } from "#data/data-lists"; +import { getPokeballAtlasKey } from "#data/pokeball"; +import { Button } from "#enums/buttons"; import type { PokeballType } from "#enums/pokeball"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; +import { UiMode } from "#enums/ui-mode"; +import { HealShopCostModifier, LockModifierTiersModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { ModifierTypeOption } from "#modifiers/modifier-type"; +import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "#modifiers/modifier-type"; +import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { addTextObject, getModifierTierTextTint, getTextColor, getTextStyleOptions, TextStyle } from "#ui/text"; +import { formatMoney, NumberHolder } from "#utils/common"; +import i18next from "i18next"; +import Phaser from "phaser"; export const SHOP_OPTIONS_ROW_LIMIT = 7; const SINGLE_SHOP_ROW_YOFFSET = 12; const DOUBLE_SHOP_ROW_YOFFSET = 24; const OPTION_BUTTON_YPOSITION = -62; -export default class ModifierSelectUiHandler extends AwaitableUiHandler { +export class ModifierSelectUiHandler extends AwaitableUiHandler { private modifierContainer: Phaser.GameObjects.Container; private rerollButtonContainer: Phaser.GameObjects.Container; private lockRarityButtonContainer: Phaser.GameObjects.Container; @@ -269,13 +269,22 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { globalScene.updateBiomeWaveText(); globalScene.updateMoneyText(); + // DO NOT REMOVE: Fixes bug which allows action input to be processed before the UI is shown, + // causing errors if reroll is selected + this.awaitingActionInput = false; + + // TODO: Replace with `Promise.withResolvers` when possible. + let tweenResolve: () => void; + const tweenPromise = new Promise(resolve => (tweenResolve = resolve)); let i = 0; + // TODO: Rework this bespoke logic for animating the modifier options. globalScene.tweens.addCounter({ ease: "Sine.easeIn", duration: 1250, onUpdate: t => { - const value = t.getValue(); + // The bang here is safe, as `getValue()` only returns null if the tween has been destroyed (which obviously isn't the case inside onUpdate) + const value = t.getValue()!; const index = Math.floor(value * typeOptions.length); if (index > i && index <= typeOptions.length) { const option = this.options[i]; @@ -286,67 +295,77 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { i++; } }, + onComplete: () => { + tweenResolve(); + }, }); - globalScene.time.delayedCall(1000 + maxUpgradeCount * 2000, () => { - for (const shopOption of this.shopOptionsRows.flat()) { - shopOption.show(0, 0); - } + let shopResolve: () => void; + const shopPromise = new Promise(resolve => (shopResolve = resolve)); + tweenPromise.then(() => { + globalScene.time.delayedCall(1000, () => { + for (const shopOption of this.shopOptionsRows.flat()) { + shopOption.show(0, 0); + } + shopResolve(); + }); }); - globalScene.time.delayedCall(4000 + maxUpgradeCount * 2000, () => { - if (partyHasHeldItem) { - this.transferButtonContainer.setAlpha(0); - this.transferButtonContainer.setVisible(true); + shopPromise.then(() => { + globalScene.time.delayedCall(500, () => { + if (partyHasHeldItem) { + this.transferButtonContainer.setAlpha(0); + this.transferButtonContainer.setVisible(true); + globalScene.tweens.add({ + targets: this.transferButtonContainer, + alpha: 1, + duration: 250, + }); + } + + this.rerollButtonContainer.setAlpha(0); + this.checkButtonContainer.setAlpha(0); + this.lockRarityButtonContainer.setAlpha(0); + this.continueButtonContainer.setAlpha(0); + this.rerollButtonContainer.setVisible(true); + this.checkButtonContainer.setVisible(true); + this.continueButtonContainer.setVisible(this.rerollCost < 0); + this.lockRarityButtonContainer.setVisible(canLockRarities); + globalScene.tweens.add({ - targets: this.transferButtonContainer, + targets: [this.checkButtonContainer, this.continueButtonContainer], alpha: 1, duration: 250, }); - } - this.rerollButtonContainer.setAlpha(0); - this.checkButtonContainer.setAlpha(0); - this.lockRarityButtonContainer.setAlpha(0); - this.continueButtonContainer.setAlpha(0); - this.rerollButtonContainer.setVisible(true); - this.checkButtonContainer.setVisible(true); - this.continueButtonContainer.setVisible(this.rerollCost < 0); - this.lockRarityButtonContainer.setVisible(canLockRarities); + globalScene.tweens.add({ + targets: [this.rerollButtonContainer, this.lockRarityButtonContainer], + alpha: this.rerollCost < 0 ? 0.5 : 1, + duration: 250, + }); - globalScene.tweens.add({ - targets: [this.checkButtonContainer, this.continueButtonContainer], - alpha: 1, - duration: 250, - }); + const updateCursorTarget = () => { + if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { + this.setRowCursor(0); + this.setCursor(2); + } else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) { + this.setRowCursor(ShopCursorTarget.REWARDS); + this.setCursor(0); + } else { + this.setRowCursor(globalScene.shopCursorTarget); + this.setCursor(0); + } + }; - globalScene.tweens.add({ - targets: [this.rerollButtonContainer, this.lockRarityButtonContainer], - alpha: this.rerollCost < 0 ? 0.5 : 1, - duration: 250, - }); + updateCursorTarget(); - const updateCursorTarget = () => { - if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { - this.setRowCursor(0); - this.setCursor(2); - } else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) { - this.setRowCursor(ShopCursorTarget.REWARDS); - this.setCursor(0); - } else { - this.setRowCursor(globalScene.shopCursorTarget); - this.setCursor(0); - } - }; - - updateCursorTarget(); - - handleTutorial(Tutorial.Select_Item).then(res => { - if (res) { - updateCursorTarget(); - } - this.awaitingActionInput = true; - this.onActionInput = args[2]; + handleTutorial(Tutorial.Select_Item).then(res => { + if (res) { + updateCursorTarget(); + } + this.awaitingActionInput = true; + this.onActionInput = args[2]; + }); }); }); @@ -687,7 +706,11 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { scale: 0.01, duration: 250, ease: "Cubic.easeIn", - onComplete: () => options.forEach(o => o.destroy()), + onComplete: () => { + options.forEach(o => { + o.destroy(); + }); + }, }); [ @@ -819,7 +842,7 @@ class ModifierOption extends Phaser.GameObjects.Container { if (!globalScene) { return; } - const value = t.getValue(); + const value = t.getValue()!; if (!bounce && value > lastValue) { globalScene.playSound("se/pb_bounce_1", { volume: 1 / ++bounceCount, @@ -832,41 +855,38 @@ class ModifierOption extends Phaser.GameObjects.Container { }, }); + // TODO: Figure out proper delay between chains and then convert this into a single tween chain + // rather than starting multiple tween chains. for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) { - const upgradeIndex = u; - globalScene.time.delayedCall( - remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (upgradeIndex + 1 + upgradeCountOffset)), - () => { - globalScene.playSound("se/upgrade", { - rate: 1 + 0.25 * upgradeIndex, - }); - this.pbTint.setPosition(this.pb.x, this.pb.y); - this.pbTint.setTintFill(0xffffff); - this.pbTint.setAlpha(0); - this.pbTint.setVisible(true); - globalScene.tweens.add({ + globalScene.tweens.chain({ + tweens: [ + { + delay: remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (u + 1 + upgradeCountOffset)), + onStart: () => { + globalScene.playSound("se/upgrade", { + rate: 1 + 0.25 * u, + }); + this.pbTint.setPosition(this.pb.x, this.pb.y).setTintFill(0xffffff).setVisible(true).setAlpha(0); + }, targets: this.pbTint, alpha: 1, duration: 1000, ease: "Sine.easeIn", onComplete: () => { - this.pb.setTexture( - "pb", - this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (upgradeIndex + 1)), - ); - globalScene.tweens.add({ - targets: this.pbTint, - alpha: 0, - duration: 750, - ease: "Sine.easeOut", - onComplete: () => { - this.pbTint.setVisible(false); - }, - }); + this.pb.setTexture("pb", this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (u + 1))); }, - }); - }, - ); + }, + { + targets: this.pbTint, + alpha: 0, + duration: 750, + ease: "Sine.easeOut", + onComplete: () => { + this.pbTint.setVisible(false); + }, + }, + ], + }); } } diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 2b230d609fd..1747021b7ec 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -1,11 +1,11 @@ import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import { TextStyle, addTextObject } from "./text"; -import { addWindow } from "./ui-theme"; -import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common"; -import type Move from "../data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; import { PokemonType } from "#enums/pokemon-type"; +import type { Move } from "#moves/move"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, getLocalizedSpriteKey } from "#utils/common"; import i18next from "i18next"; export interface MoveInfoOverlaySettings { @@ -30,7 +30,7 @@ const DESC_HEIGHT = 48; const BORDER = 8; const GLOBAL_SCALE = 6; -export default class MoveInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle { +export class MoveInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle { public active = false; private move: Move; @@ -80,7 +80,6 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem }, }, ); - this.desc.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); // limit the text rendering, required for scrolling later on const maskPointOrigin = { diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 83ce88714f5..37f0efb50e4 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -1,22 +1,22 @@ -import { addBBCodeTextObject, getBBCodeFrag, TextStyle } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; +import { globalScene } from "#app/global-scene"; +import { getPokeballAtlasKey } from "#data/pokeball"; import { Button } from "#enums/buttons"; -import { addWindow, WindowVariant } from "./ui-theme"; -import type { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; -import { PartyUiMode } from "./party-ui-handler"; -import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { fixedInt, isNullOrUndefined } from "#app/utils/common"; -import { getPokeballAtlasKey } from "../data/pokeball"; -import type { OptionSelectSettings } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import i18next from "i18next"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { UiMode } from "#enums/ui-mode"; +import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; +import type { OptionSelectSettings } from "#mystery-encounters/encounter-phase-utils"; +import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option"; +import type { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { PartyUiMode } from "#ui/party-ui-handler"; +import { addBBCodeTextObject, getBBCodeFrag, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow, WindowVariant } from "#ui/ui-theme"; +import { fixedInt, isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { globalScene } from "#app/global-scene"; -export default class MysteryEncounterUiHandler extends UiHandler { +export class MysteryEncounterUiHandler extends UiHandler { private cursorContainer: Phaser.GameObjects.Container; private cursorObj?: Phaser.GameObjects.Image; @@ -517,7 +517,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { descriptionTextObject.setMask(abilityDescriptionTextMask); - const descriptionLineCount = Math.floor(descriptionTextObject.displayHeight / 10); + const descriptionLineCount = Math.floor(descriptionTextObject.displayHeight / 9.2); if (this.descriptionScrollTween) { this.descriptionScrollTween.remove(); @@ -591,9 +591,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { // Auto-color options green/blue for good/bad by looking for (+)/(-) if (text) { - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; + const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0]; text = text.replace( - /(\(\+\)[^\(\[]*)/gi, + /(\(\+\)[^([]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + @@ -601,7 +601,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { primaryStyleString, ); text = text.replace( - /(\(\-\)[^\(\[]*)/gi, + /(\(-\)[^([]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + @@ -614,6 +614,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { const tooltipTextObject = addBBCodeTextObject(6, 7, text, TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 600 }, fontSize: "72px", + padding: { top: 8 }, + lineSpacing: 1.25, }); this.tooltipContainer.add(tooltipTextObject); @@ -627,7 +629,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { const textMask = tooltipTextMaskRect.createGeometryMask(); tooltipTextObject.setMask(textMask); - const tooltipLineCount = Math.floor(tooltipTextObject.displayHeight / 11.2); + const tooltipLineCount = Math.floor(tooltipTextObject.displayHeight / 10.2); if (this.tooltipScrollTween) { this.tooltipScrollTween.remove(); diff --git a/src/ui/party-exp-bar.ts b/src/ui/party-exp-bar.ts index 7b7f5277ca1..0d6ec936a92 100644 --- a/src/ui/party-exp-bar.ts +++ b/src/ui/party-exp-bar.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import type Pokemon from "../field/pokemon"; -import { TextStyle, addTextObject } from "./text"; +import type { Pokemon } from "#field/pokemon"; +import { addTextObject, TextStyle } from "#ui/text"; import i18next from "i18next"; -export default class PartyExpBar extends Phaser.GameObjects.Container { +export class PartyExpBar extends Phaser.GameObjects.Container { private bg: Phaser.GameObjects.NineSlice; private pokemonIcon: Phaser.GameObjects.Container; private expText: Phaser.GameObjects.Text; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index cf6333f4580..ce5f60813c7 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,33 +1,33 @@ -import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; -import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#app/ui/text"; -import { Command } from "#enums/command"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { BooleanHolder, toReadableString, randInt, getLocalizedSpriteKey } from "#app/utils/common"; -import type { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { allMoves } from "#app/data/data-lists"; -import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { StatusEffect } from "#enums/status-effect"; -import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { addWindow } from "#app/ui/ui-theme"; -import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { FormChangeItem } from "#enums/form-change-item"; -import { getVariantTint } from "#app/sprites/variant"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { applyChallenges } from "#data/challenge"; +import { allMoves } from "#data/data-lists"; +import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; +import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; import { Button } from "#enums/buttons"; -import { applyChallenges } from "#app/data/challenge"; import { ChallengeType } from "#enums/challenge-type"; -import MoveInfoOverlay from "#app/ui/move-info-overlay"; +import { Command } from "#enums/command"; +import { FormChangeItem } from "#enums/form-change-item"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { PokemonMove } from "#moves/pokemon-move"; +import type { CommandPhase } from "#phases/command-phase"; +import { getVariantTint } from "#sprites/variant"; +import type { TurnMove } from "#types/turn-move"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { BooleanHolder, getLocalizedSpriteKey, randInt, toReadableString } from "#utils/common"; import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { getPokemonNameWithAffix } from "#app/messages"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { globalScene } from "#app/global-scene"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); @@ -144,7 +144,7 @@ export type PokemonModifierTransferSelectFilter = ( ) => string | null; export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string | null; -export default class PartyUiHandler extends MessageUiHandler { +export class PartyUiHandler extends MessageUiHandler { private partyUiMode: PartyUiMode; private fieldIndex: number; @@ -1284,17 +1284,18 @@ export default class PartyUiHandler extends MessageUiHandler { const allowBatonModifierSwitch = this.allowBatonModifierSwitch(); const isBatonPassMove = this.isBatonPassMove(); + if (allowBatonModifierSwitch && !isBatonPassMove) { + // the BATON modifier gives an extra switch option for + // pokemon-command switches, allowing buffs to be optionally passed + this.options.push(PartyOption.PASS_BATON); + } + // isBatonPassMove and allowBatonModifierSwitch shouldn't ever be true // at the same time, because they both explicitly check for a mutually // exclusive partyUiMode. But better safe than sorry. this.options.push( isBatonPassMove && !allowBatonModifierSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT, ); - if (allowBatonModifierSwitch && !isBatonPassMove) { - // the BATON modifier gives an extra switch option for - // pokemon-command switches, allowing buffs to be optionally passed - this.options.push(PartyOption.PASS_BATON); - } } this.addCommonOptions(pokemon); if (this.partyUiMode === PartyUiMode.SWITCH) { @@ -1334,13 +1335,13 @@ export default class PartyUiHandler extends MessageUiHandler { this.addCommonOptions(pokemon); break; case PartyUiMode.CHECK: + this.addCommonOptions(pokemon); if (globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase")) { const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); for (let i = 0; i < formChangeItemModifiers.length; i++) { this.options.push(PartyOption.FORM_CHANGE_ITEM + i); } } - this.addCommonOptions(pokemon); break; case PartyUiMode.SELECT: this.options.push(PartyOption.SELECT); @@ -1937,7 +1938,7 @@ class PartyCancelButton extends Phaser.GameObjects.Container { this.partyCancelPb = partyCancelPb; - const partyCancelText = addTextObject(-8, -7, i18next.t("partyUiHandler:cancel"), TextStyle.PARTY); + const partyCancelText = addTextObject(-10, -7, i18next.t("partyUiHandler:cancel"), TextStyle.PARTY_CANCEL_BUTTON); this.add(partyCancelText); } diff --git a/src/ui/pokeball-tray.ts b/src/ui/pokeball-tray.ts index 0bf47699cdb..9720aa42090 100644 --- a/src/ui/pokeball-tray.ts +++ b/src/ui/pokeball-tray.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import type Pokemon from "../field/pokemon"; +import type { Pokemon } from "#field/pokemon"; -export default class PokeballTray extends Phaser.GameObjects.Container { +export class PokeballTray extends Phaser.GameObjects.Container { private player: boolean; private bg: Phaser.GameObjects.NineSlice; diff --git a/src/ui/pokedex-info-overlay.ts b/src/ui/pokedex-info-overlay.ts index 2e889f6d2a9..6d3b8f1009f 100644 --- a/src/ui/pokedex-info-overlay.ts +++ b/src/ui/pokedex-info-overlay.ts @@ -1,9 +1,8 @@ -import type { InfoToggle } from "../battle-scene"; -import { TextStyle, addTextObject } from "./text"; -import { addWindow } from "./ui-theme"; -import { fixedInt } from "#app/utils/common"; -import i18next from "i18next"; +import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt } from "#utils/common"; export interface PokedexInfoOverlaySettings { delayVisibility?: boolean; // if true, showing the overlay will only set it to active and populate the fields and the handler using this field has to manually call setVisible later. @@ -22,7 +21,7 @@ const DESC_HEIGHT = 48; const BORDER = 8; const GLOBAL_SCALE = 6; -export default class PokedexInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle { +export class PokedexInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle { public active = false; private desc: Phaser.GameObjects.Text; @@ -55,7 +54,6 @@ export default class PokedexInfoOverlay extends Phaser.GameObjects.Container imp this.desc = addTextObject(BORDER, BORDER - 2, "", TextStyle.BATTLE_INFO, { wordWrap: { width: (this.width - (BORDER - 2) * 2) * GLOBAL_SCALE }, }); - this.desc.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); // limit the text rendering, required for scrolling later on this.maskPointOriginX = options?.x || 0; diff --git a/src/ui/pokedex-mon-container.ts b/src/ui/pokedex-mon-container.ts index da79320850d..73799870e6b 100644 --- a/src/ui/pokedex-mon-container.ts +++ b/src/ui/pokedex-mon-container.ts @@ -1,8 +1,8 @@ -import type { Variant } from "#app/sprites/variant"; import { globalScene } from "#app/global-scene"; -import { isNullOrUndefined } from "#app/utils/common"; -import type PokemonSpecies from "../data/pokemon-species"; -import { addTextObject, TextStyle } from "./text"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import type { Variant } from "#sprites/variant"; +import { addTextObject, TextStyle } from "#ui/text"; +import { isNullOrUndefined } from "#utils/common"; interface SpeciesDetails { shiny?: boolean; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 32a88ab36b2..ff96aa55772 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -1,54 +1,58 @@ -import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; -import { pokemonEvolutions, pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions"; -import type { Variant } from "#app/sprites/variant"; -import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; -import { argbFromRgba } from "@material/material-color-utilities"; -import i18next from "i18next"; +import { globalScene } from "#app/global-scene"; import { starterColors } from "#app/global-vars/starter-colors"; -import { allAbilities } from "#app/data/data-lists"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; -import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/data-lists"; -import { getNatureName } from "#app/data/nature"; -import type { SpeciesFormChange } from "#app/data/pokemon-forms"; -import { pokemonFormChanges } from "#app/data/pokemon-forms"; -import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; -import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpeciesForm, normalForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; -import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; -import { starterPassiveAbilities } from "#app/data/balance/passives"; -import { PokemonType } from "#enums/pokemon-type"; -import type { StarterAttributes } from "#app/system/game-data"; -import type { DexEntry } from "#app/@types/dex-data"; -import { AbilityAttr } from "#enums/ability-attr"; -import { DexAttr } from "#enums/dex-attr"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import { StatsContainer } from "#app/ui/stats-container"; -import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor, getTextStyleOptions } from "#app/ui/text"; -import { UiMode } from "#enums/ui-mode"; -import { addWindow } from "#app/ui/ui-theme"; -import { Egg } from "#app/data/egg"; import Overrides from "#app/overrides"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { Passive as PassiveAttr } from "#enums/passive"; -import MoveInfoOverlay from "#app/ui/move-info-overlay"; -import PokedexInfoOverlay from "#app/ui/pokedex-info-overlay"; -import { getEggTierForSpecies } from "#app/data/egg"; -import { Device } from "#enums/devices"; -import type { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { Button } from "#enums/buttons"; -import { EggSourceType } from "#enums/egg-source-types"; +import type { BiomeTierTod } from "#balance/biomes"; +import { BiomePoolTier, catchableSpecies } from "#balance/biomes"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { starterPassiveAbilities } from "#balance/passives"; +import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions, pokemonPrevolutions, pokemonStarters } from "#balance/pokemon-evolutions"; +import type { LevelMoves } from "#balance/pokemon-level-moves"; +import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#balance/pokemon-level-moves"; import { getPassiveCandyCount, - getValueReductionCandyCounts, getSameSpeciesEggCandyCounts, -} from "#app/data/balance/starters"; + getStarterValueFriendshipCap, + getValueReductionCandyCounts, + speciesStarterCosts, +} from "#balance/starters"; +import { speciesTmMoves } from "#balance/tms"; +import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; +import { Egg, getEggTierForSpecies } from "#data/egg"; +import { GrowthRate, getGrowthRateColor } from "#data/exp"; +import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; +import { getNatureName } from "#data/nature"; +import type { SpeciesFormChange } from "#data/pokemon-forms"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getPokemonSpeciesForm, normalForm } from "#data/pokemon-species"; +import { AbilityAttr } from "#enums/ability-attr"; +import type { AbilityId } from "#enums/ability-id"; +import { BiomeId } from "#enums/biome-id"; +import { Button } from "#enums/buttons"; +import { Device } from "#enums/devices"; +import { DexAttr } from "#enums/dex-attr"; +import { EggSourceType } from "#enums/egg-source-types"; +import type { MoveId } from "#enums/move-id"; +import type { Nature } from "#enums/nature"; +import { Passive as PassiveAttr } from "#enums/passive"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { TimeOfDay } from "#enums/time-of-day"; +import { UiMode } from "#enums/ui-mode"; +import type { Variant } from "#sprites/variant"; +import { getVariantIcon, getVariantTint } from "#sprites/variant"; +import type { StarterAttributes } from "#system/game-data"; +import { SettingKeyboard } from "#system/settings-keyboard"; +import type { DexEntry } from "#types/dex-data"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { BaseStatsOverlay } from "#ui/base-stats-overlay"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { PokedexInfoOverlay } from "#ui/pokedex-info-overlay"; +import { StatsContainer } from "#ui/stats-container"; +import { addBBCodeTextObject, addTextObject, getTextColor, getTextStyleOptions, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; import { BooleanHolder, getLocalizedSpriteKey, @@ -56,17 +60,11 @@ import { padInt, rgbHexToRgba, toReadableString, -} from "#app/utils/common"; -import type { Nature } from "#enums/nature"; -import { getEnumKeys } from "#app/utils/common"; -import { speciesTmMoves } from "#app/data/balance/tms"; -import type { BiomeTierTod } from "#app/data/balance/biomes"; -import { BiomePoolTier, catchableSpecies } from "#app/data/balance/biomes"; -import { BiomeId } from "#enums/biome-id"; -import { TimeOfDay } from "#app/enums/time-of-day"; -import type { AbilityId } from "#enums/ability-id"; -import { BaseStatsOverlay } from "#app/ui/base-stats-overlay"; -import { globalScene } from "#app/global-scene"; +} from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { argbFromRgba } from "@material/material-color-utilities"; +import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; interface LanguageSetting { @@ -82,13 +80,21 @@ const languageSettings: { [key: string]: LanguageSetting } = { instructionTextSize: "38px", }, de: { - starterInfoTextSize: "48px", + starterInfoTextSize: "54px", instructionTextSize: "35px", - starterInfoXPos: 33, + starterInfoXPos: 35, }, "es-ES": { - starterInfoTextSize: "56px", - instructionTextSize: "35px", + starterInfoTextSize: "50px", + instructionTextSize: "38px", + starterInfoYOffset: 0.5, + starterInfoXPos: 38, + }, + "es-MX": { + starterInfoTextSize: "50px", + instructionTextSize: "38px", + starterInfoYOffset: 0.5, + starterInfoXPos: 38, }, fr: { starterInfoTextSize: "54px", @@ -98,34 +104,53 @@ const languageSettings: { [key: string]: LanguageSetting } = { starterInfoTextSize: "56px", instructionTextSize: "38px", }, - pt_BR: { - starterInfoTextSize: "47px", - instructionTextSize: "38px", + "pt-BR": { + starterInfoTextSize: "48px", + instructionTextSize: "42px", + starterInfoYOffset: 0.5, starterInfoXPos: 33, }, zh: { - starterInfoTextSize: "47px", - instructionTextSize: "38px", - starterInfoYOffset: 1, - starterInfoXPos: 24, - }, - pt: { - starterInfoTextSize: "48px", - instructionTextSize: "42px", - starterInfoXPos: 33, + starterInfoTextSize: "56px", + instructionTextSize: "36px", + starterInfoXPos: 26, }, ko: { - starterInfoTextSize: "52px", + starterInfoTextSize: "60px", instructionTextSize: "38px", + starterInfoYOffset: -0.5, + starterInfoXPos: 30, }, ja: { - starterInfoTextSize: "51px", - instructionTextSize: "38px", + starterInfoTextSize: "48px", + instructionTextSize: "40px", + starterInfoYOffset: 1, + starterInfoXPos: 32, }, - "ca-ES": { + ca: { + starterInfoTextSize: "48px", + instructionTextSize: "38px", + starterInfoYOffset: 0.5, + starterInfoXPos: 29, + }, + da: { starterInfoTextSize: "56px", instructionTextSize: "38px", }, + tr: { + starterInfoTextSize: "56px", + instructionTextSize: "38px", + }, + ro: { + starterInfoTextSize: "56px", + instructionTextSize: "38px", + }, + ru: { + starterInfoTextSize: "46px", + instructionTextSize: "38px", + starterInfoYOffset: 0.5, + starterInfoXPos: 26, + }, }; const valueReductionMax = 2; @@ -152,7 +177,7 @@ enum MenuOptions { EVOLUTIONS, } -export default class PokedexPageUiHandler extends MessageUiHandler { +export class PokedexPageUiHandler extends MessageUiHandler { private starterSelectContainer: Phaser.GameObjects.Container; private shinyOverlay: Phaser.GameObjects.Image; private pokemonNumberText: Phaser.GameObjects.Text; @@ -309,7 +334,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.shinyOverlay.setVisible(false); this.starterSelectContainer.add(this.shinyOverlay); - this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY); + this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY_DEX_NUM); this.pokemonNumberText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNumberText); @@ -328,7 +353,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.pokemonGrowthRateLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonGrowthRateLabelText); - this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.SUMMARY_PINK, { fontSize: "36px" }); + this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.GROWTH_RATE_TYPE, { fontSize: "36px" }); this.pokemonGrowthRateText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGrowthRateText); @@ -371,9 +396,15 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.pokemonLuckLabelText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonLuckLabelText); - this.pokemonLuckText = addTextObject(8 + this.pokemonLuckLabelText.displayWidth + 2, 89, "0", TextStyle.WINDOW, { - fontSize: "56px", - }); + this.pokemonLuckText = addTextObject( + 8 + this.pokemonLuckLabelText.displayWidth + 2, + 89, + "0", + TextStyle.LUCK_VALUE, + { + fontSize: "56px", + }, + ); this.pokemonLuckText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonLuckText); @@ -470,7 +501,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("pokedexUiHandler:candyUpgrade"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.candyUpgradeLabel.setName("text-candyUpgrade-label"); @@ -491,7 +522,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("pokedexUiHandler:cycleShiny"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.shinyLabel.setName("text-shiny-label"); @@ -510,7 +541,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("pokedexUiHandler:cycleForm"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.formLabel.setName("text-form-label"); @@ -529,7 +560,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("pokedexUiHandler:cycleGender"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.genderLabel.setName("text-gender-label"); @@ -548,7 +579,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("pokedexUiHandler:cycleVariant"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.variantLabel.setName("text-variant-label"); @@ -557,9 +588,15 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.showBackSpriteIconElement.setName("show-backSprite-icon-element"); this.showBackSpriteIconElement.setScale(0.675); this.showBackSpriteIconElement.setOrigin(0.0, 0.0); - this.showBackSpriteLabel = addTextObject(60, 7, i18next.t("pokedexUiHandler:showBackSprite"), TextStyle.PARTY, { - fontSize: instructionTextSize, - }); + this.showBackSpriteLabel = addTextObject( + 60, + 7, + i18next.t("pokedexUiHandler:showBackSprite"), + TextStyle.INSTRUCTIONS_TEXT, + { + fontSize: instructionTextSize, + }, + ); this.showBackSpriteLabel.setName("show-backSprite-label"); this.starterSelectContainer.add(this.showBackSpriteIconElement); this.starterSelectContainer.add(this.showBackSpriteLabel); @@ -603,7 +640,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.menuContainer.setVisible(false); - this.menuOptions = getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); + this.menuOptions = getEnumValues(MenuOptions); this.optionSelectText = addBBCodeTextObject( 0, @@ -707,7 +744,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.starterAttributes = this.initStarterPrefs(); - this.menuOptions = getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); + this.menuOptions = getEnumValues(MenuOptions); this.menuContainer.setVisible(true); @@ -1899,14 +1936,14 @@ export default class PokedexPageUiHandler extends MessageUiHandler { if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarterCosts[this.starterId]); options.push({ - label: `x${passiveCost} ${i18next.t("pokedexUiHandler:unlockPassive")}`, + label: `×${passiveCost} ${i18next.t("pokedexUiHandler:unlockPassive")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= passiveCost) { starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= passiveCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); globalScene.gameData.saveSystem().then(success => { if (!success) { return globalScene.reset(true); @@ -1931,14 +1968,14 @@ export default class PokedexPageUiHandler extends MessageUiHandler { if (valueReduction < valueReductionMax) { const reductionCost = getValueReductionCandyCounts(speciesStarterCosts[this.starterId])[valueReduction]; options.push({ - label: `x${reductionCost} ${i18next.t("pokedexUiHandler:reduceCost")}`, + label: `×${reductionCost} ${i18next.t("pokedexUiHandler:reduceCost")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= reductionCost) { starterData.valueReduction++; if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= reductionCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); globalScene.gameData.saveSystem().then(success => { if (!success) { return globalScene.reset(true); @@ -1960,7 +1997,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { // Same species egg menu option. const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[this.starterId]); options.push({ - label: `x${sameSpeciesEggCost} ${i18next.t("pokedexUiHandler:sameSpeciesEgg")}`, + label: `×${sameSpeciesEggCost} ${i18next.t("pokedexUiHandler:sameSpeciesEgg")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= sameSpeciesEggCost) { if (globalScene.gameData.eggs.length >= 99 && !Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { @@ -1979,7 +2016,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= sameSpeciesEggCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); const egg = new Egg({ scene: globalScene, @@ -2057,7 +2094,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } let newSpecies: PokemonSpecies; if (this.filteredIndices) { - const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); + const index = this.filteredIndices.indexOf(this.species.speciesId); const newIndex = index <= 0 ? this.filteredIndices.length - 1 : index - 1; newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); } else { @@ -2096,7 +2133,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } let newSpecies: PokemonSpecies; if (this.filteredIndices) { - const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); + const index = this.filteredIndices.indexOf(this.species.speciesId); const newIndex = index >= this.filteredIndices.length - 1 ? 0 : index + 1; newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); } else { @@ -2321,7 +2358,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -2480,9 +2517,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler { const isFormSeen = this.isSeen(); this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? - this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false)); + this.pokemonNumberText.setColor( + this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false), + ); this.pokemonNumberText.setShadowColor( - this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true), + this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true), ); const assetLoadCancelled = new BooleanHolder(false); @@ -2634,7 +2673,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); this.pokemonCandyCountText.setText( - `x${species.speciesId === SpeciesId.PIKACHU ? 0 : globalScene.gameData.starterData[this.starterId].candyCount}`, + `×${species.speciesId === SpeciesId.PIKACHU ? 0 : globalScene.gameData.starterData[this.starterId].candyCount}`, ); this.pokemonCandyContainer.setVisible(true); @@ -2786,7 +2825,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(true); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? } } diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index 2bffc464793..bcf869f6f39 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -1,17 +1,15 @@ -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { OptionSelectItem } from "./abstact-option-select-ui-handler"; -import { isNullOrUndefined } from "#app/utils/common"; +import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; import { UiMode } from "#enums/ui-mode"; -import { FilterTextRow } from "./filter-text"; -import { allAbilities } from "#app/data/data-lists"; -import { allMoves } from "#app/data/data-lists"; -import { allSpecies } from "#app/data/data-lists"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { FilterTextRow } from "#ui/filter-text"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { isNullOrUndefined } from "#utils/common"; import i18next from "i18next"; -export default class PokedexScanUiHandler extends FormModalUiHandler { +export class PokedexScanUiHandler extends FormModalUiHandler { keys: string[]; reducedKeys: string[]; parallelKeys: string[]; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 1732bb005d3..c2f595cb190 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -1,53 +1,52 @@ -import type { Variant } from "#app/sprites/variant"; -import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; -import { argbFromRgba } from "@material/material-color-utilities"; -import i18next from "i18next"; +import { globalScene } from "#app/global-scene"; import { starterColors } from "#app/global-vars/starter-colors"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves"; -import type { PokemonForm } from "#app/data/pokemon-species"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpeciesForm, getPokerusStarters, normalForm } from "#app/data/pokemon-species"; -import { allSpecies } from "#app/data/data-lists"; -import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; -import { catchableSpecies } from "#app/data/balance/biomes"; -import { PokemonType } from "#enums/pokemon-type"; -import type { DexAttrProps, StarterAttributes } from "#app/system/game-data"; -import type { StarterPreferences } from "#app/utils/data"; -import type { DexEntry } from "#app/@types/dex-data"; -import { loadStarterPreferences } from "#app/utils/data"; -import { AbilityAttr } from "#enums/ability-attr"; -import { DexAttr } from "#enums/dex-attr"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; -import { TextStyle, addTextObject } from "#app/ui/text"; -import { UiMode } from "#enums/ui-mode"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { Passive as PassiveAttr } from "#enums/passive"; -import type { SpeciesId } from "#enums/species-id"; -import { Button } from "#enums/buttons"; -import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown"; -import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; -import { FilterBar } from "#app/ui/filter-bar"; -import { DropDownColumn } from "#enums/drop-down-column"; -import { ScrollBar } from "#app/ui/scroll-bar"; -import { AbilityId } from "#enums/ability-id"; +import { catchableSpecies } from "#balance/biomes"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { pokemonStarters } from "#balance/pokemon-evolutions"; +import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#balance/pokemon-level-moves"; import { getPassiveCandyCount, - getValueReductionCandyCounts, getSameSpeciesEggCandyCounts, -} from "#app/data/balance/starters"; -import { BooleanHolder, fixedInt, getLocalizedSpriteKey, padInt, randIntRange, rgbHexToRgba } from "#app/utils/common"; -import type { Nature } from "#enums/nature"; -import { addWindow } from "./ui-theme"; -import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; -import { FilterText, FilterTextRow } from "./filter-text"; -import { allAbilities } from "#app/data/data-lists"; -import { allMoves } from "#app/data/data-lists"; -import { speciesTmMoves } from "#app/data/balance/tms"; -import { pokemonStarters } from "#app/data/balance/pokemon-evolutions"; + getStarterValueFriendshipCap, + getValueReductionCandyCounts, + POKERUS_STARTER_COUNT, + speciesStarterCosts, +} from "#balance/starters"; +import { speciesTmMoves } from "#balance/tms"; +import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; +import type { PokemonForm, PokemonSpecies } from "#data/pokemon-species"; +import { getPokemonSpeciesForm, getPokerusStarters, normalForm } from "#data/pokemon-species"; +import { AbilityAttr } from "#enums/ability-attr"; +import { AbilityId } from "#enums/ability-id"; import { BiomeId } from "#enums/biome-id"; -import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { DexAttr } from "#enums/dex-attr"; +import { DropDownColumn } from "#enums/drop-down-column"; +import type { Nature } from "#enums/nature"; +import { Passive as PassiveAttr } from "#enums/passive"; +import { PokemonType } from "#enums/pokemon-type"; +import type { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import type { Variant } from "#sprites/variant"; +import { getVariantIcon, getVariantTint } from "#sprites/variant"; +import type { DexAttrProps, StarterAttributes } from "#system/game-data"; +import { SettingKeyboard } from "#system/settings-keyboard"; +import type { DexEntry } from "#types/dex-data"; +import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; +import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; +import { FilterBar } from "#ui/filter-bar"; +import { FilterText, FilterTextRow } from "#ui/filter-text"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { PokedexMonContainer } from "#ui/pokedex-mon-container"; +import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; +import { ScrollBar } from "#ui/scroll-bar"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { BooleanHolder, fixedInt, getLocalizedSpriteKey, padInt, randIntRange, rgbHexToRgba } from "#utils/common"; +import type { StarterPreferences } from "#utils/data"; +import { loadStarterPreferences } from "#utils/data"; +import { argbFromRgba } from "@material/material-color-utilities"; +import i18next from "i18next"; interface LanguageSetting { starterInfoTextSize: string; @@ -157,7 +156,7 @@ interface SpeciesDetails { natureIndex?: number; } -export default class PokedexUiHandler extends MessageUiHandler { +export class PokedexUiHandler extends MessageUiHandler { private starterSelectContainer: Phaser.GameObjects.Container; private starterSelectScrollBar: ScrollBar; private filterBarContainer: Phaser.GameObjects.Container; @@ -471,7 +470,7 @@ export default class PokedexUiHandler extends MessageUiHandler { this.pokemonNameText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNameText); - this.pokemonFormText = addTextObject(6, 121, "", TextStyle.PARTY, { + this.pokemonFormText = addTextObject(6, 121, "", TextStyle.INSTRUCTIONS_TEXT, { fontSize: textSettings.instructionTextSize, }); this.pokemonFormText.setOrigin(0, 0); @@ -483,13 +482,14 @@ export default class PokedexUiHandler extends MessageUiHandler { starterBoxContainer.add(this.starterSelectScrollBar); - this.pokerusCursorObjs = new Array(POKERUS_STARTER_COUNT).fill(null).map(() => { + this.pokerusCursorObjs = []; + for (let i = 0; i < POKERUS_STARTER_COUNT; i++) { const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus"); cursorObj.setVisible(false); cursorObj.setOrigin(0, 0); starterBoxContainer.add(cursorObj); - return cursorObj; - }); + this.pokerusCursorObjs.push(cursorObj); + } this.cursorObj = globalScene.add.image(0, 0, "select_cursor"); this.cursorObj.setOrigin(0, 0); @@ -561,7 +561,7 @@ export default class PokedexUiHandler extends MessageUiHandler { this.goFilterIconElement2.setName("sprite-goFilter2-icon-element"); this.goFilterIconElement2.setScale(0.675); this.goFilterIconElement2.setOrigin(0.0, 0.0); - this.goFilterLabel = addTextObject(30, 2, i18next.t("pokedexUiHandler:goFilters"), TextStyle.PARTY, { + this.goFilterLabel = addTextObject(30, 2, i18next.t("pokedexUiHandler:goFilters"), TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize, }); this.goFilterLabel.setName("text-goFilter-label"); @@ -577,7 +577,7 @@ export default class PokedexUiHandler extends MessageUiHandler { 20, 10, i18next.t("pokedexUiHandler:toggleDecorations"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.toggleDecorationsLabel.setName("text-toggleDecorations-label"); @@ -588,9 +588,15 @@ export default class PokedexUiHandler extends MessageUiHandler { this.showFormTrayIconElement.setName("sprite-showFormTray-icon-element"); this.showFormTrayIconElement.setScale(0.675); this.showFormTrayIconElement.setOrigin(0.0, 0.0); - this.showFormTrayLabel = addTextObject(16, 168, i18next.t("pokedexUiHandler:showForms"), TextStyle.PARTY, { - fontSize: instructionTextSize, - }); + this.showFormTrayLabel = addTextObject( + 16, + 168, + i18next.t("pokedexUiHandler:showForms"), + TextStyle.INSTRUCTIONS_TEXT, + { + fontSize: instructionTextSize, + }, + ); this.showFormTrayLabel.setName("text-showFormTray-label"); this.showFormTrayIconElement.setVisible(false); this.showFormTrayLabel.setVisible(false); @@ -1389,7 +1395,7 @@ export default class PokedexUiHandler extends MessageUiHandler { const fitsMoves = fitsMove1 && fitsMove2; if (fitsEggMove1 && !fitsLevelMove1) { - const em1 = eggMoves.findIndex(name => name === selectedMove1); + const em1 = eggMoves.indexOf(selectedMove1); if ((starterData.eggMoves & (1 << em1)) === 0) { data.eggMove1 = false; } else { @@ -1399,7 +1405,7 @@ export default class PokedexUiHandler extends MessageUiHandler { data.tmMove1 = true; } if (fitsEggMove2 && !fitsLevelMove2) { - const em2 = eggMoves.findIndex(name => name === selectedMove2); + const em2 = eggMoves.indexOf(selectedMove2); if ((starterData.eggMoves & (1 << em2)) === 0) { data.eggMove2 = false; } else { diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts index afc58c63953..8bcd62316cd 100644 --- a/src/ui/pokemon-hatch-info-container.ts +++ b/src/ui/pokemon-hatch-info-container.ts @@ -1,24 +1,24 @@ -import PokemonInfoContainer from "#app/ui/pokemon-info-container"; -import { Gender } from "#app/data/gender"; -import { PokemonType } from "#enums/pokemon-type"; -import { rgbHexToRgba, padInt } from "#app/utils/common"; -import { TextStyle, addTextObject } from "#app/ui/text"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/data-lists"; -import { SpeciesId } from "#enums/species-id"; -import { getEggTierForSpecies } from "#app/data/egg"; -import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { allMoves } from "#data/data-lists"; +import { getEggTierForSpecies } from "#data/egg"; +import type { EggHatchData } from "#data/egg-hatch-data"; +import { Gender } from "#data/gender"; +import { getPokemonSpeciesForm } from "#data/pokemon-species"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import type { PlayerPokemon } from "#field/pokemon"; +import { PokemonInfoContainer } from "#ui/pokemon-info-container"; +import { addTextObject, TextStyle } from "#ui/text"; +import { padInt, rgbHexToRgba } from "#utils/common"; import { argbFromRgba } from "@material/material-color-utilities"; -import type { EggHatchData } from "#app/data/egg-hatch-data"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; /** * Class for the hatch info summary of each pokemon * Holds an info container as well as an additional egg sprite, name, egg moves and main sprite */ -export default class PokemonHatchInfoContainer extends PokemonInfoContainer { +export class PokemonHatchInfoContainer extends PokemonInfoContainer { private currentPokemonSprite: Phaser.GameObjects.Sprite; private pokemonNumberText: Phaser.GameObjects.Text; private pokemonNameText: Phaser.GameObjects.Text; @@ -49,13 +49,11 @@ export default class PokemonHatchInfoContainer extends PokemonInfoContainer { this.pokemonListContainer.add(this.currentPokemonSprite); // setup name and number - this.pokemonNumberText = addTextObject(80, 107.5, "0000", TextStyle.SUMMARY, { fontSize: 74 }); + this.pokemonNumberText = addTextObject(84, 107, "0000", TextStyle.EGG_SUMMARY_DEX, { fontSize: 78 }); this.pokemonNumberText.setOrigin(0, 0); this.pokemonListContainer.add(this.pokemonNumberText); - this.pokemonNameText = addTextObject(7, 107.5, "", TextStyle.SUMMARY, { - fontSize: 74, - }); + this.pokemonNameText = addTextObject(7, 109, "", TextStyle.EGG_SUMMARY_NAME, { fontSize: 64 }); this.pokemonNameText.setOrigin(0, 0); this.pokemonListContainer.add(this.pokemonNameText); @@ -93,7 +91,7 @@ export default class PokemonHatchInfoContainer extends PokemonInfoContainer { const eggMoveBg = globalScene.add.nineslice(70, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2); eggMoveBg.setOrigin(1, 0); - const eggMoveLabel = addTextObject(70 - eggMoveBg.width / 2, 0, "???", TextStyle.PARTY); + const eggMoveLabel = addTextObject(70 - eggMoveBg.width / 2, 0, "???", TextStyle.MOVE_LABEL); eggMoveLabel.setOrigin(0.5, 0); this.pokemonEggMoveBgs.push(eggMoveBg); @@ -158,7 +156,7 @@ export default class PokemonHatchInfoContainer extends PokemonInfoContainer { this.pokemonCandyIcon.setVisible(true); this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); this.pokemonCandyOverlayIcon.setVisible(true); - this.pokemonCandyCountText.setText(`x${globalScene.gameData.starterData[species.speciesId].candyCount}`); + this.pokemonCandyCountText.setText(`×${globalScene.gameData.starterData[species.speciesId].candyCount}`); this.pokemonCandyCountText.setVisible(true); this.pokemonNumberText.setText(padInt(species.speciesId, 4)); diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/pokemon-icon-anim-handler.ts index 8a206167a94..408e0ebc9d3 100644 --- a/src/ui/pokemon-icon-anim-handler.ts +++ b/src/ui/pokemon-icon-anim-handler.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt, coerceArray } from "#app/utils/common"; +import { coerceArray, fixedInt } from "#utils/common"; export enum PokemonIconAnimMode { NONE, @@ -9,7 +9,7 @@ export enum PokemonIconAnimMode { type PokemonIcon = Phaser.GameObjects.Container | Phaser.GameObjects.Sprite; -export default class PokemonIconAnimHandler { +export class PokemonIconAnimHandler { private icons: Map; private toggled: boolean; diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 0056c3e2f11..c95f412c834 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -1,19 +1,19 @@ -import { getVariantTint } from "#app/sprites/variant"; -import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { globalScene } from "#app/global-scene"; -import { Gender, getGenderColor, getGenderSymbol } from "../data/gender"; -import { getNatureName } from "../data/nature"; -import { PokemonType } from "#enums/pokemon-type"; -import type Pokemon from "../field/pokemon"; -import i18next from "i18next"; -import type { StarterDataEntry } from "../system/game-data"; -import type { DexEntry } from "#app/@types/dex-data"; +import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; +import { getNatureName } from "#data/nature"; import { DexAttr } from "#enums/dex-attr"; -import { fixedInt, getShinyDescriptor } from "#app/utils/common"; -import ConfirmUiHandler from "./confirm-ui-handler"; -import { StatsContainer } from "./stats-container"; -import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; -import { addWindow } from "./ui-theme"; +import { PokemonType } from "#enums/pokemon-type"; +import type { Pokemon } from "#field/pokemon"; +import { getVariantTint } from "#sprites/variant"; +import type { StarterDataEntry } from "#system/game-data"; +import type { DexEntry } from "#types/dex-data"; +import { ConfirmUiHandler } from "#ui/confirm-ui-handler"; +import { StatsContainer } from "#ui/stats-container"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, getShinyDescriptor } from "#utils/common"; +import i18next from "i18next"; +import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; interface LanguageSetting { infoContainerTextSize: string; @@ -22,14 +22,24 @@ interface LanguageSetting { } const languageSettings: { [key: string]: LanguageSetting } = { + en: { + infoContainerTextSize: "64px", + infoContainerLabelXPos: -20, + infoContainerTextXPos: -17, + }, pt: { infoContainerTextSize: "60px", infoContainerLabelXPos: -15, infoContainerTextXPos: -12, }, + ja: { + infoContainerTextSize: "64px", + infoContainerLabelXPos: -27, + infoContainerTextXPos: -25, + }, }; -export default class PokemonInfoContainer extends Phaser.GameObjects.Container { +export class PokemonInfoContainer extends Phaser.GameObjects.Container { private readonly infoWindowWidth = 104; private pokemonFormLabelText: Phaser.GameObjects.Text; @@ -106,7 +116,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { moveBg.setOrigin(1, 0); moveBg.setName("nineslice-move-bg"); - const moveLabel = addTextObject(-moveBg.width / 2, 0, "-", TextStyle.PARTY); + const moveLabel = addTextObject(-moveBg.width / 2, 0, "-", TextStyle.MOVE_LABEL); moveLabel.setOrigin(0.5, 0); moveLabel.setName("text-move-label"); @@ -445,12 +455,12 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonShinyIcon.setPosition(82, 87); this.pokemonShinyNewIcon.setPosition(72, 87); - this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 152); - this.pokemonFormText.setPosition(infoContainerTextXPos, 152); - this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 110); - this.pokemonAbilityText.setPosition(infoContainerTextXPos, 110); - this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 125); - this.pokemonNatureText.setPosition(infoContainerTextXPos, 125); + this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 153); + this.pokemonFormText.setPosition(infoContainerTextXPos, 153); + this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 111); + this.pokemonAbilityText.setPosition(infoContainerTextXPos, 111); + this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 126); + this.pokemonNatureText.setPosition(infoContainerTextXPos, 126); this.statsContainer.setScale(0.7); this.statsContainer.setPosition(30, -3); diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index a60a53a8e7a..2466603af71 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -1,11 +1,11 @@ -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { TextStyle, addTextObject } from "./text"; -import i18next from "i18next"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { pokerogueApi } from "#api/pokerogue-api"; import { globalScene } from "#app/global-scene"; +import { UiMode } from "#enums/ui-mode"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import i18next from "i18next"; interface LanguageSetting { inputFieldFontSize?: string; @@ -20,7 +20,7 @@ const languageSettings: { [key: string]: LanguageSetting } = { }, }; -export default class RegistrationFormUiHandler extends FormModalUiHandler { +export class RegistrationFormUiHandler extends FormModalUiHandler { getModalTitle(_config?: ModalConfig): string { return i18next.t("menu:register"); } diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 7083f83865b..9da5b0e8554 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -1,10 +1,10 @@ -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; import i18next from "i18next"; -import type { PlayerPokemon } from "#app/field/pokemon"; -export default class RenameFormUiHandler extends FormModalUiHandler { +export class RenameFormUiHandler extends FormModalUiHandler { getModalTitle(_config?: ModalConfig): string { return i18next.t("menu:renamePokemon"); } diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index 06ef590c1e8..f810468aea1 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -1,18 +1,18 @@ import { globalScene } from "#app/global-scene"; -import { GameModes } from "#enums/game-modes"; -import { TextStyle, addTextObject } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import { addWindow } from "./ui-theme"; -import { fixedInt, formatLargeNumber } from "#app/utils/common"; -import type PokemonData from "../system/pokemon-data"; -import MessageUiHandler from "./message-ui-handler"; -import i18next from "i18next"; -import { Button } from "../enums/buttons"; import { BattleType } from "#enums/battle-type"; -import type { RunEntry } from "../system/game-data"; +import { Button } from "#enums/buttons"; +import { GameModes } from "#enums/game-modes"; import { PlayerGender } from "#enums/player-gender"; import { TrainerVariant } from "#enums/trainer-variant"; -import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; +import { UiMode } from "#enums/ui-mode"; +import type { RunEntry } from "#system/game-data"; +import type { PokemonData } from "#system/pokemon-data"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { RunDisplayMode } from "#ui/run-info-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, formatLargeNumber } from "#utils/common"; +import i18next from "i18next"; export type RunSelectCallback = (cursor: number) => void; @@ -24,7 +24,7 @@ export const RUN_HISTORY_LIMIT: number = 25; * It navigates similarly to the UI of the save slot select menu. * The only valid input buttons are Button.ACTION and Button.CANCEL. */ -export default class RunHistoryUiHandler extends MessageUiHandler { +export class RunHistoryUiHandler extends MessageUiHandler { private readonly maxRows = 3; private runSelectContainer: Phaser.GameObjects.Container; diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 78ab4a40407..29f95c4e4c8 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -1,32 +1,32 @@ -import { GameModes } from "#enums/game-modes"; -import UiHandler from "./ui-handler"; -import type { SessionSaveData } from "../system/game-data"; -import { TextStyle, addTextObject, addBBCodeTextObject, getTextColor } from "./text"; -import { UiMode } from "#enums/ui-mode"; -import { addWindow } from "./ui-theme"; -import { getPokeballAtlasKey } from "#app/data/pokeball"; -import { formatLargeNumber, getPlayTimeString, formatMoney, formatFancyLargeNumber } from "#app/utils/common"; -import type PokemonData from "../system/pokemon-data"; -import i18next from "i18next"; -import { Button } from "../enums/buttons"; -import { BattleType } from "#enums/battle-type"; -import { TrainerVariant } from "#enums/trainer-variant"; -import { Challenges } from "#enums/challenges"; -import { getLuckString, getLuckTextTint } from "../modifier/modifier-type"; -import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; -import { getTypeRgb } from "#app/data/type"; -import { PokemonType } from "#enums/pokemon-type"; -import { TypeColor, TypeShadow } from "#app/enums/color"; -import { getNatureStatMultiplier, getNatureName } from "../data/nature"; -import { getVariantTint } from "#app/sprites/variant"; -// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` -import * as Modifier from "#app/modifier/modifier"; -import type { SpeciesId } from "#enums/species-id"; -import { PlayerGender } from "#enums/player-gender"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { getBiomeName } from "#app/data/balance/biomes"; -import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; +import { getBiomeName } from "#balance/biomes"; +import { getNatureName, getNatureStatMultiplier } from "#data/nature"; +import { getPokeballAtlasKey } from "#data/pokeball"; +import { getTypeRgb } from "#data/type"; +import { BattleType } from "#enums/battle-type"; +import { Button } from "#enums/buttons"; +import { Challenges } from "#enums/challenges"; +import { TypeColor, TypeShadow } from "#enums/color"; +import { GameModes } from "#enums/game-modes"; +import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PlayerGender } from "#enums/player-gender"; +import { PokemonType } from "#enums/pokemon-type"; +import type { SpeciesId } from "#enums/species-id"; +import { TrainerVariant } from "#enums/trainer-variant"; +import { UiMode } from "#enums/ui-mode"; +// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts` +import * as Modifier from "#modifiers/modifier"; +import { getLuckString, getLuckTextTint } from "#modifiers/modifier-type"; +import { getVariantTint } from "#sprites/variant"; +import type { SessionSaveData } from "#system/game-data"; +import type { PokemonData } from "#system/pokemon-data"; +import { SettingKeyboard } from "#system/settings-keyboard"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common"; +import i18next from "i18next"; +import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; /** * RunInfoUiMode indicates possible overlays of RunInfoUiHandler. @@ -50,7 +50,7 @@ export enum RunDisplayMode { * I believe that it is possible that the contents/methods of the first page will be placed in their own class that is an extension of RunInfoUiHandler as more pages are added. * For now, I leave as is. */ -export default class RunInfoUiHandler extends UiHandler { +export class RunInfoUiHandler extends UiHandler { protected runDisplayMode: RunDisplayMode; protected runInfo: SessionSaveData; protected isVictory: boolean; @@ -202,7 +202,7 @@ export default class RunInfoUiHandler extends UiHandler { ); this.runContainer.add(abilityButtonContainer); } - const headerText = addTextObject(0, 0, i18next.t("runHistory:runInfo"), TextStyle.SETTINGS_LABEL); + const headerText = addTextObject(0, 0, i18next.t("runHistory:runInfo"), TextStyle.HEADER_LABEL); headerText.setOrigin(0, 0); headerText.setPositionRelative(headerBg, 8, 4); this.runContainer.add(headerText); @@ -603,7 +603,7 @@ export default class RunInfoUiHandler extends UiHandler { // Duration + Money const runInfoTextContainer = globalScene.add.container(0, 0); // Japanese is set to a greater line spacing of 35px in addBBCodeTextObject() if lineSpacing < 12. - const lineSpacing = i18next.resolvedLanguage === "ja" ? 12 : 3; + const lineSpacing = i18next.resolvedLanguage === "ja" ? 3 : 3; const runInfoText = addBBCodeTextObject(7, 0, "", TextStyle.WINDOW, { fontSize: "50px", lineSpacing: lineSpacing, @@ -768,7 +768,7 @@ export default class RunInfoUiHandler extends UiHandler { const pPassiveInfo = pokemon.passive ? passiveLabel + ": " + pokemon.getPassiveAbility().name : ""; const pAbilityInfo = abilityLabel + ": " + pokemon.getAbility().name; // Japanese is set to a greater line spacing of 35px in addBBCodeTextObject() if lineSpacing < 12. - const lineSpacing = i18next.resolvedLanguage === "ja" ? 12 : 3; + const lineSpacing = i18next.resolvedLanguage === "ja" ? 3 : 3; const pokeInfoText = addBBCodeTextObject(0, 0, pName, TextStyle.SUMMARY, { fontSize: textContainerFontSize, lineSpacing: lineSpacing, @@ -866,7 +866,7 @@ export default class RunInfoUiHandler extends UiHandler { moveContainer.setScale(0.5); const moveBg = globalScene.add.nineslice(0, 0, "type_bgs", "unknown", 85, 15, 2, 2, 2, 2); moveBg.setOrigin(1, 0); - const moveLabel = addTextObject(-moveBg.width / 2, 2, "-", TextStyle.PARTY); + const moveLabel = addTextObject(-moveBg.width / 2, 1, "-", TextStyle.MOVE_LABEL); moveLabel.setOrigin(0.5, 0); moveLabel.setName("text-move-label"); pokemonMoveBgs.push(moveBg); diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 7b4d46203c9..bcbe60265cd 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -1,17 +1,17 @@ -import i18next from "i18next"; +import { GameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; -import { GameMode } from "../game-mode"; -// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` -import * as Modifier from "#app/modifier/modifier"; -import type { SessionSaveData } from "../system/game-data"; -import type PokemonData from "../system/pokemon-data"; -import { isNullOrUndefined, fixedInt, getPlayTimeString, formatLargeNumber } from "#app/utils/common"; -import MessageUiHandler from "./message-ui-handler"; -import { TextStyle, addTextObject } from "./text"; import { UiMode } from "#enums/ui-mode"; -import { addWindow } from "./ui-theme"; -import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; +// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts` +import * as Modifier from "#modifiers/modifier"; +import type { SessionSaveData } from "#system/game-data"; +import type { PokemonData } from "#system/pokemon-data"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { RunDisplayMode } from "#ui/run-info-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import { fixedInt, formatLargeNumber, getPlayTimeString, isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; const SESSION_SLOTS_COUNT = 5; const SLOTS_ON_SCREEN = 3; @@ -23,7 +23,7 @@ export enum SaveSlotUiMode { export type SaveSlotSelectCallback = (cursor: number) => void; -export default class SaveSlotSelectUiHandler extends MessageUiHandler { +export class SaveSlotSelectUiHandler extends MessageUiHandler { private saveSlotSelectContainer: Phaser.GameObjects.Container; private sessionSlotsContainer: Phaser.GameObjects.Container; private saveSlotSelectMessageBox: Phaser.GameObjects.NineSlice; diff --git a/src/ui/saving-icon-handler.ts b/src/ui/saving-icon-handler.ts index 3b7db549a4a..6923017218f 100644 --- a/src/ui/saving-icon-handler.ts +++ b/src/ui/saving-icon-handler.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils/common"; +import { fixedInt } from "#utils/common"; -export default class SavingIconHandler extends Phaser.GameObjects.Container { +export class SavingIconHandler extends Phaser.GameObjects.Container { private icon: Phaser.GameObjects.Sprite; private animActive: boolean; diff --git a/src/ui/scrollable-grid-handler.ts b/src/ui/scrollable-grid-handler.ts index 54f07d69fb7..3c6458f5083 100644 --- a/src/ui/scrollable-grid-handler.ts +++ b/src/ui/scrollable-grid-handler.ts @@ -1,6 +1,6 @@ import { Button } from "#enums/buttons"; -import type UiHandler from "#app/ui/ui-handler"; -import type { ScrollBar } from "#app/ui/scroll-bar"; +import type { ScrollBar } from "#ui/scroll-bar"; +import type { UiHandler } from "#ui/ui-handler"; type UpdateGridCallbackFunction = () => void; type UpdateDetailsCallbackFunction = (index: number) => void; @@ -16,7 +16,7 @@ type UpdateDetailsCallbackFunction = (index: number) => void; * - in `UiHandler.processInput`: call `processNavigationInput` to have it handle the cursor updates while calling the defined callbacks * - in `UiHandler.clear`: call `reset` */ -export default class ScrollableGridUiHandler { +export class ScrollableGridUiHandler { private readonly ROWS: number; private readonly COLUMNS: number; private handler: UiHandler; diff --git a/src/ui/session-reload-modal-ui-handler.ts b/src/ui/session-reload-modal-ui-handler.ts index f866783afe8..ab1197324a6 100644 --- a/src/ui/session-reload-modal-ui-handler.ts +++ b/src/ui/session-reload-modal-ui-handler.ts @@ -1,9 +1,9 @@ -import type { ModalConfig } from "./modal-ui-handler"; -import { ModalUiHandler } from "./modal-ui-handler"; -import { addTextObject, TextStyle } from "./text"; import type { UiMode } from "#enums/ui-mode"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { ModalUiHandler } from "#ui/modal-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; -export default class SessionReloadModalUiHandler extends ModalUiHandler { +export class SessionReloadModalUiHandler extends ModalUiHandler { constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/settings/abstract-binding-ui-handler.ts index a4707418b7c..aaf3572b489 100644 --- a/src/ui/settings/abstract-binding-ui-handler.ts +++ b/src/ui/settings/abstract-binding-ui-handler.ts @@ -1,18 +1,18 @@ -import UiHandler from "../ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import { addWindow } from "../ui-theme"; -import { addTextObject, TextStyle } from "../text"; -import { Button } from "#enums/buttons"; -import { NavigationManager } from "#app/ui/settings/navigationMenu"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import type { UiMode } from "#enums/ui-mode"; +import { NavigationManager } from "#ui/navigationMenu"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import i18next from "i18next"; type CancelFn = (succes?: boolean) => boolean; /** * Abstract class for handling UI elements related to button bindings. */ -export default abstract class AbstractBindingUiHandler extends UiHandler { +export abstract class AbstractBindingUiHandler extends UiHandler { // Containers for different segments of the UI. protected optionSelectContainer: Phaser.GameObjects.Container; protected actionsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index e3631c062df..67cc3ad7c03 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -1,15 +1,15 @@ -import UiHandler from "#app/ui/ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import type { InterfaceConfig } from "#app/inputs-controller"; -import { addWindow } from "#app/ui/ui-theme"; -import { addTextObject, TextStyle } from "#app/ui/text"; -import { ScrollBar } from "#app/ui/scroll-bar"; -import { getIconWithSettingName } from "#app/configs/inputs/configHandler"; -import NavigationMenu, { NavigationManager } from "#app/ui/settings/navigationMenu"; -import type { Device } from "#enums/devices"; -import { Button } from "#enums/buttons"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; +import type { InterfaceConfig } from "#app/inputs-controller"; +import { Button } from "#enums/buttons"; +import type { Device } from "#enums/devices"; +import type { UiMode } from "#enums/ui-mode"; +import { getIconWithSettingName } from "#inputs/configHandler"; +import { NavigationManager, NavigationMenu } from "#ui/navigationMenu"; +import { ScrollBar } from "#ui/scroll-bar"; +import { addTextObject, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import i18next from "i18next"; export interface InputsIcons { [key: string]: Phaser.GameObjects.Sprite; @@ -27,7 +27,7 @@ export interface LayoutConfig { /** * Abstract class for handling UI elements related to control settings. */ -export default abstract class AbstractControlSettingsUiHandler extends UiHandler { +export abstract class AbstractControlSettingsUiHandler extends UiHandler { protected settingsContainer: Phaser.GameObjects.Container; protected optionsContainer: Phaser.GameObjects.Container; protected navigationContainer: NavigationMenu; @@ -209,7 +209,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler settingFiltered.forEach((setting, s) => { // Convert the setting key from format 'Key_Name' to 'Key name' for display. - const settingName = setting.replace(/\_/g, " "); + const settingName = setting.replace(/_/g, " "); // Create and add a text object for the setting name to the scene. const isLock = this.settingBlacklisted.includes(this.setting[setting]); diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 6db9840a818..303ad229ea6 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -1,20 +1,20 @@ -import { TextStyle, addTextObject } from "#app/ui/text"; -import { UiMode } from "#enums/ui-mode"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import { addWindow } from "#app/ui/ui-theme"; -import { ScrollBar } from "#app/ui/scroll-bar"; -import { Button } from "#enums/buttons"; -import type { InputsIcons } from "#app/ui/settings/abstract-control-settings-ui-handler"; -import NavigationMenu, { NavigationManager } from "#app/ui/settings/navigationMenu"; -import type { SettingType } from "#app/system/settings/settings"; -import { Setting, SettingKeys } from "#app/system/settings/settings"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; +import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import type { SettingType } from "#system/settings"; +import { Setting, SettingKeys } from "#system/settings"; +import type { InputsIcons } from "#ui/abstract-control-settings-ui-handler"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { NavigationManager, NavigationMenu } from "#ui/navigationMenu"; +import { ScrollBar } from "#ui/scroll-bar"; +import { addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; +import i18next from "i18next"; /** * Abstract class for handling UI elements related to settings. */ -export default class AbstractSettingsUiHandler extends MessageUiHandler { +export class AbstractSettingsUiHandler extends MessageUiHandler { private settingsContainer: Phaser.GameObjects.Container; private optionsContainer: Phaser.GameObjects.Container; private messageBoxContainer: Phaser.GameObjects.Container; diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts index 9ddb54131d5..c514e93ec51 100644 --- a/src/ui/settings/gamepad-binding-ui-handler.ts +++ b/src/ui/settings/gamepad-binding-ui-handler.ts @@ -1,12 +1,12 @@ -import AbstractBindingUiHandler from "./abstract-binding-ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import { Device } from "#enums/devices"; -import { getIconWithSettingName, getKeyWithKeycode } from "#app/configs/inputs/configHandler"; -import { addTextObject, TextStyle } from "#app/ui/text"; import { globalScene } from "#app/global-scene"; +import { Device } from "#enums/devices"; +import type { UiMode } from "#enums/ui-mode"; +import { getIconWithSettingName, getKeyWithKeycode } from "#inputs/configHandler"; +import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; import i18next from "i18next"; -export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { +export class GamepadBindingUiHandler extends AbstractBindingUiHandler { constructor(mode: UiMode | null = null) { super(mode); globalScene.input.gamepad?.on("down", this.gamepadButtonDown, this); diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index 183b8269e78..a789225a8ef 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -1,12 +1,12 @@ -import AbstractBindingUiHandler from "./abstract-binding-ui-handler"; -import type { UiMode } from "#enums/ui-mode"; -import { getKeyWithKeycode } from "#app/configs/inputs/configHandler"; -import { Device } from "#enums/devices"; -import { addTextObject, TextStyle } from "#app/ui/text"; import { globalScene } from "#app/global-scene"; +import { Device } from "#enums/devices"; +import type { UiMode } from "#enums/ui-mode"; +import { getKeyWithKeycode } from "#inputs/configHandler"; +import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; import i18next from "i18next"; -export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { +export class KeyboardBindingUiHandler extends AbstractBindingUiHandler { constructor(mode: UiMode | null = null) { super(mode); // Listen to gamepad button down events to initiate binding. diff --git a/src/ui/settings/move-touch-controls-handler.ts b/src/ui/settings/move-touch-controls-handler.ts index f684d27f748..60572529c89 100644 --- a/src/ui/settings/move-touch-controls-handler.ts +++ b/src/ui/settings/move-touch-controls-handler.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import type TouchControl from "#app/touch-controls"; -import type UI from "#app/ui/ui"; +import type { TouchControl } from "#app/touch-controls"; +import type { UI } from "#ui/ui"; import i18next from "i18next"; export const TOUCH_CONTROL_POSITIONS_LANDSCAPE = "touchControlPositionsLandscape"; @@ -24,7 +24,7 @@ type ToolbarRefs = { /** * Handles the dragging of touch controls around the screen. */ -export default class MoveTouchControlsHandler { +export class MoveTouchControlsHandler { /** The element that is currently being dragged */ private draggingElement: HTMLElement | null = null; diff --git a/src/ui/settings/navigationMenu.ts b/src/ui/settings/navigationMenu.ts index ad3d4ccf0b5..1303c32d3a5 100644 --- a/src/ui/settings/navigationMenu.ts +++ b/src/ui/settings/navigationMenu.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { UiMode } from "#enums/ui-mode"; -import type { InputsIcons } from "#app/ui/settings/abstract-control-settings-ui-handler"; -import { addTextObject, setTextStyle, TextStyle } from "#app/ui/text"; -import { addWindow } from "#app/ui/ui-theme"; import { Button } from "#enums/buttons"; +import { UiMode } from "#enums/ui-mode"; +import type { InputsIcons } from "#ui/abstract-control-settings-ui-handler"; +import { addTextObject, setTextStyle, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; import i18next from "i18next"; const LEFT = "LEFT"; @@ -16,7 +16,7 @@ export class NavigationManager { private static instance: NavigationManager; public modes: UiMode[]; public selectedMode: UiMode = UiMode.SETTINGS; - public navigationMenus: NavigationMenu[] = new Array(); + public navigationMenus: NavigationMenu[] = []; public labels: string[]; /** @@ -103,9 +103,9 @@ export class NavigationManager { } } -export default class NavigationMenu extends Phaser.GameObjects.Container { +export class NavigationMenu extends Phaser.GameObjects.Container { private navigationIcons: InputsIcons; - protected headerTitles: Phaser.GameObjects.Text[] = new Array(); + protected headerTitles: Phaser.GameObjects.Text[] = []; /** * Creates an instance of NavigationMenu. @@ -144,7 +144,7 @@ export default class NavigationMenu extends Phaser.GameObjects.Container { let relative: Phaser.GameObjects.Sprite | Phaser.GameObjects.Text = iconPreviousTab; let relativeWidth: number = iconPreviousTab.width * 6; for (const label of navigationManager.labels) { - const labelText = addTextObject(0, 0, label, TextStyle.SETTINGS_LABEL); + const labelText = addTextObject(0, 0, label, TextStyle.SETTINGS_LABEL_NAVBAR); labelText.setOrigin(0, 0); labelText.setPositionRelative(relative, 6 + relativeWidth / 6, 0); this.add(labelText); diff --git a/src/ui/settings/option-select-ui-handler.ts b/src/ui/settings/option-select-ui-handler.ts index af9760814ac..d7b699e6e50 100644 --- a/src/ui/settings/option-select-ui-handler.ts +++ b/src/ui/settings/option-select-ui-handler.ts @@ -1,7 +1,7 @@ -import AbstractOptionSelectUiHandler from "../abstact-option-select-ui-handler"; import { UiMode } from "#enums/ui-mode"; +import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; -export default class OptionSelectUiHandler extends AbstractOptionSelectUiHandler { +export class OptionSelectUiHandler extends AbstractOptionSelectUiHandler { constructor(mode: UiMode = UiMode.OPTION_SELECT) { super(mode); } diff --git a/src/ui/settings/settings-audio-ui-handler.ts b/src/ui/settings/settings-audio-ui-handler.ts index 019d66d7428..aa55f5489cc 100644 --- a/src/ui/settings/settings-audio-ui-handler.ts +++ b/src/ui/settings/settings-audio-ui-handler.ts @@ -1,9 +1,8 @@ import type { UiMode } from "#enums/ui-mode"; -import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; -import { SettingType } from "#app/system/settings/settings"; -("#app/inputs-controller"); +import { SettingType } from "#system/settings"; +import { AbstractSettingsUiHandler } from "#ui/abstract-settings-ui-handler"; -export default class SettingsAudioUiHandler extends AbstractSettingsUiHandler { +export class SettingsAudioUiHandler extends AbstractSettingsUiHandler { /** * Creates an instance of SettingsAudioUiHandler. * diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts index 0636bd25567..3c261d6ddab 100644 --- a/src/ui/settings/settings-display-ui-handler.ts +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -1,9 +1,8 @@ import type { UiMode } from "#enums/ui-mode"; -import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; -import { SettingKeys, SettingType } from "#app/system/settings/settings"; -("#app/inputs-controller"); +import { SettingKeys, SettingType } from "#system/settings"; +import { AbstractSettingsUiHandler } from "#ui/abstract-settings-ui-handler"; -export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler { +export class SettingsDisplayUiHandler extends AbstractSettingsUiHandler { /** * Creates an instance of SettingsGamepadUiHandler. * diff --git a/src/ui/settings/settings-gamepad-ui-handler.ts b/src/ui/settings/settings-gamepad-ui-handler.ts index 7d269deab14..63dd4c74618 100644 --- a/src/ui/settings/settings-gamepad-ui-handler.ts +++ b/src/ui/settings/settings-gamepad-ui-handler.ts @@ -1,21 +1,21 @@ -import { addTextObject, TextStyle } from "../text"; +import { globalScene } from "#app/global-scene"; +import type { InterfaceConfig } from "#app/inputs-controller"; +import { Device } from "#enums/devices"; import type { UiMode } from "#enums/ui-mode"; +import pad_dualshock from "#inputs/pad_dualshock"; +import pad_unlicensedSNES from "#inputs/pad_unlicensedSNES"; +import pad_xbox360 from "#inputs/pad_xbox360"; import { - setSettingGamepad, SettingGamepad, + setSettingGamepad, settingGamepadBlackList, settingGamepadDefaults, settingGamepadOptions, -} from "../../system/settings/settings-gamepad"; -import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; -import pad_dualshock from "#app/configs/inputs/pad_dualshock"; -import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES"; -import type { InterfaceConfig } from "#app/inputs-controller"; -import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; -import { Device } from "#enums/devices"; -import { truncateString } from "#app/utils/common"; +} from "#system/settings-gamepad"; +import { AbstractControlSettingsUiHandler } from "#ui/abstract-control-settings-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { truncateString } from "#utils/common"; import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; /** * Class representing the settings UI handler for gamepads. @@ -23,7 +23,7 @@ import { globalScene } from "#app/global-scene"; * @extends AbstractControlSettingsUiHandler */ -export default class SettingsGamepadUiHandler extends AbstractControlSettingsUiHandler { +export class SettingsGamepadUiHandler extends AbstractControlSettingsUiHandler { /** * Creates an instance of SettingsGamepadUiHandler. * diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts index c334ee8f1fc..581e3e938be 100644 --- a/src/ui/settings/settings-keyboard-ui-handler.ts +++ b/src/ui/settings/settings-keyboard-ui-handler.ts @@ -1,28 +1,28 @@ +import { globalScene } from "#app/global-scene"; +import type { InterfaceConfig } from "#app/inputs-controller"; +import { Device } from "#enums/devices"; import { UiMode } from "#enums/ui-mode"; -import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; +import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty"; +import { deleteBind } from "#inputs/configHandler"; import { - setSettingKeyboard, SettingKeyboard, + setSettingKeyboard, settingKeyboardBlackList, settingKeyboardDefaults, settingKeyboardOptions, -} from "#app/system/settings/settings-keyboard"; -import { reverseValueToKeySetting, truncateString } from "#app/utils/common"; -import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; -import type { InterfaceConfig } from "#app/inputs-controller"; -import { addTextObject, TextStyle } from "#app/ui/text"; -import { deleteBind } from "#app/configs/inputs/configHandler"; -import { Device } from "#enums/devices"; -import { NavigationManager } from "#app/ui/settings/navigationMenu"; +} from "#system/settings-keyboard"; +import { AbstractControlSettingsUiHandler } from "#ui/abstract-control-settings-ui-handler"; +import { NavigationManager } from "#ui/navigationMenu"; +import { addTextObject, TextStyle } from "#ui/text"; +import { reverseValueToKeySetting, truncateString } from "#utils/common"; import i18next from "i18next"; -import { globalScene } from "#app/global-scene"; /** * Class representing the settings UI handler for keyboards. * * @extends AbstractControlSettingsUiHandler */ -export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUiHandler { +export class SettingsKeyboardUiHandler extends AbstractControlSettingsUiHandler { /** * Creates an instance of SettingsKeyboardUiHandler. * diff --git a/src/ui/settings/settings-ui-handler.ts b/src/ui/settings/settings-ui-handler.ts index 8d61044ff91..9461e987864 100644 --- a/src/ui/settings/settings-ui-handler.ts +++ b/src/ui/settings/settings-ui-handler.ts @@ -1,8 +1,8 @@ -import { SettingType } from "../../system/settings/settings"; import type { UiMode } from "#enums/ui-mode"; -import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; +import { SettingType } from "#system/settings"; +import { AbstractSettingsUiHandler } from "#ui/abstract-settings-ui-handler"; -export default class SettingsUiHandler extends AbstractSettingsUiHandler { +export class SettingsUiHandler extends AbstractSettingsUiHandler { /** * Creates an instance of SettingsGamepadUiHandler. * diff --git a/src/ui/starter-container.ts b/src/ui/starter-container.ts index 71e40844f38..4c174dc5955 100644 --- a/src/ui/starter-container.ts +++ b/src/ui/starter-container.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; -import type PokemonSpecies from "../data/pokemon-species"; -import { addTextObject, TextStyle } from "./text"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { addTextObject, TextStyle } from "#ui/text"; export class StarterContainer extends Phaser.GameObjects.Container { public species: PokemonSpecies; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 20f613fb694..18a3fbc30a3 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,67 +1,64 @@ -import type { CandyUpgradeNotificationChangedEvent } from "#app/events/battle-scene"; -import { BattleSceneEventType } from "#app/events/battle-scene"; -import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import type { Variant } from "#app/sprites/variant"; -import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; -import { argbFromRgba } from "@material/material-color-utilities"; -import i18next from "i18next"; -import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { starterColors } from "#app/global-vars/starter-colors"; +import type { Ability } from "#abilities/ability"; +import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import type { Ability } from "#app/data/abilities/ability"; -import { allAbilities } from "#app/data/data-lists"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; -import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/data-lists"; -import { getNatureName } from "#app/data/nature"; -import { pokemonFormChanges } from "#app/data/pokemon-forms"; -import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; -import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves"; -import type PokemonSpecies from "#app/data/pokemon-species"; -import { getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species"; -import { allSpecies } from "#app/data/data-lists"; -import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; -import { PokemonType } from "#enums/pokemon-type"; -import { GameModes } from "#enums/game-modes"; -import type { DexAttrProps, StarterMoveset, StarterAttributes } from "#app/system/game-data"; -import type { StarterPreferences } from "#app/utils/data"; -import type { DexEntry } from "#app/@types/dex-data"; -import { loadStarterPreferences, saveStarterPreferences } from "#app/utils/data"; -import { AbilityAttr } from "#enums/ability-attr"; -import { DexAttr } from "#enums/dex-attr"; -import { Tutorial, handleTutorial } from "#app/tutorial"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; -import { StatsContainer } from "#app/ui/stats-container"; -import { TextStyle, addBBCodeTextObject, addTextObject } from "#app/ui/text"; -import { UiMode } from "#enums/ui-mode"; -import { addWindow } from "#app/ui/ui-theme"; -import { Egg } from "#app/data/egg"; +import { starterColors } from "#app/global-vars/starter-colors"; import Overrides from "#app/overrides"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { Passive as PassiveAttr } from "#enums/passive"; -import { applyChallenges } from "#app/data/challenge"; -import { ChallengeType } from "#enums/challenge-type"; -import MoveInfoOverlay from "#app/ui/move-info-overlay"; -import { getEggTierForSpecies } from "#app/data/egg"; -import { Device } from "#enums/devices"; -import type { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { Button } from "#enums/buttons"; -import { EggSourceType } from "#enums/egg-source-types"; -import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown"; -import { StarterContainer } from "#app/ui/starter-container"; -import { FilterBar } from "#app/ui/filter-bar"; -import { DropDownColumn } from "#enums/drop-down-column"; -import { ScrollBar } from "#app/ui/scroll-bar"; -import { AbilityId } from "#enums/ability-id"; +import { handleTutorial, Tutorial } from "#app/tutorial"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import type { LevelMoves } from "#balance/pokemon-level-moves"; +import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#balance/pokemon-level-moves"; import { getPassiveCandyCount, - getValueReductionCandyCounts, getSameSpeciesEggCandyCounts, -} from "#app/data/balance/starters"; + getStarterValueFriendshipCap, + getValueReductionCandyCounts, + POKERUS_STARTER_COUNT, + speciesStarterCosts, +} from "#balance/starters"; +import { applyChallenges, checkStarterValidForChallenge } from "#data/challenge"; +import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; +import { Egg, getEggTierForSpecies } from "#data/egg"; +import { GrowthRate, getGrowthRateColor } from "#data/exp"; +import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; +import { getNatureName } from "#data/nature"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { getPokemonSpeciesForm, getPokerusStarters } from "#data/pokemon-species"; +import { AbilityAttr } from "#enums/ability-attr"; +import { AbilityId } from "#enums/ability-id"; +import { Button } from "#enums/buttons"; +import { ChallengeType } from "#enums/challenge-type"; +import { Device } from "#enums/devices"; +import { DexAttr } from "#enums/dex-attr"; +import { DropDownColumn } from "#enums/drop-down-column"; +import { EggSourceType } from "#enums/egg-source-types"; +import { GameModes } from "#enums/game-modes"; +import type { MoveId } from "#enums/move-id"; +import type { Nature } from "#enums/nature"; +import { Passive as PassiveAttr } from "#enums/passive"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import type { CandyUpgradeNotificationChangedEvent } from "#events/battle-scene"; +import { BattleSceneEventType } from "#events/battle-scene"; +import type { Variant } from "#sprites/variant"; +import { getVariantIcon, getVariantTint } from "#sprites/variant"; +import { achvs } from "#system/achv"; +import type { DexAttrProps, StarterAttributes, StarterMoveset } from "#system/game-data"; +import { SettingKeyboard } from "#system/settings-keyboard"; +import type { DexEntry } from "#types/dex-data"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; +import { FilterBar } from "#ui/filter-bar"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { MoveInfoOverlay } from "#ui/move-info-overlay"; +import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; +import { ScrollBar } from "#ui/scroll-bar"; +import { StarterContainer } from "#ui/starter-container"; +import { StatsContainer } from "#ui/stats-container"; +import { addBBCodeTextObject, addTextObject, TextStyle } from "#ui/text"; +import { addWindow } from "#ui/ui-theme"; import { BooleanHolder, fixedInt, @@ -72,12 +69,13 @@ import { randIntRange, rgbHexToRgba, toReadableString, -} from "#app/utils/common"; -import type { Nature } from "#enums/nature"; -import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { achvs } from "#app/system/achv"; +} from "#utils/common"; +import type { StarterPreferences } from "#utils/data"; +import { loadStarterPreferences, saveStarterPreferences } from "#utils/data"; +import { argbFromRgba } from "@material/material-color-utilities"; +import i18next from "i18next"; import type { GameObjects } from "phaser"; -import { checkStarterValidForChallenge } from "#app/data/challenge"; +import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -148,10 +146,10 @@ const languageSettings: { [key: string]: LanguageSetting } = { starterInfoXPos: 30, }, ja: { - starterInfoTextSize: "62px", - instructionTextSize: "38px", - starterInfoYOffset: 0.5, - starterInfoXPos: 33, + starterInfoTextSize: "48px", + instructionTextSize: "40px", + starterInfoYOffset: 1, + starterInfoXPos: 32, }, ca: { starterInfoTextSize: "48px", @@ -265,7 +263,7 @@ interface SpeciesDetails { teraType?: PokemonType; } -export default class StarterSelectUiHandler extends MessageUiHandler { +export class StarterSelectUiHandler extends MessageUiHandler { private starterSelectContainer: Phaser.GameObjects.Container; private starterSelectScrollBar: ScrollBar; private filterBarContainer: Phaser.GameObjects.Container; @@ -624,7 +622,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }); this.starterSelectContainer.add(this.pokemonSprite); - this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY); + this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY_DEX_NUM); this.pokemonNumberText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNumberText); @@ -643,7 +641,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonGrowthRateLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonGrowthRateLabelText); - this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.SUMMARY_PINK, { fontSize: "36px" }); + this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.GROWTH_RATE_TYPE, { fontSize: "36px" }); this.pokemonGrowthRateText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGrowthRateText); @@ -743,7 +741,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMoveBgs = []; this.pokemonEggMoveLabels = []; - this.valueLimitLabel = addTextObject(teamWindowX + 17, 150, "0/10", TextStyle.TOOLTIP_CONTENT); + this.valueLimitLabel = addTextObject(teamWindowX + 17, 150, "0/10", TextStyle.STARTER_VALUE_LIMIT); this.valueLimitLabel.setOrigin(0.5, 0); this.starterSelectContainer.add(this.valueLimitLabel); @@ -800,21 +798,23 @@ export default class StarterSelectUiHandler extends MessageUiHandler { starterBoxContainer.add(this.starterSelectScrollBar); - this.pokerusCursorObjs = new Array(POKERUS_STARTER_COUNT).fill(null).map(() => { + this.pokerusCursorObjs = []; + for (let i = 0; i < POKERUS_STARTER_COUNT; i++) { const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus"); cursorObj.setVisible(false); cursorObj.setOrigin(0, 0); starterBoxContainer.add(cursorObj); - return cursorObj; - }); + this.pokerusCursorObjs.push(cursorObj); + } - this.starterCursorObjs = new Array(6).fill(null).map(() => { + this.starterCursorObjs = []; + for (let i = 0; i < 6; i++) { const cursorObj = globalScene.add.image(0, 0, "select_cursor_highlight"); cursorObj.setVisible(false); cursorObj.setOrigin(0, 0); starterBoxContainer.add(cursorObj); - return cursorObj; - }); + this.starterCursorObjs.push(cursorObj); + } this.cursorObj = globalScene.add.image(0, 0, "select_cursor"); this.cursorObj.setOrigin(0, 0); @@ -843,15 +843,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterSelectContainer.add(starterBoxContainer); - this.starterIcons = new Array(6).fill(null).map((_, i) => { + this.starterIcons = []; + for (let i = 0; i < 6; i++) { const icon = globalScene.add.sprite(teamWindowX + 7, calcStarterIconY(i), "pokemon_icons_0"); icon.setScale(0.5); icon.setOrigin(0, 0); icon.setFrame("unknown"); this.starterSelectContainer.add(icon); this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE); - return icon; - }); + this.starterIcons.push(icon); + } this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types")); this.type1Icon.setScale(0.5); @@ -869,9 +870,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonLuckLabelText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonLuckLabelText); - this.pokemonLuckText = addTextObject(8 + this.pokemonLuckLabelText.displayWidth + 2, 89, "0", TextStyle.WINDOW, { - fontSize: "56px", - }); + this.pokemonLuckText = addTextObject( + 8 + this.pokemonLuckLabelText.displayWidth + 2, + 89, + "0", + TextStyle.LUCK_VALUE, + { + fontSize: "56px", + }, + ); this.pokemonLuckText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonLuckText); @@ -944,7 +951,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const moveBg = globalScene.add.nineslice(0, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2); moveBg.setOrigin(1, 0); - const moveLabel = addTextObject(-moveBg.width / 2, 0, "-", TextStyle.PARTY); + const moveLabel = addTextObject(-moveBg.width / 2, 0, "-", TextStyle.MOVE_LABEL); moveLabel.setOrigin(0.5, 0); this.pokemonMoveBgs.push(moveBg); @@ -961,7 +968,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { -this.pokemonMoveBgs[0].width / 2, 56, "(+0)", - TextStyle.PARTY, + TextStyle.MOVE_LABEL, ); this.pokemonAdditionalMoveCountLabel.setOrigin(0.5, 0); @@ -983,7 +990,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const eggMoveBg = globalScene.add.nineslice(0, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2); eggMoveBg.setOrigin(1, 0); - const eggMoveLabel = addTextObject(-eggMoveBg.width / 2, 0, "???", TextStyle.PARTY); + const eggMoveLabel = addTextObject(-eggMoveBg.width / 2, 0, "???", TextStyle.MOVE_LABEL); eggMoveLabel.setOrigin(0.5, 0); this.pokemonEggMoveBgs.push(eggMoveBg); @@ -1027,7 +1034,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleShiny"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.shinyLabel.setName("text-shiny-label"); @@ -1046,7 +1053,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleForm"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.formLabel.setName("text-form-label"); @@ -1065,7 +1072,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleGender"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.genderLabel.setName("text-gender-label"); @@ -1084,7 +1091,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleAbility"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.abilityLabel.setName("text-ability-label"); @@ -1103,7 +1110,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleNature"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.natureLabel.setName("text-nature-label"); @@ -1122,7 +1129,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleTera"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.teraLabel.setName("text-tera-label"); @@ -1141,7 +1148,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY, i18next.t("starterSelectUiHandler:goFilter"), - TextStyle.PARTY, + TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, ); this.goFilterLabel.setName("text-goFilter-label"); @@ -2202,14 +2209,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarterCosts[this.lastSpecies.speciesId]); options.push({ - label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")}`, + label: `×${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= passiveCost) { starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= passiveCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); globalScene.gameData.saveSystem().then(success => { if (!success) { return globalScene.reset(true); @@ -2242,14 +2249,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { valueReduction ]; options.push({ - label: `x${reductionCost} ${i18next.t("starterSelectUiHandler:reduceCost")}`, + label: `×${reductionCost} ${i18next.t("starterSelectUiHandler:reduceCost")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= reductionCost) { starterData.valueReduction++; if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= reductionCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); globalScene.gameData.saveSystem().then(success => { if (!success) { return globalScene.reset(true); @@ -2276,7 +2283,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // Same species egg menu option. const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[this.lastSpecies.speciesId]); options.push({ - label: `x${sameSpeciesEggCost} ${i18next.t("starterSelectUiHandler:sameSpeciesEgg")}`, + label: `×${sameSpeciesEggCost} ${i18next.t("starterSelectUiHandler:sameSpeciesEgg")}`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= sameSpeciesEggCost) { if (globalScene.gameData.eggs.length >= 99 && !Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { @@ -2295,7 +2302,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= sameSpeciesEggCost; } - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.pokemonCandyCountText.setText(`×${starterData.candyCount}`); const egg = new Egg({ species: this.lastSpecies.speciesId, @@ -2822,7 +2829,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { iconElement: GameObjects.Sprite, controlLabel: GameObjects.Text, ): void { - // biome-ignore lint/suspicious/noImplicitAnyLet: TODO let iconPath: string; // touch controls cannot be rebound as is, and are just emulating a keyboard event. // Additionally, since keyboard controls can be rebound (and will be displayed when they are), we need to have special handling for the touch controls @@ -2856,7 +2862,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting); } - // @ts-ignore: TODO can iconPath actually be undefined? + // @ts-expect-error: TODO can iconPath actually be undefined? iconElement.setTexture(gamepadType, iconPath); iconElement.setPosition(this.instructionRowX, this.instructionRowY); controlLabel.setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY); @@ -3481,7 +3487,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -3565,7 +3571,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonShinyIcon.setY(117); this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); - this.pokemonCandyCountText.setText(`x${globalScene.gameData.starterData[species.speciesId].candyCount}`); + this.pokemonCandyCountText.setText(`×${globalScene.gameData.starterData[species.speciesId].candyCount}`); this.pokemonCandyContainer.setVisible(true); this.pokemonFormText.setY(42); this.pokemonHatchedIcon.setVisible(true); @@ -3819,9 +3825,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? - this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false)); + this.pokemonNumberText.setColor( + this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false), + ); this.pokemonNumberText.setShadowColor( - this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true), + this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true), ); if (forSeen ? this.speciesStarterDexEntry?.seenAttr : this.speciesStarterDexEntry?.caughtAttr) { @@ -4489,7 +4497,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id)); const props = globalScene.gameData.getSpeciesDexAttrProps( diff --git a/src/ui/stats-container.ts b/src/ui/stats-container.ts index 8cc74e64e96..6b89e80b80a 100644 --- a/src/ui/stats-container.ts +++ b/src/ui/stats-container.ts @@ -1,8 +1,8 @@ -import type BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; -import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; -import { PERMANENT_STATS, getStatKey } from "#app/enums/stat"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; +import { getStatKey, PERMANENT_STATS } from "#enums/stat"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui/text"; +import i18next from "i18next"; +import type BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; const ivChartSize = 24; const ivChartStatCoordMultipliers = [ @@ -19,7 +19,7 @@ const ivLabelOffset = [0, sideLabelOffset, -sideLabelOffset, sideLabelOffset, -s const ivChartLabelyOffset = [0, 5, 0, 5, 0, 0]; // doing this so attack does not overlap with (+N) const ivChartStatIndexes = [0, 1, 2, 5, 4, 3]; // swap special attack and speed -const defaultIvChartData = new Array(12).fill(null).map(() => 0); +const defaultIvChartData: number[] = new Array(12).fill(0); export class StatsContainer extends Phaser.GameObjects.Container { private showDiff: boolean; @@ -86,7 +86,7 @@ export class StatsContainer extends Phaser.GameObjects.Container { 4 + (this.showDiff ? 0 : ivChartLabelyOffset[s]), i18next.t(getStatKey(s)), - TextStyle.TOOLTIP_CONTENT, + TextStyle.STATS_HEXAGON, ); statLabel.setOrigin(0.5); @@ -94,7 +94,7 @@ export class StatsContainer extends Phaser.GameObjects.Container { statLabel.x - (this.showDiff ? 0 : ivLabelOffset[s]), statLabel.y + 8, "0", - TextStyle.TOOLTIP_CONTENT, + TextStyle.STATS_HEXAGON, ); this.ivStatValueTexts[s].setOrigin(0.5); @@ -147,7 +147,7 @@ export class StatsContainer extends Phaser.GameObjects.Container { duration: 1000, ease: "Cubic.easeOut", onUpdate: (tween: Phaser.Tweens.Tween) => { - const progress = tween.getValue(); + const progress = tween.getValue() ?? 1; const interpolatedData = ivChartData.map( (v: number, i: number) => v * progress + lastIvChartData[i] * (1 - progress), ); diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index d30322de293..da0a7f9a40f 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -1,45 +1,44 @@ -import { starterColors } from "#app/global-vars/starter-colors"; -import { globalScene } from "#app/global-scene"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "#app/ui/ui-handler"; -import { - getLocalizedSpriteKey, - rgbHexToRgba, - padInt, - getEnumValues, - fixedInt, - isNullOrUndefined, - toReadableString, - formatStat, - getShinyDescriptor, -} from "#app/utils/common"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; -import { argbFromRgba } from "@material/material-color-utilities"; -import { getTypeRgb } from "#app/data/type"; -import { PokemonType } from "#enums/pokemon-type"; -import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag } from "#app/ui/text"; -import type Move from "#app/data/moves/move"; -import { MoveCategory } from "#enums/MoveCategory"; -import { getPokeballAtlasKey } from "#app/data/pokeball"; -import { getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { getLevelRelExp, getLevelTotalExp } from "#app/data/exp"; -import { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { StatusEffect } from "#enums/status-effect"; -import { getBiomeName } from "#app/data/balance/biomes"; -import { getNatureName, getNatureStatMultiplier } from "#app/data/nature"; +import type { Ability } from "#abilities/ability"; import { loggedInUser } from "#app/account"; -import type { Variant } from "#app/sprites/variant"; -import { getVariantTint } from "#app/sprites/variant"; +import { globalScene } from "#app/global-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; +import { getBiomeName } from "#balance/biomes"; +import { getStarterValueFriendshipCap, speciesStarterCosts } from "#balance/starters"; +import { getLevelRelExp, getLevelTotalExp } from "#data/exp"; +import { getGenderColor, getGenderSymbol } from "#data/gender"; +import { getNatureName, getNatureStatMultiplier } from "#data/nature"; +import { getPokeballAtlasKey } from "#data/pokeball"; +import { getTypeRgb } from "#data/type"; import { Button } from "#enums/buttons"; -import type { Ability } from "#app/data/abilities/ability"; -import i18next from "i18next"; -import { modifierSortFunc } from "#app/modifier/modifier"; -import { PlayerGender } from "#enums/player-gender"; -import { Stat, PERMANENT_STATS, getStatKey } from "#enums/stat"; +import { MoveCategory } from "#enums/MoveCategory"; import { Nature } from "#enums/nature"; -import { achvs } from "#app/system/achv"; +import { PlayerGender } from "#enums/player-gender"; +import { PokemonType } from "#enums/pokemon-type"; +import { getStatKey, PERMANENT_STATS, Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon } from "#field/pokemon"; +import { modifierSortFunc, PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { Move } from "#moves/move"; +import type { PokemonMove } from "#moves/pokemon-move"; +import type { Variant } from "#sprites/variant"; +import { getVariantTint } from "#sprites/variant"; +import { achvs } from "#system/achv"; +import { addBBCodeTextObject, addTextObject, getBBCodeFrag, TextStyle } from "#ui/text"; +import { UiHandler } from "#ui/ui-handler"; +import { + fixedInt, + formatStat, + getLocalizedSpriteKey, + getShinyDescriptor, + isNullOrUndefined, + padInt, + rgbHexToRgba, + toReadableString, +} from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { argbFromRgba } from "@material/material-color-utilities"; +import i18next from "i18next"; enum Page { PROFILE, @@ -64,7 +63,7 @@ interface abilityContainer { descriptionText: Phaser.GameObjects.Text | null; } -export default class SummaryUiHandler extends UiHandler { +export class SummaryUiHandler extends UiHandler { private summaryUiMode: SummaryUiMode; private summaryContainer: Phaser.GameObjects.Container; @@ -117,7 +116,7 @@ export default class SummaryUiHandler extends UiHandler { private pokemon: PlayerPokemon | null; private playerParty: boolean; - /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items**/ + /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items*/ private newMove: Move | null; private moveSelectFunction: Function | null; private transitioning: boolean; @@ -148,7 +147,7 @@ export default class SummaryUiHandler extends UiHandler { this.tabSprite.setOrigin(1, 1); this.summaryContainer.add(this.tabSprite); - const summaryLabel = addTextObject(4, -165, i18next.t("pokemonSummary:pokemonInfo"), TextStyle.SUMMARY); + const summaryLabel = addTextObject(4, -165, i18next.t("pokemonSummary:pokemonInfo"), TextStyle.SUMMARY_HEADER); summaryLabel.setOrigin(0, 1); this.summaryContainer.add(summaryLabel); @@ -418,7 +417,7 @@ export default class SummaryUiHandler extends UiHandler { } this.candyCountText.setText( - `x${globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()].candyCount}`, + `×${globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()].candyCount}`, ); this.candyShadow.setCrop(0, 0, 16, candyCropY); @@ -430,7 +429,7 @@ export default class SummaryUiHandler extends UiHandler { this.friendshipShadow.on("pointerout", () => globalScene.ui.hideTooltip()); } - this.friendshipText.setText(`${this.pokemon?.friendship || "0"} / 255`); + this.friendshipText.setText(` ${this.pokemon?.friendship || "0"}/255`); this.friendshipShadow.setCrop(0, 0, 16, 16 - 16 * ((this.pokemon?.friendship || 0) / 255)); @@ -864,7 +863,7 @@ export default class SummaryUiHandler extends UiHandler { 141 + luckLabelText.displayWidth + 2, 28, this.pokemon.getLuck().toString(), - TextStyle.SUMMARY, + TextStyle.LUCK_VALUE, ); luckText.setOrigin(0, 0); luckText.setTint(getVariantTint(Math.min(this.pokemon.getLuck() - 1, 2) as Variant)); @@ -917,11 +916,11 @@ export default class SummaryUiHandler extends UiHandler { abilityInfo.labelImage.setOrigin(0, 0); profileContainer.add(abilityInfo.labelImage); - abilityInfo.nameText = addTextObject(7, 66, abilityInfo.ability?.name!, TextStyle.SUMMARY_ALT); // TODO: is this bang correct? + abilityInfo.nameText = addTextObject(7, 68, abilityInfo.ability?.name!, TextStyle.SUMMARY_ALT); // TODO: is this bang correct? abilityInfo.nameText.setOrigin(0, 1); profileContainer.add(abilityInfo.nameText); - abilityInfo.descriptionText = addTextObject(7, 69, abilityInfo.ability?.description!, TextStyle.WINDOW_ALT, { + abilityInfo.descriptionText = addTextObject(7, 71, abilityInfo.ability?.description!, TextStyle.WINDOW_ALT, { wordWrap: { width: 1224 }, }); // TODO: is this bang correct? abilityInfo.descriptionText.setOrigin(0, 0); @@ -1000,16 +999,16 @@ export default class SummaryUiHandler extends UiHandler { 16 * rowIndex, statName, natureStatMultiplier === 1 - ? TextStyle.SUMMARY + ? TextStyle.SUMMARY_STATS : natureStatMultiplier > 1 - ? TextStyle.SUMMARY_PINK - : TextStyle.SUMMARY_BLUE, + ? TextStyle.SUMMARY_STATS_PINK + : TextStyle.SUMMARY_STATS_BLUE, ); const ivLabel = addTextObject( 115 * colIndex + (colIndex === 1 ? 5 : 0), 16 * rowIndex, statName, - this.pokemon?.ivs[stat] === 31 ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, + this.pokemon?.ivs[stat] === 31 ? TextStyle.SUMMARY_STATS_GOLD : TextStyle.SUMMARY_STATS, ); statLabel.setOrigin(0.5, 0); diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index 8106e4de2da..5ea89929cda 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -1,18 +1,18 @@ +import { globalScene } from "#app/global-scene"; +import { SubstituteTag } from "#data/battler-tags"; import { BattlerIndex } from "#enums/battler-index"; -import { UiMode } from "#enums/ui-mode"; -import UiHandler from "./ui-handler"; -import { isNullOrUndefined, fixedInt } from "#app/utils/common"; -import { getMoveTargets } from "#app/data/moves/move-utils"; import { Button } from "#enums/buttons"; import type { MoveId } from "#enums/move-id"; -import type Pokemon from "#app/field/pokemon"; -import type { ModifierBar } from "#app/modifier/modifier"; -import { SubstituteTag } from "#app/data/battler-tags"; -import { globalScene } from "#app/global-scene"; +import { UiMode } from "#enums/ui-mode"; +import type { Pokemon } from "#field/pokemon"; +import type { ModifierBar } from "#modifiers/modifier"; +import { getMoveTargets } from "#moves/move-utils"; +import { UiHandler } from "#ui/ui-handler"; +import { fixedInt, isNullOrUndefined } from "#utils/common"; export type TargetSelectCallback = (targets: BattlerIndex[]) => void; -export default class TargetSelectUiHandler extends UiHandler { +export class TargetSelectUiHandler extends UiHandler { private fieldIndex: number; private move: MoveId; private targetSelectCallback: TargetSelectCallback; @@ -157,8 +157,8 @@ export default class TargetSelectUiHandler extends UiHandler { yoyo: true, onUpdate: t => { for (const target of this.targetsHighlighted) { - target.setAlpha(t.getValue()); - this.highlightItems(target.id, t.getValue()); + target.setAlpha(t.getValue() ?? 1); + this.highlightItems(target.id, t.getValue() ?? 1); } }, }); diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/test-dialogue-ui-handler.ts index b1e5047955a..c57c73ca777 100644 --- a/src/ui/test-dialogue-ui-handler.ts +++ b/src/ui/test-dialogue-ui-handler.ts @@ -1,13 +1,13 @@ -import type { InputFieldConfig } from "./form-modal-ui-handler"; -import { FormModalUiHandler } from "./form-modal-ui-handler"; -import type { ModalConfig } from "./modal-ui-handler"; -import i18next from "i18next"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { OptionSelectItem } from "./abstact-option-select-ui-handler"; -import { isNullOrUndefined } from "#app/utils/common"; import { UiMode } from "#enums/ui-mode"; +import type { PlayerPokemon } from "#field/pokemon"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { isNullOrUndefined } from "#utils/common"; +import i18next from "i18next"; -export default class TestDialogueUiHandler extends FormModalUiHandler { +export class TestDialogueUiHandler extends FormModalUiHandler { keys: string[]; setup() { diff --git a/src/ui/text.ts b/src/ui/text.ts index 8812d8ee4a8..b2a1894c85c 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -1,37 +1,58 @@ +import { globalScene } from "#app/global-scene"; import { EggTier } from "#enums/egg-type"; +import { ModifierTier } from "#enums/modifier-tier"; import { UiTheme } from "#enums/ui-theme"; +import i18next from "#plugins/i18n"; import type Phaser from "phaser"; import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; -import InputText from "phaser3-rex-plugins/plugins/inputtext"; -import { globalScene } from "#app/global-scene"; -import { ModifierTier } from "../enums/modifier-tier"; -import i18next from "#app/plugins/i18n"; +import type InputText from "phaser3-rex-plugins/plugins/inputtext"; export enum TextStyle { MESSAGE, WINDOW, WINDOW_ALT, + WINDOW_BATTLE_COMMAND, BATTLE_INFO, PARTY, PARTY_RED, + PARTY_CANCEL_BUTTON, + INSTRUCTIONS_TEXT, + MOVE_LABEL, SUMMARY, + SUMMARY_DEX_NUM, + SUMMARY_DEX_NUM_GOLD, SUMMARY_ALT, + SUMMARY_HEADER, SUMMARY_RED, SUMMARY_BLUE, SUMMARY_PINK, SUMMARY_GOLD, SUMMARY_GRAY, SUMMARY_GREEN, + SUMMARY_STATS, + SUMMARY_STATS_BLUE, + SUMMARY_STATS_PINK, + SUMMARY_STATS_GOLD, + LUCK_VALUE, + STATS_HEXAGON, + GROWTH_RATE_TYPE, MONEY, // Money default styling (pale yellow) MONEY_WINDOW, // Money displayed in Windows (needs different colors based on theme) + HEADER_LABEL, STATS_LABEL, STATS_VALUE, SETTINGS_VALUE, SETTINGS_LABEL, + SETTINGS_LABEL_NAVBAR, SETTINGS_SELECTED, SETTINGS_LOCKED, + EGG_LIST, + EGG_SUMMARY_NAME, + EGG_SUMMARY_DEX, + STARTER_VALUE_LIMIT, TOOLTIP_TITLE, TOOLTIP_CONTENT, + FILTER_BAR_MAIN, MOVE_INFO_CONTENT, MOVE_PP_FULL, MOVE_PP_HALF_FULL, @@ -73,10 +94,6 @@ export function addTextObject( ret.setLineSpacing(scale * 30); } - if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") { - ret.setLineSpacing(ret.lineSpacing + 35); - } - return ret; } @@ -122,10 +139,6 @@ export function addBBCodeTextObject( ret.setLineSpacing(scale * 60); } - if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") { - ret.setLineSpacing(ret.lineSpacing + 35); - } - return ret; } @@ -139,8 +152,7 @@ export function addTextInputObject( ): InputText { const { scale, styleOptions } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions); - const ret = new InputText(globalScene, x, y, width, height, styleOptions as InputText.IConfig); - globalScene.add.existing(ret); + const ret = globalScene.add.rexInputText(x, y, width, height, styleOptions as InputText.IConfig); ret.setScale(scale); return ret; @@ -154,7 +166,7 @@ export function getTextStyleOptions( const lang = i18next.resolvedLanguage; let shadowXpos = 4; let shadowYpos = 5; - let scale = 0.1666666667; + const scale = 0.1666666667; const defaultFontSize = 96; let styleOptions: Phaser.Types.GameObjects.Text.TextStyle = { @@ -166,13 +178,58 @@ export function getTextStyleOptions( }, }; - if (i18next.resolvedLanguage === "ja") { - scale = 0.1388888889; - styleOptions.padding = { top: 2, bottom: 4 }; - } - switch (style) { - case TextStyle.SUMMARY: + case TextStyle.SUMMARY: { + const fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { top: 6, bottom: 4 }; + break; + } + styleOptions.fontSize = fontSizeLabel; + break; + } + // shadowXpos = 5; + // shadowYpos = 5; + // break; + case TextStyle.SUMMARY_HEADER: { + let fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { bottom: 7 }; + fontSizeLabel = "80px"; + break; + } + styleOptions.fontSize = fontSizeLabel; + break; + } + // shadowXpos = 5; + // shadowYpos = 5; + // break; + case TextStyle.SUMMARY_DEX_NUM: { + const fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { top: 2, bottom: 10 }; + break; + } + styleOptions.fontSize = fontSizeLabel; + shadowXpos = 5; + shadowYpos = 5; + break; + } + case TextStyle.SUMMARY_DEX_NUM_GOLD: { + const fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { top: 2, bottom: 10 }; + break; + } + styleOptions.fontSize = fontSizeLabel; + shadowXpos = 5; + shadowYpos = 5; + break; + } case TextStyle.SUMMARY_ALT: case TextStyle.SUMMARY_BLUE: case TextStyle.SUMMARY_RED: @@ -180,6 +237,10 @@ export function getTextStyleOptions( case TextStyle.SUMMARY_GOLD: case TextStyle.SUMMARY_GRAY: case TextStyle.SUMMARY_GREEN: + case TextStyle.SUMMARY_STATS: + case TextStyle.SUMMARY_STATS_BLUE: + case TextStyle.SUMMARY_STATS_PINK: + case TextStyle.SUMMARY_STATS_GOLD: case TextStyle.WINDOW: case TextStyle.WINDOW_ALT: case TextStyle.ME_OPTION_DEFAULT: @@ -187,6 +248,43 @@ export function getTextStyleOptions( shadowXpos = 3; shadowYpos = 3; break; + case TextStyle.LUCK_VALUE: { + const fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { top: -6, bottom: 2 }; + break; + } + styleOptions.fontSize = fontSizeLabel; + shadowXpos = 3; + shadowYpos = 4; + break; + } + case TextStyle.GROWTH_RATE_TYPE: { + switch (lang) { + case "ja": + styleOptions.padding = { left: 24 }; + break; + } + styleOptions.fontSize = defaultFontSize - 30; + shadowXpos = 3; + shadowYpos = 3; + break; + } + case TextStyle.WINDOW_BATTLE_COMMAND: { + let fontSizeLabel = "96px"; + switch (lang) { + case "ja": + styleOptions.padding = { top: 2 }; + fontSizeLabel = "92px"; + break; + } + styleOptions.fontSize = fontSizeLabel; + break; + } + // shadowXpos = 5; + // shadowYpos = 5; + // break; case TextStyle.STATS_LABEL: { let fontSizeLabel = "96px"; switch (lang) { @@ -218,10 +316,80 @@ export function getTextStyleOptions( break; } case TextStyle.MESSAGE: - case TextStyle.SETTINGS_LABEL: - case TextStyle.SETTINGS_LOCKED: - case TextStyle.SETTINGS_SELECTED: + styleOptions.fontSize = defaultFontSize; break; + case TextStyle.HEADER_LABEL: { + switch (lang) { + case "ja": + styleOptions.padding = { top: 6 }; + break; + } + break; + } + case TextStyle.SETTINGS_VALUE: + case TextStyle.SETTINGS_LABEL: { + shadowXpos = 3; + shadowYpos = 3; + let fontSizeValue = "96px"; + switch (lang) { + case "ja": + fontSizeValue = "80px"; + styleOptions.padding = { top: 10 }; + break; + default: + fontSizeValue = "96px"; + break; + } + styleOptions.fontSize = fontSizeValue; + break; + } + case TextStyle.SETTINGS_LABEL_NAVBAR: { + shadowXpos = 3; + shadowYpos = 3; + let fontSizeValue = "96px"; + switch (lang) { + case "ja": + fontSizeValue = "92px"; + break; + default: + fontSizeValue = "96px"; + break; + } + styleOptions.fontSize = fontSizeValue; + break; + } + case TextStyle.SETTINGS_LOCKED: { + shadowXpos = 3; + shadowYpos = 3; + let fontSizeValue = "96px"; + switch (lang) { + case "ja": + fontSizeValue = "80px"; + styleOptions.padding = { top: 10 }; + break; + default: + fontSizeValue = "96px"; + break; + } + styleOptions.fontSize = fontSizeValue; + break; + } + case TextStyle.SETTINGS_SELECTED: { + shadowXpos = 3; + shadowYpos = 3; + let fontSizeValue = "96px"; + switch (lang) { + case "ja": + fontSizeValue = "80px"; + styleOptions.padding = { top: 10 }; + break; + default: + fontSizeValue = "96px"; + break; + } + styleOptions.fontSize = fontSizeValue; + break; + } case TextStyle.BATTLE_INFO: case TextStyle.MONEY: case TextStyle.MONEY_WINDOW: @@ -231,11 +399,108 @@ export function getTextStyleOptions( shadowYpos = 3.5; break; case TextStyle.PARTY: - case TextStyle.PARTY_RED: + case TextStyle.PARTY_RED: { + switch (lang) { + case "ja": + styleOptions.padding = { top: -12, bottom: 4 }; + break; + } styleOptions.fontSize = defaultFontSize - 30; styleOptions.fontFamily = "pkmnems"; break; - case TextStyle.TOOLTIP_CONTENT: + } + case TextStyle.PARTY_CANCEL_BUTTON: { + switch (lang) { + case "ja": + styleOptions.fontSize = defaultFontSize - 42; + styleOptions.padding = { top: 4 }; + break; + default: + styleOptions.fontSize = defaultFontSize - 30; + styleOptions.padding = { left: 12 }; + break; + } + styleOptions.fontFamily = "pkmnems"; + break; + } + case TextStyle.INSTRUCTIONS_TEXT: { + switch (lang) { + case "ja": + styleOptions.padding = { top: -3, bottom: 4 }; + break; + } + styleOptions.fontSize = defaultFontSize - 30; + styleOptions.fontFamily = "pkmnems"; + shadowXpos = 3; + shadowYpos = 3; + break; + } + case TextStyle.MOVE_LABEL: { + switch (lang) { + case "ja": + styleOptions.fontSize = defaultFontSize - 16; + styleOptions.padding = { top: -14, bottom: 8 }; + break; + default: + styleOptions.fontSize = defaultFontSize - 30; + break; + } + styleOptions.fontFamily = "pkmnems"; + break; + } + case TextStyle.EGG_LIST: + styleOptions.fontSize = defaultFontSize - 34; + break; + case TextStyle.EGG_SUMMARY_NAME: { + switch (lang) { + case "ja": + styleOptions.padding = { top: -1 }; + break; + } + break; + } + case TextStyle.EGG_SUMMARY_DEX: { + switch (lang) { + case "ja": + styleOptions.padding = { top: 2 }; + break; + } + break; + } + case TextStyle.STARTER_VALUE_LIMIT: + styleOptions.fontSize = defaultFontSize - 36; + shadowXpos = 3; + shadowYpos = 3; + break; + case TextStyle.TOOLTIP_CONTENT: { + switch (lang) { + case "ja": + styleOptions.fontSize = defaultFontSize - 44; + styleOptions.padding = { top: 10, right: 10 }; + break; + default: + styleOptions.fontSize = defaultFontSize - 32; + break; + } + shadowXpos = 3; + shadowYpos = 3; + break; + } + case TextStyle.FILTER_BAR_MAIN: { + switch (lang) { + case "ja": + styleOptions.fontSize = defaultFontSize - 48; + styleOptions.padding = { top: 10, right: 10 }; + break; + default: + styleOptions.fontSize = defaultFontSize - 32; + break; + } + shadowXpos = 3; + shadowYpos = 3; + break; + } + case TextStyle.STATS_HEXAGON: styleOptions.fontSize = defaultFontSize - 32; shadowXpos = 3; shadowYpos = 3; @@ -300,7 +565,7 @@ export function getTextWithColors( ): string { // Apply primary styling before anything else let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; + const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0]; /* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY} * or it will look wrong in legacy mode because of the different window background color @@ -320,7 +585,7 @@ export function getTextWithColors( }); // Remove extra style block at the end - return text.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, ""); + return text.replace(/\[color=[^[]*\]\[shadow=[^[]*\]\[\/color\]\[\/shadow\]/gi, ""); } // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a giant switch which is the best option. @@ -330,9 +595,14 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui case TextStyle.MESSAGE: return !shadow ? "#f8f8f8" : "#6b5a73"; case TextStyle.WINDOW: + case TextStyle.WINDOW_BATTLE_COMMAND: case TextStyle.MOVE_INFO_CONTENT: + case TextStyle.STATS_HEXAGON: case TextStyle.MOVE_PP_FULL: + case TextStyle.EGG_LIST: case TextStyle.TOOLTIP_CONTENT: + case TextStyle.FILTER_BAR_MAIN: + case TextStyle.STARTER_VALUE_LIMIT: case TextStyle.SETTINGS_VALUE: if (isLegacyTheme) { return !shadow ? "#484848" : "#d0d0c8"; @@ -361,12 +631,22 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui } return !shadow ? "#f8f8f8" : "#6b5a73"; case TextStyle.PARTY: + case TextStyle.PARTY_CANCEL_BUTTON: + case TextStyle.INSTRUCTIONS_TEXT: + case TextStyle.MOVE_LABEL: return !shadow ? "#f8f8f8" : "#707070"; case TextStyle.PARTY_RED: return !shadow ? "#f89890" : "#984038"; case TextStyle.SUMMARY: + case TextStyle.SUMMARY_DEX_NUM: + case TextStyle.SUMMARY_HEADER: + case TextStyle.SUMMARY_STATS: + case TextStyle.EGG_SUMMARY_NAME: + case TextStyle.EGG_SUMMARY_DEX: + case TextStyle.LUCK_VALUE: return !shadow ? "#f8f8f8" : "#636363"; case TextStyle.SUMMARY_ALT: + case TextStyle.GROWTH_RATE_TYPE: if (isLegacyTheme) { return !shadow ? "#f8f8f8" : "#636363"; } @@ -375,10 +655,14 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui case TextStyle.TOOLTIP_TITLE: return !shadow ? "#e70808" : "#ffbd73"; case TextStyle.SUMMARY_BLUE: + case TextStyle.SUMMARY_STATS_BLUE: return !shadow ? "#40c8f8" : "#006090"; case TextStyle.SUMMARY_PINK: + case TextStyle.SUMMARY_STATS_PINK: return !shadow ? "#f89890" : "#984038"; case TextStyle.SUMMARY_GOLD: + case TextStyle.SUMMARY_DEX_NUM_GOLD: + case TextStyle.SUMMARY_STATS_GOLD: case TextStyle.MONEY: return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold case TextStyle.MONEY_WINDOW: @@ -399,6 +683,8 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui case TextStyle.SUMMARY_GREEN: return !shadow ? "#78c850" : "#306850"; case TextStyle.SETTINGS_LABEL: + case TextStyle.SETTINGS_LABEL_NAVBAR: + case TextStyle.HEADER_LABEL: case TextStyle.PERFECT_IV: return !shadow ? "#f8b050" : "#c07800"; case TextStyle.SETTINGS_SELECTED: diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index 5f5116a2da0..6deaf1405d7 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -1,11 +1,11 @@ -import { fixedInt } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; -import { BattleSceneEventType } from "../events/battle-scene"; import { EaseType } from "#enums/ease-type"; import { TimeOfDay } from "#enums/time-of-day"; +import { BattleSceneEventType } from "#events/battle-scene"; +import { fixedInt } from "#utils/common"; /** A small self contained UI element that displays the time of day as an icon */ -export default class TimeOfDayWidget extends Phaser.GameObjects.Container { +export class TimeOfDayWidget extends Phaser.GameObjects.Container { /** The {@linkcode Phaser.GameObjects.Sprite} that represents the foreground of the current time of day */ private readonly timeOfDayIconFgs: Phaser.GameObjects.Sprite[] = new Array(2); /** The {@linkcode Phaser.GameObjects.Sprite} that represents the middle-ground of the current time of day */ diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 50e77bbdd14..b7c37538a3e 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -1,19 +1,19 @@ -import OptionSelectUiHandler from "./settings/option-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { fixedInt, randInt, randItem } from "#app/utils/common"; -import { TextStyle, addTextObject } from "./text"; -import { getSplashMessages } from "../data/splash-messages"; -import i18next from "i18next"; -import { TimedEventDisplay } from "#app/timed-event-manager"; -import { version } from "../../package.json"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { globalScene } from "#app/global-scene"; -import type { SpeciesId } from "#enums/species-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { PlayerGender } from "#enums/player-gender"; +import { pokerogueApi } from "#api/pokerogue-api"; import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { TimedEventDisplay } from "#app/timed-event-manager"; +import { getSplashMessages } from "#data/splash-messages"; +import { PlayerGender } from "#enums/player-gender"; +import type { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { version } from "#package.json"; +import { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { fixedInt, randInt, randItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import i18next from "i18next"; -export default class TitleUiHandler extends OptionSelectUiHandler { +export class TitleUiHandler extends OptionSelectUiHandler { /** If the stats can not be retrieved, use this fallback value */ private static readonly BATTLES_WON_FALLBACK: number = -1; diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index d3784c1225c..c7b25c90205 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -1,13 +1,13 @@ import { globalScene } from "#app/global-scene"; -import type { TextStyle } from "./text"; -import { getTextColor } from "./text"; -import type { UiMode } from "#enums/ui-mode"; import type { Button } from "#enums/buttons"; +import type { UiMode } from "#enums/ui-mode"; +import type { TextStyle } from "#ui/text"; +import { getTextColor } from "#ui/text"; /** * A basic abstract class to act as a holder and processor for UI elements. */ -export default abstract class UiHandler { +export abstract class UiHandler { protected mode: number | null; protected cursor = 0; public active = false; diff --git a/src/ui/ui-theme.ts b/src/ui/ui-theme.ts index c3931aea23b..f2d434c024a 100644 --- a/src/ui/ui-theme.ts +++ b/src/ui/ui-theme.ts @@ -1,6 +1,6 @@ -import { UiTheme } from "#enums/ui-theme"; -import { legacyCompatibleImages } from "#app/scene-base"; import { globalScene } from "#app/global-scene"; +import { legacyCompatibleImages } from "#app/scene-base"; +import { UiTheme } from "#enums/ui-theme"; export enum WindowVariant { NORMAL, diff --git a/src/ui/ui.ts b/src/ui/ui.ts index ad496df6382..394409bcb9d 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -1,63 +1,63 @@ import { globalScene } from "#app/global-scene"; -import type UiHandler from "./ui-handler"; -import BattleMessageUiHandler from "./battle-message-ui-handler"; -import CommandUiHandler from "./command-ui-handler"; -import PartyUiHandler from "./party-ui-handler"; -import FightUiHandler from "./fight-ui-handler"; -import MessageUiHandler from "./message-ui-handler"; -import ConfirmUiHandler from "./confirm-ui-handler"; -import ModifierSelectUiHandler from "./modifier-select-ui-handler"; -import BallUiHandler from "./ball-ui-handler"; -import SummaryUiHandler from "./summary-ui-handler"; -import StarterSelectUiHandler from "./starter-select-ui-handler"; -import EvolutionSceneHandler from "./evolution-scene-handler"; -import TargetSelectUiHandler from "./target-select-ui-handler"; -import SettingsUiHandler from "./settings/settings-ui-handler"; -import SettingsGamepadUiHandler from "./settings/settings-gamepad-ui-handler"; -import GameChallengesUiHandler from "./challenges-select-ui-handler"; -import { TextStyle, addTextObject } from "./text"; -import AchvBar from "./achv-bar"; -import MenuUiHandler from "./menu-ui-handler"; -import AchvsUiHandler from "./achvs-ui-handler"; -import OptionSelectUiHandler from "./settings/option-select-ui-handler"; -import EggHatchSceneHandler from "./egg-hatch-scene-handler"; -import EggListUiHandler from "./egg-list-ui-handler"; -import EggGachaUiHandler from "./egg-gacha-ui-handler"; -import PokedexUiHandler from "./pokedex-ui-handler"; -import { addWindow } from "./ui-theme"; -import LoginFormUiHandler from "./login-form-ui-handler"; -import RegistrationFormUiHandler from "./registration-form-ui-handler"; -import LoadingModalUiHandler from "./loading-modal-ui-handler"; -import { executeIf } from "#app/utils/common"; -import GameStatsUiHandler from "./game-stats-ui-handler"; -import AwaitableUiHandler from "./awaitable-ui-handler"; -import SaveSlotSelectUiHandler from "./save-slot-select-ui-handler"; -import TitleUiHandler from "./title-ui-handler"; -import SavingIconHandler from "./saving-icon-handler"; -import UnavailableModalUiHandler from "./unavailable-modal-ui-handler"; -import SessionReloadModalUiHandler from "./session-reload-modal-ui-handler"; import type { Button } from "#enums/buttons"; -import i18next from "i18next"; -import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler"; -import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; -import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler"; -import SettingsDisplayUiHandler from "./settings/settings-display-ui-handler"; -import SettingsAudioUiHandler from "./settings/settings-audio-ui-handler"; -import { PlayerGender } from "#enums/player-gender"; -import type BgmBar from "#app/ui/bgm-bar"; -import RenameFormUiHandler from "./rename-form-ui-handler"; -import AdminUiHandler from "./admin-ui-handler"; -import RunHistoryUiHandler from "./run-history-ui-handler"; -import RunInfoUiHandler from "./run-info-ui-handler"; -import EggSummaryUiHandler from "./egg-summary-ui-handler"; -import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler"; -import AutoCompleteUiHandler from "./autocomplete-ui-handler"; import { Device } from "#enums/devices"; -import MysteryEncounterUiHandler from "./mystery-encounter-ui-handler"; -import PokedexScanUiHandler from "./pokedex-scan-ui-handler"; -import PokedexPageUiHandler from "./pokedex-page-ui-handler"; -import { NavigationManager } from "./settings/navigationMenu"; +import { PlayerGender } from "#enums/player-gender"; import { UiMode } from "#enums/ui-mode"; +import { AchvBar } from "#ui/achv-bar"; +import { AchvsUiHandler } from "#ui/achvs-ui-handler"; +import { AdminUiHandler } from "#ui/admin-ui-handler"; +import { AutoCompleteUiHandler } from "#ui/autocomplete-ui-handler"; +import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; +import { BallUiHandler } from "#ui/ball-ui-handler"; +import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler"; +import type { BgmBar } from "#ui/bgm-bar"; +import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler"; +import { CommandUiHandler } from "#ui/command-ui-handler"; +import { ConfirmUiHandler } from "#ui/confirm-ui-handler"; +import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler"; +import { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler"; +import { EggListUiHandler } from "#ui/egg-list-ui-handler"; +import { EggSummaryUiHandler } from "#ui/egg-summary-ui-handler"; +import { EvolutionSceneHandler } from "#ui/evolution-scene-handler"; +import { FightUiHandler } from "#ui/fight-ui-handler"; +import { GameStatsUiHandler } from "#ui/game-stats-ui-handler"; +import { GamepadBindingUiHandler } from "#ui/gamepad-binding-ui-handler"; +import { KeyboardBindingUiHandler } from "#ui/keyboard-binding-ui-handler"; +import { LoadingModalUiHandler } from "#ui/loading-modal-ui-handler"; +import { LoginFormUiHandler } from "#ui/login-form-ui-handler"; +import { MenuUiHandler } from "#ui/menu-ui-handler"; +import { MessageUiHandler } from "#ui/message-ui-handler"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler"; +import { NavigationManager } from "#ui/navigationMenu"; +import { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; +import { PartyUiHandler } from "#ui/party-ui-handler"; +import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler"; +import { PokedexScanUiHandler } from "#ui/pokedex-scan-ui-handler"; +import { PokedexUiHandler } from "#ui/pokedex-ui-handler"; +import { RegistrationFormUiHandler } from "#ui/registration-form-ui-handler"; +import { RenameFormUiHandler } from "#ui/rename-form-ui-handler"; +import { RunHistoryUiHandler } from "#ui/run-history-ui-handler"; +import { RunInfoUiHandler } from "#ui/run-info-ui-handler"; +import { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler"; +import { SavingIconHandler } from "#ui/saving-icon-handler"; +import { SessionReloadModalUiHandler } from "#ui/session-reload-modal-ui-handler"; +import { SettingsAudioUiHandler } from "#ui/settings-audio-ui-handler"; +import { SettingsDisplayUiHandler } from "#ui/settings-display-ui-handler"; +import { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler"; +import { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler"; +import { SettingsUiHandler } from "#ui/settings-ui-handler"; +import { StarterSelectUiHandler } from "#ui/starter-select-ui-handler"; +import { SummaryUiHandler } from "#ui/summary-ui-handler"; +import { TargetSelectUiHandler } from "#ui/target-select-ui-handler"; +import { TestDialogueUiHandler } from "#ui/test-dialogue-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { TitleUiHandler } from "#ui/title-ui-handler"; +import type { UiHandler } from "#ui/ui-handler"; +import { addWindow } from "#ui/ui-theme"; +import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler"; +import { executeIf } from "#utils/common"; +import i18next from "i18next"; const transitionModes = [ UiMode.SAVE_SLOT, @@ -103,7 +103,7 @@ const noTransitionModes = [ UiMode.RUN_INFO, ]; -export default class UI extends Phaser.GameObjects.Container { +export class UI extends Phaser.GameObjects.Container { private mode: UiMode; private modeChain: UiMode[]; public handlers: UiHandler[]; diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index 5bed55ec24a..420a47664a7 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -1,14 +1,14 @@ -import type { ModalConfig } from "./modal-ui-handler"; -import { ModalUiHandler } from "./modal-ui-handler"; -import { addTextObject, TextStyle } from "./text"; -import type { UiMode } from "#enums/ui-mode"; import { updateUserInfo } from "#app/account"; -import { sessionIdKey } from "#app/utils/common"; -import { removeCookie } from "#app/utils/cookies"; -import i18next from "i18next"; import { globalScene } from "#app/global-scene"; +import type { UiMode } from "#enums/ui-mode"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import { ModalUiHandler } from "#ui/modal-ui-handler"; +import { addTextObject, TextStyle } from "#ui/text"; +import { sessionIdKey } from "#utils/common"; +import { removeCookie } from "#utils/cookies"; +import i18next from "i18next"; -export default class UnavailableModalUiHandler extends ModalUiHandler { +export class UnavailableModalUiHandler extends ModalUiHandler { private reconnectTimer: NodeJS.Timeout | null; private reconnectDuration: number; private reconnectCallback: () => void; diff --git a/src/utils/common.ts b/src/utils/common.ts index e19e5976507..e9ba3acb5e5 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -1,18 +1,26 @@ +import { pokerogueApi } from "#api/pokerogue-api"; import { MoneyFormat } from "#enums/money-format"; import { MoveId } from "#enums/move-id"; +import type { Variant } from "#sprites/variant"; import i18next from "i18next"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import type { Variant } from "#app/sprites/variant"; export type nil = null | undefined; export const MissingTextureKey = "__MISSING"; +// TODO: Draft tests for these utility functions +// TODO: Break up this file +/** + * Convert a `snake_case` string in any capitalization (such as one from an enum reverse mapping) + * into a readable `Title Case` version. + * @param str - The snake case string to be converted. + * @returns The result of converting `str` into title case. + */ export function toReadableString(str: string): string { return str - .replace(/\_/g, " ") + .replace(/_/g, " ") .split(" ") - .map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`) + .map(s => capitalizeFirstLetter(s.toLowerCase())) .join(" "); } @@ -201,19 +209,19 @@ export function formatLargeNumber(count: number, threshold: number): string { let suffix = ""; switch (Math.ceil(ret.length / 3) - 1) { case 1: - suffix = "K"; + suffix = i18next.t("common:abrThousand"); break; case 2: - suffix = "M"; + suffix = i18next.t("common:abrMillion"); break; case 3: - suffix = "B"; + suffix = i18next.t("common:abrBillion"); break; case 4: - suffix = "T"; + suffix = i18next.t("common:abrTrillion"); break; case 5: - suffix = "q"; + suffix = i18next.t("common:abrQuadrillion"); break; default: return "?"; @@ -227,15 +235,31 @@ export function formatLargeNumber(count: number, threshold: number): string { } // Abbreviations from 10^0 to 10^33 -const AbbreviationsLargeNumber: string[] = ["", "K", "M", "B", "t", "q", "Q", "s", "S", "o", "n", "d"]; +function getAbbreviationsLargeNumber(): string[] { + return [ + "", + i18next.t("common:abrThousand"), + i18next.t("common:abrMillion"), + i18next.t("common:abrBillion"), + i18next.t("common:abrTrillion"), + i18next.t("common:abrQuadrillion"), + i18next.t("common:abrQuintillion"), + i18next.t("common:abrSextillion"), + i18next.t("common:abrSeptillion"), + i18next.t("common:abrOctillion"), + i18next.t("common:abrNonillion"), + i18next.t("common:abrDecillion"), + ]; +} export function formatFancyLargeNumber(number: number, rounded = 3): string { + const abbreviations = getAbbreviationsLargeNumber(); let exponent: number; if (number < 1000) { exponent = 0; } else { - const maxExp = AbbreviationsLargeNumber.length - 1; + const maxExp = abbreviations.length - 1; exponent = Math.floor(Math.log(number) / Math.log(1000)); exponent = Math.min(exponent, maxExp); @@ -243,7 +267,7 @@ export function formatFancyLargeNumber(number: number, rounded = 3): string { number /= Math.pow(1000, exponent); } - return `${(exponent === 0) || number % 1 === 0 ? number : number.toFixed(rounded)}${AbbreviationsLargeNumber[exponent]}`; + return `${exponent === 0 || number % 1 === 0 ? number : number.toFixed(rounded)}${abbreviations[exponent]}`; } export function formatMoney(format: MoneyFormat, amount: number) { @@ -257,18 +281,6 @@ export function formatStat(stat: number, forHp = false): string { return formatLargeNumber(stat, forHp ? 100000 : 1000000); } -export function getEnumKeys(enumType: any): string[] { - return Object.values(enumType) - .filter(v => Number.isNaN(Number.parseInt(v!.toString()))) - .map(v => v!.toString()); -} - -export function getEnumValues(enumType: any): number[] { - return Object.values(enumType) - .filter(v => !Number.isNaN(Number.parseInt(v!.toString()))) - .map(v => Number.parseInt(v!.toString())); -} - export function executeIf(condition: boolean, promiseFunc: () => Promise): Promise { return condition ? promiseFunc() : new Promise(resolve => resolve(null)); } @@ -325,6 +337,10 @@ export class NumberHolder { constructor(value: number) { this.value = value; } + + valueOf(): number { + return this.value; + } } export class FixedInt { @@ -333,6 +349,10 @@ export class FixedInt { constructor(value: number) { this.value = value; } + + [Symbol.toPrimitive](_hint: string): number { + return this.value; + } } export function fixedInt(value: number): number { @@ -583,7 +603,7 @@ export function isBetween(num: number, min: number, max: number): boolean { * @param move the move for which the animation filename is needed */ export function animationFileName(move: MoveId): string { - return MoveId[move].toLowerCase().replace(/\_/g, "-"); + return MoveId[move].toLowerCase().replace(/_/g, "-"); } /** @@ -620,25 +640,3 @@ export function coerceArray(input: T): T extends any[] ? T : [T]; export function coerceArray(input: T): T | [T] { return Array.isArray(input) ? input : [input]; } - -/** - * Returns the name of the key that matches the enum [object] value. - * @param input - The enum [object] to check - * @param val - The value to get the key of - * @returns The name of the key with the specified value - * @example - * const thing = { - * one: 1, - * two: 2, - * } as const; - * console.log(enumValueToKey(thing, thing.two)); // output: "two" - * @throws An `Error` if an invalid enum value is passed to the function - */ -export function enumValueToKey>(input: T, val: T[keyof T]): keyof T { - for (const [key, value] of Object.entries(input)) { - if (val === value) { - return key as keyof T; - } - } - throw new Error(`Invalid value passed to \`enumValueToKey\`! Value: ${val}`); -} diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts index 5ed793c0451..06391e6f4c7 100644 --- a/src/utils/cookies.ts +++ b/src/utils/cookies.ts @@ -1,8 +1,8 @@ -import { isBeta } from "./utility-vars"; +import { isBeta } from "#utils/utility-vars"; export function setCookie(cName: string, cValue: string): void { const expiration = new Date(); - expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3 /*7*/); + expiration.setTime(Date.now() + 3600000 * 24 * 30 * 3 /*7*/); document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; } diff --git a/src/utils/data.ts b/src/utils/data.ts index 5a28657d034..932ea38d504 100644 --- a/src/utils/data.ts +++ b/src/utils/data.ts @@ -1,7 +1,7 @@ import { loggedInUser } from "#app/account"; -import type { StarterAttributes } from "#app/system/game-data"; -import { AES, enc } from "crypto-js"; import { saveKey } from "#app/constants"; +import type { StarterAttributes } from "#system/game-data"; +import { AES, enc } from "crypto-js"; /** * Perform a deep copy of an object. diff --git a/src/utils/enums.ts b/src/utils/enums.ts new file mode 100644 index 00000000000..98cb4272ee9 --- /dev/null +++ b/src/utils/enums.ts @@ -0,0 +1,74 @@ +import type { EnumOrObject, EnumValues, NormalEnum, TSNumericEnum } from "#app/@types/enum-types"; +import type { InferKeys } from "#app/@types/type-helpers"; + +/** + * Return the string keys of an Enum object, excluding reverse-mapped numbers. + * @param enumType - The numeric enum to retrieve keys for + * @returns An ordered array of all of `enumType`'s string keys + * @example + * enum fruit { + * apple = 1, + * banana = 2, + * cherry = 3, + * orange = 12, + * }; + * + * console.log(getEnumKeys(fruit)); // output: ["apple", "banana", "cherry", "orange"] + * @remarks + * To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.keys} instead. + */ +export function getEnumKeys(enumType: TSNumericEnum): (keyof E)[] { + // All enum values are either normal numbers or reverse mapped strings, so we can retrieve the keys by filtering out numbers. + return Object.values(enumType).filter(v => typeof v === "string"); +} + +/** + * Return the numeric values of a numeric Enum object, excluding reverse-mapped strings. + * @param enumType - The enum object to retrieve keys for + * @returns An ordered array of all of `enumType`'s number values + * @example + * enum fruit { + * apple = 1, + * banana = 2, + * cherry = 3, + * orange = 12, + * }; + * + * console.log(getEnumValues(fruit)); // output: [1, 2, 3, 12] + * + * @remarks + * To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.values} instead. + */ +// NB: This intentionally does not use `EnumValues` as using `E[keyof E]` leads to improved variable highlighting in IDEs. +export function getEnumValues(enumType: TSNumericEnum): E[keyof E][] { + return Object.values(enumType).filter(v => typeof v !== "string") as E[keyof E][]; +} + +/** + * Return the name of the key that matches the given Enum value. + * Can be used to emulate Typescript reverse mapping for `const object`s or string enums. + * @param object - The {@linkcode NormalEnum} to check + * @param val - The value to get the key of + * @returns The name of the key with the specified value + * @example + * const thing = { + * one: 1, + * two: 2, + * } as const; + * console.log(enumValueToKey(thing, 2)); // output: "two" + * @throws Error if an invalid enum value is passed to the function + * @remarks + * If multiple keys map to the same value, the first one (in insertion order) will be retrieved, + * but the return type will be the union of ALL their corresponding keys. + */ +export function enumValueToKey>( + object: NormalEnum, + val: V, +): InferKeys { + for (const [key, value] of Object.entries(object)) { + if (val === value) { + return key as InferKeys; + } + } + throw new Error(`Invalid value passed to \`enumValueToKey\`! Value: ${val}`); +} diff --git a/src/utils/modifier-utils.ts b/src/utils/modifier-utils.ts index 3be4af3730c..58108e7a599 100644 --- a/src/utils/modifier-utils.ts +++ b/src/utils/modifier-utils.ts @@ -1,3 +1,4 @@ +import { modifierTypes } from "#data/data-lists"; import { ModifierPoolType } from "#enums/modifier-pool-type"; import { dailyStarterModifierPool, @@ -5,10 +6,9 @@ import { modifierPool, trainerModifierPool, wildModifierPool, -} from "#app/modifier/modifier-pools"; -import type { ModifierPool, ModifierTypeFunc } from "#app/@types/modifier-types"; -import { modifierTypes } from "#app/data/data-lists"; -import type { ModifierType } from "#app/modifier/modifier-type"; +} from "#modifiers/modifier-pools"; +import type { ModifierType } from "#modifiers/modifier-type"; +import type { ModifierPool, ModifierTypeFunc } from "#types/modifier-types"; export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool { switch (poolType) { diff --git a/src/utils/pokemon-utils.ts b/src/utils/pokemon-utils.ts index 9f87c36b050..da39cfe11ad 100644 --- a/src/utils/pokemon-utils.ts +++ b/src/utils/pokemon-utils.ts @@ -1,5 +1,5 @@ -import { allSpecies } from "#app/data/data-lists"; -import type PokemonSpecies from "#app/data/pokemon-species"; +import { allSpecies } from "#data/data-lists"; +import type { PokemonSpecies } from "#data/pokemon-species"; import type { SpeciesId } from "#enums/species-id"; /** diff --git a/test/abilities/ability_activation_order.test.ts b/test/abilities/ability_activation_order.test.ts index 0b8e354cc7d..8344ba6be11 100644 --- a/test/abilities/ability_activation_order.test.ts +++ b/test/abilities/ability_activation_order.test.ts @@ -3,7 +3,7 @@ import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/ability_duplication.test.ts b/test/abilities/ability_duplication.test.ts index d392b7043e4..0c88b65a00e 100644 --- a/test/abilities/ability_duplication.test.ts +++ b/test/abilities/ability_duplication.test.ts @@ -1,10 +1,10 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Ability Duplication", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/ability_timing.test.ts b/test/abilities/ability_timing.test.ts index 124591597be..c3e841d8216 100644 --- a/test/abilities/ability_timing.test.ts +++ b/test/abilities/ability_timing.test.ts @@ -1,11 +1,11 @@ -import { BattleStyle } from "#app/enums/battle-style"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import i18next from "#app/plugins/i18n"; -import { UiMode } from "#enums/ui-mode"; import { AbilityId } from "#enums/ability-id"; +import { BattleStyle } from "#enums/battle-style"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { CommandPhase } from "#phases/command-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/analytic.test.ts b/test/abilities/analytic.test.ts index 5dbf4365186..0bacfe6775b 100644 --- a/test/abilities/analytic.test.ts +++ b/test/abilities/analytic.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { isBetween, toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { isBetween, toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/anger-point.test.ts b/test/abilities/anger-point.test.ts new file mode 100644 index 00000000000..576062eadeb --- /dev/null +++ b/test/abilities/anger-point.test.ts @@ -0,0 +1,78 @@ +import { PostReceiveCritStatStageChangeAbAttr } from "#abilities/ability"; +import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Ability - Anger Point", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.BALL_FETCH) + .battleStyle("single") + .criticalHits(false) + .enemySpecies(SpeciesId.MAGIKARP) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH) + .enemyLevel(100); + }); + + it("should set the user's attack stage to +6 when hit by a critical hit", async () => { + game.override.enemyAbility(AbilityId.ANGER_POINT).moveset(MoveId.FALSE_SWIPE); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + const enemy = game.scene.getEnemyPokemon()!; + + // minimize the enemy's attack stage to ensure it is always set to +6 + enemy.setStatStage(Stat.ATK, -6); + vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true); + game.move.select(MoveId.FALSE_SWIPE); + await game.phaseInterceptor.to("BerryPhase"); + expect(enemy.getStatStage(Stat.ATK)).toBe(6); + }); + + it("should only proc once when a multi-hit move crits on the first hit", async () => { + game.override + .moveset(MoveId.BULLET_SEED) + .enemyLevel(50) + .enemyAbility(AbilityId.ANGER_POINT) + .ability(AbilityId.SKILL_LINK); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true); + const angerPointSpy = vi.spyOn(PostReceiveCritStatStageChangeAbAttr.prototype, "apply"); + game.move.select(MoveId.BULLET_SEED); + await game.phaseInterceptor.to("BerryPhase"); + expect(angerPointSpy).toHaveBeenCalledTimes(1); + }); + + it("should set a contrary user's attack stage to -6 when hit by a critical hit", async () => { + game.override + .enemyAbility(AbilityId.ANGER_POINT) + .enemyPassiveAbility(AbilityId.CONTRARY) + .enemyHasPassiveAbility(true) + .moveset(MoveId.FALSE_SWIPE); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true); + enemy.setStatStage(Stat.ATK, 6); + game.move.select(MoveId.FALSE_SWIPE); + await game.phaseInterceptor.to("BerryPhase"); + expect(enemy.getStatStage(Stat.ATK)).toBe(-6); + }); +}); diff --git a/test/abilities/arena_trap.test.ts b/test/abilities/arena_trap.test.ts index cc6888be102..c47cf3cd327 100644 --- a/test/abilities/arena_trap.test.ts +++ b/test/abilities/arena_trap.test.ts @@ -1,10 +1,10 @@ -import { allAbilities } from "#app/data/data-lists"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Arena Trap", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/aroma_veil.test.ts b/test/abilities/aroma_veil.test.ts index 56cce749b9f..fb08d60c24f 100644 --- a/test/abilities/aroma_veil.test.ts +++ b/test/abilities/aroma_veil.test.ts @@ -1,13 +1,13 @@ -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerTagType } from "#enums/battler-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerIndex } from "#enums/battler-index"; -import type { PlayerPokemon } from "#app/field/pokemon"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import type { PlayerPokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Aroma Veil", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/aura_break.test.ts b/test/abilities/aura_break.test.ts index 657d363bd97..6f33e3f047f 100644 --- a/test/abilities/aura_break.test.ts +++ b/test/abilities/aura_break.test.ts @@ -1,8 +1,8 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/battery.test.ts b/test/abilities/battery.test.ts index db129e72b0b..13ebd52367a 100644 --- a/test/abilities/battery.test.ts +++ b/test/abilities/battery.test.ts @@ -1,10 +1,10 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/battle_bond.test.ts b/test/abilities/battle_bond.test.ts index adb4a9c6e7a..ef8c2ffed75 100644 --- a/test/abilities/battle_bond.test.ts +++ b/test/abilities/battle_bond.test.ts @@ -1,11 +1,11 @@ -import { allMoves } from "#app/data/data-lists"; -import { MultiHitType } from "#enums/MultiHitType"; -import { Status } from "#app/data/status-effect"; +import { allMoves } from "#data/data-lists"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; +import { MultiHitType } from "#enums/MultiHitType"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - BATTLE BOND", () => { diff --git a/test/abilities/beast_boost.test.ts b/test/abilities/beast_boost.test.ts index 8be9156ce07..0a49728698b 100644 --- a/test/abilities/beast_boost.test.ts +++ b/test/abilities/beast_boost.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/commander.test.ts b/test/abilities/commander.test.ts index 99512d7a733..8c19ee083c5 100644 --- a/test/abilities/commander.test.ts +++ b/test/abilities/commander.test.ts @@ -1,15 +1,15 @@ +import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { SpeciesId } from "#enums/species-id"; import type { EffectiveStat } from "#enums/stat"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; -import { MoveResult } from "#enums/move-result"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -200,8 +200,6 @@ describe("Abilities - Commander", () => { game.move.select(MoveId.DIVE, 0, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, 1); - - await game.phaseInterceptor.to("CommandPhase"); await game.toNextTurn(); expect(tatsugiri.getTag(BattlerTagType.UNDERWATER)).toBeDefined(); diff --git a/test/abilities/competitive.test.ts b/test/abilities/competitive.test.ts index f12b06837dc..7dcc677a74e 100644 --- a/test/abilities/competitive.test.ts +++ b/test/abilities/competitive.test.ts @@ -1,9 +1,9 @@ -import { Stat } from "#enums/stat"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/contrary.test.ts b/test/abilities/contrary.test.ts index 6962abb0fec..7db48dedbfd 100644 --- a/test/abilities/contrary.test.ts +++ b/test/abilities/contrary.test.ts @@ -1,8 +1,8 @@ -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/corrosion.test.ts b/test/abilities/corrosion.test.ts index 2a0464d1146..22abc158fc7 100644 --- a/test/abilities/corrosion.test.ts +++ b/test/abilities/corrosion.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/costar.test.ts b/test/abilities/costar.test.ts index a40d84e72f5..c8c0ffd3184 100644 --- a/test/abilities/costar.test.ts +++ b/test/abilities/costar.test.ts @@ -1,10 +1,9 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { MessagePhase } from "#phases/message-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; @@ -39,7 +38,6 @@ describe("Abilities - COSTAR", () => { let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); game.move.select(MoveId.NASTY_PLOT); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -47,7 +45,6 @@ describe("Abilities - COSTAR", () => { expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(0); game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); @@ -67,7 +64,6 @@ describe("Abilities - COSTAR", () => { expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2); game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); diff --git a/test/abilities/cud_chew.test.ts b/test/abilities/cud_chew.test.ts index 70c282bf8a8..8fc62b38528 100644 --- a/test/abilities/cud_chew.test.ts +++ b/test/abilities/cud_chew.test.ts @@ -1,5 +1,4 @@ -import { RepeatBerryNextTurnAbAttr } from "#app/data/abilities/ability"; -import Pokemon from "#app/field/pokemon"; +import { CudChewConsumeBerryAbAttr } from "#abilities/ability"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { AbilityId } from "#enums/ability-id"; @@ -7,7 +6,8 @@ import { BerryType } from "#enums/berry-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { Pokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -196,7 +196,7 @@ describe("Abilities - Cud Chew", () => { describe("regurgiates berries", () => { it("re-triggers effects on eater without pushing to array", async () => { - const apply = vi.spyOn(RepeatBerryNextTurnAbAttr.prototype, "apply"); + const apply = vi.spyOn(CudChewConsumeBerryAbAttr.prototype, "apply"); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); const farigiraf = game.scene.getPlayerPokemon()!; diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index 2a4a3c36bcc..86b5b68e901 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; -import type { MovePhase } from "#app/phases/move-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { MovePhase } from "#phases/move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/defiant.test.ts b/test/abilities/defiant.test.ts index ef26b5bfca3..44187429e2b 100644 --- a/test/abilities/defiant.test.ts +++ b/test/abilities/defiant.test.ts @@ -1,9 +1,9 @@ -import { Stat } from "#enums/stat"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/desolate-land.test.ts b/test/abilities/desolate-land.test.ts index 90565d9caf8..fc066fa8955 100644 --- a/test/abilities/desolate-land.test.ts +++ b/test/abilities/desolate-land.test.ts @@ -1,13 +1,14 @@ -import { PokeballType } from "#app/enums/pokeball"; -import { WeatherType } from "#app/enums/weather-type"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { Command } from "#enums/command"; +import { globalScene } from "#app/global-scene"; import { AbilityId } from "#enums/ability-id"; +import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { WeatherType } from "#enums/weather-type"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Desolate Land", () => { let phaserGame: Phaser.Game; @@ -145,6 +146,7 @@ describe("Abilities - Desolate Land", () => { expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN); vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); + vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index 4ccd825e574..9bd36def8d7 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Disguise", () => { @@ -165,7 +165,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(disguisedForm); }); - it("reverts to Disguised form on biome change when fainted", async () => { + it("reverts to Disguised form when fainted", async () => { game.override .startingWave(10) .starterSpecies(0) @@ -181,10 +181,6 @@ describe("Abilities - Disguise", () => { game.move.select(MoveId.SPLASH); await game.killPokemon(mimikyu1); - game.doSelectPartyPokemon(1); - await game.toNextTurn(); - game.move.select(MoveId.SPLASH); - await game.doKillOpponents(); await game.phaseInterceptor.to("QuietFormChangePhase"); expect(mimikyu1.formIndex).toBe(disguisedForm); diff --git a/test/abilities/dry_skin.test.ts b/test/abilities/dry_skin.test.ts index 31620beb9bd..712d3295e6f 100644 --- a/test/abilities/dry_skin.test.ts +++ b/test/abilities/dry_skin.test.ts @@ -1,7 +1,7 @@ -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/early_bird.test.ts b/test/abilities/early_bird.test.ts index dcdfd8e4956..be4a2c802af 100644 --- a/test/abilities/early_bird.test.ts +++ b/test/abilities/early_bird.test.ts @@ -1,10 +1,10 @@ -import { Status } from "#app/data/status-effect"; -import { MoveResult } from "#enums/move-result"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/flash_fire.test.ts b/test/abilities/flash_fire.test.ts index 3be156a1578..d6085c4e591 100644 --- a/test/abilities/flash_fire.test.ts +++ b/test/abilities/flash_fire.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { SpeciesId } from "#enums/species-id"; -import { MovePhase } from "#app/phases/move-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { MovePhase } from "#phases/move-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/flower_gift.test.ts b/test/abilities/flower_gift.test.ts index f072afe7f34..b283729d951 100644 --- a/test/abilities/flower_gift.test.ts +++ b/test/abilities/flower_gift.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#app/enums/stat"; -import { WeatherType } from "#app/enums/weather-type"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { WeatherType } from "#enums/weather-type"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/flower_veil.test.ts b/test/abilities/flower_veil.test.ts index 7cabfa23e96..e890ea24fad 100644 --- a/test/abilities/flower_veil.test.ts +++ b/test/abilities/flower_veil.test.ts @@ -1,15 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { allAbilities } from "#app/data/data-lists"; describe("Abilities - Flower Veil", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/forecast.test.ts b/test/abilities/forecast.test.ts index 9111766ebdf..372e32a27fa 100644 --- a/test/abilities/forecast.test.ts +++ b/test/abilities/forecast.test.ts @@ -1,15 +1,15 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { WeatherType } from "#app/enums/weather-type"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { WeatherType } from "#enums/weather-type"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { MovePhase } from "#phases/move-phase"; +import { PostSummonPhase } from "#phases/post-summon-phase"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index c5809e18e96..e26fd5b5f1f 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -1,13 +1,12 @@ +import { allAbilities, allMoves } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveCategory } from "#enums/MoveCategory"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; -import { allMoves } from "#app/data/data-lists"; -import { MoveCategory } from "#enums/MoveCategory"; describe("Moves - Friend Guard", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/good_as_gold.test.ts b/test/abilities/good_as_gold.test.ts index 89e354b1f34..956553a96d9 100644 --- a/test/abilities/good_as_gold.test.ts +++ b/test/abilities/good_as_gold.test.ts @@ -1,15 +1,15 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allAbilities } from "#app/data/data-lists"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { Stat } from "#app/enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; -import { WeatherType } from "#app/enums/weather-type"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -45,7 +45,7 @@ describe("Abilities - Good As Gold", () => { const player = game.scene.getPlayerPokemon()!; - game.move.select(MoveId.SPLASH, 0); + game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); @@ -54,12 +54,13 @@ describe("Abilities - Good As Gold", () => { }); it("should block memento and prevent the user from fainting", async () => { - game.override.enemyMoveset([MoveId.MEMENTO]); + game.override.enemyAbility(AbilityId.GOOD_AS_GOLD); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.MEMENTO); + + game.move.use(MoveId.MEMENTO); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.isFainted()).toBe(false); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().isFainted()).toBe(false); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should not block any status moves that target the field, one side, or all pokemon", async () => { diff --git a/test/abilities/gorilla_tactics.test.ts b/test/abilities/gorilla_tactics.test.ts index a8b09461ea0..05714fe77ea 100644 --- a/test/abilities/gorilla_tactics.test.ts +++ b/test/abilities/gorilla_tactics.test.ts @@ -1,14 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; -import { SpeciesId } from "#enums/species-id"; -import { Stat } from "#app/enums/stat"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; import { MoveUseMode } from "#enums/move-use-mode"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { RandomMoveAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Gorilla Tactics", () => { let phaserGame: Phaser.Game; @@ -28,8 +28,10 @@ describe("Abilities - Gorilla Tactics", () => { game = new GameManager(phaserGame); game.override .battleStyle("single") + .criticalHits(false) .enemyAbility(AbilityId.BALL_FETCH) .enemySpecies(SpeciesId.MAGIKARP) + .enemyMoveset(MoveId.SPLASH) .enemyLevel(30) .moveset([MoveId.SPLASH, MoveId.TACKLE, MoveId.GROWL, MoveId.METRONOME]) .ability(AbilityId.GORILLA_TACTICS); @@ -42,7 +44,6 @@ describe("Abilities - Gorilla Tactics", () => { const initialAtkStat = darmanitan.getStat(Stat.ATK); game.move.select(MoveId.SPLASH); - await game.move.forceEnemyMove(MoveId.SPLASH); await game.toEndOfTurn(); expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5); @@ -59,7 +60,6 @@ describe("Abilities - Gorilla Tactics", () => { // First turn, lock move to Growl game.move.select(MoveId.GROWL); - await game.move.forceEnemyMove(MoveId.SPLASH); await game.toNextTurn(); // Second turn, Growl is interrupted by Disable @@ -72,7 +72,7 @@ describe("Abilities - Gorilla Tactics", () => { // Third turn, Struggle is used game.move.select(MoveId.TACKLE); - await game.move.forceEnemyMove(MoveId.SPLASH); //prevent protect from being used by the enemy + await game.move.forceEnemyMove(MoveId.SPLASH); // prevent disable from being used by the enemy await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase"); @@ -91,7 +91,7 @@ describe("Abilities - Gorilla Tactics", () => { game.move.select(MoveId.METRONOME); await game.phaseInterceptor.to("TurnEndPhase"); - // Gorilla Tactics should bypass dancer and instruct + // Gorilla Tactics should lock into Metronome, not tackle expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(true); expect(darmanitan.isMoveRestricted(MoveId.METRONOME)).toBe(false); expect(darmanitan.getLastXMoves(-1)).toEqual([ @@ -106,11 +106,13 @@ describe("Abilities - Gorilla Tactics", () => { const darmanitan = game.field.getPlayerPokemon(); game.move.select(MoveId.TACKLE); - await game.move.selectEnemyMove(MoveId.PROTECT); + await game.move.forceEnemyMove(MoveId.PROTECT); await game.toEndOfTurn(); expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true); expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false); + const enemy = game.field.getEnemyPokemon(); + expect(enemy.hp).toBe(enemy.getMaxHp()); }); it("should activate when a move is succesfully executed but misses", async () => { @@ -119,7 +121,6 @@ describe("Abilities - Gorilla Tactics", () => { const darmanitan = game.field.getPlayerPokemon(); game.move.select(MoveId.TACKLE); - await game.move.selectEnemyMove(MoveId.SPLASH); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.move.forceMiss(); await game.toEndOfTurn(); diff --git a/test/abilities/guard-dog.test.ts b/test/abilities/guard-dog.test.ts new file mode 100644 index 00000000000..aef8f081975 --- /dev/null +++ b/test/abilities/guard-dog.test.ts @@ -0,0 +1,39 @@ +import { AbilityId } from "#enums/ability-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Ability - Guard Dog", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.GUARD_DOG) + .battleStyle("single") + .enemySpecies(SpeciesId.MAGIKARP) + .enemyAbility(AbilityId.INTIMIDATE); + }); + + it("should raise attack by 1 stage when Intimidated instead of being lowered", async () => { + await game.classicMode.startBattle([SpeciesId.MABOSSTIFF]); + + const mabostiff = game.field.getPlayerPokemon(); + expect(mabostiff.getStatStage(Stat.ATK)).toBe(1); + expect(mabostiff.waveData.abilitiesApplied.has(AbilityId.GUARD_DOG)).toBe(true); + expect(game.phaseInterceptor.log.filter(l => l === "StatStageChangePhase")).toHaveLength(1); + }); +}); diff --git a/test/abilities/gulp_missile.test.ts b/test/abilities/gulp_missile.test.ts index e47b22f0c06..69ed6c51ac9 100644 --- a/test/abilities/gulp_missile.test.ts +++ b/test/abilities/gulp_missile.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import type Pokemon from "#app/field/pokemon"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/harvest.test.ts b/test/abilities/harvest.test.ts index 662eeed6dd0..244de383b44 100644 --- a/test/abilities/harvest.test.ts +++ b/test/abilities/harvest.test.ts @@ -1,16 +1,16 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PostTurnRestoreBerryAbAttr } from "#app/data/abilities/ability"; -import type Pokemon from "#app/field/pokemon"; -import { BerryModifier, PreserveBerryModifier } from "#app/modifier/modifier"; -import type { ModifierOverride } from "#app/modifier/modifier-type"; -import type { BooleanHolder } from "#app/utils/common"; +import { PostTurnRestoreBerryAbAttr } from "#abilities/ability"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BerryType } from "#enums/berry-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { BerryModifier, PreserveBerryModifier } from "#modifiers/modifier"; +import type { ModifierOverride } from "#modifiers/modifier-type"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { BooleanHolder } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -95,7 +95,7 @@ describe("Abilities - Harvest", () => { // Give ourselves harvest and disable enemy neut gas, // but force our roll to fail so we don't accidentally recover anything - vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApplyPostTurn").mockReturnValueOnce(false); + vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApply").mockReturnValueOnce(false); game.override.ability(AbilityId.HARVEST); game.move.select(MoveId.GASTRO_ACID); await game.move.selectEnemyMove(MoveId.NUZZLE); diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts index b37c9effeb0..8ce8b5bada0 100644 --- a/test/abilities/healer.test.ts +++ b/test/abilities/healer.test.ts @@ -1,14 +1,14 @@ +import { PostTurnResetStatusAbAttr } from "#abilities/ability"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import { isNullOrUndefined } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { isNullOrUndefined } from "#app/utils/common"; -import { allAbilities } from "#app/data/data-lists"; -import type Pokemon from "#app/field/pokemon"; -import { PostTurnResetStatusAbAttr } from "#app/data/abilities/ability"; describe("Abilities - Healer", () => { let phaserGame: Phaser.Game; @@ -42,7 +42,7 @@ describe("Abilities - Healer", () => { }); it("should not queue a message phase for healing if the ally has fainted", async () => { - const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApplyPostTurn"); + const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApply"); game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); diff --git a/test/abilities/heatproof.test.ts b/test/abilities/heatproof.test.ts index 39c9dff8289..0d76c1ad40b 100644 --- a/test/abilities/heatproof.test.ts +++ b/test/abilities/heatproof.test.ts @@ -1,10 +1,10 @@ -import { SpeciesId } from "#enums/species-id"; -import { StatusEffect } from "#app/enums/status-effect"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/honey_gather.test.ts b/test/abilities/honey_gather.test.ts index f8700f3e6f7..65c590083f5 100644 --- a/test/abilities/honey_gather.test.ts +++ b/test/abilities/honey_gather.test.ts @@ -1,9 +1,10 @@ -import type { CommandPhase } from "#app/phases/command-phase"; -import { Command } from "#enums/command"; +import Overrides from "#app/overrides"; import { AbilityId } from "#enums/ability-id"; +import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -63,6 +64,8 @@ describe("Abilities - Honey Gather", () => { // something weird is going on with the test framework, so this is required to prevent a crash const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "scene", "get").mockReturnValue(game.scene); + // Expects next wave so run must succeed + vi.spyOn(Overrides, "RUN_SUCCESS_OVERRIDE", "get").mockReturnValue(true); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); diff --git a/test/abilities/hustle.test.ts b/test/abilities/hustle.test.ts index 57a1a2e1d86..94045d231d1 100644 --- a/test/abilities/hustle.test.ts +++ b/test/abilities/hustle.test.ts @@ -1,9 +1,9 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#app/enums/stat"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/hyper_cutter.test.ts b/test/abilities/hyper_cutter.test.ts index 211be9a0533..212083b62b6 100644 --- a/test/abilities/hyper_cutter.test.ts +++ b/test/abilities/hyper_cutter.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/ice_face.test.ts b/test/abilities/ice_face.test.ts index c42713d7e6c..a0e2a949672 100644 --- a/test/abilities/ice_face.test.ts +++ b/test/abilities/ice_face.test.ts @@ -1,14 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -259,7 +259,7 @@ describe("Abilities - Ice Face", () => { const eiscue = game.scene.getEnemyPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); expect(eiscue.hasAbility(AbilityId.ICE_FACE)).toBe(true); }); @@ -269,13 +269,9 @@ describe("Abilities - Ice Face", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.SIMPLE_BEAM); - - await game.phaseInterceptor.to(TurnInitPhase); - const eiscue = game.scene.getEnemyPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); expect(game.scene.getPlayerPokemon()!.hasAbility(AbilityId.TRACE)).toBe(true); }); diff --git a/test/abilities/illuminate.test.ts b/test/abilities/illuminate.test.ts index ec4f6436041..0fb31379542 100644 --- a/test/abilities/illuminate.test.ts +++ b/test/abilities/illuminate.test.ts @@ -1,9 +1,9 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Illuminate", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index 1c2809ad813..0624c5e19f4 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -1,9 +1,9 @@ -import { Gender } from "#app/data/gender"; -import { PokeballType } from "#app/enums/pokeball"; +import { Gender } from "#data/gender"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/immunity.test.ts b/test/abilities/immunity.test.ts index 063ab57f11a..6f6fac10bf9 100644 --- a/test/abilities/immunity.test.ts +++ b/test/abilities/immunity.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts deleted file mode 100644 index 30491139877..00000000000 --- a/test/abilities/imposter.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; -import { AbilityId } from "#enums/ability-id"; - -// TODO: Add more tests once Imposter is fully implemented -describe("Abilities - Imposter", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .battleStyle("single") - .enemySpecies(SpeciesId.MEW) - .enemyLevel(200) - .enemyAbility(AbilityId.BEAST_BOOST) - .enemyPassiveAbility(AbilityId.BALL_FETCH) - .enemyMoveset(MoveId.SPLASH) - .ability(AbilityId.IMPOSTER) - .moveset(MoveId.SPLASH); - }); - - it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { - await game.classicMode.startBattle([SpeciesId.DITTO]); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(player.getAbility()).toBe(enemy.getAbility()); - expect(player.getGender()).toBe(enemy.getGender()); - - expect(player.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); - for (const s of EFFECTIVE_STATS) { - expect(player.getStat(s, false)).toBe(enemy.getStat(s, false)); - } - - for (const s of BATTLE_STATS) { - expect(player.getStatStage(s)).toBe(enemy.getStatStage(s)); - } - - const playerMoveset = player.getMoveset(); - const enemyMoveset = player.getMoveset(); - - expect(playerMoveset.length).toBe(enemyMoveset.length); - for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { - expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId); - } - - const playerTypes = player.getTypes(); - const enemyTypes = enemy.getTypes(); - - expect(playerTypes.length).toBe(enemyTypes.length); - for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) { - expect(playerTypes[i]).toBe(enemyTypes[i]); - } - }); - - it("should copy in-battle overridden stats", async () => { - game.override.enemyMoveset([MoveId.POWER_SPLIT]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); - const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); - expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); - - expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); - expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); - }); - - it("should set each move's pp to a maximum of 5", async () => { - game.override.enemyMoveset([MoveId.SWORDS_DANCE, MoveId.GROWL, MoveId.SKETCH, MoveId.RECOVER]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - const player = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - player.getMoveset().forEach(move => { - // Should set correct maximum PP without touching `ppUp` - if (move) { - if (move.moveId === MoveId.SKETCH) { - expect(move.getMovePp()).toBe(1); - } else { - expect(move.getMovePp()).toBe(5); - } - expect(move.ppUp).toBe(0); - } - }); - }); - - it("should activate its ability if it copies one that activates on summon", async () => { - game.override.enemyAbility(AbilityId.INTIMIDATE); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to("MoveEndPhase"); - - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); - }); - - it("should persist transformed attributes across reloads", async () => { - game.override.moveset([MoveId.ABSORB]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - game.move.select(MoveId.SPLASH); - await game.doKillOpponents(); - await game.toNextWave(); - - expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); - expect(game.scene.currentBattle.waveIndex).toBe(2); - - await game.reload.reloadSession(); - - const playerReloaded = game.scene.getPlayerPokemon()!; - const playerMoveset = player.getMoveset(); - - expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(playerReloaded.getAbility()).toBe(enemy.getAbility()); - expect(playerReloaded.getGender()).toBe(enemy.getGender()); - - expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); - for (const s of EFFECTIVE_STATS) { - expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false)); - } - - expect(playerMoveset.length).toEqual(1); - expect(playerMoveset[0]?.moveId).toEqual(MoveId.SPLASH); - }); - - it("should stay transformed with the correct form after reload", async () => { - game.override.moveset([MoveId.ABSORB]).enemySpecies(SpeciesId.UNOWN); - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const enemy = game.scene.getEnemyPokemon()!; - - // change form - enemy.species.forms[5]; - enemy.species.formIndex = 5; - - game.move.select(MoveId.SPLASH); - await game.doKillOpponents(); - await game.toNextWave(); - - expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); - expect(game.scene.currentBattle.waveIndex).toBe(2); - - await game.reload.reloadSession(); - - const playerReloaded = game.scene.getPlayerPokemon()!; - - expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); - }); -}); diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index a5de035a136..9f1eed466b9 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -1,13 +1,13 @@ +import { allMoves } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { Stat } from "#enums/stat"; -import { StatusEffect } from "#enums/status-effect"; -import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/innards-out.test.ts b/test/abilities/innards-out.test.ts new file mode 100644 index 00000000000..a10465ce1cb --- /dev/null +++ b/test/abilities/innards-out.test.ts @@ -0,0 +1,62 @@ +import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Innards Out", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.BALL_FETCH) + .battleStyle("single") + .criticalHits(false) + .enemySpecies(SpeciesId.MAGIKARP) + .enemyAbility(AbilityId.INNARDS_OUT) + .enemyMoveset(MoveId.SPLASH) + .startingLevel(100); + }); + + it("should damage opppnents that faint the ability holder for equal damage", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + const magikarp = game.field.getEnemyPokemon(); + magikarp.hp = 20; + game.move.use(MoveId.X_SCISSOR); + await game.toEndOfTurn(); + + expect(magikarp.isFainted()).toBe(true); + const feebas = game.field.getPlayerPokemon(); + expect(feebas.getInverseHp()).toBe(20); + }); + + it("should not damage an ally in Double Battles", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + const [magikarp1, magikarp2] = game.scene.getEnemyField(); + magikarp1.hp = 1; + + game.move.use(MoveId.PROTECT); + await game.move.forceEnemyMove(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.SURF); + await game.toEndOfTurn(); + + expect(magikarp1.isFainted()).toBe(true); + expect(magikarp2.getInverseHp()).toBe(0); + }); +}); diff --git a/test/abilities/insomnia.test.ts b/test/abilities/insomnia.test.ts index 800663823c3..2f683b516eb 100644 --- a/test/abilities/insomnia.test.ts +++ b/test/abilities/insomnia.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/intimidate.test.ts b/test/abilities/intimidate.test.ts index 3dcd9bcd129..9ff408aaeb5 100644 --- a/test/abilities/intimidate.test.ts +++ b/test/abilities/intimidate.test.ts @@ -1,12 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { UiMode } from "#enums/ui-mode"; -import { Stat } from "#enums/stat"; -import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Intimidate", () => { let phaserGame: Phaser.Game; @@ -25,109 +23,68 @@ describe("Abilities - Intimidate", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override + .criticalHits(false) .battleStyle("single") .enemySpecies(SpeciesId.RATTATA) .enemyAbility(AbilityId.INTIMIDATE) - .enemyPassiveAbility(AbilityId.HYDRATION) .ability(AbilityId.INTIMIDATE) - .startingWave(3) + .passiveAbility(AbilityId.NO_GUARD) .enemyMoveset(MoveId.SPLASH); }); - it("should lower ATK stat stage by 1 of enemy Pokemon on entry and player switch", async () => { - await game.classicMode.runToSummon([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - UiMode.CONFIRM, - () => { - game.setMode(UiMode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase"), - ); - await game.phaseInterceptor.to("CommandPhase", false); + it("should lower all opponents' ATK by 1 stage on entry and switch", async () => { + await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); - let playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; - - expect(playerPokemon.species.speciesId).toBe(SpeciesId.MIGHTYENA); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + const enemy = game.field.getEnemyPokemon(); + expect(enemy.getStatStage(Stat.ATK)).toBe(-1); game.doSwitchPokemon(1); - await game.phaseInterceptor.run("CommandPhase"); - await game.phaseInterceptor.to("CommandPhase"); - - playerPokemon = game.scene.getPlayerPokemon()!; - expect(playerPokemon.species.speciesId).toBe(SpeciesId.POOCHYENA); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); - }); - - it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => { - game.override.battleStyle("double").startingWave(3); - await game.classicMode.runToSummon([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - UiMode.CONFIRM, - () => { - game.setMode(UiMode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase"), - ); - await game.phaseInterceptor.to("CommandPhase", false); - - const playerField = game.scene.getPlayerField()!; - const enemyField = game.scene.getEnemyField()!; - - expect(enemyField[0].getStatStage(Stat.ATK)).toBe(-2); - expect(enemyField[1].getStatStage(Stat.ATK)).toBe(-2); - expect(playerField[0].getStatStage(Stat.ATK)).toBe(-2); - expect(playerField[1].getStatStage(Stat.ATK)).toBe(-2); - }); - - it("should not activate again if there is no switch or new entry", async () => { - game.override.startingWave(2).moveset([MoveId.SPLASH]); - await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); - - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; - - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); - - game.move.select(MoveId.SPLASH); await game.toNextTurn(); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(enemy.getStatStage(Stat.ATK)).toBe(-2); }); - it("should lower ATK stat stage by 1 for every switch", async () => { - game.override.moveset([MoveId.SPLASH]).enemyMoveset([MoveId.VOLT_SWITCH]).startingWave(5); - await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); + it("should lower ATK of all opponents in a double battle", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.MIGHTYENA]); - const playerPokemon = game.scene.getPlayerPokemon()!; - let enemyPokemon = game.scene.getEnemyPokemon()!; + const [enemy1, enemy2] = game.scene.getEnemyField(); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(enemy1.getStatStage(Stat.ATK)).toBe(-1); + expect(enemy2.getStatStage(Stat.ATK)).toBe(-1); + }); - game.move.select(getMovePosition(game.scene, 0, MoveId.SPLASH)); + it("should not trigger on switching moves used by wild Pokemon", async () => { + game.override.enemyMoveset(MoveId.VOLT_SWITCH); + await game.classicMode.startBattle([SpeciesId.VENUSAUR]); + + const player = game.field.getPlayerPokemon(); + expect(player.getStatStage(Stat.ATK)).toBe(-1); + + game.move.use(MoveId.SPLASH); await game.toNextTurn(); - enemyPokemon = game.scene.getEnemyPokemon()!; + // doesn't lower attack due to not actually switching out + expect(player.getStatStage(Stat.ATK)).toBe(-1); + }); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-2); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + it("should trigger on moves that switch user/target out during trainer battles", async () => { + game.override.startingWave(5).enemyLevel(100); + await game.classicMode.startBattle([SpeciesId.MIGHTYENA]); - game.move.select(MoveId.SPLASH); + const player = game.field.getPlayerPokemon(); + expect(player.getStatStage(Stat.ATK)).toBe(-1); + + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.TELEPORT); await game.toNextTurn(); - enemyPokemon = game.scene.getEnemyPokemon()!; + expect(player.getStatStage(Stat.ATK)).toBe(-2); - expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-3); - expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + game.move.use(MoveId.DRAGON_TAIL); + await game.move.forceEnemyMove(MoveId.SPLASH); + await game.toNextTurn(); + + expect(player.getStatStage(Stat.ATK)).toBe(-3); }); }); diff --git a/test/abilities/intrepid_sword.test.ts b/test/abilities/intrepid_sword.test.ts index d9b81e9552e..7465b331791 100644 --- a/test/abilities/intrepid_sword.test.ts +++ b/test/abilities/intrepid_sword.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/libero.test.ts b/test/abilities/libero.test.ts deleted file mode 100644 index eaa2630e90d..00000000000 --- a/test/abilities/libero.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { allMoves } from "#app/data/data-lists"; -import { PokemonType } from "#enums/pokemon-type"; -import { Weather } from "#app/data/weather"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BiomeId } from "#enums/biome-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; - -describe("Abilities - Libero", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .battleStyle("single") - .ability(AbilityId.LIBERO) - .startingLevel(100) - .enemySpecies(SpeciesId.RATTATA) - .enemyMoveset(MoveId.ENDURE); - }); - - test("ability applies and changes a pokemon's type", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - }); - - // Test for Gen9+ functionality, we are using previous funcionality - test.skip("ability applies only once per switch in", async () => { - game.override.moveset([MoveId.SPLASH, MoveId.AGILITY]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.BULBASAUR]); - - let leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - - game.move.select(MoveId.AGILITY); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.LIBERO); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]]; - const moveType = PokemonType[allMoves[MoveId.AGILITY].type]; - expect(leadPokemonType).not.toBe(moveType); - - await game.toNextTurn(); - game.doSwitchPokemon(1); - await game.toNextTurn(); - game.doSwitchPokemon(1); - await game.toNextTurn(); - - leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - }); - - test("ability applies correctly even if the pokemon's move has a variable type", async () => { - game.override.moveset([MoveId.WEATHER_BALL]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.move.select(MoveId.WEATHER_BALL); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.LIBERO); - expect(leadPokemon.getTypes()).toHaveLength(1); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]], - moveType = PokemonType[PokemonType.FIRE]; - expect(leadPokemonType).toBe(moveType); - }); - - test("ability applies correctly even if the type has changed by another ability", async () => { - game.override.moveset([MoveId.TACKLE]).passiveAbility(AbilityId.REFRIGERATE); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.LIBERO); - expect(leadPokemon.getTypes()).toHaveLength(1); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]], - moveType = PokemonType[PokemonType.ICE]; - expect(leadPokemonType).toBe(moveType); - }); - - test("ability applies correctly even if the pokemon's move calls another move", async () => { - game.override.moveset([MoveId.NATURE_POWER]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.scene.arena.biomeType = BiomeId.MOUNTAIN; - game.move.select(MoveId.NATURE_POWER); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.AIR_SLASH); - }); - - test("ability applies correctly even if the pokemon's move is delayed / charging", async () => { - game.override.moveset([MoveId.DIG]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.DIG); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.DIG); - }); - - test("ability applies correctly even if the pokemon's move misses", async () => { - game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.SPLASH); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.move.forceMiss(); - await game.phaseInterceptor.to(TurnEndPhase); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon.isFullHp()).toBe(true); - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability applies correctly even if the pokemon's move is protected against", async () => { - game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.PROTECT); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => { - game.override.moveset([MoveId.TACKLE]).enemySpecies(SpeciesId.GASTLY); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability is not applied if pokemon's type is the same as the move's type", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - leadPokemon.summonData.types = [allMoves[MoveId.SPLASH].type]; - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.LIBERO); - }); - - test("ability is not applied if pokemon is terastallized", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - leadPokemon.isTerastallized = true; - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.LIBERO); - }); - - test("ability is not applied if pokemon uses struggle", async () => { - game.override.moveset([MoveId.STRUGGLE]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.STRUGGLE); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.LIBERO); - }); - - test("ability is not applied if the pokemon's move fails", async () => { - game.override.moveset([MoveId.BURN_UP]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.BURN_UP); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.LIBERO); - }); - - test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => { - game.override.moveset([MoveId.TRICK_OR_TREAT]).enemySpecies(SpeciesId.GASTLY); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TRICK_OR_TREAT); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TRICK_OR_TREAT); - }); - - test("ability applies correctly and the pokemon curses itself", async () => { - game.override.moveset([MoveId.CURSE]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.CURSE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.CURSE); - expect(leadPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); - }); -}); - -function testPokemonTypeMatchesDefaultMoveType(pokemon: PlayerPokemon, move: MoveId) { - expect(pokemon.waveData.abilitiesApplied).toContain(AbilityId.LIBERO); - expect(pokemon.getTypes()).toHaveLength(1); - const pokemonType = PokemonType[pokemon.getTypes()[0]], - moveType = PokemonType[allMoves[move].type]; - expect(pokemonType).toBe(moveType); -} diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts index 2dc29500454..2d5b6881869 100644 --- a/test/abilities/lightningrod.test.ts +++ b/test/abilities/lightningrod.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -36,10 +36,8 @@ describe("Abilities - Lightningrod", () => { it("should redirect electric type moves", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -52,10 +50,8 @@ describe("Abilities - Lightningrod", () => { game.override.moveset([MoveId.SPLASH, MoveId.AERIAL_ACE]); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -68,8 +64,7 @@ describe("Abilities - Lightningrod", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -81,31 +76,25 @@ describe("Abilities - Lightningrod", () => { it("should not redirect moves changed from electric type via ability", async () => { game.override.ability(AbilityId.NORMALIZE); - await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(false); }); it("should redirect moves changed to electric type via ability", async () => { - game.override.ability(AbilityId.GALVANIZE).moveset(MoveId.TACKLE); - await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); + game.override.ability(AbilityId.GALVANIZE); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; - - game.move.select(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(true); diff --git a/test/abilities/limber.test.ts b/test/abilities/limber.test.ts index 77acd8b3dab..ed140c09fb1 100644 --- a/test/abilities/limber.test.ts +++ b/test/abilities/limber.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/magic_bounce.test.ts b/test/abilities/magic_bounce.test.ts index 04bfcbbfe8f..84b4cea8376 100644 --- a/test/abilities/magic_bounce.test.ts +++ b/test/abilities/magic_bounce.test.ts @@ -1,15 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { Stat } from "#app/enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Magic Bounce", () => { @@ -160,7 +159,8 @@ describe("Abilities - Magic Bounce", () => { expect(game.scene.getEnemyPokemon()!.getTag(BattlerTagType.CURSED)).toBeDefined(); }); - it("should not cause encore to be interrupted after bouncing", async () => { + // TODO: enable when Magic Bounce is fixed to properly reset the hit count + it.todo("should not cause encore to be interrupted after bouncing", async () => { game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]).enemyMoveset([MoveId.TACKLE, MoveId.GROWL]); // game.override.ability(AbilityId.MOLD_BREAKER); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); @@ -168,7 +168,7 @@ describe("Abilities - Magic Bounce", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; // Give the player MOLD_BREAKER for this turn to bypass Magic Bounce. - vi.spyOn(playerPokemon, "getAbility").mockReturnValue(allAbilities[AbilityId.MOLD_BREAKER]); + const playerAbilitySpy = game.field.mockAbility(playerPokemon, AbilityId.MOLD_BREAKER); // turn 1 game.move.select(MoveId.ENCORE); @@ -178,7 +178,7 @@ describe("Abilities - Magic Bounce", () => { expect(enemyPokemon.getTag(BattlerTagType.ENCORE)!["moveId"]).toBe(MoveId.TACKLE); // turn 2 - vi.spyOn(playerPokemon, "getAbility").mockRestore(); + playerAbilitySpy.mockRestore(); game.move.select(MoveId.GROWL); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/abilities/magic_guard.test.ts b/test/abilities/magic_guard.test.ts index f135a761bba..63b22f347fb 100644 --- a/test/abilities/magic_guard.test.ts +++ b/test/abilities/magic_guard.test.ts @@ -1,19 +1,17 @@ -import { getArenaTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { getStatusEffectCatchRateMultiplier } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -describe("Abilities - Magic Guard", () => { +describe("AbilityId - Magic Guard", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -29,404 +27,142 @@ describe("Abilities - Magic Guard", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override - /** Player Pokemon overrides */ .ability(AbilityId.MAGIC_GUARD) - .moveset([MoveId.SPLASH]) + .enemySpecies(SpeciesId.BLISSEY) + .enemyAbility(AbilityId.NO_GUARD) .startingLevel(100) - /** Enemy Pokemon overrides */ - .enemySpecies(SpeciesId.SNORLAX) - .enemyAbility(AbilityId.INSOMNIA) - .enemyMoveset(MoveId.SPLASH) .enemyLevel(100); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Magic_Guard_(Ability) - it("ability should prevent damage caused by weather", async () => { - game.override.weather(WeatherType.SANDSTORM); - + it.each<{ name: string; move?: MoveId; enemyMove?: MoveId }>([ + { name: "Non-Volatile Status Conditions", enemyMove: MoveId.TOXIC }, + { name: "Volatile Status Conditions", enemyMove: MoveId.LEECH_SEED }, + { name: "Crash Damage", move: MoveId.HIGH_JUMP_KICK, enemyMove: MoveId.PROTECT }, // Protect triggers crash damage + { name: "Variable Recoil Moves", move: MoveId.DOUBLE_EDGE }, + { name: "HP% Recoil Moves", move: MoveId.CHLOROBLAST }, + ])("should prevent damage from $name", async ({ move = MoveId.SPLASH, enemyMove = MoveId.SPLASH }) => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + game.move.use(move); + await game.move.forceEnemyMove(enemyMove); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toEndOfTurn(); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).toBeDefined(); - - game.move.select(MoveId.SPLASH); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) has not taken damage from weather - * - The enemy Pokemon (without Magic Guard) has taken damage from weather - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + const magikarp = game.field.getPlayerPokemon(); + expect(magikarp.hp).toBe(magikarp.getMaxHp()); }); - it("ability should prevent damage caused by status effects but other non-damage effects still apply", async () => { - //Toxic keeps track of the turn counters -> important that Magic Guard keeps track of post-Toxic turns - game.override.statusEffect(StatusEffect.POISON); + it.each<{ abName: string; move?: MoveId; enemyMove?: MoveId; passive?: AbilityId; enemyAbility?: AbilityId }>([ + { abName: "Bad Dreams", enemyMove: MoveId.SPORE, enemyAbility: AbilityId.BAD_DREAMS }, + { abName: "Aftermath", move: MoveId.PSYCHIC_FANGS, enemyAbility: AbilityId.AFTERMATH }, + { abName: "Innards Out", move: MoveId.PSYCHIC_FANGS, enemyAbility: AbilityId.INNARDS_OUT }, + { abName: "Rough Skin", move: MoveId.PSYCHIC_FANGS, enemyAbility: AbilityId.ROUGH_SKIN }, + { abName: "Dry Skin", move: MoveId.SUNNY_DAY, passive: AbilityId.DRY_SKIN }, + { abName: "Liquid Ooze", move: MoveId.DRAIN_PUNCH, enemyAbility: AbilityId.LIQUID_OOZE }, + ])( + "should prevent damage from $abName", + async ({ + move = MoveId.SPLASH, + enemyMove = MoveId.SPLASH, + passive = AbilityId.BALL_FETCH, + enemyAbility = AbilityId.BALL_FETCH, + }) => { + game.override.enemyLevel(1).passiveAbility(passive).enemyAbility(enemyAbility); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + game.move.use(move); + await game.move.forceEnemyMove(enemyMove); + await game.toEndOfTurn(); + + const magikarp = game.field.getPlayerPokemon(); + expect(magikarp.hp).toBe(magikarp.getMaxHp()); + }, + ); + + it.each<{ name: string; move?: MoveId; enemyMove?: MoveId }>([ + { name: "Struggle recoil", move: MoveId.STRUGGLE }, + { name: "Self-induced HP cutting", move: MoveId.BELLY_DRUM }, + { name: "Confusion self-damage", enemyMove: MoveId.CONFUSE_RAY }, + ])("should not prevent damage from $name", async ({ move = MoveId.SPLASH, enemyMove = MoveId.SPLASH }) => { + game.override.confusionActivation(true); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + game.move.use(move); + await game.move.forceEnemyMove(enemyMove); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); // Ensure confuse ray goes first + await game.toEndOfTurn(); - game.move.select(MoveId.SPLASH); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) has not taken damage from poison - * - The Pokemon's CatchRateMultiplier should be 1.5 - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(leadPokemon.status!.effect)).toBe(1.5); + const magikarp = game.field.getPlayerPokemon(); + expect(magikarp.hp).toBeLessThan(magikarp.getMaxHp()); }); - it("ability effect should not persist when the ability is replaced", async () => { - game.override.enemyMoveset(MoveId.WORRY_SEED).statusEffect(StatusEffect.POISON); - + it("should preserve toxic turn count and deal appropriate damage when disabled", async () => { + game.override.statusEffect(StatusEffect.TOXIC); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.SPLASH); + await game.toNextTurn(); - game.move.select(MoveId.SPLASH); + const magikarp = game.field.getPlayerPokemon(); + expect(magikarp.hp).toBe(magikarp.getMaxHp()); + expect(magikarp.status?.toxicTurnCount).toBe(1); - await game.phaseInterceptor.to(TurnEndPhase); + // have a few turns pass + game.move.use(MoveId.SPLASH); + await game.toNextTurn(); + game.move.use(MoveId.SPLASH); + await game.toNextTurn(); + game.move.use(MoveId.SPLASH); + await game.toNextTurn(); - /** - * Expect: - * - The player Pokemon (that just lost its Magic Guard ability) has taken damage from poison - */ - expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); + expect(magikarp.status?.toxicTurnCount).toBe(4); + + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.GASTRO_ACID); + await game.toNextTurn(); + + expect(magikarp.status?.toxicTurnCount).toBe(5); + expect(magikarp.getHpRatio(true)).toBeCloseTo(11 / 16, 1); }); - it("Magic Guard prevents damage caused by burn but other non-damaging effects are still applied", async () => { - game.override.enemyStatusEffect(StatusEffect.BURN).enemyAbility(AbilityId.MAGIC_GUARD); - + it("should preserve burn physical damage halving & status catch boost", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.SPLASH); + // NB: Burn applies directly to the physical dmg formula, so we can't just check attack here + game.move.use(MoveId.TACKLE); + await game.move.forceEnemyMove(MoveId.WILL_O_WISP); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const magikarp = game.field.getPlayerPokemon(); + expect(magikarp.hp).toBe(magikarp.getMaxHp()); + expect(magikarp.status?.effect).toBe(StatusEffect.BURN); + expect(getStatusEffectCatchRateMultiplier(magikarp.status!.effect)).toBe(1.5); - await game.phaseInterceptor.to(TurnEndPhase); + // Heal blissey to full & use tackle again + const blissey = game.field.getEnemyPokemon(); + const prevDmg = blissey.getInverseHp(); + blissey.hp = blissey.getMaxHp(); - /** - * Expect: - * - The enemy Pokemon (with Magic Guard) has not taken damage from burn - * - The enemy Pokemon's physical attack damage is halved (TBD) - * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 - */ - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); + game.move.use(MoveId.TACKLE); + await game.toNextTurn(); + + const burntDmg = blissey.getInverseHp(); + expect(burntDmg).toBeCloseTo(toDmgValue(prevDmg / 2), 0); }); - it("Magic Guard prevents damage caused by toxic but other non-damaging effects are still applied", async () => { - game.override.enemyStatusEffect(StatusEffect.TOXIC).enemyAbility(AbilityId.MAGIC_GUARD); - + it("should prevent damage from entry hazards, but not Toxic Spikes poison", async () => { + game.scene.arena.addTag(ArenaTagType.SPIKES, -1, MoveId.SPIKES, 0, ArenaTagSide.PLAYER); + game.scene.arena.addTag(ArenaTagType.TOXIC_SPIKES, -1, MoveId.TOXIC_SPIKES, 0, ArenaTagSide.PLAYER); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.SPLASH); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - const toxicStartCounter = enemyPokemon.status!.toxicTurnCount; - //should be 0 - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The enemy Pokemon (with Magic Guard) has not taken damage from toxic - * - The enemy Pokemon's status effect duration should be incremented - * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 - */ - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(enemyPokemon.status!.toxicTurnCount).toBeGreaterThan(toxicStartCounter); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); - }); - - it("Magic Guard prevents damage caused by entry hazards", async () => { - //Adds and applies Spikes to both sides of the arena - const newTag = getArenaTag(ArenaTagType.SPIKES, 5, MoveId.SPIKES, 0, 0, ArenaTagSide.BOTH)!; - game.scene.arena.tags.push(newTag); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.SPLASH); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) has not taken damage from spikes - * - The enemy Pokemon (without Magic Guard) has taken damage from spikes - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }); - - it("Magic Guard does not prevent poison from Toxic Spikes", async () => { - //Adds and applies Spikes to both sides of the arena - const playerTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, MoveId.TOXIC_SPIKES, 0, 0, ArenaTagSide.PLAYER)!; - const enemyTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, MoveId.TOXIC_SPIKES, 0, 0, ArenaTagSide.ENEMY)!; - game.scene.arena.tags.push(playerTag); - game.scene.arena.tags.push(enemyTag); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.SPLASH); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - Both Pokemon gain the poison status effect - * - The player Pokemon (with Magic Guard) has not taken damage from poison - * - The enemy Pokemon (without Magic Guard) has taken damage from poison - */ - expect(leadPokemon.status!.effect).toBe(StatusEffect.POISON); - expect(enemyPokemon.status!.effect).toBe(StatusEffect.POISON); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }); - - it("Magic Guard prevents against damage from volatile status effects", async () => { - await game.classicMode.startBattle([SpeciesId.DUSKULL]); - game.override.moveset([MoveId.CURSE]).enemyAbility(AbilityId.MAGIC_GUARD); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.CURSE); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) has cut its HP to inflict curse - * - The enemy Pokemon (with Magic Guard) is cursed - * - The enemy Pokemon (with Magic Guard) does not lose HP from being cursed - */ - expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - expect(enemyPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }); - - it("Magic Guard prevents crash damage", async () => { - game.override.moveset([MoveId.HIGH_JUMP_KICK]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.HIGH_JUMP_KICK); - await game.move.forceMiss(); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) misses High Jump Kick but does not lose HP as a result - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - it("Magic Guard prevents damage from recoil", async () => { - game.override.moveset([MoveId.TAKE_DOWN]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.TAKE_DOWN); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) uses a recoil move but does not lose HP from recoil - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - it("Magic Guard does not prevent damage from Struggle's recoil", async () => { - game.override.moveset([MoveId.STRUGGLE]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.STRUGGLE); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) uses Struggle but does lose HP from Struggle's recoil - */ - expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }); - - //This tests different move attributes than the recoil tests above - it("Magic Guard prevents self-damage from attacking moves", async () => { - game.override.moveset([MoveId.STEEL_BEAM]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.STEEL_BEAM); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) uses a move with an HP cost but does not lose HP from using it - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - /* - it("Magic Guard does not prevent self-damage from confusion", async () => { - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - game.move.select(MoveId.CHARM); - - await game.phaseInterceptor.to(TurnEndPhase); - }); -*/ - - it("Magic Guard does not prevent self-damage from non-attacking moves", async () => { - game.override.moveset([MoveId.BELLY_DRUM]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.BELLY_DRUM); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) uses a non-attacking move with an HP cost and thus loses HP from using it - */ - expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }); - - it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async () => { - //Tests the ability Bad Dreams - game.override.statusEffect(StatusEffect.SLEEP); - //enemy pokemon is given Spore just in case player pokemon somehow awakens during test - game.override - .enemyMoveset([MoveId.SPORE, MoveId.SPORE, MoveId.SPORE, MoveId.SPORE]) - .enemyAbility(AbilityId.BAD_DREAMS); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.SPLASH); - - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute - * - The player Pokemon is asleep - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.status!.effect).toBe(StatusEffect.SLEEP); - }); - - it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => { - //Tests the abilities Innards Out/Aftermath - game.override.moveset([MoveId.TACKLE]).enemyAbility(AbilityId.AFTERMATH); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.hp = 1; - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute - * - The enemy Pokemon has fainted - */ - expect(enemyPokemon.hp).toBe(0); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => { - //Tests the abilities Iron Barbs/Rough Skin - game.override.moveset([MoveId.TACKLE]).enemyAbility(AbilityId.IRON_BARBS); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute - * - The player Pokemon's move should have connected - */ - expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => { - //Tests the ability Liquid Ooze - game.override.moveset([MoveId.ABSORB]).enemyAbility(AbilityId.LIQUID_OOZE); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - game.move.select(MoveId.ABSORB); - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute - * - The player Pokemon's move should have connected - */ - expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }); - - it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => { - game.override.passiveAbility(AbilityId.SOLAR_POWER).weather(WeatherType.SUNNY); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - /** - * Expect: - * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute - */ - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); + // Magic guard prevented damage but not poison + const player = game.field.getPlayerPokemon(); + expect(player.hp).toBe(player.getMaxHp()); + expect(player.status?.effect).toBe(StatusEffect.POISON); }); }); diff --git a/test/abilities/magma_armor.test.ts b/test/abilities/magma_armor.test.ts index 0a8f3024291..cc40ea9f679 100644 --- a/test/abilities/magma_armor.test.ts +++ b/test/abilities/magma_armor.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/mimicry.test.ts b/test/abilities/mimicry.test.ts index 7ac7aeb599f..f377ab89ffe 100644 --- a/test/abilities/mimicry.test.ts +++ b/test/abilities/mimicry.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { PokemonType } from "#enums/pokemon-type"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/mirror_armor.test.ts b/test/abilities/mirror_armor.test.ts index 71b072c7d06..4d4ee973527 100644 --- a/test/abilities/mirror_armor.test.ts +++ b/test/abilities/mirror_armor.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#enums/battler-index"; // TODO: When Magic Bounce is implemented, make a test for its interaction with mirror guard, use screech diff --git a/test/abilities/mold_breaker.test.ts b/test/abilities/mold_breaker.test.ts index 28a077e8908..c3214cdc224 100644 --- a/test/abilities/mold_breaker.test.ts +++ b/test/abilities/mold_breaker.test.ts @@ -1,9 +1,7 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { globalScene } from "#app/global-scene"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -24,29 +22,28 @@ describe("Abilities - Mold Breaker", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([MoveId.SPLASH]) .ability(AbilityId.MOLD_BREAKER) .battleStyle("single") .criticalHits(false) .enemySpecies(SpeciesId.MAGIKARP) - .enemyAbility(AbilityId.BALL_FETCH) + .enemyAbility(AbilityId.STURDY) .enemyMoveset(MoveId.SPLASH); }); it("should turn off the ignore abilities arena variable after the user's move", async () => { - game.override - .enemyMoveset(MoveId.SPLASH) - .ability(AbilityId.MOLD_BREAKER) - .moveset([MoveId.ERUPTION]) - .startingLevel(100) - .enemyLevel(2); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + await game.classicMode.startBattle([SpeciesId.PINSIR]); - expect(enemy.isFainted()).toBe(false); - game.move.select(MoveId.SPLASH); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("MoveEndPhase", true); - expect(globalScene.arena.ignoreAbilities).toBe(false); + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + game.move.use(MoveId.X_SCISSOR); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(game.scene.arena.ignoreAbilities).toBe(true); + expect(game.scene.arena.ignoringEffectSource).toBe(player.getBattlerIndex()); + + await game.toEndOfTurn(); + expect(game.scene.arena.ignoreAbilities).toBe(false); + expect(enemy.isFainted()).toBe(true); }); }); diff --git a/test/abilities/moody.test.ts b/test/abilities/moody.test.ts index a3e321928b8..150456bc0f7 100644 --- a/test/abilities/moody.test.ts +++ b/test/abilities/moody.test.ts @@ -1,8 +1,8 @@ -import { BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -68,7 +68,7 @@ describe("Abilities - Moody", () => { }); it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => { - await game.classicMode.startBattle(); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); const playerPokemon = game.scene.getPlayerPokemon()!; diff --git a/test/abilities/moxie.test.ts b/test/abilities/moxie.test.ts index a85ed081448..882b4b0df2f 100644 --- a/test/abilities/moxie.test.ts +++ b/test/abilities/moxie.test.ts @@ -1,14 +1,14 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { VictoryPhase } from "#phases/victory-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#enums/battler-index"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; describe("Abilities - Moxie", () => { let phaserGame: Phaser.Game; @@ -65,8 +65,7 @@ describe("Abilities - Moxie", () => { secondPokemon.hp = 1; - game.move.select(moveToUse); - game.selectTarget(BattlerIndex.PLAYER_2); + game.move.select(moveToUse, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/test/abilities/mummy.test.ts b/test/abilities/mummy.test.ts index d6c40541d25..6c94fea407b 100644 --- a/test/abilities/mummy.test.ts +++ b/test/abilities/mummy.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/mycelium_might.test.ts b/test/abilities/mycelium_might.test.ts index 2cdda4353b5..fa3ab8e5301 100644 --- a/test/abilities/mycelium_might.test.ts +++ b/test/abilities/mycelium_might.test.ts @@ -1,10 +1,10 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#enums/stat"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -40,7 +40,7 @@ describe("Abilities - Mycelium Might", () => { * https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 - **/ + */ it("will move last in its priority bracket and ignore protective abilities", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); diff --git a/test/abilities/neutralizing_gas.test.ts b/test/abilities/neutralizing_gas.test.ts index 2408a78f11d..bcc00e4249a 100644 --- a/test/abilities/neutralizing_gas.test.ts +++ b/test/abilities/neutralizing_gas.test.ts @@ -1,13 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { Command } from "#enums/command"; +import { globalScene } from "#app/global-scene"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; import { PokeballType } from "#enums/pokeball"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -164,6 +165,7 @@ describe("Abilities - Neutralizing Gas", () => { expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); + vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); @@ -178,7 +180,7 @@ describe("Abilities - Neutralizing Gas", () => { const enemy = game.scene.getEnemyPokemon()!; const weatherChangeAttr = enemy.getAbilityAttrs("PostSummonWeatherChangeAbAttr", false)[0]; - vi.spyOn(weatherChangeAttr, "applyPostSummon"); + const weatherChangeSpy = vi.spyOn(weatherChangeAttr, "apply"); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); @@ -187,6 +189,6 @@ describe("Abilities - Neutralizing Gas", () => { await game.killPokemon(game.scene.getPlayerPokemon()!); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); - expect(weatherChangeAttr.applyPostSummon).not.toHaveBeenCalled(); + expect(weatherChangeSpy).not.toHaveBeenCalled(); }); }); diff --git a/test/abilities/no_guard.test.ts b/test/abilities/no_guard.test.ts index dc35e0e1b9a..f6f55ce3b80 100644 --- a/test/abilities/no_guard.test.ts +++ b/test/abilities/no_guard.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { HitCheckResult } from "#enums/hit-check-result"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { HitCheckResult } from "#enums/hit-check-result"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - No Guard", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/normal-move-type-change.test.ts b/test/abilities/normal-move-type-change.test.ts index d9e39b32a7c..aa673ddde29 100644 --- a/test/abilities/normal-move-type-change.test.ts +++ b/test/abilities/normal-move-type-change.test.ts @@ -1,15 +1,14 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { PokemonType } from "#enums/pokemon-type"; +import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; -import { allAbilities } from "#app/data/data-lists"; -import { toDmgValue } from "#app/utils/common"; /** * Tests for abilities that change the type of normal moves to diff --git a/test/abilities/normalize.test.ts b/test/abilities/normalize.test.ts index 1a4b93f3f32..1128dd1a562 100644 --- a/test/abilities/normalize.test.ts +++ b/test/abilities/normalize.test.ts @@ -1,11 +1,11 @@ import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; -import { allMoves } from "#app/data/data-lists"; -import { toDmgValue } from "#app/utils/common"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/oblivious.test.ts b/test/abilities/oblivious.test.ts index dab8394edd0..8b18fe65455 100644 --- a/test/abilities/oblivious.test.ts +++ b/test/abilities/oblivious.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/own_tempo.test.ts b/test/abilities/own_tempo.test.ts index 869953b4e93..bdb247c4987 100644 --- a/test/abilities/own_tempo.test.ts +++ b/test/abilities/own_tempo.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/parental_bond.test.ts b/test/abilities/parental_bond.test.ts index fd914b86100..4937857c8e1 100644 --- a/test/abilities/parental_bond.test.ts +++ b/test/abilities/parental_bond.test.ts @@ -1,12 +1,12 @@ -import { PokemonType } from "#enums/pokemon-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -193,6 +193,7 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.turnData.hitCount).toBe(2); }); + // TODO: consolidate all these tests into 1 block it("should only trigger post-target move effects once", async () => { game.override.moveset([MoveId.MIND_BLOWN]); @@ -233,42 +234,6 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.isOfType(PokemonType.FIRE)).toBe(false); }); - it("Moves boosted by this ability and Multi-Lens should strike 3 times", async () => { - game.override.moveset([MoveId.TACKLE]).startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.TACKLE); - - await game.phaseInterceptor.to("DamageAnimPhase"); - - expect(leadPokemon.turnData.hitCount).toBe(3); - }); - - it("Seismic Toss boosted by this ability and Multi-Lens should strike 3 times", async () => { - game.override.moveset([MoveId.SEISMIC_TOSS]).startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; - - const enemyStartingHp = enemyPokemon.hp; - - game.move.select(MoveId.SEISMIC_TOSS); - await game.move.forceHit(); - - await game.phaseInterceptor.to("DamageAnimPhase"); - - expect(leadPokemon.turnData.hitCount).toBe(3); - - await game.phaseInterceptor.to("MoveEndPhase", false); - - expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); - }); - it("Hyper Beam boosted by this ability should strike twice, then recharge", async () => { game.override.moveset([MoveId.HYPER_BEAM]); diff --git a/test/abilities/pastel_veil.test.ts b/test/abilities/pastel_veil.test.ts index b8e8873ed36..88f5415524f 100644 --- a/test/abilities/pastel_veil.test.ts +++ b/test/abilities/pastel_veil.test.ts @@ -1,11 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -63,7 +62,6 @@ describe("Abilities - Pastel Veil", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(magikarp.status?.effect).toBe(StatusEffect.POISON); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH); game.doSwitchPokemon(2); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/test/abilities/perish_body.test.ts b/test/abilities/perish_body.test.ts index 4852533816b..83a862164a1 100644 --- a/test/abilities/perish_body.test.ts +++ b/test/abilities/perish_body.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/power_construct.test.ts b/test/abilities/power_construct.test.ts index 2ea14597ef9..0917082d7bd 100644 --- a/test/abilities/power_construct.test.ts +++ b/test/abilities/power_construct.test.ts @@ -1,11 +1,11 @@ -import { Status } from "#app/data/status-effect"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Abilities - POWER CONSTRUCT", () => { diff --git a/test/abilities/power_spot.test.ts b/test/abilities/power_spot.test.ts index 11f75588386..f07ece17245 100644 --- a/test/abilities/power_spot.test.ts +++ b/test/abilities/power_spot.test.ts @@ -1,10 +1,10 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/protean-libero.test.ts b/test/abilities/protean-libero.test.ts new file mode 100644 index 00000000000..35be3fcf63d --- /dev/null +++ b/test/abilities/protean-libero.test.ts @@ -0,0 +1,272 @@ +import { allMoves } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import type { PlayerPokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Abilities - Protean/Libero", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleStyle("single") + .ability(AbilityId.PROTEAN) + .startingLevel(100) + .moveset([MoveId.CURSE, MoveId.DIG, MoveId.SPLASH]) + .enemySpecies(SpeciesId.RATTATA) + .enemyMoveset(MoveId.SPLASH); + }); + + /** + * Assert that the protean/libero ability triggered to change the user's type to + * the type of its most recently used move. + * Takes into account type overrides from effects. + * @param pokemon - The {@linkcode PlayerPokemon} being checked. + * @remarks + * This will clear the given Pokemon's `abilitiesApplied` set after being called to allow for easier multi-turn testing. + */ + function expectTypeChange(pokemon: PlayerPokemon) { + expect(pokemon.waveData.abilitiesApplied).toContainEqual(expect.toBeOneOf([AbilityId.PROTEAN, AbilityId.LIBERO])); + const lastMove = allMoves[pokemon.getLastXMoves()[0].move]!; + + const pokemonTypes = pokemon.getTypes().map(pt => PokemonType[pt]); + const moveType = PokemonType[pokemon.getMoveType(lastMove)]; + expect(pokemonTypes).toEqual([moveType]); + pokemon.waveData.abilitiesApplied.clear(); + } + + /** + * Assert that the protean/libero ability did NOT trigger to change the user's type to + * the type of its most recently used move. + * Takes into account type overrides from effects. + * @param pokemon - The {@linkcode PlayerPokemon} being checked. + * @remarks + * This will clear the given Pokemon's `abilitiesApplied` set after being called to allow for easier multi-turn testing. + */ + function expectNoTypeChange(pokemon: PlayerPokemon) { + expect(pokemon.waveData.abilitiesApplied).not.toContainEqual( + expect.toBeOneOf([AbilityId.PROTEAN, AbilityId.LIBERO]), + ); + const lastMove = allMoves[pokemon.getLastXMoves()[0].move]!; + + const pokemonTypes = pokemon.getTypes().map(pt => PokemonType[pt]); + const moveType = PokemonType[pokemon.getMoveType(lastMove, true)]; + expect(pokemonTypes).not.toEqual([moveType]); + pokemon.waveData.abilitiesApplied.clear(); + } + + it.each([ + { name: "Protean", ability: AbilityId.PROTEAN }, + { name: "Libero", ability: AbilityId.PROTEAN }, + ])("$name should change the user's type to the type of the move being used", async ({ ability }) => { + game.override.ability(ability); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const leadPokemon = game.field.getPlayerPokemon(); + + game.move.use(MoveId.SPLASH); + await game.toEndOfTurn(); + + expectTypeChange(leadPokemon); + }); + + // Test for Gen9+ functionality, we are using previous funcionality + it.skip("should apply only once per switch in", async () => { + game.override.moveset([MoveId.SPLASH, MoveId.AGILITY]); + await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.BULBASAUR]); + + const bulbasaur = game.field.getPlayerPokemon(); + + game.move.select(MoveId.SPLASH); + await game.toEndOfTurn(); + + expectTypeChange(bulbasaur); + + game.move.select(MoveId.AGILITY); + await game.toEndOfTurn(); + + expectNoTypeChange(bulbasaur); + + // switch out and back in + game.doSwitchPokemon(1); + await game.toNextTurn(); + game.doSwitchPokemon(1); + await game.toNextTurn(); + + expect(bulbasaur.isOnField()).toBe(true); + + game.move.select(MoveId.SPLASH); + await game.toEndOfTurn(); + + expectTypeChange(bulbasaur); + }); + + it.each<{ category: string; move?: MoveId; passive?: AbilityId; enemyMove?: MoveId }>([ + { category: "Variable type Moves'", move: MoveId.WEATHER_BALL, passive: AbilityId.DROUGHT }, + { category: "Type Change Abilities'", passive: AbilityId.REFRIGERATE }, + { category: "Move-calling Moves'", move: MoveId.NATURE_POWER, passive: AbilityId.PSYCHIC_SURGE }, + { category: "Ion Deluge's", enemyMove: MoveId.ION_DELUGE }, + { category: "Electrify's", enemyMove: MoveId.ELECTRIFY }, + ])( + "should respect $category final type", + async ({ move = MoveId.TACKLE, passive = AbilityId.NONE, enemyMove = MoveId.SPLASH }) => { + game.override.passiveAbility(passive); + await game.classicMode.startBattle([SpeciesId.LINOONE]); // Pure normal type for move overrides + + const linoone = game.field.getPlayerPokemon(); + + game.move.use(move); + await game.move.forceEnemyMove(enemyMove); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + // We stop before running `TurnEndPhase` so that the effects of `BattlerTag`s (such as from Electrify) + // are still active when checking the move's type + await game.phaseInterceptor.to("TurnEndPhase", false); + + expectTypeChange(linoone); + }, + ); + + it.each<{ cause: string; move?: MoveId; passive?: AbilityId; enemyMove?: MoveId }>([ + { cause: "misses", move: MoveId.FOCUS_BLAST }, + { cause: "is protected against", enemyMove: MoveId.PROTECT }, + { cause: "is ineffective", move: MoveId.EARTHQUAKE }, + { cause: "matches only one of its types", move: MoveId.NIGHT_SLASH }, + { cause: "is blocked by terrain", move: MoveId.SHADOW_SNEAK, passive: AbilityId.PSYCHIC_SURGE }, + ])( + "should still trigger if the user's move $cause", + async ({ move = MoveId.TACKLE, passive = AbilityId.NONE, enemyMove = MoveId.SPLASH }) => { + game.override.passiveAbility(passive).enemySpecies(SpeciesId.SKARMORY); + await game.classicMode.startBattle([SpeciesId.MEOWSCARADA]); + + vi.spyOn(allMoves[MoveId.FOCUS_BLAST], "accuracy", "get").mockReturnValue(0); + + const meow = game.field.getPlayerPokemon(); + + game.move.use(move); + await game.move.forceEnemyMove(enemyMove); + await game.toEndOfTurn(); + + expectTypeChange(meow); + }, + ); + + it.each<{ cause: string; move?: MoveId; tera?: boolean; passive?: AbilityId }>([ + { cause: "user is terastallized to any type", tera: true }, + { cause: "user uses Struggle", move: MoveId.STRUGGLE }, + { cause: "the user's move is blocked by weather", move: MoveId.FIRE_BLAST, passive: AbilityId.PRIMORDIAL_SEA }, + { cause: "the user's move fails", move: MoveId.BURN_UP }, + ])("should not apply if $cause", async ({ move = MoveId.TACKLE, tera = false, passive = AbilityId.NONE }) => { + game.override.enemyPassiveAbility(passive); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const karp = game.field.getPlayerPokemon(); + + karp.teraType = PokemonType.STEEL; + + game.move.use(move, BattlerIndex.PLAYER, undefined, tera); + await game.toEndOfTurn(); + + expectNoTypeChange(karp); + }); + + it("should not apply if user is already the move's type", async () => { + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const karp = game.field.getPlayerPokemon(); + + game.move.use(MoveId.WATERFALL); + await game.toEndOfTurn(); + + expect(karp.waveData.abilitiesApplied.size).toBe(0); + expect(karp.getTypes()).toEqual([allMoves[MoveId.WATERFALL].type]); + }); + + it.each<{ moveName: string; move: MoveId }>([ + { moveName: "Roar", move: MoveId.ROAR }, + { moveName: "Whirlwind", move: MoveId.WHIRLWIND }, + { moveName: "Forest's Curse", move: MoveId.FORESTS_CURSE }, + { moveName: "Trick-or-Treat", move: MoveId.TRICK_OR_TREAT }, + ])("should still apply if the user's $moveName fails", async ({ move }) => { + game.override.battleType(BattleType.TRAINER).enemySpecies(SpeciesId.TREVENANT); // ghost/grass makes both moves fail + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const leadPokemon = game.field.getPlayerPokemon(); + + game.move.use(move); + // KO all off-field opponents for Whirlwind and co. + for (const enemyMon of game.scene.getEnemyParty()) { + if (!enemyMon.isActive()) { + enemyMon.hp = 0; + } + } + await game.toEndOfTurn(); + + expectTypeChange(leadPokemon); + }); + + it("should trigger on the first turn of charging moves", async () => { + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const karp = game.field.getPlayerPokemon(); + + game.move.select(MoveId.DIG); + await game.toEndOfTurn(); + + expectTypeChange(karp); + + await game.toEndOfTurn(); + expect(karp.waveData.abilitiesApplied).not.toContain(AbilityId.PROTEAN); + }); + + it("should cause the user to cast Ghost-type Curse on itself", async () => { + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const karp = game.field.getPlayerPokemon(); + expect(karp.isOfType(PokemonType.GHOST)).toBe(false); + + game.move.select(MoveId.CURSE); + await game.toEndOfTurn(); + + expectTypeChange(karp); + expect(karp.getHpRatio(true)).toBeCloseTo(0.25); + expect(karp.getTag(BattlerTagType.CURSED)).toBeDefined(); + }); + + it("should not trigger during Focus Punch's start-of-turn message or being interrupted", async () => { + game.override.moveset(MoveId.FOCUS_PUNCH).enemyMoveset(MoveId.ABSORB); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + + const karp = game.field.getPlayerPokemon(); + expect(karp.isOfType(PokemonType.FIGHTING)).toBe(false); + + game.move.select(MoveId.FOCUS_PUNCH); + + await game.phaseInterceptor.to("MessagePhase"); + expect(karp.isOfType(PokemonType.FIGHTING)).toBe(false); + + await game.toEndOfTurn(); + + expectNoTypeChange(karp); + expect(karp.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + }); +}); diff --git a/test/abilities/protean.test.ts b/test/abilities/protean.test.ts deleted file mode 100644 index 09c9addbc35..00000000000 --- a/test/abilities/protean.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { allMoves } from "#app/data/data-lists"; -import { PokemonType } from "#enums/pokemon-type"; -import { Weather } from "#app/data/weather"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BiomeId } from "#enums/biome-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; - -describe("Abilities - Protean", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .battleStyle("single") - .ability(AbilityId.PROTEAN) - .startingLevel(100) - .enemySpecies(SpeciesId.RATTATA) - .enemyMoveset(MoveId.ENDURE); - }); - - test("ability applies and changes a pokemon's type", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - }); - - // Test for Gen9+ functionality, we are using previous funcionality - test.skip("ability applies only once per switch in", async () => { - game.override.moveset([MoveId.SPLASH, MoveId.AGILITY]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.BULBASAUR]); - - let leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - - game.move.select(MoveId.AGILITY); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.PROTEAN); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]]; - const moveType = PokemonType[allMoves[MoveId.AGILITY].type]; - expect(leadPokemonType).not.toBe(moveType); - - await game.toNextTurn(); - game.doSwitchPokemon(1); - await game.toNextTurn(); - game.doSwitchPokemon(1); - await game.toNextTurn(); - - leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.SPLASH); - }); - - test("ability applies correctly even if the pokemon's move has a variable type", async () => { - game.override.moveset([MoveId.WEATHER_BALL]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.move.select(MoveId.WEATHER_BALL); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.PROTEAN); - expect(leadPokemon.getTypes()).toHaveLength(1); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]], - moveType = PokemonType[PokemonType.FIRE]; - expect(leadPokemonType).toBe(moveType); - }); - - test("ability applies correctly even if the type has changed by another ability", async () => { - game.override.moveset([MoveId.TACKLE]).passiveAbility(AbilityId.REFRIGERATE); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).toContain(AbilityId.PROTEAN); - expect(leadPokemon.getTypes()).toHaveLength(1); - const leadPokemonType = PokemonType[leadPokemon.getTypes()[0]], - moveType = PokemonType[PokemonType.ICE]; - expect(leadPokemonType).toBe(moveType); - }); - - test("ability applies correctly even if the pokemon's move calls another move", async () => { - game.override.moveset([MoveId.NATURE_POWER]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.scene.arena.biomeType = BiomeId.MOUNTAIN; - game.move.select(MoveId.NATURE_POWER); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.AIR_SLASH); - }); - - test("ability applies correctly even if the pokemon's move is delayed / charging", async () => { - game.override.moveset([MoveId.DIG]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.DIG); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.DIG); - }); - - test("ability applies correctly even if the pokemon's move misses", async () => { - game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.SPLASH); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.move.forceMiss(); - await game.phaseInterceptor.to(TurnEndPhase); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon.isFullHp()).toBe(true); - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability applies correctly even if the pokemon's move is protected against", async () => { - game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.PROTECT); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => { - game.override.moveset([MoveId.TACKLE]).enemySpecies(SpeciesId.GASTLY); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TACKLE); - }); - - test("ability is not applied if pokemon's type is the same as the move's type", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - leadPokemon.summonData.types = [allMoves[MoveId.SPLASH].type]; - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.PROTEAN); - }); - - test("ability is not applied if pokemon is terastallized", async () => { - game.override.moveset([MoveId.SPLASH]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - leadPokemon.isTerastallized = true; - - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.PROTEAN); - }); - - test("ability is not applied if pokemon uses struggle", async () => { - game.override.moveset([MoveId.STRUGGLE]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.STRUGGLE); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.PROTEAN); - }); - - test("ability is not applied if the pokemon's move fails", async () => { - game.override.moveset([MoveId.BURN_UP]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.BURN_UP); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(leadPokemon.waveData.abilitiesApplied).not.toContain(AbilityId.PROTEAN); - }); - - test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => { - game.override.moveset([MoveId.TRICK_OR_TREAT]).enemySpecies(SpeciesId.GASTLY); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.TRICK_OR_TREAT); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.TRICK_OR_TREAT); - }); - - test("ability applies correctly and the pokemon curses itself", async () => { - game.override.moveset([MoveId.CURSE]); - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - game.move.select(MoveId.CURSE); - await game.phaseInterceptor.to(TurnEndPhase); - - testPokemonTypeMatchesDefaultMoveType(leadPokemon, MoveId.CURSE); - expect(leadPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); - }); -}); - -function testPokemonTypeMatchesDefaultMoveType(pokemon: PlayerPokemon, move: MoveId) { - expect(pokemon.waveData.abilitiesApplied).toContain(AbilityId.PROTEAN); - expect(pokemon.getTypes()).toHaveLength(1); - const pokemonType = PokemonType[pokemon.getTypes()[0]], - moveType = PokemonType[allMoves[move].type]; - expect(pokemonType).toBe(moveType); -} diff --git a/test/abilities/protosynthesis.test.ts b/test/abilities/protosynthesis.test.ts index ef860fc0cd5..5629ea503d7 100644 --- a/test/abilities/protosynthesis.test.ts +++ b/test/abilities/protosynthesis.test.ts @@ -1,11 +1,11 @@ import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { BattlerIndex } from "#enums/battler-index"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Protosynthesis", () => { diff --git a/test/abilities/quick_draw.test.ts b/test/abilities/quick_draw.test.ts index 11418f31375..f881adce5ed 100644 --- a/test/abilities/quick_draw.test.ts +++ b/test/abilities/quick_draw.test.ts @@ -1,9 +1,9 @@ -import { allAbilities } from "#app/data/data-lists"; -import { FaintPhase } from "#app/phases/faint-phase"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { FaintPhase } from "#phases/faint-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; diff --git a/test/abilities/sand_spit.test.ts b/test/abilities/sand_spit.test.ts index 2b519745649..dfd8a342ccc 100644 --- a/test/abilities/sand_spit.test.ts +++ b/test/abilities/sand_spit.test.ts @@ -1,8 +1,8 @@ -import { WeatherType } from "#app/enums/weather-type"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/sand_veil.test.ts b/test/abilities/sand_veil.test.ts index 35a0a3347ff..2ad9ef7eaa1 100644 --- a/test/abilities/sand_veil.test.ts +++ b/test/abilities/sand_veil.test.ts @@ -1,13 +1,13 @@ -import { allAbilities } from "#app/data/data-lists"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { StatMultiplierAbAttrParams } from "#types/ability-types"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; @@ -46,23 +46,18 @@ describe("Abilities - Sand Veil", () => { vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[AbilityId.SAND_VEIL]); const sandVeilAttr = allAbilities[AbilityId.SAND_VEIL].getAttrs("StatMultiplierAbAttr")[0]; - vi.spyOn(sandVeilAttr, "applyStatStage").mockImplementation( - (_pokemon, _passive, _simulated, stat, statValue, _args) => { - if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { - statValue.value *= -1; // will make all attacks miss - return true; - } - return false; - }, - ); + vi.spyOn(sandVeilAttr, "apply").mockImplementation(({ stat, statVal }: StatMultiplierAbAttrParams) => { + if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { + statVal.value *= -1; // will make all attacks miss + return true; + } + return false; + }); expect(leadPokemon[0].hasAbility(AbilityId.SAND_VEIL)).toBe(true); expect(leadPokemon[1].hasAbility(AbilityId.SAND_VEIL)).toBe(false); game.move.select(MoveId.SPLASH); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase, false); diff --git a/test/abilities/sap_sipper.test.ts b/test/abilities/sap_sipper.test.ts index c70730f0711..a8fe10d7fc5 100644 --- a/test/abilities/sap_sipper.test.ts +++ b/test/abilities/sap_sipper.test.ts @@ -1,16 +1,16 @@ -import { Stat } from "#enums/stat"; -import { TerrainType } from "#app/data/terrain"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { allMoves } from "#data/data-lists"; +import { TerrainType } from "#data/terrain"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { RandomMoveAttr } from "#moves/move"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; // See also: TypeImmunityAbAttr describe("Abilities - Sap Sipper", () => { diff --git a/test/abilities/schooling.test.ts b/test/abilities/schooling.test.ts index 646beafb80f..94bb4cb80f9 100644 --- a/test/abilities/schooling.test.ts +++ b/test/abilities/schooling.test.ts @@ -1,11 +1,11 @@ -import { Status } from "#app/data/status-effect"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Abilities - SCHOOLING", () => { diff --git a/test/abilities/screen_cleaner.test.ts b/test/abilities/screen_cleaner.test.ts index e896d2e51f9..8b53ab30d49 100644 --- a/test/abilities/screen_cleaner.test.ts +++ b/test/abilities/screen_cleaner.test.ts @@ -1,10 +1,10 @@ -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { PostSummonPhase } from "#phases/post-summon-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -28,11 +28,11 @@ describe("Abilities - Screen Cleaner", () => { }); it("removes Aurora Veil", async () => { - game.override.moveset([MoveId.HAIL]).enemyMoveset(MoveId.AURORA_VEIL); + game.override.enemyMoveset(MoveId.AURORA_VEIL); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); - game.move.select(MoveId.HAIL); + game.move.use(MoveId.HAIL); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.AURORA_VEIL)).toBeDefined(); @@ -49,7 +49,7 @@ describe("Abilities - Screen Cleaner", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.LIGHT_SCREEN)).toBeDefined(); @@ -66,7 +66,7 @@ describe("Abilities - Screen Cleaner", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.REFLECT)).toBeDefined(); diff --git a/test/abilities/seed_sower.test.ts b/test/abilities/seed_sower.test.ts index 7a898f5bcf9..8573f0242ea 100644 --- a/test/abilities/seed_sower.test.ts +++ b/test/abilities/seed_sower.test.ts @@ -1,8 +1,8 @@ -import { TerrainType } from "#app/data/terrain"; +import { TerrainType } from "#data/terrain"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/serene_grace.test.ts b/test/abilities/serene_grace.test.ts index 2acf2981537..aaa0f26f78b 100644 --- a/test/abilities/serene_grace.test.ts +++ b/test/abilities/serene_grace.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { allMoves } from "#app/data/data-lists"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Serene Grace", () => { diff --git a/test/abilities/sheer_force.test.ts b/test/abilities/sheer_force.test.ts index e90b8d08611..0b0c0c10ccf 100644 --- a/test/abilities/sheer_force.test.ts +++ b/test/abilities/sheer_force.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/data-lists"; describe("Abilities - Sheer Force", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/shell-armor.test.ts b/test/abilities/shell-armor.test.ts index ea0ed4a95dd..e10c3a90444 100644 --- a/test/abilities/shell-armor.test.ts +++ b/test/abilities/shell-armor.test.ts @@ -1,12 +1,12 @@ -import type Move from "#app/data/moves/move"; -import Pokemon from "#app/field/pokemon"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; describe("Abilities - Shell Armor", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 6bb63fd16a5..371c7d9cf29 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { applyAbAttrs, applyPreDefendAbAttrs } from "#app/data/abilities/apply-ab-attrs"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { NumberHolder } from "#app/utils/common"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -52,25 +52,16 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(MoveId.AIR_SLASH); const chance = new NumberHolder(move.chance); - await applyAbAttrs( - "MoveEffectChanceMultiplierAbAttr", - phase.getUserPokemon()!, - null, - false, + applyAbAttrs("MoveEffectChanceMultiplierAbAttr", { + pokemon: phase.getUserPokemon()!, chance, move, - phase.getFirstTarget(), - false, - ); - await applyPreDefendAbAttrs( - "IgnoreMoveEffectsAbAttr", - phase.getFirstTarget()!, - phase.getUserPokemon()!, - null, - null, - false, + }); + applyAbAttrs("IgnoreMoveEffectsAbAttr", { + pokemon: phase.getFirstTarget()!, + move, chance, - ); + }); expect(chance.value).toBe(0); }); diff --git a/test/abilities/shields_down.test.ts b/test/abilities/shields_down.test.ts index 7b4803915f1..7678c7d8dac 100644 --- a/test/abilities/shields_down.test.ts +++ b/test/abilities/shields_down.test.ts @@ -1,12 +1,12 @@ -import { Status } from "#app/data/status-effect"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Abilities - SHIELDS DOWN", () => { diff --git a/test/abilities/simple.test.ts b/test/abilities/simple.test.ts index 703f8fcffac..a709c8696d3 100644 --- a/test/abilities/simple.test.ts +++ b/test/abilities/simple.test.ts @@ -1,8 +1,8 @@ -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/speed_boost.test.ts b/test/abilities/speed_boost.test.ts index a4445d085f3..975fc8ebecf 100644 --- a/test/abilities/speed_boost.test.ts +++ b/test/abilities/speed_boost.test.ts @@ -1,13 +1,14 @@ -import { Stat } from "#enums/stat"; +import Overrides from "#app/overrides"; import { AbilityId } from "#enums/ability-id"; +import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { AttemptRunPhase } from "#phases/attempt-run-phase"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { Command } from "#enums/command"; -import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Speed Boost", () => { let phaserGame: Phaser.Game; @@ -96,12 +97,15 @@ describe("Abilities - Speed Boost", () => { }); it("should not trigger if pokemon fails to escape", async () => { + //Account for doubles, should not trigger on either pokemon + game.override.battleStyle("double"); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); + vi.spyOn(Overrides, "RUN_SUCCESS_OVERRIDE", "get").mockReturnValue(false); + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); - const runPhase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; - runPhase.forceFailEscape = true; + await game.phaseInterceptor.to(AttemptRunPhase); await game.toNextTurn(); diff --git a/test/abilities/stakeout.test.ts b/test/abilities/stakeout.test.ts index b3844bf17ca..85838a52abe 100644 --- a/test/abilities/stakeout.test.ts +++ b/test/abilities/stakeout.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { isBetween } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { isBetween } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/stall.test.ts b/test/abilities/stall.test.ts index e8ee23fb972..c012586eea5 100644 --- a/test/abilities/stall.test.ts +++ b/test/abilities/stall.test.ts @@ -1,10 +1,10 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; describe("Abilities - Stall", () => { let phaserGame: Phaser.Game; @@ -35,7 +35,7 @@ describe("Abilities - Stall", () => { * References: * https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority - **/ + */ it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); diff --git a/test/abilities/steely_spirit.test.ts b/test/abilities/steely_spirit.test.ts index 7a2bd98b6b1..0ca7bebe17a 100644 --- a/test/abilities/steely_spirit.test.ts +++ b/test/abilities/steely_spirit.test.ts @@ -1,9 +1,8 @@ -import { allAbilities } from "#app/data/data-lists"; -import { allMoves } from "#app/data/data-lists"; +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/storm_drain.test.ts b/test/abilities/storm_drain.test.ts index 8eedf0c7ce8..59626546578 100644 --- a/test/abilities/storm_drain.test.ts +++ b/test/abilities/storm_drain.test.ts @@ -1,12 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +// TODO: Condense with lightning rod tests sometime describe("Abilities - Storm Drain", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -39,7 +40,7 @@ describe("Abilities - Storm Drain", () => { const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; - enemy2.summonData.ability = AbilityId.STORM_DRAIN; + game.field.mockAbility(enemy2, AbilityId.STORM_DRAIN); game.move.select(MoveId.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -55,7 +56,7 @@ describe("Abilities - Storm Drain", () => { const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; - enemy2.summonData.ability = AbilityId.STORM_DRAIN; + game.field.mockAbility(enemy2, AbilityId.STORM_DRAIN); game.move.select(MoveId.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -68,8 +69,7 @@ describe("Abilities - Storm Drain", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.STORM_DRAIN; + game.field.mockAbility(enemy2, AbilityId.STORM_DRAIN); game.move.select(MoveId.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -85,8 +85,7 @@ describe("Abilities - Storm Drain", () => { const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.STORM_DRAIN; + game.field.mockAbility(enemy2, AbilityId.STORM_DRAIN); game.move.select(MoveId.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -96,16 +95,15 @@ describe("Abilities - Storm Drain", () => { }); it("should redirect moves changed to water type via ability", async () => { - game.override.ability(AbilityId.LIQUID_VOICE).moveset(MoveId.PSYCHIC_NOISE); - await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); + game.override.ability(AbilityId.LIQUID_VOICE); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; - enemy2.summonData.ability = AbilityId.STORM_DRAIN; + game.field.mockAbility(enemy2, AbilityId.STORM_DRAIN); - game.move.select(MoveId.PSYCHIC_NOISE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + game.move.use(MoveId.ROUND, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(true); diff --git a/test/abilities/sturdy.test.ts b/test/abilities/sturdy.test.ts index e7b2c05040d..0cde40a91da 100644 --- a/test/abilities/sturdy.test.ts +++ b/test/abilities/sturdy.test.ts @@ -1,10 +1,10 @@ -import type { EnemyPokemon } from "#app/field/pokemon"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { EnemyPokemon } from "#field/pokemon"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/abilities/super_luck.test.ts b/test/abilities/super_luck.test.ts index 60ed2534fe9..954251583f8 100644 --- a/test/abilities/super_luck.test.ts +++ b/test/abilities/super_luck.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/supreme_overlord.test.ts b/test/abilities/supreme_overlord.test.ts index 7143b590f68..b68153a2cc8 100644 --- a/test/abilities/supreme_overlord.test.ts +++ b/test/abilities/supreme_overlord.test.ts @@ -1,13 +1,13 @@ -import { MoveId } from "#enums/move-id"; -import type Move from "#app/data/moves/move"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { SpeciesId } from "#enums/species-id"; import { BattlerIndex } from "#enums/battler-index"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import type { Move } from "#moves/move"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/data-lists"; describe("Abilities - Supreme Overlord", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/sweet_veil.test.ts b/test/abilities/sweet_veil.test.ts index ed9cb20afcc..ca98e827c4f 100644 --- a/test/abilities/sweet_veil.test.ts +++ b/test/abilities/sweet_veil.test.ts @@ -1,11 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -80,7 +79,6 @@ describe("Abilities - Sweet Veil", () => { expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH); game.doSwitchPokemon(2); diff --git a/test/abilities/synchronize.test.ts b/test/abilities/synchronize.test.ts index 030509ac4e6..1874560a166 100644 --- a/test/abilities/synchronize.test.ts +++ b/test/abilities/synchronize.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/tera_shell.test.ts b/test/abilities/tera_shell.test.ts index a52a01862ff..bd461e3aeba 100644 --- a/test/abilities/tera_shell.test.ts +++ b/test/abilities/tera_shell.test.ts @@ -1,8 +1,8 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/thermal_exchange.test.ts b/test/abilities/thermal_exchange.test.ts index 70af864d413..5e684b982e7 100644 --- a/test/abilities/thermal_exchange.test.ts +++ b/test/abilities/thermal_exchange.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/trace.test.ts b/test/abilities/trace.test.ts index f17bec159ab..7b87b3669b6 100644 --- a/test/abilities/trace.test.ts +++ b/test/abilities/trace.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 6e24e10d168..8d738db9076 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -1,15 +1,15 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { StealHeldItemChanceAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import type Pokemon from "#app/field/pokemon"; -import type { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import type { ContactHeldItemTransferChanceModifier } from "#modifiers/modifier"; +import { StealHeldItemChanceAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -22,10 +22,7 @@ describe("Abilities - Unburden", () => { */ function getHeldItemCount(pokemon: Pokemon): number { const stackCounts = pokemon.getHeldItems().map(m => m.getStackCount()); - if (stackCounts.length) { - return stackCounts.reduce((a, b) => a + b); - } - return 0; + return stackCounts.reduce((a, b) => a + b, 0); } beforeAll(() => { @@ -277,7 +274,7 @@ describe("Abilities - Unburden", () => { const initialTreeckoSpeed = treecko.getStat(Stat.SPD); const initialPurrloinSpeed = purrloin.getStat(Stat.SPD); const unburdenAttr = treecko.getAbilityAttrs("PostItemLostAbAttr")[0]; - vi.spyOn(unburdenAttr, "applyPostItemLost"); + vi.spyOn(unburdenAttr, "apply"); // Player uses Baton Pass, which also passes the Baton item game.move.select(MoveId.BATON_PASS); @@ -288,7 +285,7 @@ describe("Abilities - Unburden", () => { expect(getHeldItemCount(purrloin)).toBe(1); expect(treecko.getEffectiveStat(Stat.SPD)).toBe(initialTreeckoSpeed); expect(purrloin.getEffectiveStat(Stat.SPD)).toBe(initialPurrloinSpeed); - expect(unburdenAttr.applyPostItemLost).not.toHaveBeenCalled(); + expect(unburdenAttr.apply).not.toHaveBeenCalled(); }); it("should not speed up a Pokemon after it loses the ability Unburden", async () => { diff --git a/test/abilities/unseen_fist.test.ts b/test/abilities/unseen_fist.test.ts index 26de77d4643..c986a112517 100644 --- a/test/abilities/unseen_fist.test.ts +++ b/test/abilities/unseen_fist.test.ts @@ -1,12 +1,12 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; describe("Abilities - Unseen Fist", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/victory_star.test.ts b/test/abilities/victory_star.test.ts index 4742dd96aa6..aff91a7702b 100644 --- a/test/abilities/victory_star.test.ts +++ b/test/abilities/victory_star.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/vital_spirit.test.ts b/test/abilities/vital_spirit.test.ts index 18764f94a6b..7530b2c6c04 100644 --- a/test/abilities/vital_spirit.test.ts +++ b/test/abilities/vital_spirit.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/volt_absorb.test.ts b/test/abilities/volt_absorb.test.ts index 6bea70ee2a4..12de6ceff87 100644 --- a/test/abilities/volt_absorb.test.ts +++ b/test/abilities/volt_absorb.test.ts @@ -1,13 +1,13 @@ -import { Stat } from "#enums/stat"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#enums/battler-index"; -import { SpeciesId } from "#enums/species-id"; // See also: TypeImmunityAbAttr describe("Abilities - Volt Absorb", () => { diff --git a/test/abilities/wandering_spirit.test.ts b/test/abilities/wandering_spirit.test.ts index 950cec6d27c..3d26238e970 100644 --- a/test/abilities/wandering_spirit.test.ts +++ b/test/abilities/wandering_spirit.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/water_bubble.test.ts b/test/abilities/water_bubble.test.ts index 455a2e368c4..c8d01eb2800 100644 --- a/test/abilities/water_bubble.test.ts +++ b/test/abilities/water_bubble.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/water_veil.test.ts b/test/abilities/water_veil.test.ts index 2df06ec1a21..dcfe5b60fe9 100644 --- a/test/abilities/water_veil.test.ts +++ b/test/abilities/water_veil.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index 1db0b80fcd0..d6e6908e19b 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -1,16 +1,16 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; -import GameManager from "#test/testUtils/gameManager"; -import { toDmgValue } from "#app/utils/common"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -108,7 +108,7 @@ describe("Abilities - Wimp Out", () => { }); it("Trapping moves do not prevent Wimp Out from activating.", async () => { - game.override.enemyMoveset([MoveId.SPIRIT_SHACKLE]).startingLevel(53).enemyLevel(45); + game.override.enemyMoveset([MoveId.SPIRIT_SHACKLE]).startingLevel(1).passiveAbility(AbilityId.STURDY); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); game.move.select(MoveId.SPLASH); @@ -123,7 +123,7 @@ describe("Abilities - Wimp Out", () => { }); it("If this Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out.", async () => { - game.override.startingLevel(95).enemyMoveset([MoveId.U_TURN]); + game.override.startingLevel(1).enemyMoveset([MoveId.U_TURN]).passiveAbility(AbilityId.STURDY); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/wind_power.test.ts b/test/abilities/wind_power.test.ts index 8e657997008..255e8fa69be 100644 --- a/test/abilities/wind_power.test.ts +++ b/test/abilities/wind_power.test.ts @@ -1,9 +1,9 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/wind_rider.test.ts b/test/abilities/wind_rider.test.ts index ea1747fcae9..e7b55d2ef65 100644 --- a/test/abilities/wind_rider.test.ts +++ b/test/abilities/wind_rider.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/wonder_skin.test.ts b/test/abilities/wonder_skin.test.ts index 886882ab6fd..f55c303c9e3 100644 --- a/test/abilities/wonder_skin.test.ts +++ b/test/abilities/wonder_skin.test.ts @@ -1,9 +1,9 @@ -import { allMoves } from "#app/data/data-lists"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -66,7 +66,7 @@ describe("Abilities - Wonder Skin", () => { it(`does not affect pokemon with ${ability[1]}`, async () => { const moveToCheck = allMoves[MoveId.CHARM]; - // @ts-ignore ts doesn't know that ability[0] is an ability and not a string... + // @ts-expect-error ts doesn't know that ability[0] is an ability and not a string... game.override.ability(ability[0]); vi.spyOn(moveToCheck, "calculateBattleAccuracy"); diff --git a/test/abilities/zen_mode.test.ts b/test/abilities/zen_mode.test.ts index 5df2b3a6bc7..89dc0690ead 100644 --- a/test/abilities/zen_mode.test.ts +++ b/test/abilities/zen_mode.test.ts @@ -1,9 +1,9 @@ -import { Status } from "#app/data/status-effect"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/abilities/zero_to_hero.test.ts b/test/abilities/zero_to_hero.test.ts index 7d0128a4dbc..84b3ddab60c 100644 --- a/test/abilities/zero_to_hero.test.ts +++ b/test/abilities/zero_to_hero.test.ts @@ -1,11 +1,11 @@ -import { Status } from "#app/data/status-effect"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - ZERO TO HERO", () => { diff --git a/test/account.test.ts b/test/account.test.ts index 77368b0b64c..52d52778dad 100644 --- a/test/account.test.ts +++ b/test/account.test.ts @@ -1,7 +1,7 @@ -import * as bypassLogin from "#app/global-vars/bypass-login"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { describe, expect, it, vi } from "vitest"; +import { pokerogueApi } from "#api/pokerogue-api"; import { initLoggedInUser, loggedInUser, updateUserInfo } from "#app/account"; +import * as bypassLogin from "#app/global-vars/bypass-login"; +import { describe, expect, it, vi } from "vitest"; describe("account", () => { describe("initLoggedInUser", () => { diff --git a/test/achievements/achievement.test.ts b/test/achievements/achievement.test.ts index 5a74c77fe6a..d736485b25f 100644 --- a/test/achievements/achievement.test.ts +++ b/test/achievements/achievement.test.ts @@ -1,20 +1,20 @@ -import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; +import type { BattleScene } from "#app/battle-scene"; +import { TurnHeldItemTransferModifier } from "#modifiers/modifier"; import { Achv, AchvTier, + achvs, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, - achvs, -} from "#app/system/achv"; -import { NumberHolder } from "#app/utils/common"; -import GameManager from "#test/testUtils/gameManager"; +} from "#system/achv"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type BattleScene from "#app/battle-scene"; describe("check some Achievement related stuff", () => { it("should check Achievement creation", () => { diff --git a/test/arena/arena_gravity.test.ts b/test/arena/arena_gravity.test.ts index 36fe0b58308..d1cee1655a1 100644 --- a/test/arena/arena_gravity.test.ts +++ b/test/arena/arena_gravity.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/arena/grassy_terrain.test.ts b/test/arena/grassy_terrain.test.ts index 5f78d8f801d..541402123c4 100644 --- a/test/arena/grassy_terrain.test.ts +++ b/test/arena/grassy_terrain.test.ts @@ -1,8 +1,8 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/arena/weather_fog.test.ts b/test/arena/weather_fog.test.ts index e6984e13bd0..f55a12e3e0f 100644 --- a/test/arena/weather_fog.test.ts +++ b/test/arena/weather_fog.test.ts @@ -1,10 +1,10 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/arena/weather_hail.test.ts b/test/arena/weather_hail.test.ts index 27cf46fa9f2..c6f27f647fe 100644 --- a/test/arena/weather_hail.test.ts +++ b/test/arena/weather_hail.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/arena/weather_sandstorm.test.ts b/test/arena/weather_sandstorm.test.ts index 61001abe1f4..a600aeed000 100644 --- a/test/arena/weather_sandstorm.test.ts +++ b/test/arena/weather_sandstorm.test.ts @@ -1,9 +1,9 @@ import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#app/enums/stat"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/arena/weather_strong_winds.test.ts b/test/arena/weather_strong_winds.test.ts index d0d256816eb..b0b1bca8c4c 100644 --- a/test/arena/weather_strong_winds.test.ts +++ b/test/arena/weather_strong_winds.test.ts @@ -1,10 +1,10 @@ -import { allMoves } from "#app/data/data-lists"; -import { StatusEffect } from "#app/enums/status-effect"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -86,7 +86,7 @@ describe("Weather - Strong Winds", () => { const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1; - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); expect(game.scene.arena.weather?.weatherType).toBeUndefined(); diff --git a/test/battle-scene.test.ts b/test/battle-scene.test.ts index 44f1364441b..a920c48ea75 100644 --- a/test/battle-scene.test.ts +++ b/test/battle-scene.test.ts @@ -1,6 +1,6 @@ import { LoadingScene } from "#app/loading-scene"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; describe("BattleScene", () => { let phaserGame: Phaser.Game; diff --git a/test/battle/ability_swap.test.ts b/test/battle/ability_swap.test.ts index 3dd92576e3b..525c038d011 100644 --- a/test/battle/ability_swap.test.ts +++ b/test/battle/ability_swap.test.ts @@ -1,9 +1,9 @@ -import { allAbilities } from "#app/data/data-lists"; -import { Stat } from "#app/enums/stat"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/battle/battle-order.test.ts b/test/battle/battle-order.test.ts index 114592c8c8e..a47da66c4d7 100644 --- a/test/battle/battle-order.test.ts +++ b/test/battle/battle-order.test.ts @@ -1,10 +1,10 @@ -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { SelectTargetPhase } from "#app/phases/select-target-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { SelectTargetPhase } from "#phases/select-target-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/battle/battle.test.ts b/test/battle/battle.test.ts index bf2c3968aa6..7a2365fb6d8 100644 --- a/test/battle/battle.test.ts +++ b/test/battle/battle.test.ts @@ -1,32 +1,32 @@ -import { allSpecies } from "#app/data/data-lists"; -import { Stat } from "#enums/stat"; import { getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { LoginPhase } from "#app/phases/login-phase"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import { SelectGenderPhase } from "#app/phases/select-gender-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { SwitchPhase } from "#app/phases/switch-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import GameManager from "#test/testUtils/gameManager"; -import { generateStarter } from "#test/testUtils/gameManagerUtils"; -import { UiMode } from "#enums/ui-mode"; +import { allSpecies } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BiomeId } from "#enums/biome-id"; +import { GameModes } from "#enums/game-modes"; import { MoveId } from "#enums/move-id"; import { PlayerGender } from "#enums/player-gender"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { UiMode } from "#enums/ui-mode"; +import { BattleEndPhase } from "#phases/battle-end-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { LoginPhase } from "#phases/login-phase"; +import { NextEncounterPhase } from "#phases/next-encounter-phase"; +import { SelectGenderPhase } from "#phases/select-gender-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import { SummonPhase } from "#phases/summon-phase"; +import { SwitchPhase } from "#phases/switch-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { generateStarter } from "#test/testUtils/gameManagerUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { BiomeId } from "#enums/biome-id"; -describe("Test Battle Phase", () => { +describe("Phase - Battle Phase", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -197,47 +197,25 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase); }); - it("2vs1", async () => { - game.override.battleStyle("single"); - game.override.enemySpecies(SpeciesId.MIGHTYENA); - game.override.enemyAbility(AbilityId.HYDRATION); - game.override.ability(AbilityId.HYDRATION); - await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); - }, 20000); + it.each([ + { name: "1v1", double: false, qty: 1 }, + { name: "2v1", double: false, qty: 2 }, + { name: "2v2", double: true, qty: 2 }, + { name: "4v2", double: true, qty: 4 }, + ])("should not crash when starting $name battle", async ({ double, qty }) => { + game.override + .battleStyle(double ? "double" : "single") + .enemySpecies(SpeciesId.MIGHTYENA) + .enemyAbility(AbilityId.HYDRATION) + .ability(AbilityId.HYDRATION); - it("1vs1", async () => { - game.override.battleStyle("single"); - game.override.enemySpecies(SpeciesId.MIGHTYENA); - game.override.enemyAbility(AbilityId.HYDRATION); - game.override.ability(AbilityId.HYDRATION); - await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); - }, 20000); + await game.classicMode.startBattle( + [SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE].slice(0, qty), + ); - it("2vs2", async () => { - game.override.battleStyle("double"); - game.override.enemySpecies(SpeciesId.MIGHTYENA); - game.override.enemyAbility(AbilityId.HYDRATION); - game.override.ability(AbilityId.HYDRATION); - game.override.startingWave(3); - await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); - }, 20000); - - it("4vs2", async () => { - game.override.battleStyle("double"); - game.override.enemySpecies(SpeciesId.MIGHTYENA); - game.override.enemyAbility(AbilityId.HYDRATION); - game.override.ability(AbilityId.HYDRATION); - game.override.startingWave(3); - await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]); - expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); - }, 20000); + expect(game.scene.phaseManager.getCurrentPhase()).toBeInstanceOf(CommandPhase); + }); it("kill opponent pokemon", async () => { const moveToUse = MoveId.SPLASH; diff --git a/test/battle/damage_calculation.test.ts b/test/battle/damage_calculation.test.ts index 19cdf6b9237..fbcefcb0693 100644 --- a/test/battle/damage_calculation.test.ts +++ b/test/battle/damage_calculation.test.ts @@ -1,11 +1,10 @@ -import { allMoves } from "#app/data/data-lists"; -import type { EnemyPersistentModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; +import { allMoves, modifierTypes } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { EnemyPersistentModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/battle/double_battle.test.ts b/test/battle/double_battle.test.ts index 8e606a99ae0..7e0aa0f6c69 100644 --- a/test/battle/double_battle.test.ts +++ b/test/battle/double_battle.test.ts @@ -1,13 +1,13 @@ -import { Status } from "#app/data/status-effect"; -import { AbilityId } from "#enums/ability-id"; import { getGameMode } from "#app/game-mode"; +import { Status } from "#data/status-effect"; +import { AbilityId } from "#enums/ability-id"; import { GameModes } from "#enums/game-modes"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { BattleEndPhase } from "#phases/battle-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/battle/inverse_battle.test.ts b/test/battle/inverse_battle.test.ts index 66cab3e2d84..f9fd8f80a37 100644 --- a/test/battle/inverse_battle.test.ts +++ b/test/battle/inverse_battle.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { Challenges } from "#enums/challenges"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/battle/special_battle.test.ts b/test/battle/special_battle.test.ts index 4988e526b1e..36b543b0666 100644 --- a/test/battle/special_battle.test.ts +++ b/test/battle/special_battle.test.ts @@ -1,8 +1,8 @@ -import { UiMode } from "#enums/ui-mode"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/battlerTags/octolock.test.ts b/test/battlerTags/octolock.test.ts index d0214f495fc..5b395a180a2 100644 --- a/test/battlerTags/octolock.test.ts +++ b/test/battlerTags/octolock.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import type Pokemon from "#app/field/pokemon"; -import { OctolockTag, TrappedTag } from "#app/data/battler-tags"; +import { OctolockTag, TrappedTag } from "#data/battler-tags"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; describe("BattlerTag - OctolockTag", () => { describe("lapse behavior", () => { diff --git a/test/battlerTags/stockpiling.test.ts b/test/battlerTags/stockpiling.test.ts index 37873db9eab..9024b3edb3c 100644 --- a/test/battlerTags/stockpiling.test.ts +++ b/test/battlerTags/stockpiling.test.ts @@ -1,10 +1,10 @@ -import { StockpilingTag } from "#app/data/battler-tags"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonSummonData } from "#app/field/pokemon"; import * as messages from "#app/messages"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { StockpilingTag } from "#data/battler-tags"; +import { PokemonSummonData } from "#data/pokemon-data"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; beforeEach(() => { @@ -101,7 +101,8 @@ describe("BattlerTag - StockpilingTag", () => { }); describe("stack limit, stat tracking, and removal", () => { - it("can be added up to three times, even when one stat does not change", async () => { + // TODO: do we even want this file at all? regardless, this test is broken and is also likely unimportant + it.todo("can be added up to three times, even when one stat does not change", async () => { const mockPokemon = { summonData: new PokemonSummonData(), getBattlerIndex: () => 0, @@ -150,7 +151,7 @@ describe("BattlerTag - StockpilingTag", () => { expect(subject.stockpiledCount).toBe(3); vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(_phase => { - throw new Error("Should not be called a fourth time"); + expect.fail("Should not be called a fourth time"); }); // fourth stack should not be applied diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index 06aedab19f4..825bebf1a1e 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -1,16 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { PokemonTurnData, TurnMove } from "#app/field/pokemon"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; -import type BattleScene from "#app/battle-scene"; -import { BindTag, SubstituteTag } from "#app/data/battler-tags"; +import type { BattleScene } from "#app/battle-scene"; +import * as messages from "#app/messages"; +import { BindTag, SubstituteTag } from "#data/battler-tags"; +import { allMoves } from "#data/data-lists"; +import type { PokemonTurnData } from "#data/pokemon-data"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { MoveId } from "#enums/move-id"; -import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; -import * as messages from "#app/messages"; -import { allMoves } from "#app/data/data-lists"; -import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveResult } from "#enums/move-result"; +import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import type { Pokemon } from "#field/pokemon"; +import type { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { TurnMove } from "#types/turn-move"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("BattlerTag - SubstituteTag", () => { let phaserGame: Phaser.Game; diff --git a/test/boss-pokemon.test.ts b/test/boss-pokemon.test.ts index 6eb6d262c56..06a2f313016 100644 --- a/test/boss-pokemon.test.ts +++ b/test/boss-pokemon.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { EFFECTIVE_STATS } from "#app/enums/stat"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { toDmgValue } from "#app/utils/common"; +import { SpeciesId } from "#enums/species-id"; +import { EFFECTIVE_STATS } from "#enums/stat"; +import type { EnemyPokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Boss Pokemon / Shields", () => { let phaserGame: Phaser.Game; diff --git a/test/daily_mode.test.ts b/test/daily_mode.test.ts index 41dcf51ccf6..0fa7c0dc846 100644 --- a/test/daily_mode.test.ts +++ b/test/daily_mode.test.ts @@ -1,14 +1,12 @@ +import { pokerogueApi } from "#api/pokerogue-api"; import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; -import { MapModifier } from "#app/modifier/modifier"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; +import { MapModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; - -//const TIMEOUT = 20 * 1000; describe("Daily Mode", () => { let phaserGame: Phaser.Game; diff --git a/test/data/splash_messages.test.ts b/test/data/splash_messages.test.ts index 773b2715825..9763d85a583 100644 --- a/test/data/splash_messages.test.ts +++ b/test/data/splash_messages.test.ts @@ -1,6 +1,6 @@ -import { getSplashMessages } from "#app/data/splash-messages"; -import { describe, expect, it, vi, afterEach, beforeEach } from "vitest"; import * as Constants from "#app/constants"; +import { getSplashMessages } from "#data/splash-messages"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; describe("Data - Splash Messages", () => { it("should contain at least 15 splash messages", () => { diff --git a/test/data/status_effect.test.ts b/test/data/status_effect.test.ts index e697ee3f1c7..2ea4dce4147 100644 --- a/test/data/status_effect.test.ts +++ b/test/data/status_effect.test.ts @@ -1,17 +1,17 @@ import { - Status, getStatusEffectActivationText, getStatusEffectDescriptor, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, -} from "#app/data/status-effect"; -import { MoveResult } from "#enums/move-result"; + Status, +} from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import { mockI18next } from "#test/testUtils/testUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/eggs/egg.test.ts b/test/eggs/egg.test.ts index 0d5d09c5179..b27124bd786 100644 --- a/test/eggs/egg.test.ts +++ b/test/eggs/egg.test.ts @@ -1,13 +1,13 @@ -import { speciesEggTiers } from "#app/data/balance/species-egg-tiers"; -import { Egg, getLegendaryGachaSpeciesForTimestamp, getValidLegendaryGachaSpecies } from "#app/data/egg"; -import { allSpecies } from "#app/data/data-lists"; -import { EggSourceType } from "#app/enums/egg-source-types"; -import { EggTier } from "#app/enums/egg-type"; -import { VariantTier } from "#app/enums/variant-tier"; -import EggData from "#app/system/egg-data"; -import * as Utils from "#app/utils/common"; +import { speciesEggTiers } from "#balance/species-egg-tiers"; +import { allSpecies } from "#data/data-lists"; +import { Egg, getLegendaryGachaSpeciesForTimestamp, getValidLegendaryGachaSpecies } from "#data/egg"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { VariantTier } from "#enums/variant-tier"; +import { EggData } from "#system/egg-data"; +import { GameManager } from "#test/testUtils/gameManager"; +import * as Utils from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/eggs/manaphy-egg.test.ts b/test/eggs/manaphy-egg.test.ts index a58dfa9beba..319b5a15d7b 100644 --- a/test/eggs/manaphy-egg.test.ts +++ b/test/eggs/manaphy-egg.test.ts @@ -1,8 +1,8 @@ -import { Egg } from "#app/data/egg"; -import { EggSourceType } from "#app/enums/egg-source-types"; -import { EggTier } from "#app/enums/egg-type"; +import { Egg } from "#data/egg"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/endless_boss.test.ts b/test/endless_boss.test.ts index d219cacf317..99c179ceeb6 100644 --- a/test/endless_boss.test.ts +++ b/test/endless_boss.test.ts @@ -1,8 +1,8 @@ import { BiomeId } from "#enums/biome-id"; -import { SpeciesId } from "#enums/species-id"; import { GameModes } from "#enums/game-modes"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; const EndlessBossWave = { Minor: 250, diff --git a/test/enemy_command.test.ts b/test/enemy_command.test.ts index fabb1f1fdb0..249c819a107 100644 --- a/test/enemy_command.test.ts +++ b/test/enemy_command.test.ts @@ -1,13 +1,13 @@ -import type BattleScene from "#app/battle-scene"; -import { allMoves } from "#app/data/data-lists"; -import { MoveCategory } from "#enums/MoveCategory"; +import type { BattleScene } from "#app/battle-scene"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { AiType } from "#enums/ai-type"; +import { MoveCategory } from "#enums/MoveCategory"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import type { EnemyPokemon } from "#app/field/pokemon"; -import { AiType } from "#enums/ai-type"; -import { randSeedInt } from "#app/utils/common"; -import GameManager from "#test/testUtils/gameManager"; +import type { EnemyPokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import { randSeedInt } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index 2cc0934f7c1..338d2a1dcfc 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -1,10 +1,9 @@ -import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; -import type { CommandPhase } from "#app/phases/command-phase"; -import { Command } from "#enums/command"; -import { NumberHolder } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { Command } from "#enums/command"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { AttemptRunPhase } from "#phases/attempt-run-phase"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -45,8 +44,6 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new NumberHolder(0); - // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping const escapeChances: { pokemonSpeedRatio: number; @@ -91,8 +88,8 @@ describe("Escape chance calculations", () => { 20, escapeChances[i].pokemonSpeedRatio * enemySpeed, ]); - phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); - expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); + expect(chance).toBe(escapeChances[i].expectedEscapeChance); } }); @@ -118,8 +115,6 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new NumberHolder(0); - // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping const escapeChances: { pokemonSpeedRatio: number; @@ -172,9 +167,9 @@ describe("Escape chance calculations", () => { 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5], ]); - phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); // checks to make sure the escape values are the same - expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + expect(chance).toBe(escapeChances[i].expectedEscapeChance); // checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe( escapeChances[i].pokemonSpeedRatio * totalEnemySpeed, @@ -197,7 +192,6 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping const escapeChances: { @@ -256,8 +250,8 @@ describe("Escape chance calculations", () => { 20, escapeChances[i].pokemonSpeedRatio * enemySpeed, ]); - phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); - expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); + expect(chance).toBe(escapeChances[i].expectedEscapeChance); } }); @@ -283,7 +277,6 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping const escapeChances: { @@ -349,9 +342,9 @@ describe("Escape chance calculations", () => { 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5], ]); - phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); // checks to make sure the escape values are the same - expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + expect(chance).toBe(escapeChances[i].expectedEscapeChance); // checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe( escapeChances[i].pokemonSpeedRatio * totalEnemySpeed, diff --git a/test/evolution.test.ts b/test/evolution.test.ts index c4bcca785c1..ffede4d09d6 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -1,13 +1,9 @@ -import { - pokemonEvolutions, - SpeciesFormEvolution, - SpeciesWildEvolutionDelay, -} from "#app/data/balance/pokemon-evolutions"; +import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#balance/pokemon-evolutions"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import * as Utils from "#app/utils/common"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import * as Utils from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/field/pokemon-id-checks.test.ts b/test/field/pokemon-id-checks.test.ts new file mode 100644 index 00000000000..065a1ffeb7e --- /dev/null +++ b/test/field/pokemon-id-checks.test.ts @@ -0,0 +1,79 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import type { Pokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Field - Pokemon ID Checks", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.NO_GUARD) + .battleStyle("single") + .battleType(BattleType.TRAINER) + .criticalHits(false) + .enemyLevel(100) + .enemySpecies(SpeciesId.ARCANINE) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH); + }); + + function onlyUnique(array: T[]): T[] { + return [...new Set(array)]; + } + + // TODO: We currently generate IDs as a pure random integer; enable once unique UUIDs are added + it.todo("2 Pokemon should not be able to generate with the same ID during 1 encounter", async () => { + game.override.battleType(BattleType.TRAINER); // enemy generates 2 mons + await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.ABRA]); + + const ids = (game.scene.getPlayerParty() as Pokemon[]).concat(game.scene.getEnemyParty()).map((p: Pokemon) => p.id); + const uniqueIds = onlyUnique(ids); + + expect(ids).toHaveLength(uniqueIds.length); + }); + + it("should not prevent Battler Tags from triggering if user has PID of 0", async () => { + await game.classicMode.startBattle([SpeciesId.TREECKO, SpeciesId.AERODACTYL]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + // Override player pokemon PID to be 0 + player.id = 0; + expect(player.getTag(BattlerTagType.DESTINY_BOND)).toBeUndefined(); + + game.move.use(MoveId.DESTINY_BOND); + game.doSelectPartyPokemon(1); + await game.move.forceEnemyMove(MoveId.FLAME_WHEEL); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + const dBondTag = player.getTag(BattlerTagType.DESTINY_BOND)!; + expect(dBondTag).toBeDefined(); + expect(dBondTag.sourceId).toBe(0); + expect(dBondTag.getSourcePokemon()).toBe(player); + + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(player.isFainted()).toBe(true); + expect(enemy.isFainted()).toBe(true); + }); +}); diff --git a/test/field/pokemon.test.ts b/test/field/pokemon.test.ts index 774d46b18fe..8c04dc61b32 100644 --- a/test/field/pokemon.test.ts +++ b/test/field/pokemon.test.ts @@ -1,11 +1,11 @@ -import { SpeciesId } from "#enums/species-id"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { PokeballType } from "#enums/pokeball"; -import type BattleScene from "#app/battle-scene"; +import type { BattleScene } from "#app/battle-scene"; +import { CustomPokemonData } from "#data/pokemon-data"; import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; import { PokemonType } from "#enums/pokemon-type"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Spec - Pokemon", () => { let phaserGame: Phaser.Game; @@ -31,7 +31,7 @@ describe("Spec - Pokemon", () => { const pkm = game.scene.getPlayerPokemon()!; expect(pkm).toBeDefined(); - expect(pkm.trySetStatus(undefined)).toBe(true); + expect(pkm.trySetStatus(undefined)).toBe(false); }); describe("Add To Party", () => { diff --git a/test/final_boss.test.ts b/test/final_boss.test.ts index 071f83285e7..9dc54490330 100644 --- a/test/final_boss.test.ts +++ b/test/final_boss.test.ts @@ -1,11 +1,11 @@ -import { GameModes } from "#enums/game-modes"; -import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { AbilityId } from "#enums/ability-id"; import { BiomeId } from "#enums/biome-id"; +import { GameModes } from "#enums/game-modes"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnHeldItemTransferModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const FinalWave = { @@ -29,7 +29,6 @@ describe("Final Boss", () => { .startingBiome(BiomeId.END) .criticalHits(false) .enemyMoveset(MoveId.SPLASH) - .moveset([MoveId.SPLASH, MoveId.WILL_O_WISP, MoveId.DRAGON_PULSE]) .startingLevel(10000); }); @@ -63,33 +62,31 @@ describe("Final Boss", () => { expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(SpeciesId.ETERNATUS); }); - it("should not have passive enabled on Eternatus", async () => { + it("should initially spawn in regular form without passive & 4 boss segments", async () => { await game.runToFinalBossEncounter([SpeciesId.BIDOOF], GameModes.CLASSIC); const eternatus = game.scene.getEnemyPokemon()!; + expect(eternatus.formIndex).toBe(0); + expect(eternatus.bossSegments).toBe(4); + expect(eternatus.bossSegmentIndex).toBe(3); expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); expect(eternatus.hasPassive()).toBe(false); }); it("should change form on direct hit down to last boss fragment", async () => { await game.runToFinalBossEncounter([SpeciesId.KYUREM], GameModes.CLASSIC); - await game.phaseInterceptor.to("CommandPhase"); - // Eternatus phase 1 + // phase 1 const eternatus = game.scene.getEnemyPokemon()!; const phase1Hp = eternatus.getMaxHp(); - expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus.formIndex).toBe(0); - expect(eternatus.bossSegments).toBe(4); - expect(eternatus.bossSegmentIndex).toBe(3); - game.move.select(MoveId.DRAGON_PULSE); + game.move.use(MoveId.DRAGON_PULSE); await game.toNextTurn(); - // Eternatus phase 2: changed form, healed and restored its shields + // Eternatus phase 2: changed form, healed fully and restored its shields expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus.hp).toBeGreaterThan(phase1Hp); expect(eternatus.hp).toBe(eternatus.getMaxHp()); + expect(eternatus.getMaxHp()).toBeGreaterThan(phase1Hp); expect(eternatus.formIndex).toBe(1); expect(eternatus.bossSegments).toBe(5); expect(eternatus.bossSegmentIndex).toBe(4); @@ -100,19 +97,13 @@ describe("Final Boss", () => { it("should change form on status damage down to last boss fragment", async () => { game.override.ability(AbilityId.NO_GUARD); - - await game.runToFinalBossEncounter([SpeciesId.BIDOOF], GameModes.CLASSIC); - await game.phaseInterceptor.to("CommandPhase"); + await game.runToFinalBossEncounter([SpeciesId.SALAZZLE], GameModes.CLASSIC); // Eternatus phase 1 const eternatus = game.scene.getEnemyPokemon()!; const phase1Hp = eternatus.getMaxHp(); - expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus.formIndex).toBe(0); - expect(eternatus.bossSegments).toBe(4); - expect(eternatus.bossSegmentIndex).toBe(3); - game.move.select(MoveId.WILL_O_WISP); + game.move.use(MoveId.WILL_O_WISP); await game.toNextTurn(); expect(eternatus.status?.effect).toBe(StatusEffect.BURN); @@ -120,19 +111,19 @@ describe("Final Boss", () => { const lastShieldHp = Math.ceil(phase1Hp / eternatus.bossSegments); // Stall until the burn is one hit away from breaking the last shield while (eternatus.hp - tickDamage > lastShieldHp) { - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.toNextTurn(); } expect(eternatus.bossSegmentIndex).toBe(1); - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.toNextTurn(); // Eternatus phase 2: changed form, healed and restored its shields expect(eternatus.hp).toBeGreaterThan(phase1Hp); expect(eternatus.hp).toBe(eternatus.getMaxHp()); - expect(eternatus.status).toBeFalsy(); + expect(eternatus.status?.effect).toBeUndefined(); expect(eternatus.formIndex).toBe(1); expect(eternatus.bossSegments).toBe(5); expect(eternatus.bossSegmentIndex).toBe(4); diff --git a/test/game-mode.test.ts b/test/game-mode.test.ts index c5ce1e02852..52de7e6a705 100644 --- a/test/game-mode.test.ts +++ b/test/game-mode.test.ts @@ -1,9 +1,9 @@ import type { GameMode } from "#app/game-mode"; import { getGameMode } from "#app/game-mode"; import { GameModes } from "#enums/game-modes"; +import { GameManager } from "#test/testUtils/gameManager"; +import * as Utils from "#utils/common"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as Utils from "#app/utils/common"; -import GameManager from "#test/testUtils/gameManager"; describe("game-mode", () => { let phaserGame: Phaser.Game; diff --git a/test/imports.test.ts b/test/imports.test.ts index 8a5c735e950..04e9462d8d0 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -1,4 +1,4 @@ -import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; +import { initStatsKeys } from "#ui/game-stats-ui-handler"; import { describe, expect, it } from "vitest"; async function importModule() { diff --git a/test/inputs/inputs.test.ts b/test/inputs/inputs.test.ts index 1f566672f00..ce208d6ce6d 100644 --- a/test/inputs/inputs.test.ts +++ b/test/inputs/inputs.test.ts @@ -1,7 +1,7 @@ -import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; -import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; -import GameManager from "#test/testUtils/gameManager"; -import InputsHandler from "#test/testUtils/inputsHandler"; +import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty"; +import pad_xbox360 from "#inputs/pad_xbox360"; +import { GameManager } from "#test/testUtils/gameManager"; +import { InputsHandler } from "#test/testUtils/inputsHandler"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/internals.test.ts b/test/internals.test.ts index bd603ec22fc..bb3cd89565c 100644 --- a/test/internals.test.ts +++ b/test/internals.test.ts @@ -1,6 +1,6 @@ import { AbilityId } from "#enums/ability-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/items/dire_hit.test.ts b/test/items/dire_hit.test.ts index 25fe9c8b876..2473dd0038e 100644 --- a/test/items/dire_hit.test.ts +++ b/test/items/dire_hit.test.ts @@ -1,25 +1,25 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { TempCritBoosterModifier } from "#app/modifier/modifier"; import { UiMode } from "#enums/ui-mode"; -import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Button } from "#app/enums/buttons"; -import { CommandPhase } from "#app/phases/command-phase"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; +import { TempCritBoosterModifier } from "#modifiers/modifier"; +import { BattleEndPhase } from "#phases/battle-end-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { NewBattlePhase } from "#phases/new-battle-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Dire Hit", () => { let phaserGame: Phaser.Game; let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); @@ -58,8 +58,7 @@ describe("Items - Dire Hit", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - game.move.select(MoveId.SPLASH); - + game.move.use(MoveId.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(BattleEndPhase); diff --git a/test/items/double_battle_chance_booster.test.ts b/test/items/double_battle_chance_booster.test.ts index d1a9e826cda..df6a3c6d7de 100644 --- a/test/items/double_battle_chance_booster.test.ts +++ b/test/items/double_battle_chance_booster.test.ts @@ -1,13 +1,13 @@ +import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; import { SpeciesId } from "#enums/species-id"; -import { DoubleBattleChanceBoosterModifier } from "#app/modifier/modifier"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { DoubleBattleChanceBoosterModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import { UiMode } from "#enums/ui-mode"; -import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Button } from "#app/enums/buttons"; describe("Items - Double Battle Chance Boosters", () => { let phaserGame: Phaser.Game; diff --git a/test/items/eviolite.test.ts b/test/items/eviolite.test.ts index 353cdbf91b4..74aff03616c 100644 --- a/test/items/eviolite.test.ts +++ b/test/items/eviolite.test.ts @@ -1,16 +1,16 @@ -import { StatBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder, randItem } from "#app/utils/common"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { StatBoosterModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder, randItem } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Eviolite", () => { let phaserGame: Phaser.Game; let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/exp_booster.test.ts b/test/items/exp_booster.test.ts index 44d7721aba2..6c8c16d08f4 100644 --- a/test/items/exp_booster.test.ts +++ b/test/items/exp_booster.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; -import { PokemonExpBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder } from "#app/utils/common"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { PokemonExpBoosterModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("EXP Modifier Items", () => { @@ -10,7 +10,7 @@ describe("EXP Modifier Items", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/grip_claw.test.ts b/test/items/grip_claw.test.ts index 9c3e6548140..6e25cdf6c08 100644 --- a/test/items/grip_claw.test.ts +++ b/test/items/grip_claw.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import type Pokemon from "#app/field/pokemon"; -import type { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BerryType } from "#enums/berry-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import type { Pokemon } from "#field/pokemon"; +import type { ContactHeldItemTransferChanceModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Grip Claw", () => { @@ -14,7 +14,7 @@ describe("Items - Grip Claw", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index eedb6667b9b..7ee3dff23d6 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -1,9 +1,9 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { randInt } from "#app/utils/common"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { randInt } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Leek", () => { @@ -11,7 +11,7 @@ describe("Items - Leek", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/leftovers.test.ts b/test/items/leftovers.test.ts index 21319d2c9d7..9f2c532ef8b 100644 --- a/test/items/leftovers.test.ts +++ b/test/items/leftovers.test.ts @@ -1,9 +1,9 @@ -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/items/light_ball.test.ts b/test/items/light_ball.test.ts index 6dfed3389b9..eebcc81c224 100644 --- a/test/items/light_ball.test.ts +++ b/test/items/light_ball.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; -import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils/common"; +import { modifierTypes } from "#data/data-lists"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { Stat } from "#enums/stat"; +import { SpeciesStatBoosterModifier } from "#modifiers/modifier"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Light Ball", () => { @@ -13,7 +13,7 @@ describe("Items - Light Ball", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/lock_capsule.test.ts b/test/items/lock_capsule.test.ts index beacc3a3907..e98e6880eb1 100644 --- a/test/items/lock_capsule.test.ts +++ b/test/items/lock_capsule.test.ts @@ -1,10 +1,10 @@ import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; import { ModifierTier } from "#enums/modifier-tier"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { MoveId } from "#enums/move-id"; import { UiMode } from "#enums/ui-mode"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Items - Lock Capsule", () => { @@ -12,7 +12,7 @@ describe("Items - Lock Capsule", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/metal_powder.test.ts b/test/items/metal_powder.test.ts index e731f6e7295..17baa057fd5 100644 --- a/test/items/metal_powder.test.ts +++ b/test/items/metal_powder.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; -import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils/common"; +import { modifierTypes } from "#data/data-lists"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { Stat } from "#enums/stat"; +import { SpeciesStatBoosterModifier } from "#modifiers/modifier"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Metal Powder", () => { @@ -13,7 +13,7 @@ describe("Items - Metal Powder", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/multi_lens.test.ts b/test/items/multi_lens.test.ts index 8a3161970c0..56d452df9d2 100644 --- a/test/items/multi_lens.test.ts +++ b/test/items/multi_lens.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -211,21 +211,4 @@ describe("Items - Multi Lens", () => { // TODO: Update hit count to 1 once Future Sight is fixed to not activate held items if user is off the field expect(enemyPokemon.damageAndUpdate).toHaveBeenCalledTimes(2); }); - - it("should not allow Pollen Puff to heal ally more than once", async () => { - game.override.battleStyle("double").moveset([MoveId.POLLEN_PUFF, MoveId.ENDURE]); - await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.OMANYTE]); - - const [, rightPokemon] = game.scene.getPlayerField(); - - rightPokemon.damageAndUpdate(rightPokemon.hp - 1); - - game.move.select(MoveId.POLLEN_PUFF, 0, BattlerIndex.PLAYER_2); - game.move.select(MoveId.ENDURE, 1); - - await game.toNextTurn(); - - // Pollen Puff heals with a ratio of 0.5, as long as Pollen Puff triggers only once the pokemon will always be <= (0.5 * Max HP) + 1 - expect(rightPokemon.hp).toBeLessThanOrEqual(0.5 * rightPokemon.getMaxHp() + 1); - }); }); diff --git a/test/items/mystical_rock.test.ts b/test/items/mystical_rock.test.ts index 091815aa604..f1e9bb91ecf 100644 --- a/test/items/mystical_rock.test.ts +++ b/test/items/mystical_rock.test.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Items - Mystical Rock", () => { @@ -11,7 +11,7 @@ describe("Items - Mystical Rock", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/quick_powder.test.ts b/test/items/quick_powder.test.ts index af99f51273d..6c8cb0751c3 100644 --- a/test/items/quick_powder.test.ts +++ b/test/items/quick_powder.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; -import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils/common"; +import { modifierTypes } from "#data/data-lists"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { Stat } from "#enums/stat"; +import { SpeciesStatBoosterModifier } from "#modifiers/modifier"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Quick Powder", () => { @@ -13,7 +13,7 @@ describe("Items - Quick Powder", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/reviver_seed.test.ts b/test/items/reviver_seed.test.ts index f444a6eac66..f0929ee0993 100644 --- a/test/items/reviver_seed.test.ts +++ b/test/items/reviver_seed.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { PokemonInstantReviveModifier } from "#app/modifier/modifier"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { PokemonInstantReviveModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/items/scope_lens.test.ts b/test/items/scope_lens.test.ts index 16be8aab930..866164629f8 100644 --- a/test/items/scope_lens.test.ts +++ b/test/items/scope_lens.test.ts @@ -1,8 +1,8 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Scope Lens", () => { @@ -10,7 +10,7 @@ describe("Items - Scope Lens", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/temp_stat_stage_booster.test.ts b/test/items/temp_stat_stage_booster.test.ts index b8cd0cde4eb..454a469700a 100644 --- a/test/items/temp_stat_stage_booster.test.ts +++ b/test/items/temp_stat_stage_booster.test.ts @@ -1,23 +1,23 @@ -import { BATTLE_STATS, Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import Phase from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { MoveId } from "#enums/move-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; -import { TempStatStageBoosterModifier } from "#app/modifier/modifier"; +import { Button } from "#enums/buttons"; +import { MoveId } from "#enums/move-id"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; +import { SpeciesId } from "#enums/species-id"; +import { BATTLE_STATS, Stat } from "#enums/stat"; import { UiMode } from "#enums/ui-mode"; -import { Button } from "#app/enums/buttons"; -import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; +import { TempStatStageBoosterModifier } from "#modifiers/modifier"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Temporary Stat Stage Boosters", () => { let phaserGame: Phaser.Game; let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/thick_club.test.ts b/test/items/thick_club.test.ts index bc019ee99f8..3a993d64a0c 100644 --- a/test/items/thick_club.test.ts +++ b/test/items/thick_club.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; -import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; -import i18next from "#app/plugins/i18n"; -import { NumberHolder, randInt } from "#app/utils/common"; +import { modifierTypes } from "#data/data-lists"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phase from "phaser"; +import { Stat } from "#enums/stat"; +import { SpeciesStatBoosterModifier } from "#modifiers/modifier"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder, randInt } from "#utils/common"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Thick Club", () => { @@ -13,7 +13,7 @@ describe("Items - Thick Club", () => { let game: GameManager; beforeAll(() => { - phaserGame = new Phase.Game({ + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, }); }); diff --git a/test/items/toxic_orb.test.ts b/test/items/toxic_orb.test.ts index e0d86655028..0f4acedbfb5 100644 --- a/test/items/toxic_orb.test.ts +++ b/test/items/toxic_orb.test.ts @@ -1,9 +1,9 @@ -import i18next from "#app/plugins/i18n"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import i18next from "#plugins/i18n"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/misc.test.ts b/test/misc.test.ts index 12ed165d9d9..5afd8f898c6 100644 --- a/test/misc.test.ts +++ b/test/misc.test.ts @@ -1,5 +1,4 @@ -// import { apiFetch } from "#app/utils"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import { waitUntil } from "#test/testUtils/gameManagerUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/ability-ignore-moves.test.ts b/test/moves/ability-ignore-moves.test.ts new file mode 100644 index 00000000000..99c5b8f8efe --- /dev/null +++ b/test/moves/ability-ignore-moves.test.ts @@ -0,0 +1,108 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Ability-Ignoring Moves", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([MoveId.MOONGEIST_BEAM, MoveId.SUNSTEEL_STRIKE, MoveId.PHOTON_GEYSER, MoveId.METRONOME]) + .ability(AbilityId.BALL_FETCH) + .startingLevel(200) + .battleStyle("single") + .criticalHits(false) + .enemySpecies(SpeciesId.MAGIKARP) + .enemyAbility(AbilityId.STURDY) + .enemyMoveset(MoveId.SPLASH); + }); + + it.each<{ name: string; move: MoveId }>([ + { name: "Sunsteel Strike", move: MoveId.SUNSTEEL_STRIKE }, + { name: "Moongeist Beam", move: MoveId.MOONGEIST_BEAM }, + { name: "Photon Geyser", move: MoveId.PHOTON_GEYSER }, + ])("$name should ignore enemy abilities during move use", async ({ move }) => { + await game.classicMode.startBattle([SpeciesId.NECROZMA]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + game.move.select(move); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(game.scene.arena.ignoreAbilities).toBe(true); + expect(game.scene.arena.ignoringEffectSource).toBe(player.getBattlerIndex()); + + await game.toEndOfTurn(); + expect(game.scene.arena.ignoreAbilities).toBe(false); + expect(enemy.isFainted()).toBe(true); + }); + + it("should not ignore enemy abilities when called by Metronome", async () => { + await game.classicMode.startBattle([SpeciesId.MILOTIC]); + game.move.forceMetronomeMove(MoveId.PHOTON_GEYSER, true); + + const enemy = game.field.getEnemyPokemon(); + game.move.select(MoveId.METRONOME); + await game.toEndOfTurn(); + + expect(enemy.isFainted()).toBe(false); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].move).toBe(MoveId.PHOTON_GEYSER); + }); + + it("should not ignore enemy abilities when called by Mirror Move", async () => { + game.override.moveset(MoveId.MIRROR_MOVE).enemyMoveset(MoveId.SUNSTEEL_STRIKE); + + await game.classicMode.startBattle([SpeciesId.MILOTIC]); + + const enemy = game.field.getEnemyPokemon(); + game.move.select(MoveId.MIRROR_MOVE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toEndOfTurn(); + + expect(enemy.isFainted()).toBe(false); + expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0].move).toBe(MoveId.SUNSTEEL_STRIKE); + }); + + // TODO: Verify this behavior on cart + it("should ignore enemy abilities when called by Instruct", async () => { + game.override.moveset([MoveId.SUNSTEEL_STRIKE, MoveId.INSTRUCT]).battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.SOLGALEO, SpeciesId.LUNALA]); + + const solgaleo = game.field.getPlayerPokemon(); + + game.move.select(MoveId.SUNSTEEL_STRIKE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(MoveId.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to("MoveEffectPhase"); // initial attack + await game.phaseInterceptor.to("MoveEffectPhase"); // instruct + await game.phaseInterceptor.to("MoveEffectPhase"); // instructed move use + + expect(game.scene.arena.ignoreAbilities).toBe(true); + expect(game.scene.arena.ignoringEffectSource).toBe(solgaleo.getBattlerIndex()); + + await game.toEndOfTurn(); + + // Both the initial and redirected instruct use ignored sturdy + const [enemy1, enemy2] = game.scene.getEnemyField(); + expect(enemy1.isFainted()).toBe(true); + expect(enemy2.isFainted()).toBe(true); + }); +}); diff --git a/test/moves/after_you.test.ts b/test/moves/after_you.test.ts index 37186dcc7a5..e961342dc36 100644 --- a/test/moves/after_you.test.ts +++ b/test/moves/after_you.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { MoveResult } from "#enums/move-result"; -import { MovePhase } from "#app/phases/move-phase"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MovePhase } from "#phases/move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/alluring_voice.test.ts b/test/moves/alluring_voice.test.ts index ba096391f1b..d72d7b4f906 100644 --- a/test/moves/alluring_voice.test.ts +++ b/test/moves/alluring_voice.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/aromatherapy.test.ts b/test/moves/aromatherapy.test.ts index bfe315a1390..39850c4f841 100644 --- a/test/moves/aromatherapy.test.ts +++ b/test/moves/aromatherapy.test.ts @@ -1,11 +1,10 @@ -import { StatusEffect } from "#app/enums/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Aromatherapy", () => { let phaserGame: Phaser.Game; @@ -40,7 +39,6 @@ describe("Moves - Aromatherapy", () => { vi.spyOn(partyPokemon, "resetStatus"); game.move.select(MoveId.AROMATHERAPY, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -62,7 +60,6 @@ describe("Moves - Aromatherapy", () => { vi.spyOn(rightOpp, "resetStatus"); game.move.select(MoveId.AROMATHERAPY, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -86,7 +83,6 @@ describe("Moves - Aromatherapy", () => { vi.spyOn(partyPokemon, "resetStatus"); game.move.select(MoveId.AROMATHERAPY, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); diff --git a/test/moves/assist.test.ts b/test/moves/assist.test.ts index 27aa5528f17..eff153534dd 100644 --- a/test/moves/assist.test.ts +++ b/test/moves/assist.test.ts @@ -1,11 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#app/enums/stat"; -import { MoveResult } from "#enums/move-result"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -80,7 +79,6 @@ describe("Moves - Assist", () => { // Player uses Sketch to copy Swords Dance, Player_2 stalls a turn. Player will attempt Assist and should have no usable moves await game.toNextTurn(); game.move.select(MoveId.ASSIST, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.PROTECT, 1); await game.toNextTurn(); @@ -96,7 +94,6 @@ describe("Moves - Assist", () => { game.move.changeMoveset(shuckle, [MoveId.ASSIST, MoveId.SKETCH, MoveId.PROTECT, MoveId.DRAGON_TAIL]); game.move.select(MoveId.ASSIST, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.ASSIST, 1); await game.toNextTurn(); diff --git a/test/moves/astonish.test.ts b/test/moves/astonish.test.ts index 48deadf7a01..7db149c9975 100644 --- a/test/moves/astonish.test.ts +++ b/test/moves/astonish.test.ts @@ -1,13 +1,13 @@ -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index b9ae79e4155..24e7b1857cc 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -1,18 +1,17 @@ -import type BattleScene from "#app/battle-scene"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import type Pokemon from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils/common"; +import type { BattleScene } from "#app/battle-scene"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; let globalScene: BattleScene; @@ -52,10 +51,10 @@ describe("Moves - Aurora Veil", () => { game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -71,10 +70,10 @@ describe("Moves - Aurora Veil", () => { game.move.select(moveToUse); game.move.select(moveToUse, 1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -82,72 +81,48 @@ describe("Moves - Aurora Veil", () => { }); it("reduces damage of special attacks by half in a single battle", async () => { - const moveToUse = MoveId.ABSORB; await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); + game.move.use(MoveId.ABSORB); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.ABSORB], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); + expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * singleBattleMultiplier); }); it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleStyle("double"); - - const moveToUse = MoveId.DAZZLING_GLEAM; - await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE]); - - game.move.select(moveToUse); - game.move.select(moveToUse, 1); - - await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], - ); - - expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); - }); - - it("does not affect physical critical hits", async () => { - game.override.moveset([MoveId.WICKED_BLOW]); - const moveToUse = MoveId.WICKED_BLOW; await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); - + game.move.use(MoveId.ABSORB); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.ABSORB], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power); + + expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * doubleBattleMultiplier); }); it("does not affect critical hits", async () => { - game.override.moveset([MoveId.FROST_BREATH]); - const moveToUse = MoveId.FROST_BREATH; - vi.spyOn(allMoves[MoveId.FROST_BREATH], "accuracy", "get").mockReturnValue(100); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); + game.move.use(MoveId.WICKED_BLOW); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.WICKED_BLOW], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power); + expect(mockedDmg).toBe(allMoves[MoveId.WICKED_BLOW].power); }); }); diff --git a/test/moves/autotomize.test.ts b/test/moves/autotomize.test.ts index 000dd19b8f5..01021870b88 100644 --- a/test/moves/autotomize.test.ts +++ b/test/moves/autotomize.test.ts @@ -1,9 +1,9 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Autotomize", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/baddy_bad.test.ts b/test/moves/baddy_bad.test.ts index ffdf9f0309c..0af04f0c0c7 100644 --- a/test/moves/baddy_bad.test.ts +++ b/test/moves/baddy_bad.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -33,7 +33,7 @@ describe("Moves - Baddy Bad", () => { game.override.enemyMoveset(MoveId.PROTECT); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - game.move.select(MoveId.BADDY_BAD); + game.move.use(MoveId.BADDY_BAD); await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.arena.tags.length).toBe(0); diff --git a/test/moves/baneful_bunker.test.ts b/test/moves/baneful_bunker.test.ts index c9cc2025a58..09050dcbb44 100644 --- a/test/moves/baneful_bunker.test.ts +++ b/test/moves/baneful_bunker.test.ts @@ -1,11 +1,11 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { BattlerIndex } from "#enums/battler-index"; -import { StatusEffect } from "#app/enums/status-effect"; describe("Moves - Baneful Bunker", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/baton_pass.test.ts b/test/moves/baton_pass.test.ts index 1b1b0620133..af49ac0db9f 100644 --- a/test/moves/baton_pass.test.ts +++ b/test/moves/baton_pass.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/beak_blast.test.ts b/test/moves/beak_blast.test.ts index 2cb9f9bdd6f..60a6bb75066 100644 --- a/test/moves/beak_blast.test.ts +++ b/test/moves/beak_blast.test.ts @@ -1,12 +1,12 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { StatusEffect } from "#app/enums/status-effect"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { BerryPhase } from "#phases/berry-phase"; +import { MovePhase } from "#phases/move-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/beat_up.test.ts b/test/moves/beat_up.test.ts index 7c4686ab4fa..8387b6f671b 100644 --- a/test/moves/beat_up.test.ts +++ b/test/moves/beat_up.test.ts @@ -1,9 +1,9 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { StatusEffect } from "#app/enums/status-effect"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/belly_drum.test.ts b/test/moves/belly_drum.test.ts index 239b3c3d794..9044e10589f 100644 --- a/test/moves/belly_drum.test.ts +++ b/test/moves/belly_drum.test.ts @@ -1,12 +1,12 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils/common"; +import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { AbilityId } from "#enums/ability-id"; // RATIO : HP Cost of Move const RATIO = 2; diff --git a/test/moves/burning_jealousy.test.ts b/test/moves/burning_jealousy.test.ts index ce0a1ce57c9..9f8dfc29ee4 100644 --- a/test/moves/burning_jealousy.test.ts +++ b/test/moves/burning_jealousy.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { StatusEffect } from "#app/enums/status-effect"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/camouflage.test.ts b/test/moves/camouflage.test.ts index 6f28493b1e5..39bf8f6d5b5 100644 --- a/test/moves/camouflage.test.ts +++ b/test/moves/camouflage.test.ts @@ -1,10 +1,10 @@ +import { TerrainType } from "#data/terrain"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { TerrainType } from "#app/data/terrain"; -import { PokemonType } from "#enums/pokemon-type"; import { BattlerIndex } from "#enums/battler-index"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/ceaseless_edge.test.ts b/test/moves/ceaseless_edge.test.ts index 1dec98fe3a8..2d28f4b1a1b 100644 --- a/test/moves/ceaseless_edge.test.ts +++ b/test/moves/ceaseless_edge.test.ts @@ -1,13 +1,13 @@ -import { ArenaTrapTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; +import { ArenaTrapTag } from "#data/arena-tag"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; diff --git a/test/moves/chilly_reception.test.ts b/test/moves/chilly_reception.test.ts index 4d15bfff284..f34e61873cc 100644 --- a/test/moves/chilly_reception.test.ts +++ b/test/moves/chilly_reception.test.ts @@ -1,11 +1,11 @@ -import { RandomMoveAttr } from "#app/data/moves/move"; -import { MoveResult } from "#enums/move-result"; import { getPokemonNameWithAffix } from "#app/messages"; +import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#app/enums/ability-id"; import { WeatherType } from "#enums/weather-type"; -import GameManager from "#test/testUtils/gameManager"; +import { RandomMoveAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/chloroblast.test.ts b/test/moves/chloroblast.test.ts index 6c790bac150..c87f79c440a 100644 --- a/test/moves/chloroblast.test.ts +++ b/test/moves/chloroblast.test.ts @@ -1,8 +1,8 @@ -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/clangorous_soul.test.ts b/test/moves/clangorous_soul.test.ts index f08503acdc6..dc208087bb8 100644 --- a/test/moves/clangorous_soul.test.ts +++ b/test/moves/clangorous_soul.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; /** HP Cost of Move */ const RATIO = 3; diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 8d135d5e2c7..e6a81cb5627 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -1,14 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { Stat } from "#app/enums/stat"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Copycat", () => { let phaserGame: Phaser.Game; @@ -65,7 +64,7 @@ describe("Moves - Copycat", () => { it("should copy the called move when the last move successfully calls another", async () => { game.override.moveset([MoveId.SPLASH, MoveId.METRONOME]).enemyMoveset(MoveId.COPYCAT); await game.classicMode.startBattle([SpeciesId.DRAMPA]); - vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.SWORDS_DANCE); + game.move.forceMetronomeMove(MoveId.SWORDS_DANCE, true); game.move.select(MoveId.METRONOME); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); // Player moves first so enemy can copy Swords Dance diff --git a/test/moves/crafty_shield.test.ts b/test/moves/crafty_shield.test.ts index 12a607a2a1b..98c0dfd7c9a 100644 --- a/test/moves/crafty_shield.test.ts +++ b/test/moves/crafty_shield.test.ts @@ -1,13 +1,12 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { BerryPhase } from "#phases/berry-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; describe("Moves - Crafty Shield", () => { let phaserGame: Phaser.Game; @@ -42,9 +41,6 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.CRAFTY_SHIELD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -60,9 +56,6 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.CRAFTY_SHIELD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -78,9 +71,6 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.CRAFTY_SHIELD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -94,9 +84,6 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.CRAFTY_SHIELD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SWORDS_DANCE, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/test/moves/defog.test.ts b/test/moves/defog.test.ts index 17dc0613e9b..253eac4f22c 100644 --- a/test/moves/defog.test.ts +++ b/test/moves/defog.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/destiny_bond.test.ts b/test/moves/destiny_bond.test.ts index a78d46b464b..29bbfabb09f 100644 --- a/test/moves/destiny_bond.test.ts +++ b/test/moves/destiny_bond.test.ts @@ -1,16 +1,16 @@ -import type { ArenaTrapTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; +import type { ArenaTrapTag } from "#data/arena-tag"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { PokemonInstantReviveModifier } from "#modifiers/modifier"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { BattlerIndex } from "#enums/battler-index"; -import { StatusEffect } from "#enums/status-effect"; -import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; describe("Moves - Destiny Bond", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/diamond_storm.test.ts b/test/moves/diamond_storm.test.ts index 20067b3a0f3..1eb86f94b76 100644 --- a/test/moves/diamond_storm.test.ts +++ b/test/moves/diamond_storm.test.ts @@ -1,9 +1,9 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index a80ac0405fe..9d431b78838 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import { MoveResult } from "#enums/move-result"; -import { describe, beforeAll, afterEach, beforeEach, it, expect } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Dig", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/disable.test.ts b/test/moves/disable.test.ts index b113acb9525..f6b1c24ac7d 100644 --- a/test/moves/disable.test.ts +++ b/test/moves/disable.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { RandomMoveAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { RandomMoveAttr } from "#app/data/moves/move"; describe("Moves - Disable", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/dive.test.ts b/test/moves/dive.test.ts index 9c467976775..c7e4f99f6cb 100644 --- a/test/moves/dive.test.ts +++ b/test/moves/dive.test.ts @@ -1,13 +1,13 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatusEffect } from "#enums/status-effect"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Dive", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/doodle.test.ts b/test/moves/doodle.test.ts index 2dbd5c08c8f..5f4b8ab9b39 100644 --- a/test/moves/doodle.test.ts +++ b/test/moves/doodle.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/double_team.test.ts b/test/moves/double_team.test.ts index d1f6c5be3e4..9cdae33af41 100644 --- a/test/moves/double_team.test.ts +++ b/test/moves/double_team.test.ts @@ -1,9 +1,9 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/dragon_cheer.test.ts b/test/moves/dragon_cheer.test.ts index 56feac513a1..7d1f35fe2d4 100644 --- a/test/moves/dragon_cheer.test.ts +++ b/test/moves/dragon_cheer.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/dragon_rage.test.ts b/test/moves/dragon_rage.test.ts index c5bed3377fa..46e7dfa5a21 100644 --- a/test/moves/dragon_rage.test.ts +++ b/test/moves/dragon_rage.test.ts @@ -1,14 +1,14 @@ -import { Stat } from "#enums/stat"; -import { PokemonType } from "#enums/pokemon-type"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SpeciesId } from "#enums/species-id"; describe("Moves - Dragon Rage", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/dragon_tail.test.ts b/test/moves/dragon_tail.test.ts index 8c456f27853..c5a77716c56 100644 --- a/test/moves/dragon_tail.test.ts +++ b/test/moves/dragon_tail.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { Status } from "#app/data/status-effect"; -import { Challenges } from "#enums/challenges"; -import { StatusEffect } from "#enums/status-effect"; -import { PokemonType } from "#enums/pokemon-type"; +import { allMoves } from "#data/data-lists"; +import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { Challenges } from "#enums/challenges"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index 3febf918de8..fcbbccd5123 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -1,11 +1,11 @@ +import { allMoves } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveId } from "#enums/move-id"; -import type Move from "#app/data/moves/move"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Move } from "#moves/move"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/effectiveness.test.ts b/test/moves/effectiveness.test.ts index 58b2d07b1b6..58e041fb5c5 100644 --- a/test/moves/effectiveness.test.ts +++ b/test/moves/effectiveness.test.ts @@ -1,12 +1,12 @@ -import { allMoves } from "#app/data/data-lists"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { TrainerSlot } from "#enums/trainer-slot"; -import { PokemonType } from "#enums/pokemon-type"; +import * as Messages from "#app/messages"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import * as Messages from "#app/messages"; -import GameManager from "#test/testUtils/gameManager"; +import { TrainerSlot } from "#enums/trainer-slot"; +import { GameManager } from "#test/testUtils/gameManager"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import Phaser from "phaser"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/electrify.test.ts b/test/moves/electrify.test.ts index b6a3cac9fff..c3aa925a331 100644 --- a/test/moves/electrify.test.ts +++ b/test/moves/electrify.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Electrify", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/electro_shot.test.ts b/test/moves/electro_shot.test.ts index 1d69e9c2fa7..cb696b162d5 100644 --- a/test/moves/electro_shot.test.ts +++ b/test/moves/electro_shot.test.ts @@ -1,13 +1,13 @@ +import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { WeatherType } from "#enums/weather-type"; -import { MoveResult } from "#enums/move-result"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Electro Shot", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/encore.test.ts b/test/moves/encore.test.ts index 772b7c96060..c20dc194ead 100644 --- a/test/moves/encore.test.ts +++ b/test/moves/encore.test.ts @@ -1,10 +1,10 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/endure.test.ts b/test/moves/endure.test.ts index 7738f7426fe..717c9d58211 100644 --- a/test/moves/endure.test.ts +++ b/test/moves/endure.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/entrainment.test.ts b/test/moves/entrainment.test.ts index 65586149492..9d7da455525 100644 --- a/test/moves/entrainment.test.ts +++ b/test/moves/entrainment.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fairy_lock.test.ts b/test/moves/fairy_lock.test.ts index b9f854cdc93..5f20c39c6f8 100644 --- a/test/moves/fairy_lock.test.ts +++ b/test/moves/fairy_lock.test.ts @@ -1,9 +1,9 @@ -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fake_out.test.ts b/test/moves/fake_out.test.ts index 4fc83e5aa07..815de9b9225 100644 --- a/test/moves/fake_out.test.ts +++ b/test/moves/fake_out.test.ts @@ -1,6 +1,6 @@ -import GameManager from "#test/testUtils/gameManager"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/false_swipe.test.ts b/test/moves/false_swipe.test.ts index bf9c8307c22..0aa16ff43e1 100644 --- a/test/moves/false_swipe.test.ts +++ b/test/moves/false_swipe.test.ts @@ -1,8 +1,8 @@ -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fell_stinger.test.ts b/test/moves/fell_stinger.test.ts index 0737db9105f..c3ea17904af 100644 --- a/test/moves/fell_stinger.test.ts +++ b/test/moves/fell_stinger.test.ts @@ -1,13 +1,13 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; -import { WeatherType } from "#app/enums/weather-type"; -import { allMoves } from "#app/data/data-lists"; +import { StatusEffect } from "#enums/status-effect"; +import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Fell Stinger", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/fillet_away.test.ts b/test/moves/fillet_away.test.ts index 1e00f2ee02d..3ba4f5740eb 100644 --- a/test/moves/fillet_away.test.ts +++ b/test/moves/fillet_away.test.ts @@ -1,9 +1,9 @@ -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils/common"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/first-attack-double-power.test.ts b/test/moves/first-attack-double-power.test.ts new file mode 100644 index 00000000000..7d1d9c4d762 --- /dev/null +++ b/test/moves/first-attack-double-power.test.ts @@ -0,0 +1,121 @@ +import { allMoves } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Fishious Rend & Bolt Beak", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.STURDY) + .battleStyle("single") + .startingWave(5) + .criticalHits(false) + .enemyLevel(100) + .enemySpecies(SpeciesId.DRACOVISH) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH); + }); + + it.each<{ name: string; move: MoveId }>([ + { name: "Bolt Beak", move: MoveId.BOLT_BEAK }, + { name: "Fishious Rend", move: MoveId.FISHIOUS_REND }, + ])("$name should double power if the user moves before the target", async ({ move }) => { + const powerSpy = vi.spyOn(allMoves[move], "calculateBattlePower"); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + // turn 1: enemy, then player (no boost) + game.move.use(move); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[move].power); + + // turn 2: player, then enemy (boost) + game.move.use(move); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[move].power * 2); + }); + + it("should only consider the selected target in Double Battles", async () => { + game.override.battleStyle("double"); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); + await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MILOTIC]); + + // Use move after everyone but P1 and enemy 1 have already moved + game.move.use(MoveId.BOLT_BEAK, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.BOLT_BEAK].power * 2); + }); + + it("should double power on the turn the target switches in", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); + + game.move.use(MoveId.BOLT_BEAK); + game.forceEnemyToSwitch(); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.BOLT_BEAK].power * 2); + }); + + it("should double power on forced switch-induced sendouts", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const powerSpy = vi.spyOn(allMoves[MoveId.BOLT_BEAK], "calculateBattlePower"); + + game.move.use(MoveId.BOLT_BEAK); + await game.move.forceEnemyMove(MoveId.U_TURN); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.BOLT_BEAK].power * 2); + }); + + it.each<{ type: string; allyMove: MoveId }>([ + { type: "a Dancer-induced", allyMove: MoveId.FIERY_DANCE }, + { type: "an Instructed", allyMove: MoveId.INSTRUCT }, + ])("should double power if $type move is used as the target's first action that turn", async ({ allyMove }) => { + game.override.battleStyle("double").enemyAbility(AbilityId.DANCER); + const powerSpy = vi.spyOn(allMoves[MoveId.FISHIOUS_REND], "calculateBattlePower"); + await game.classicMode.startBattle([SpeciesId.DRACOVISH, SpeciesId.ARCTOZOLT]); + + // Simulate enemy having used splash last turn to allow Instruct to copy it + const enemy = game.field.getEnemyPokemon(); + enemy.pushMoveHistory({ + move: MoveId.SPLASH, + targets: [BattlerIndex.ENEMY], + turn: game.scene.currentBattle.turn - 1, + useMode: MoveUseMode.NORMAL, + }); + + game.move.use(MoveId.FISHIOUS_REND, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.use(allyMove, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.FISHIOUS_REND].power); + }); +}); diff --git a/test/moves/fissure.test.ts b/test/moves/fissure.test.ts index 27031a7736d..6fadf867e8a 100644 --- a/test/moves/fissure.test.ts +++ b/test/moves/fissure.test.ts @@ -1,13 +1,13 @@ -import { Stat } from "#enums/stat"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SpeciesId } from "#enums/species-id"; describe("Moves - Fissure", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/flame_burst.test.ts b/test/moves/flame_burst.test.ts index 0a378df1077..76d9a100e37 100644 --- a/test/moves/flame_burst.test.ts +++ b/test/moves/flame_burst.test.ts @@ -1,10 +1,10 @@ -import { allAbilities } from "#app/data/data-lists"; +import { allAbilities } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import type Pokemon from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/flower_shield.test.ts b/test/moves/flower_shield.test.ts index c3c5e5cf870..16a1efacf35 100644 --- a/test/moves/flower_shield.test.ts +++ b/test/moves/flower_shield.test.ts @@ -1,12 +1,12 @@ -import { Stat } from "#enums/stat"; -import { SemiInvulnerableTag } from "#app/data/battler-tags"; -import { PokemonType } from "#enums/pokemon-type"; -import { BiomeId } from "#enums/biome-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { SemiInvulnerableTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; +import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fly.test.ts b/test/moves/fly.test.ts index 7d8a6ee659e..c27db3686b4 100644 --- a/test/moves/fly.test.ts +++ b/test/moves/fly.test.ts @@ -1,14 +1,14 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatusEffect } from "#enums/status-effect"; -import { MoveResult } from "#enums/move-result"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Fly", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/focus_punch.test.ts b/test/moves/focus_punch.test.ts index 38b57b201c0..5123e99c6ff 100644 --- a/test/moves/focus_punch.test.ts +++ b/test/moves/focus_punch.test.ts @@ -1,12 +1,12 @@ -import { BerryPhase } from "#app/phases/berry-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { MoveHeaderPhase } from "#app/phases/move-header-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { MessagePhase } from "#phases/message-phase"; +import { MoveHeaderPhase } from "#phases/move-header-phase"; +import { SwitchSummonPhase } from "#phases/switch-summon-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/follow_me.test.ts b/test/moves/follow_me.test.ts index a99ac0b6c00..891b1844512 100644 --- a/test/moves/follow_me.test.ts +++ b/test/moves/follow_me.test.ts @@ -1,10 +1,10 @@ -import { Stat } from "#enums/stat"; -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/foresight.test.ts b/test/moves/foresight.test.ts index 96a341582f9..3163f65dd79 100644 --- a/test/moves/foresight.test.ts +++ b/test/moves/foresight.test.ts @@ -1,7 +1,7 @@ import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/forests_curse.test.ts b/test/moves/forests_curse.test.ts index 81ced674a33..812d3c57217 100644 --- a/test/moves/forests_curse.test.ts +++ b/test/moves/forests_curse.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { PokemonType } from "#enums/pokemon-type"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/freeze_dry.test.ts b/test/moves/freeze_dry.test.ts index f1577d3d6c5..5639330db1b 100644 --- a/test/moves/freeze_dry.test.ts +++ b/test/moves/freeze_dry.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { PokemonType } from "#enums/pokemon-type"; +import { BattlerIndex } from "#enums/battler-index"; import { Challenges } from "#enums/challenges"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/freezy_frost.test.ts b/test/moves/freezy_frost.test.ts index 55f67de085f..9cf2f38b286 100644 --- a/test/moves/freezy_frost.test.ts +++ b/test/moves/freezy_frost.test.ts @@ -1,12 +1,11 @@ -import { Stat } from "#enums/stat"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/data-lists"; -import { CommandPhase } from "#app/phases/command-phase"; describe("Moves - Freezy Frost", () => { let phaserGame: Phaser.Game; @@ -77,7 +76,6 @@ describe("Moves - Freezy Frost", () => { const [leftOpp, rightOpp] = game.scene.getEnemyField(); game.move.select(MoveId.HOWL, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -87,7 +85,6 @@ describe("Moves - Freezy Frost", () => { expect(rightOpp.getStatStage(Stat.ATK)).toBe(2); game.move.select(MoveId.FREEZY_FROST, 0, leftOpp.getBattlerIndex()); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); diff --git a/test/moves/fusion_bolt.test.ts b/test/moves/fusion_bolt.test.ts index c92a483a497..ad6810cb412 100644 --- a/test/moves/fusion_bolt.test.ts +++ b/test/moves/fusion_bolt.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fusion_flare.test.ts b/test/moves/fusion_flare.test.ts index c0df347fcce..d86f784bce4 100644 --- a/test/moves/fusion_flare.test.ts +++ b/test/moves/fusion_flare.test.ts @@ -1,8 +1,8 @@ -import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index 1967e9f12d1..1777eb5e07e 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -1,14 +1,14 @@ -import { Stat } from "#enums/stat"; +import { allMoves } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { MovePhase } from "#app/phases/move-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import type { Move } from "#moves/move"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { MovePhase } from "#phases/move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/future_sight.test.ts b/test/moves/future_sight.test.ts index 7de70a88d10..96b71b8b0f1 100644 --- a/test/moves/future_sight.test.ts +++ b/test/moves/future_sight.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/gastro_acid.test.ts b/test/moves/gastro_acid.test.ts index 39167987809..9b8e8f33250 100644 --- a/test/moves/gastro_acid.test.ts +++ b/test/moves/gastro_acid.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { MoveResult } from "#enums/move-result"; import { BattleType } from "#enums/battle-type"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Gastro Acid", () => { @@ -100,12 +100,12 @@ describe("Moves - Gastro Acid", () => { await game.toNextTurn(); expect(enemyPokemon.summonData.abilitySuppressed).toBe(true); - game.move.select(MoveId.WATER_GUN); + game.move.use(MoveId.WATER_GUN); await game.toNextTurn(); // water gun should've dealt damage due to suppressed Water Absorb - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - game.move.select(MoveId.SPORE); + game.move.use(MoveId.SPORE); await game.toEndOfTurn(); // Comatose should block stauts effect diff --git a/test/moves/geomancy.test.ts b/test/moves/geomancy.test.ts index 452022b4cf1..100c4d85637 100644 --- a/test/moves/geomancy.test.ts +++ b/test/moves/geomancy.test.ts @@ -1,12 +1,12 @@ -import type { EffectiveStat } from "#enums/stat"; -import { Stat } from "#enums/stat"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { EffectiveStat } from "#enums/stat"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Geomancy", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/gigaton_hammer.test.ts b/test/moves/gigaton_hammer.test.ts index d80743f4ed0..69186886f40 100644 --- a/test/moves/gigaton_hammer.test.ts +++ b/test/moves/gigaton_hammer.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#enums/battler-index"; -import GameManager from "#test/testUtils/gameManager"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/glaive_rush.test.ts b/test/moves/glaive_rush.test.ts index 0b6f30da71a..a09c35604b9 100644 --- a/test/moves/glaive_rush.test.ts +++ b/test/moves/glaive_rush.test.ts @@ -1,8 +1,8 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/growth.test.ts b/test/moves/growth.test.ts index 6eec30be49f..94df21271a1 100644 --- a/test/moves/growth.test.ts +++ b/test/moves/growth.test.ts @@ -1,12 +1,12 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Growth", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/grudge.test.ts b/test/moves/grudge.test.ts index 36030c88bad..ff18cd5d6a4 100644 --- a/test/moves/grudge.test.ts +++ b/test/moves/grudge.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { BattlerIndex } from "#enums/battler-index"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/guard_split.test.ts b/test/moves/guard_split.test.ts index 878bb80f251..7972296f6ae 100644 --- a/test/moves/guard_split.test.ts +++ b/test/moves/guard_split.test.ts @@ -1,11 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Guard Split", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/guard_swap.test.ts b/test/moves/guard_swap.test.ts index d2c33e45df0..eda4349b9f8 100644 --- a/test/moves/guard_swap.test.ts +++ b/test/moves/guard_swap.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat, BATTLE_STATS } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { BATTLE_STATS, Stat } from "#enums/stat"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Guard Swap", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/hard_press.test.ts b/test/moves/hard_press.test.ts index e57c9af981f..f61f5084752 100644 --- a/test/moves/hard_press.test.ts +++ b/test/moves/hard_press.test.ts @@ -1,12 +1,12 @@ -import { allMoves } from "#app/data/data-lists"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Move } from "#moves/move"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type Move from "#app/data/moves/move"; describe("Moves - Hard Press", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/haze.test.ts b/test/moves/haze.test.ts index f3e3dafae0a..990bbc62ae8 100644 --- a/test/moves/haze.test.ts +++ b/test/moves/haze.test.ts @@ -1,11 +1,11 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Haze", () => { describe("integration tests", () => { diff --git a/test/moves/heal_bell.test.ts b/test/moves/heal_bell.test.ts index 914307b4795..e7f6548713a 100644 --- a/test/moves/heal_bell.test.ts +++ b/test/moves/heal_bell.test.ts @@ -1,11 +1,10 @@ -import { StatusEffect } from "#app/enums/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Heal Bell", () => { let phaserGame: Phaser.Game; @@ -40,7 +39,6 @@ describe("Moves - Heal Bell", () => { vi.spyOn(partyPokemon, "resetStatus"); game.move.select(MoveId.HEAL_BELL, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -62,7 +60,6 @@ describe("Moves - Heal Bell", () => { vi.spyOn(rightOpp, "resetStatus"); game.move.select(MoveId.HEAL_BELL, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -86,7 +83,6 @@ describe("Moves - Heal Bell", () => { vi.spyOn(partyPokemon, "resetStatus"); game.move.select(MoveId.HEAL_BELL, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); diff --git a/test/moves/heal_block.test.ts b/test/moves/heal_block.test.ts index 77a10927930..0f3ff0df683 100644 --- a/test/moves/heal_block.test.ts +++ b/test/moves/heal_block.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -42,7 +42,7 @@ describe("Moves - Heal Block", () => { const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - player.damageAndUpdate(enemy.getMaxHp() - 1); + player.damageAndUpdate(player.getMaxHp() - 1); game.move.select(MoveId.ABSORB); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); diff --git a/test/moves/heart_swap.test.ts b/test/moves/heart_swap.test.ts index e9e407b6b30..c90b13b88ef 100644 --- a/test/moves/heart_swap.test.ts +++ b/test/moves/heart_swap.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { BATTLE_STATS } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { BATTLE_STATS } from "#enums/stat"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Heart Swap", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/hyper_beam.test.ts b/test/moves/hyper_beam.test.ts index bca7cba4e9a..aa3ecfe910b 100644 --- a/test/moves/hyper_beam.test.ts +++ b/test/moves/hyper_beam.test.ts @@ -1,11 +1,11 @@ -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/imprison.test.ts b/test/moves/imprison.test.ts index 26eadb685f9..0007c07985c 100644 --- a/test/moves/imprison.test.ts +++ b/test/moves/imprison.test.ts @@ -1,11 +1,11 @@ +import { AbilityId } from "#enums/ability-id"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { ArenaTagType } from "#enums/arena-tag-type"; describe("Moves - Imprison", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index d12859301b6..c73091a0eea 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -1,15 +1,16 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#enums/move-result"; -import type { MovePhase } from "#app/phases/move-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import { RandomMoveAttr } from "#moves/move"; +import type { MovePhase } from "#phases/move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { TurnMove } from "#types/turn-move"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -202,21 +203,32 @@ describe("Moves - Instruct", () => { game.override.battleStyle("double").enemyMoveset(MoveId.SPLASH).enemySpecies(SpeciesId.MAGIKARP).enemyLevel(1); await game.classicMode.startBattle([SpeciesId.HISUI_ELECTRODE, SpeciesId.KOMMO_O]); - const [electrode, kommo_o] = game.scene.getPlayerField()!; - game.move.changeMoveset(electrode, MoveId.CHLOROBLAST); + const [electrode, kommo_o] = game.scene.getPlayerField(); + game.move.changeMoveset(electrode, MoveId.THUNDERBOLT); game.move.changeMoveset(kommo_o, MoveId.INSTRUCT); - game.move.select(MoveId.CHLOROBLAST, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(MoveId.THUNDERBOLT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); - await game.phaseInterceptor.to("BerryPhase"); + await game.toEndOfTurn(); - // Chloroblast always deals 50% max HP% recoil UNLESS you whiff - // due to lack of targets or similar, - // so all we have to do is check whether electrode fainted or not. - // Naturally, both karps should also be dead as well. - expect(electrode.isFainted()).toBe(true); - const [karp1, karp2] = game.scene.getEnemyField()!; + expect(electrode.getMoveHistory()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + result: MoveResult.SUCCESS, + move: MoveId.THUNDERBOLT, + targets: [BattlerIndex.ENEMY], + useMode: MoveUseMode.NORMAL, + }), + expect.objectContaining({ + result: MoveResult.SUCCESS, + move: MoveId.THUNDERBOLT, + targets: [BattlerIndex.ENEMY_2], + useMode: MoveUseMode.NORMAL, + }), + ]), + ); + const [karp1, karp2] = game.scene.getEnemyField(); expect(karp1.isFainted()).toBe(true); expect(karp2.isFainted()).toBe(true); }); @@ -349,7 +361,7 @@ describe("Moves - Instruct", () => { useMode: MoveUseMode.NORMAL, }); - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toEndOfTurn(); expect(game.field.getEnemyPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); diff --git a/test/moves/jaw_lock.test.ts b/test/moves/jaw_lock.test.ts index 4e103c14f98..b5e98fbf6d8 100644 --- a/test/moves/jaw_lock.test.ts +++ b/test/moves/jaw_lock.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { BerryPhase } from "#phases/berry-phase"; +import { FaintPhase } from "#phases/faint-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/lash_out.test.ts b/test/moves/lash_out.test.ts index 33a58914978..b529351e01f 100644 --- a/test/moves/lash_out.test.ts +++ b/test/moves/lash_out.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/last-resort.test.ts b/test/moves/last-resort.test.ts index 3cb98a0cd92..4eaf1ee7f5d 100644 --- a/test/moves/last-resort.test.ts +++ b/test/moves/last-resort.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/last_respects.test.ts b/test/moves/last_respects.test.ts index 14aa5ad3309..e438971125a 100644 --- a/test/moves/last_respects.test.ts +++ b/test/moves/last_respects.test.ts @@ -1,11 +1,11 @@ -import { MoveId } from "#enums/move-id"; -import { BattlerIndex } from "#enums/battler-index"; -import { SpeciesId } from "#enums/species-id"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import type { Move } from "#moves/move"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index 8c436aacf6a..5dcdc3c271c 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -1,15 +1,15 @@ -import type BattleScene from "#app/battle-scene"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; +import type { BattleScene } from "#app/battle-scene"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import type Pokemon from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils/common"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/lucky_chant.test.ts b/test/moves/lucky_chant.test.ts index a57d231134b..afcdd901382 100644 --- a/test/moves/lucky_chant.test.ts +++ b/test/moves/lucky_chant.test.ts @@ -1,10 +1,10 @@ import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { BattlerIndex } from "#enums/battler-index"; describe("Moves - Lucky Chant", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/lunar_blessing.test.ts b/test/moves/lunar_blessing.test.ts index 2709ccbacf3..481156748e3 100644 --- a/test/moves/lunar_blessing.test.ts +++ b/test/moves/lunar_blessing.test.ts @@ -1,9 +1,8 @@ -import { StatusEffect } from "#app/enums/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -47,7 +46,6 @@ describe("Moves - Lunar Blessing", () => { vi.spyOn(rightPlayer, "heal"); game.move.select(MoveId.LUNAR_BLESSING, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -67,7 +65,6 @@ describe("Moves - Lunar Blessing", () => { vi.spyOn(rightPlayer, "resetStatus"); game.move.select(MoveId.LUNAR_BLESSING, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); diff --git a/test/moves/lunar_dance.test.ts b/test/moves/lunar_dance.test.ts index aea1e31b616..3fcf953a37c 100644 --- a/test/moves/lunar_dance.test.ts +++ b/test/moves/lunar_dance.test.ts @@ -1,11 +1,10 @@ -import { StatusEffect } from "#app/enums/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Lunar Dance", () => { let phaserGame: Phaser.Game; @@ -40,7 +39,6 @@ describe("Moves - Lunar Dance", () => { game.move.select(MoveId.SPLASH, 0); game.move.select(MoveId.SPLASH, 1); - await game.phaseInterceptor.to(CommandPhase); await game.toNextTurn(); // Bulbasaur should still be burned and have used a PP for splash and not at max hp @@ -51,7 +49,6 @@ describe("Moves - Lunar Dance", () => { // Switch out Bulbasaur for Rattata so we can swtich bulbasaur back in with lunar dance game.doSwitchPokemon(2); game.move.select(MoveId.SPLASH, 1); - await game.phaseInterceptor.to(CommandPhase); await game.toNextTurn(); game.move.select(MoveId.SPLASH, 0); @@ -67,7 +64,6 @@ describe("Moves - Lunar Dance", () => { game.move.select(MoveId.SPLASH, 0); game.move.select(MoveId.LUNAR_DANCE); - await game.phaseInterceptor.to(CommandPhase); await game.toNextTurn(); // Using Lunar dance again should fail because nothing in party and rattata should be alive diff --git a/test/moves/magic_coat.test.ts b/test/moves/magic_coat.test.ts index 566c206f1ba..4a026ceb84e 100644 --- a/test/moves/magic_coat.test.ts +++ b/test/moves/magic_coat.test.ts @@ -1,15 +1,15 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { Stat } from "#app/enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; -import { MoveResult } from "#enums/move-result"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Magic Coat", () => { diff --git a/test/moves/magnet_rise.test.ts b/test/moves/magnet_rise.test.ts index d8fc6a74225..da76c178be8 100644 --- a/test/moves/magnet_rise.test.ts +++ b/test/moves/magnet_rise.test.ts @@ -1,15 +1,12 @@ -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Magnet Rise", () => { let phaserGame: Phaser.Game; let game: GameManager; - const moveToUse = MoveId.MAGNET_RISE; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -25,38 +22,36 @@ describe("Moves - Magnet Rise", () => { game = new GameManager(phaserGame); game.override .battleStyle("single") - .starterSpecies(SpeciesId.MAGNEZONE) .enemySpecies(SpeciesId.RATTATA) - .enemyMoveset(MoveId.DRILL_RUN) + .enemyMoveset(MoveId.EARTHQUAKE) .criticalHits(false) - .enemyLevel(1) - .moveset([moveToUse, MoveId.SPLASH, MoveId.GRAVITY, MoveId.BATON_PASS]); + .enemyLevel(1); }); - it("MAGNET RISE", async () => { - await game.classicMode.startBattle(); + it("should make the user immune to ground-type moves", async () => { + await game.classicMode.startBattle([SpeciesId.MAGNEZONE]); - const startingHp = game.scene.getPlayerParty()[0].hp; - game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); - const finalHp = game.scene.getPlayerParty()[0].hp; - const hpLost = finalHp - startingHp; - expect(hpLost).toBe(0); + game.move.use(MoveId.MAGNET_RISE); + await game.toEndOfTurn(); + + const magnezone = game.field.getPlayerPokemon(); + expect(magnezone.hp).toBe(magnezone.getMaxHp()); + expect(magnezone.isGrounded()).toBe(false); }); - it("MAGNET RISE - Gravity", async () => { - await game.classicMode.startBattle(); + it("should be removed by gravity", async () => { + await game.classicMode.startBattle([SpeciesId.MAGNEZONE]); - const startingHp = game.scene.getPlayerParty()[0].hp; - game.move.select(moveToUse); - await game.phaseInterceptor.to(CommandPhase); - let finalHp = game.scene.getPlayerParty()[0].hp; - let hpLost = finalHp - startingHp; - expect(hpLost).toBe(0); - game.move.select(MoveId.GRAVITY); - await game.phaseInterceptor.to(TurnEndPhase); - finalHp = game.scene.getPlayerParty()[0].hp; - hpLost = finalHp - startingHp; - expect(hpLost).not.toBe(0); + game.move.use(MoveId.MAGNET_RISE); + await game.toNextTurn(); + + const magnezone = game.field.getPlayerPokemon(); + expect(magnezone.hp).toBe(magnezone.getMaxHp()); + + game.move.use(MoveId.GRAVITY); + await game.toEndOfTurn(); + + expect(magnezone.hp).toBeLessThan(magnezone.getMaxHp()); + expect(magnezone.isGrounded()).toBe(true); }); }); diff --git a/test/moves/make_it_rain.test.ts b/test/moves/make_it_rain.test.ts index 13b8e8f1853..47f1bc89fc1 100644 --- a/test/moves/make_it_rain.test.ts +++ b/test/moves/make_it_rain.test.ts @@ -1,12 +1,12 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; describe("Moves - Make It Rain", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/mat_block.test.ts b/test/moves/mat_block.test.ts index d77f538a973..954e2a8135d 100644 --- a/test/moves/mat_block.test.ts +++ b/test/moves/mat_block.test.ts @@ -1,13 +1,13 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BerryPhase } from "#phases/berry-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Moves - Mat Block", () => { let phaserGame: Phaser.Game; @@ -42,9 +42,6 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.MAT_BLOCK); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -60,9 +57,6 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.MAT_BLOCK); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -76,7 +70,6 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -85,7 +78,6 @@ describe("Moves - Mat Block", () => { await game.phaseInterceptor.to(CommandPhase, false); game.move.select(MoveId.MAT_BLOCK); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.MAT_BLOCK, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/test/moves/metal_burst.test.ts b/test/moves/metal_burst.test.ts index 4d07d59c16d..c99b4324878 100644 --- a/test/moves/metal_burst.test.ts +++ b/test/moves/metal_burst.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/metronome.test.ts b/test/moves/metronome.test.ts index 670f7bb9b88..8ca9e63582e 100644 --- a/test/moves/metronome.test.ts +++ b/test/moves/metronome.test.ts @@ -1,18 +1,17 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags"; -import type { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; +import { RechargingTag, SemiInvulnerableTag } from "#data/battler-tags"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { Stat } from "#app/enums/stat"; -import { MoveResult } from "#enums/move-result"; -import { CommandPhase } from "#app/phases/command-phase"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { MoveUseMode } from "#enums/move-use-mode"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import type { RandomMoveAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Metronome", () => { let phaserGame: Phaser.Game; @@ -125,7 +124,6 @@ describe("Moves - Metronome", () => { vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(MoveId.AROMATIC_MIST); game.move.select(MoveId.METRONOME, 0); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -148,6 +146,6 @@ describe("Moves - Metronome", () => { const hasFled = enemyPokemon.switchOutStatus; expect(!isVisible && hasFled).toBe(true); - await game.phaseInterceptor.to("CommandPhase"); + await game.toNextTurn(); }); }); diff --git a/test/moves/miracle_eye.test.ts b/test/moves/miracle_eye.test.ts index 9281931621f..d77da857068 100644 --- a/test/moves/miracle_eye.test.ts +++ b/test/moves/miracle_eye.test.ts @@ -1,8 +1,8 @@ import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/mirror_move.test.ts b/test/moves/mirror_move.test.ts index 03de93c56dc..7f833c08bf5 100644 --- a/test/moves/mirror_move.test.ts +++ b/test/moves/mirror_move.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#app/enums/stat"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/mist.test.ts b/test/moves/mist.test.ts index 4191aae0a46..b420b234e15 100644 --- a/test/moves/mist.test.ts +++ b/test/moves/mist.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/moongeist_beam.test.ts b/test/moves/moongeist_beam.test.ts deleted file mode 100644 index 367fe67cff5..00000000000 --- a/test/moves/moongeist_beam.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { allMoves } from "#app/data/data-lists"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; - -describe("Moves - Moongeist Beam", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .moveset([MoveId.MOONGEIST_BEAM, MoveId.METRONOME]) - .ability(AbilityId.BALL_FETCH) - .startingLevel(200) - .battleStyle("single") - .criticalHits(false) - .enemySpecies(SpeciesId.MAGIKARP) - .enemyAbility(AbilityId.STURDY) - .enemyMoveset(MoveId.SPLASH); - }); - - // Also covers Photon Geyser and Sunsteel Strike - it("should ignore enemy abilities", async () => { - await game.classicMode.startBattle([SpeciesId.MILOTIC]); - - const enemy = game.scene.getEnemyPokemon()!; - - game.move.select(MoveId.MOONGEIST_BEAM); - await game.phaseInterceptor.to("BerryPhase"); - - expect(enemy.isFainted()).toBe(true); - }); - - // Also covers Photon Geyser and Sunsteel Strike - it("should not ignore enemy abilities when called by another move, such as metronome", async () => { - await game.classicMode.startBattle([SpeciesId.MILOTIC]); - vi.spyOn(allMoves[MoveId.METRONOME].getAttrs("RandomMoveAttr")[0], "getMoveOverride").mockReturnValue( - MoveId.MOONGEIST_BEAM, - ); - - game.move.select(MoveId.METRONOME); - await game.phaseInterceptor.to("BerryPhase"); - - expect(game.scene.getEnemyPokemon()!.isFainted()).toBe(false); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].move).toBe(MoveId.MOONGEIST_BEAM); - }); -}); diff --git a/test/moves/multi_target.test.ts b/test/moves/multi_target.test.ts index bc6b24c474d..a58fb8d3a6b 100644 --- a/test/moves/multi_target.test.ts +++ b/test/moves/multi_target.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; -import { SpeciesId } from "#enums/species-id"; -import { toDmgValue } from "#app/utils/common"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/nightmare.test.ts b/test/moves/nightmare.test.ts index e005def85ad..07579a6790c 100644 --- a/test/moves/nightmare.test.ts +++ b/test/moves/nightmare.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/obstruct.test.ts b/test/moves/obstruct.test.ts index fc534180bef..ef589213c74 100644 --- a/test/moves/obstruct.test.ts +++ b/test/moves/obstruct.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/octolock.test.ts b/test/moves/octolock.test.ts index 2a2f8743ad2..bf2f8dc5865 100644 --- a/test/moves/octolock.test.ts +++ b/test/moves/octolock.test.ts @@ -1,9 +1,9 @@ -import { TrappedTag } from "#app/data/battler-tags"; +import { TrappedTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/order_up.test.ts b/test/moves/order_up.test.ts index fc505809fe6..2c9702fd6cb 100644 --- a/test/moves/order_up.test.ts +++ b/test/moves/order_up.test.ts @@ -1,12 +1,12 @@ +import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; +import { SpeciesId } from "#enums/species-id"; import type { EffectiveStat } from "#enums/stat"; import { Stat } from "#enums/stat"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/parting_shot.test.ts b/test/moves/parting_shot.test.ts index fdeab6bfc7c..86a14330a3e 100644 --- a/test/moves/parting_shot.test.ts +++ b/test/moves/parting_shot.test.ts @@ -1,14 +1,14 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { BerryPhase } from "#phases/berry-phase"; +import { FaintPhase } from "#phases/faint-phase"; +import { MessagePhase } from "#phases/message-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { Stat } from "#enums/stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Parting Shot", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/payback.test.ts b/test/moves/payback.test.ts new file mode 100644 index 00000000000..20e832288a3 --- /dev/null +++ b/test/moves/payback.test.ts @@ -0,0 +1,69 @@ +import { allMoves } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; + +describe("Move - Payback", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let powerSpy: MockInstance; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.BALL_FETCH) + .battleStyle("single") + .criticalHits(false) + .enemyLevel(100) + .enemySpecies(SpeciesId.DRACOVISH) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH) + .startingModifier([{ name: "POKEBALL", count: 5 }]); + + powerSpy = vi.spyOn(allMoves[MoveId.PAYBACK], "calculateBattlePower"); + }); + + it("should double power if the user moves after the target", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + // turn 1: enemy, then player (boost) + game.move.use(MoveId.PAYBACK); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.PAYBACK].power * 2); + + // turn 2: player, then enemy (no boost) + game.move.use(MoveId.PAYBACK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.PAYBACK].power); + }); + + // TODO: Enable test once ability to force catch failure is added + it.todo("should trigger for enemies on player failed ball catch", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + game.doThrowPokeball(PokeballType.POKEBALL); + await game.move.forceEnemyMove(MoveId.PAYBACK); + await game.toEndOfTurn(); + + expect(powerSpy).toHaveLastReturnedWith(allMoves[MoveId.PAYBACK].power * 2); + }); +}); diff --git a/test/moves/plasma_fists.test.ts b/test/moves/plasma_fists.test.ts index 7d1985be13e..7a5ba77b62d 100644 --- a/test/moves/plasma_fists.test.ts +++ b/test/moves/plasma_fists.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Plasma Fists", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index f653e245f6f..bd0c981517c 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -1,17 +1,16 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allMoves } from "#app/data/data-lists"; -import { PokemonType } from "#enums/pokemon-type"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { Stat } from "#enums/stat"; -import { toDmgValue } from "#app/utils/common"; +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Pledge Moves", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/pollen_puff.test.ts b/test/moves/pollen_puff.test.ts index cd0a138e96c..d372f72a390 100644 --- a/test/moves/pollen_puff.test.ts +++ b/test/moves/pollen_puff.test.ts @@ -1,8 +1,8 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/powder.test.ts b/test/moves/powder.test.ts index 8f47f2ff428..3e60c0d9fec 100644 --- a/test/moves/powder.test.ts +++ b/test/moves/powder.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; -import { BerryPhase } from "#app/phases/berry-phase"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; +import { MoveResult } from "#enums/move-result"; import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/power_shift.test.ts b/test/moves/power_shift.test.ts index cd5252c3f2f..069d793a794 100644 --- a/test/moves/power_shift.test.ts +++ b/test/moves/power_shift.test.ts @@ -1,8 +1,8 @@ +import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { Stat } from "#app/enums/stat"; -import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/power_split.test.ts b/test/moves/power_split.test.ts index c0fcd317182..ef6fda419ca 100644 --- a/test/moves/power_split.test.ts +++ b/test/moves/power_split.test.ts @@ -1,11 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Power Split", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/power_swap.test.ts b/test/moves/power_swap.test.ts index 82662850c77..cdd52fbc524 100644 --- a/test/moves/power_swap.test.ts +++ b/test/moves/power_swap.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat, BATTLE_STATS } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { BATTLE_STATS, Stat } from "#enums/stat"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Power Swap", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/power_trick.test.ts b/test/moves/power_trick.test.ts index af90525d263..29a8107d891 100644 --- a/test/moves/power_trick.test.ts +++ b/test/moves/power_trick.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Power Trick", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/protect.test.ts b/test/moves/protect.test.ts index 601afb43ec3..9fd54d44891 100644 --- a/test/moves/protect.test.ts +++ b/test/moves/protect.test.ts @@ -1,15 +1,15 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; +import { ArenaTrapTag } from "#data/arena-tag"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/data-lists"; -import { ArenaTrapTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; describe("Moves - Protect", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/psycho_shift.test.ts b/test/moves/psycho_shift.test.ts index e4eb2bd8ef6..6414a91543b 100644 --- a/test/moves/psycho_shift.test.ts +++ b/test/moves/psycho_shift.test.ts @@ -1,8 +1,8 @@ -import { StatusEffect } from "#app/enums/status-effect"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/purify.test.ts b/test/moves/purify.test.ts index 2e8e312f23c..a5d7d86851b 100644 --- a/test/moves/purify.test.ts +++ b/test/moves/purify.test.ts @@ -1,11 +1,11 @@ +import { Status } from "#data/status-effect"; import { BattlerIndex } from "#enums/battler-index"; -import { Status } from "#app/data/status-effect"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/quash.test.ts b/test/moves/quash.test.ts index 158c409cf05..632d37a4e13 100644 --- a/test/moves/quash.test.ts +++ b/test/moves/quash.test.ts @@ -1,13 +1,13 @@ -import { SpeciesId } from "#enums/species-id"; -import { MoveId } from "#enums/move-id"; import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; -import { WeatherType } from "#enums/weather-type"; +import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { describe, beforeAll, afterEach, beforeEach, it, expect } from "vitest"; import { MoveUseMode } from "#enums/move-use-mode"; +import { SpeciesId } from "#enums/species-id"; +import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Quash", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/quick_guard.test.ts b/test/moves/quick_guard.test.ts index 7146b804ffa..5e2e1ae1f24 100644 --- a/test/moves/quick_guard.test.ts +++ b/test/moves/quick_guard.test.ts @@ -1,12 +1,12 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; describe("Moves - Quick Guard", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/rage_fist.test.ts b/test/moves/rage_fist.test.ts index 31dd987cb87..d7928ddd310 100644 --- a/test/moves/rage_fist.test.ts +++ b/test/moves/rage_fist.test.ts @@ -1,13 +1,13 @@ -import { BattlerIndex } from "#enums/battler-index"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; -import GameManager from "#test/testUtils/gameManager"; +import type { Move } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { BattleType } from "#enums/battle-type"; describe("Moves - Rage Fist", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/rage_powder.test.ts b/test/moves/rage_powder.test.ts index 845bff33d04..a0b836f0917 100644 --- a/test/moves/rage_powder.test.ts +++ b/test/moves/rage_powder.test.ts @@ -1,8 +1,8 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index ef8c80070bf..47c49ea4d36 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -1,15 +1,15 @@ -import type BattleScene from "#app/battle-scene"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; +import type { BattleScene } from "#app/battle-scene"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import type Pokemon from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils/common"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Pokemon } from "#field/pokemon"; +import type { Move } from "#moves/move"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/reflect_type.test.ts b/test/moves/reflect_type.test.ts index 0915069764c..499c167bee0 100644 --- a/test/moves/reflect_type.test.ts +++ b/test/moves/reflect_type.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { PokemonType } from "#enums/pokemon-type"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -30,30 +30,26 @@ describe("Moves - Reflect Type", () => { }); it("will make the user Normal/Grass if targetting a typeless Pokemon affected by Forest's Curse", async () => { - game.override - .moveset([MoveId.FORESTS_CURSE, MoveId.REFLECT_TYPE]) - .startingLevel(60) - .enemySpecies(SpeciesId.CHARMANDER) - .enemyMoveset([MoveId.BURN_UP, MoveId.SPLASH]); + game.override.startingLevel(60).enemySpecies(SpeciesId.CHARMANDER); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); - game.move.select(MoveId.SPLASH); - await game.move.selectEnemyMove(MoveId.BURN_UP); + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.BURN_UP); await game.toNextTurn(); - game.move.select(MoveId.FORESTS_CURSE); - await game.move.selectEnemyMove(MoveId.SPLASH); + game.move.use(MoveId.FORESTS_CURSE); + await game.move.forceEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(enemyPokemon?.getTypes().includes(PokemonType.UNKNOWN)).toBe(true); - expect(enemyPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true); + expect(enemyPokemon.getTypes().includes(PokemonType.UNKNOWN)).toBe(true); + expect(enemyPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true); - game.move.select(MoveId.REFLECT_TYPE); - await game.move.selectEnemyMove(MoveId.SPLASH); + game.move.use(MoveId.REFLECT_TYPE); + await game.move.forceEnemyMove(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); - expect(playerPokemon?.getTypes()[0]).toBe(PokemonType.NORMAL); - expect(playerPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true); + expect(playerPokemon.getTypes()[0]).toBe(PokemonType.NORMAL); + expect(playerPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true); }); }); diff --git a/test/moves/relic_song.test.ts b/test/moves/relic_song.test.ts index b3dd400a202..36c46769515 100644 --- a/test/moves/relic_song.test.ts +++ b/test/moves/relic_song.test.ts @@ -1,9 +1,9 @@ -import { PokemonType } from "#enums/pokemon-type"; -import { Challenges } from "#app/enums/challenges"; import { AbilityId } from "#enums/ability-id"; +import { Challenges } from "#enums/challenges"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/retaliate.test.ts b/test/moves/retaliate.test.ts index dd5029a7c83..3eefbe3ceb6 100644 --- a/test/moves/retaliate.test.ts +++ b/test/moves/retaliate.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; +import { allMoves } from "#data/data-lists"; import { MoveId } from "#enums/move-id"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; +import { SpeciesId } from "#enums/species-id"; +import type { Move } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Retaliate", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/revival_blessing.test.ts b/test/moves/revival_blessing.test.ts index 1f2ba869e4f..a9a6bd0fd7d 100644 --- a/test/moves/revival_blessing.test.ts +++ b/test/moves/revival_blessing.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; -import { toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { toDmgValue } from "#utils/common"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/role_play.test.ts b/test/moves/role_play.test.ts index cfa38ed38b7..dda7f319cd5 100644 --- a/test/moves/role_play.test.ts +++ b/test/moves/role_play.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/rollout.test.ts b/test/moves/rollout.test.ts index 9639a2e5408..eed61f82ab4 100644 --- a/test/moves/rollout.test.ts +++ b/test/moves/rollout.test.ts @@ -1,9 +1,8 @@ -import { allMoves } from "#app/data/data-lists"; -import { CommandPhase } from "#app/phases/command-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -57,8 +56,7 @@ describe("Moves - Rollout", () => { for (let i = 0; i < turns; i++) { game.move.select(MoveId.ROLLOUT); - await game.phaseInterceptor.to(CommandPhase); - + await game.toNextTurn(); dmgHistory.push(previousHp - enemyPkm.hp); previousHp = enemyPkm.hp; } diff --git a/test/moves/roost.test.ts b/test/moves/roost.test.ts index 3707e0ead45..30a71721d03 100644 --- a/test/moves/roost.test.ts +++ b/test/moves/roost.test.ts @@ -1,11 +1,11 @@ import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/round.test.ts b/test/moves/round.test.ts index 8e07a819131..a706fd15e9b 100644 --- a/test/moves/round.test.ts +++ b/test/moves/round.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/safeguard.test.ts b/test/moves/safeguard.test.ts index 8d5303e3feb..2651d8473be 100644 --- a/test/moves/safeguard.test.ts +++ b/test/moves/safeguard.test.ts @@ -1,12 +1,12 @@ +import { allAbilities } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; -import { allAbilities } from "#app/data/data-lists"; -import { StatusEffect } from "#app/enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { AbilityId } from "#enums/ability-id"; describe("Moves - Safeguard", () => { let phaserGame: Phaser.Game; @@ -140,9 +140,8 @@ describe("Moves - Safeguard", () => { game.field.mockAbility(player, AbilityId.STATIC); vi.spyOn( allAbilities[AbilityId.STATIC].getAttrs("PostDefendContactApplyStatusEffectAbAttr")[0], - "chance", - "get", - ).mockReturnValue(100); + "canApply", + ).mockReturnValue(true); game.move.select(MoveId.SPLASH); await game.move.forceEnemyMove(MoveId.SAFEGUARD); diff --git a/test/moves/scale_shot.test.ts b/test/moves/scale_shot.test.ts index e2c86091378..4e420f6b3ef 100644 --- a/test/moves/scale_shot.test.ts +++ b/test/moves/scale_shot.test.ts @@ -1,16 +1,16 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Scale Shot", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index adb91162ae8..8ec69fb276a 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -1,17 +1,16 @@ +import { allAbilities, allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/data-lists"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { StatusEffect } from "#enums/status-effect"; -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { allAbilities } from "#app/data/data-lists"; describe("Moves - Secret Power", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/shed_tail.test.ts b/test/moves/shed_tail.test.ts index 81ab9215bc9..8a837a642c9 100644 --- a/test/moves/shed_tail.test.ts +++ b/test/moves/shed_tail.test.ts @@ -1,11 +1,11 @@ -import { SubstituteTag } from "#app/data/battler-tags"; -import { MoveResult } from "#enums/move-result"; +import { SubstituteTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Shed Tail", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/shell_side_arm.test.ts b/test/moves/shell_side_arm.test.ts index 35246e10e3f..685cebce4cf 100644 --- a/test/moves/shell_side_arm.test.ts +++ b/test/moves/shell_side_arm.test.ts @@ -1,11 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import type { ShellSideArmCategoryAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { Move, ShellSideArmCategoryAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/shell_trap.test.ts b/test/moves/shell_trap.test.ts index a3f55cef10f..c484d1195aa 100644 --- a/test/moves/shell_trap.test.ts +++ b/test/moves/shell_trap.test.ts @@ -1,12 +1,12 @@ +import { allMoves } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { MoveResult } from "#enums/move-result"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { BerryPhase } from "#phases/berry-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { MovePhase } from "#phases/move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/simple_beam.test.ts b/test/moves/simple_beam.test.ts index f8549944ed9..106977aea84 100644 --- a/test/moves/simple_beam.test.ts +++ b/test/moves/simple_beam.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/sketch.test.ts b/test/moves/sketch.test.ts index 5e9ce9a9e84..10ad31c35eb 100644 --- a/test/moves/sketch.test.ts +++ b/test/moves/sketch.test.ts @@ -1,15 +1,15 @@ +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; import { MoveResult } from "#enums/move-result"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { RandomMoveAttr } from "#moves/move"; +import { PokemonMove } from "#moves/pokemon-move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { StatusEffect } from "#app/enums/status-effect"; -import { BattlerIndex } from "#enums/battler-index"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; describe("Moves - Sketch", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/skill_swap.test.ts b/test/moves/skill_swap.test.ts index 7c2e6f5fd0a..d5ba509b0df 100644 --- a/test/moves/skill_swap.test.ts +++ b/test/moves/skill_swap.test.ts @@ -1,8 +1,8 @@ -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/sleep_talk.test.ts b/test/moves/sleep_talk.test.ts index 84e9d509a03..972c54463e2 100644 --- a/test/moves/sleep_talk.test.ts +++ b/test/moves/sleep_talk.test.ts @@ -1,10 +1,10 @@ -import { Stat } from "#app/enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/solar_beam.test.ts b/test/moves/solar_beam.test.ts index 55d2ee5d13c..32400abfc0f 100644 --- a/test/moves/solar_beam.test.ts +++ b/test/moves/solar_beam.test.ts @@ -1,13 +1,13 @@ -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { WeatherType } from "#enums/weather-type"; -import { MoveResult } from "#enums/move-result"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { WeatherType } from "#enums/weather-type"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Solar Beam", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/sparkly_swirl.test.ts b/test/moves/sparkly_swirl.test.ts index d1cbdd70107..d58b6efe990 100644 --- a/test/moves/sparkly_swirl.test.ts +++ b/test/moves/sparkly_swirl.test.ts @@ -1,10 +1,9 @@ -import { allMoves } from "#app/data/data-lists"; -import { StatusEffect } from "#app/enums/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -44,7 +43,6 @@ describe("Moves - Sparkly Swirl", () => { vi.spyOn(partyPokemon, "resetStatus"); game.move.select(MoveId.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex()); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); @@ -66,7 +64,6 @@ describe("Moves - Sparkly Swirl", () => { vi.spyOn(rightOpp, "resetStatus"); game.move.select(MoveId.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex()); - await game.phaseInterceptor.to(CommandPhase); game.move.select(MoveId.SPLASH, 1); await game.toNextTurn(); diff --git a/test/moves/spectral_thief.test.ts b/test/moves/spectral_thief.test.ts index e7b19eda506..acb3e22cf6c 100644 --- a/test/moves/spectral_thief.test.ts +++ b/test/moves/spectral_thief.test.ts @@ -1,11 +1,11 @@ +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/data-lists"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/speed_swap.test.ts b/test/moves/speed_swap.test.ts index 3723e7db740..e0c06bfb0f7 100644 --- a/test/moves/speed_swap.test.ts +++ b/test/moves/speed_swap.test.ts @@ -1,11 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat } from "#enums/stat"; import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Speed Swap", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/spikes.test.ts b/test/moves/spikes.test.ts index 49492701d18..424aecb5c3c 100644 --- a/test/moves/spikes.test.ts +++ b/test/moves/spikes.test.ts @@ -1,11 +1,12 @@ +import { ArenaTrapTag } from "#data/arena-tag"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { ArenaTrapTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; describe("Moves - Spikes", () => { let phaserGame: Phaser.Game; @@ -80,14 +81,19 @@ describe("Moves - Spikes", () => { expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }); - it("should work when all targets fainted", async () => { - game.override.enemySpecies(SpeciesId.DIGLETT).battleStyle("double").startingLevel(50); - await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.ROWLET]); + // TODO: re-enable after re-fixing hazards moves + it.todo("should work when all targets fainted", async () => { + game.override.enemySpecies(SpeciesId.DIGLETT).battleStyle("double").startingLevel(1000); + await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.SHUCKLE]); - game.move.select(MoveId.EARTHQUAKE); - game.move.select(MoveId.SPIKES, 1); - await game.phaseInterceptor.to("TurnEndPhase"); + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER); + game.move.use(MoveId.SPIKES, BattlerIndex.PLAYER_2); + await game.toEndOfTurn(); + + expect(enemy1.isFainted()).toBe(true); + expect(enemy2.isFainted()).toBe(true); expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeDefined(); }); }); diff --git a/test/moves/spit_up.test.ts b/test/moves/spit_up.test.ts index 00cbe2fd0ad..03e0cee1e4b 100644 --- a/test/moves/spit_up.test.ts +++ b/test/moves/spit_up.test.ts @@ -1,17 +1,17 @@ -import { Stat } from "#enums/stat"; -import { StockpilingTag } from "#app/data/battler-tags"; -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { MoveResult } from "#enums/move-result"; -import GameManager from "#test/testUtils/gameManager"; +import { StockpilingTag } from "#data/battler-tags"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; -import type Move from "#app/data/moves/move"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import type { Move } from "#moves/move"; +import { MovePhase } from "#phases/move-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { MovePhase } from "#app/phases/move-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Spit Up", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/spotlight.test.ts b/test/moves/spotlight.test.ts index a9e8cd7e2b6..4feff5eb719 100644 --- a/test/moves/spotlight.test.ts +++ b/test/moves/spotlight.test.ts @@ -1,8 +1,8 @@ import { BattlerIndex } from "#enums/battler-index"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index 4eb011c47f5..a9da98c1429 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { DamageCalculationResult } from "#app/field/pokemon"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { DamageCalculationResult } from "#types/damage-result"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/stockpile.test.ts b/test/moves/stockpile.test.ts index 3ad47d8d85e..efc1757738a 100644 --- a/test/moves/stockpile.test.ts +++ b/test/moves/stockpile.test.ts @@ -1,12 +1,11 @@ -import { Stat } from "#enums/stat"; -import { StockpilingTag } from "#app/data/battler-tags"; -import { MoveResult } from "#enums/move-result"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { StockpilingTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -50,12 +49,8 @@ describe("Moves - Stockpile", () => { // use Stockpile four times for (let i = 0; i < 4; i++) { - if (i !== 0) { - await game.phaseInterceptor.to(CommandPhase); - } - game.move.select(MoveId.STOCKPILE); - await game.phaseInterceptor.to(TurnInitPhase); + await game.toNextTurn(); const stockpilingTag = user.getTag(StockpilingTag)!; @@ -101,9 +96,6 @@ describe("Moves - Stockpile", () => { expect(user.getStatStage(Stat.DEF)).toBe(6); expect(user.getStatStage(Stat.SPDEF)).toBe(6); - // do it again, just for good measure - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); diff --git a/test/moves/struggle.test.ts b/test/moves/struggle.test.ts index e1373b29d33..2d624f6d338 100644 --- a/test/moves/struggle.test.ts +++ b/test/moves/struggle.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 182fefb74d7..39418fb5b99 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -1,21 +1,21 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { SubstituteTag, TrappedTag } from "#app/data/battler-tags"; -import { StealHeldItemChanceAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import { MoveResult } from "#enums/move-result"; -import type { CommandPhase } from "#app/phases/command-phase"; -import GameManager from "#test/testUtils/gameManager"; -import { Command } from "#enums/command"; -import { UiMode } from "#enums/ui-mode"; +import { SubstituteTag, TrappedTag } from "#data/battler-tags"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; +import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import { StealHeldItemChanceAttr } from "#moves/move"; +import type { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index c511682011f..176b00b030c 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -1,13 +1,13 @@ -import { Stat } from "#enums/stat"; -import { StockpilingTag } from "#app/data/battler-tags"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { MoveResult } from "#enums/move-result"; -import { MovePhase } from "#app/phases/move-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { StockpilingTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { MovePhase } from "#phases/move-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/synchronoise.test.ts b/test/moves/synchronoise.test.ts index 9068a40693b..fe4a316a32a 100644 --- a/test/moves/synchronoise.test.ts +++ b/test/moves/synchronoise.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/syrup_bomb.test.ts b/test/moves/syrup_bomb.test.ts index 4b2821b439c..f45070d81a5 100644 --- a/test/moves/syrup_bomb.test.ts +++ b/test/moves/syrup_bomb.test.ts @@ -1,11 +1,11 @@ +import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#enums/battler-tag-type"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { BattlerIndex } from "#enums/battler-index"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - SYRUP BOMB", () => { diff --git a/test/moves/tackle.test.ts b/test/moves/tackle.test.ts index f3c0db85a39..998afd23f9d 100644 --- a/test/moves/tackle.test.ts +++ b/test/moves/tackle.test.ts @@ -1,9 +1,9 @@ -import { Stat } from "#enums/stat"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/tail_whip.test.ts b/test/moves/tail_whip.test.ts index e98194b52dd..272816a88a6 100644 --- a/test/moves/tail_whip.test.ts +++ b/test/moves/tail_whip.test.ts @@ -1,12 +1,12 @@ -import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Tail whip", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/tailwind.test.ts b/test/moves/tailwind.test.ts index 874934fc8f3..1c8701de5bc 100644 --- a/test/moves/tailwind.test.ts +++ b/test/moves/tailwind.test.ts @@ -1,10 +1,10 @@ -import { ArenaTagSide } from "#enums/arena-tag-side"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/tar_shot.test.ts b/test/moves/tar_shot.test.ts index 8d5ea4b3582..c2ae397e43a 100644 --- a/test/moves/tar_shot.test.ts +++ b/test/moves/tar_shot.test.ts @@ -1,10 +1,10 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { Stat } from "#app/enums/stat"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerIndex } from "#enums/battler-index"; +import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/taunt.test.ts b/test/moves/taunt.test.ts index fabb95f98b2..ba76551708b 100644 --- a/test/moves/taunt.test.ts +++ b/test/moves/taunt.test.ts @@ -1,11 +1,11 @@ -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MoveResult } from "#enums/move-result"; -import { BattlerTagType } from "#enums/battler-tag-type"; describe("Moves - Taunt", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/telekinesis.test.ts b/test/moves/telekinesis.test.ts index 5c9f1e22395..92d7f8cc885 100644 --- a/test/moves/telekinesis.test.ts +++ b/test/moves/telekinesis.test.ts @@ -1,13 +1,13 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; -import { MoveResult } from "#enums/move-result"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Telekinesis", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/tera_blast.test.ts b/test/moves/tera_blast.test.ts index fbebd428cfd..d6c16c31544 100644 --- a/test/moves/tera_blast.test.ts +++ b/test/moves/tera_blast.test.ts @@ -1,13 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { Stat } from "#enums/stat"; -import type { TeraMoveCategoryAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; -import type Move from "#app/data/moves/move"; -import { PokemonType } from "#enums/pokemon-type"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import type { Move, TeraMoveCategoryAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/tera_starstorm.test.ts b/test/moves/tera_starstorm.test.ts index 2eaf2ec0372..a63573b360d 100644 --- a/test/moves/tera_starstorm.test.ts +++ b/test/moves/tera_starstorm.test.ts @@ -1,11 +1,11 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { PokemonType } from "#enums/pokemon-type"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Tera Starstorm", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/thousand_arrows.test.ts b/test/moves/thousand_arrows.test.ts index 9ecdd94a94f..3c7bacfbb2b 100644 --- a/test/moves/thousand_arrows.test.ts +++ b/test/moves/thousand_arrows.test.ts @@ -1,10 +1,10 @@ import { AbilityId } from "#enums/ability-id"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BerryPhase } from "#phases/berry-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/throat_chop.test.ts b/test/moves/throat_chop.test.ts index aa92f657c86..1a17cb1fba2 100644 --- a/test/moves/throat_chop.test.ts +++ b/test/moves/throat_chop.test.ts @@ -1,11 +1,11 @@ +import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { Stat } from "#app/enums/stat"; -import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Throat Chop", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/thunder_wave.test.ts b/test/moves/thunder_wave.test.ts index 7d2692fdfa3..06a24e454fa 100644 --- a/test/moves/thunder_wave.test.ts +++ b/test/moves/thunder_wave.test.ts @@ -1,9 +1,9 @@ -import type { EnemyPokemon } from "#app/field/pokemon"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import type { EnemyPokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/tidy_up.test.ts b/test/moves/tidy_up.test.ts index 69c9df42b36..c96718e2332 100644 --- a/test/moves/tidy_up.test.ts +++ b/test/moves/tidy_up.test.ts @@ -1,14 +1,14 @@ -import { Stat } from "#enums/stat"; -import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { SubstituteTag } from "#data/battler-tags"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SubstituteTag } from "#app/data/battler-tags"; -import { SpeciesId } from "#enums/species-id"; describe("Moves - Tidy Up", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/torment.test.ts b/test/moves/torment.test.ts index e4d926d9601..71a4f3cb504 100644 --- a/test/moves/torment.test.ts +++ b/test/moves/torment.test.ts @@ -1,12 +1,12 @@ -import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; -import GameManager from "#test/testUtils/gameManager"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { SpeciesId } from "#enums/species-id"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MoveResult } from "#enums/move-result"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; describe("Moves - Torment", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/toxic.test.ts b/test/moves/toxic.test.ts index a7ba52eb963..5aaa4e3e85d 100644 --- a/test/moves/toxic.test.ts +++ b/test/moves/toxic.test.ts @@ -1,11 +1,11 @@ +import { allMoves } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { StatusEffect } from "#enums/status-effect"; -import { BattlerIndex } from "#enums/battler-index"; -import { allMoves } from "#app/data/data-lists"; describe("Moves - Toxic", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/toxic_spikes.test.ts b/test/moves/toxic_spikes.test.ts index bf1f5acc1cb..69f09ce966a 100644 --- a/test/moves/toxic_spikes.test.ts +++ b/test/moves/toxic_spikes.test.ts @@ -1,14 +1,14 @@ -import type { ArenaTrapTag } from "#app/data/arena-tag"; -import { ArenaTagSide } from "#enums/arena-tag-side"; -import type { SessionSaveData } from "#app/system/game-data"; -import { GameData } from "#app/system/game-data"; -import { decrypt, encrypt } from "#app/utils/data"; +import type { ArenaTrapTag } from "#data/arena-tag"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import type { SessionSaveData } from "#system/game-data"; +import { GameData } from "#system/game-data"; +import { GameManager } from "#test/testUtils/gameManager"; +import { decrypt, encrypt } from "#utils/data"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/transform-imposter.test.ts b/test/moves/transform-imposter.test.ts new file mode 100644 index 00000000000..c12542e64e9 --- /dev/null +++ b/test/moves/transform-imposter.test.ts @@ -0,0 +1,379 @@ +import { Status } from "#data/status-effect"; +import { AbilityId } from "#enums/ability-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import type { EnemyPokemon } from "#field/pokemon"; +import { Pokemon } from "#field/pokemon"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; + +// TODO: Add more tests once Transform/Imposter are fully implemented +describe("Transforming Effects", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleStyle("single") + .enemySpecies(SpeciesId.MEW) + .enemyLevel(200) + .enemyAbility(AbilityId.BEAST_BOOST) + .enemyPassiveAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH) + .ability(AbilityId.STURDY); + }); + + // Contains logic shared by both Transform and Impostor (for brevity) + describe("Phases - PokemonTransformPhase", async () => { + it("should copy target's species, ability, gender, all stats except HP, all stat stages, moveset and types", async () => { + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const ditto = game.field.getPlayerPokemon(); + const mew = game.field.getEnemyPokemon(); + + mew.setStatStage(Stat.ATK, 4); + + game.move.use(MoveId.SPLASH); + game.scene.phaseManager.unshiftNew("PokemonTransformPhase", ditto.getBattlerIndex(), mew.getBattlerIndex()); + await game.toEndOfTurn(); + + expect(ditto.isTransformed()).toBe(true); + expect(ditto.getSpeciesForm().speciesId).toBe(mew.getSpeciesForm().speciesId); + expect(ditto.getAbility()).toBe(mew.getAbility()); + expect(ditto.getGender()).toBe(mew.getGender()); + + const playerStats = ditto.getStats(false); + const enemyStats = mew.getStats(false); + // HP stays the same; all other stats should carry over + expect(playerStats[0]).not.toBe(enemyStats[0]); + expect(playerStats.slice(1)).toEqual(enemyStats.slice(1)); + + // Stat stages/moveset IDs + expect(ditto.getStatStages()).toEqual(mew.getStatStages()); + + expect(ditto.getMoveset().map(m => m.moveId)).toEqual(ditto.getMoveset().map(m => m.moveId)); + + expect(ditto.getTypes()).toEqual(mew.getTypes()); + }); + + // TODO: This is not implemented + it.todo("should copy the target's original typing if target is typeless", async () => { + game.override.enemySpecies(SpeciesId.MAGMAR); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const ditto = game.field.getPlayerPokemon(); + const magmar = game.field.getEnemyPokemon(); + + game.move.use(MoveId.TRANSFORM); + await game.move.forceEnemyMove(MoveId.BURN_UP); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toEndOfTurn(); + + expect(magmar.getTypes()).toEqual([PokemonType.UNKNOWN]); + expect(ditto.getTypes()).toEqual([PokemonType.FIRE]); + }); + + it("should not consider the target's Tera Type when copying types", async () => { + game.override.enemySpecies(SpeciesId.MAGMAR); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const ditto = game.field.getPlayerPokemon(); + const magmar = game.field.getEnemyPokemon(); + magmar.isTerastallized = true; + magmar.teraType = PokemonType.DARK; + + game.move.use(MoveId.TRANSFORM); + await game.toEndOfTurn(); + + expect(ditto.getTypes(true)).toEqual([PokemonType.FIRE]); + }); + + // TODO: This is not currently implemented + it.todo("should copy volatile status effects", async () => { + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const ditto = game.field.getPlayerPokemon(); + const mew = game.field.getEnemyPokemon(); + mew.addTag(BattlerTagType.SEEDED, 0, MoveId.LEECH_SEED, ditto.id); + mew.addTag(BattlerTagType.CONFUSED, 4, MoveId.AXE_KICK, ditto.id); + + game.move.use(MoveId.TRANSFORM); + await game.toEndOfTurn(); + + expect(ditto.getTag(BattlerTagType.SEEDED)).toBeDefined(); + expect(ditto.getTag(BattlerTagType.CONFUSED)).toBeDefined(); + }); + + // TODO: This is not implemented + it.todo("should copy the target's rage fist hit count"); + + it("should not copy friendship, held items, nickname, level or non-volatile status effects", async () => { + game.override.enemyHeldItems([{ name: "BERRY", count: 1, type: BerryType.SITRUS }]); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const ditto = game.field.getPlayerPokemon(); + const mew = game.field.getEnemyPokemon(); + + mew.status = new Status(StatusEffect.POISON); + mew.friendship = 255; + mew.nickname = btoa(unescape(encodeURIComponent("Pink Furry Cat Thing"))); + + game.move.use(MoveId.TRANSFORM); + await game.toEndOfTurn(); + + expect(ditto.status?.effect).toBeUndefined(); + expect(ditto.getNameToRender()).not.toBe(mew.getNameToRender()); + expect(ditto.level).not.toBe(mew.level); + expect(ditto.friendship).not.toBe(mew.friendship); + expect(ditto.getHeldItems()).not.toEqual(mew.getHeldItems()); + }); + + it("should copy in-battle overridden stats", async () => { + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + const oldAtk = player.getStat(Stat.ATK); + const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + + game.move.use(MoveId.TRANSFORM); + await game.move.forceEnemyMove(MoveId.POWER_SPLIT); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toEndOfTurn(); + + expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(avgAtk).not.toBe(oldAtk); + }); + + it("should set each move's pp to a maximum of 5 without affecting PP ups", async () => { + game.override.enemyMoveset([MoveId.SWORDS_DANCE, MoveId.GROWL, MoveId.SKETCH, MoveId.RECOVER]); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const player = game.field.getPlayerPokemon(); + + game.move.use(MoveId.TRANSFORM); + await game.toEndOfTurn(); + + player.getMoveset().forEach(move => { + // Should set correct maximum PP without touching `ppUp` + if (move) { + if (move.moveId === MoveId.SKETCH) { + expect(move.getMovePp()).toBe(1); + } else { + expect(move.getMovePp()).toBe(5); + } + expect(move.ppUp).toBe(0); + } + }); + }); + + it("should activate its ability if it copies one that activates on summon", async () => { + game.override.enemyAbility(AbilityId.INTIMIDATE); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + game.move.use(MoveId.TRANSFORM); + game.phaseInterceptor.clearLogs(); + await game.toEndOfTurn(); + + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); + expect(game.phaseInterceptor.log).toContain("StatStageChangePhase"); + }); + + it("should persist transformed attributes across reloads", async () => { + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + game.move.use(MoveId.TRANSFORM); + await game.move.forceEnemyMove(MoveId.MEMENTO); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextWave(); + + expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + await game.reload.reloadSession(); + + const playerReloaded = game.field.getPlayerPokemon(); + const playerMoveset = player.getMoveset(); + + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getAbility()).toBe(enemy.getAbility()); + expect(playerReloaded.getGender()).toBe(enemy.getGender()); + + expect(playerMoveset.map(m => m.moveId)).toEqual([MoveId.MEMENTO]); + }); + + it("should stay transformed with the correct form after reload", async () => { + game.override.enemySpecies(SpeciesId.DARMANITAN); + await game.classicMode.startBattle([SpeciesId.DITTO]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + // change form + enemy.species.formIndex = 1; + + game.move.use(MoveId.TRANSFORM); + await game.move.forceEnemyMove(MoveId.MEMENTO); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextWave(); + + expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(player.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); + + await game.reload.reloadSession(); + + const playerReloaded = game.field.getPlayerPokemon(); + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); + }); + }); + + describe("Moves - Transform", () => { + it.each<{ cause: string; callback: (p: Pokemon) => void; player?: boolean }>([ + { + cause: "user is fused", + callback: p => vi.spyOn(p, "isFusion").mockReturnValue(true), + }, + { + cause: "target is fused", + callback: p => vi.spyOn(p, "isFusion").mockReturnValue(true), + player: false, + }, + { + cause: "user is transformed", + callback: p => vi.spyOn(p, "isTransformed").mockReturnValue(true), + }, + { + cause: "target is transformed", + callback: p => vi.spyOn(p, "isTransformed").mockReturnValue(true), + player: false, + }, + { + cause: "user has illusion", + callback: p => p.setIllusion(game.scene.getEnemyParty()[1]), + }, + { + cause: "target has illusion", + callback: p => p.setIllusion(game.scene.getEnemyParty()[1]), + player: false, + }, + { + cause: "target is behind a substitute", + callback: p => p.addTag(BattlerTagType.SUBSTITUTE, 1, MoveId.SUBSTITUTE, p.id), + player: false, + }, + ])("should fail if $cause", async ({ callback, player = true }) => { + game.override.battleType(BattleType.TRAINER); // ensures 2 enemy pokemon for illusion + await game.classicMode.startBattle([SpeciesId.DITTO, SpeciesId.ABOMASNOW]); + + callback(player ? game.field.getPlayerPokemon() : game.field.getEnemyPokemon()); + + game.move.use(MoveId.TRANSFORM); + await game.toEndOfTurn(); + + const ditto = game.field.getPlayerPokemon(); + expect(ditto.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.phaseInterceptor.log).not.toContain("PokemonTransformPhase"); + }); + }); + + describe("Abilities - Imposter", () => { + beforeEach(async () => { + game.override.ability(AbilityId.NONE); + // Mock ability index to always be HA (ensuring Ditto has Imposter and nobody else). + ( + vi.spyOn(Pokemon.prototype as any, "generateAbilityIndex") as MockInstance< + (typeof Pokemon.prototype)["generateAbilityIndex"] + > + ).mockReturnValue(3); + }); + + it.each<{ name: string; callback: (p: EnemyPokemon) => void }>([ + { + name: "opponents with substitutes", + callback: p => p.addTag(BattlerTagType.SUBSTITUTE, 1, MoveId.SUBSTITUTE, p.id), + }, + { name: "fused opponents", callback: p => vi.spyOn(p, "isFusion").mockReturnValue(true) }, + { + name: "opponents with illusions", + callback: p => p.setIllusion(game.scene.getEnemyParty()[1]), // doesn't really matter what the illusion is, merely that it exists + }, + ])("should ignore $name during target selection", async ({ callback }) => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.GYARADOS, SpeciesId.MILOTIC, SpeciesId.DITTO]); + + const ditto = game.scene.getPlayerParty()[2]; + + const [enemy1, enemy2] = game.scene.getEnemyField(); + // Override enemy 1 to be a fusion/illusion + callback(enemy1); + + expect(ditto.canTransformInto(enemy1)).toBe(false); + expect(ditto.canTransformInto(enemy2)).toBe(true); + + // Switch out to Ditto + game.doSwitchPokemon(2); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.toEndOfTurn(); + + expect(ditto.isActive()).toBe(true); + expect(ditto.isTransformed()).toBe(true); + expect(ditto.getSpeciesForm().speciesId).toBe(enemy2.getSpeciesForm().speciesId); + expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase"); + expect(game.phaseInterceptor.log).toContain("PokemonTransformPhase"); + }); + + it("should not activate if both opponents are fused or have illusions", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.GYARADOS, SpeciesId.MILOTIC, SpeciesId.DITTO]); + + const [gyarados, , ditto] = game.scene.getPlayerParty(); + const [enemy1, enemy2] = game.scene.getEnemyParty(); + // Override enemy 1 to be a fusion & enemy 2 to have illusion + vi.spyOn(enemy1, "isFusion").mockReturnValue(true); + enemy2.setIllusion(gyarados); + + expect(ditto.canTransformInto(enemy1)).toBe(false); + expect(ditto.canTransformInto(enemy2)).toBe(false); + + // Switch out to Ditto + game.doSwitchPokemon(2); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.toEndOfTurn(); + + expect(ditto.isActive()).toBe(true); + expect(ditto.isTransformed()).toBe(false); + expect(ditto.getSpeciesForm().speciesId).toBe(SpeciesId.DITTO); + expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); + expect(game.phaseInterceptor.log).not.toContain("PokemonTransformPhase"); + }); + }); +}); diff --git a/test/moves/transform.test.ts b/test/moves/transform.test.ts deleted file mode 100644 index 4fbaf0136ab..00000000000 --- a/test/moves/transform.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { MoveId } from "#enums/move-id"; -import { Stat, EFFECTIVE_STATS } from "#enums/stat"; -import { AbilityId } from "#enums/ability-id"; -import { BattlerIndex } from "#enums/battler-index"; - -// TODO: Add more tests once Transform is fully implemented -describe("Moves - Transform", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .battleStyle("single") - .enemySpecies(SpeciesId.MEW) - .enemyLevel(200) - .enemyAbility(AbilityId.BEAST_BOOST) - .enemyPassiveAbility(AbilityId.BALL_FETCH) - .enemyMoveset(MoveId.SPLASH) - .ability(AbilityId.INTIMIDATE) - .moveset([MoveId.TRANSFORM]); - }); - - it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { - await game.classicMode.startBattle([SpeciesId.DITTO]); - - game.move.select(MoveId.TRANSFORM); - await game.phaseInterceptor.to(TurnEndPhase); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(player.getAbility()).toBe(enemy.getAbility()); - expect(player.getGender()).toBe(enemy.getGender()); - - // copies all stats except hp - expect(player.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); - for (const s of EFFECTIVE_STATS) { - expect(player.getStat(s, false)).toBe(enemy.getStat(s, false)); - } - - expect(player.getStatStages()).toEqual(enemy.getStatStages()); - - // move IDs are equal - expect(player.getMoveset().map(m => m.moveId)).toEqual(enemy.getMoveset().map(m => m.moveId)); - - expect(player.getTypes()).toEqual(enemy.getTypes()); - }); - - it("should copy in-battle overridden stats", async () => { - game.override.enemyMoveset([MoveId.POWER_SPLIT]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); - const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); - - game.move.select(MoveId.TRANSFORM); - await game.phaseInterceptor.to(TurnEndPhase); - - expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); - expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); - - expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); - expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); - }); - - it("should set each move's pp to a maximum of 5", async () => { - game.override.enemyMoveset([MoveId.SWORDS_DANCE, MoveId.GROWL, MoveId.SKETCH, MoveId.RECOVER]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - const player = game.scene.getPlayerPokemon()!; - - game.move.select(MoveId.TRANSFORM); - await game.phaseInterceptor.to(TurnEndPhase); - - player.getMoveset().forEach(move => { - // Should set correct maximum PP without touching `ppUp` - if (move) { - if (move.moveId === MoveId.SKETCH) { - expect(move.getMovePp()).toBe(1); - } else { - expect(move.getMovePp()).toBe(5); - } - expect(move.ppUp).toBe(0); - } - }); - }); - - it("should activate its ability if it copies one that activates on summon", async () => { - game.override.enemyAbility(AbilityId.INTIMIDATE).ability(AbilityId.BALL_FETCH); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - game.move.select(MoveId.TRANSFORM); - - await game.phaseInterceptor.to("BerryPhase"); - - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); - }); - - it("should persist transformed attributes across reloads", async () => { - game.override.enemyMoveset([]).moveset([]); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - game.move.changeMoveset(player, MoveId.TRANSFORM); - game.move.changeMoveset(enemy, MoveId.MEMENTO); - - game.move.select(MoveId.TRANSFORM); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.toNextWave(); - - expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); - expect(game.scene.currentBattle.waveIndex).toBe(2); - - await game.reload.reloadSession(); - - const playerReloaded = game.scene.getPlayerPokemon()!; - const playerMoveset = player.getMoveset(); - - expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(playerReloaded.getAbility()).toBe(enemy.getAbility()); - expect(playerReloaded.getGender()).toBe(enemy.getGender()); - - expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); - for (const s of EFFECTIVE_STATS) { - expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false)); - } - - expect(playerMoveset.length).toEqual(1); - expect(playerMoveset[0]?.moveId).toEqual(MoveId.MEMENTO); - }); - - it("should stay transformed with the correct form after reload", async () => { - game.override.enemyMoveset([]).moveset([]).enemySpecies(SpeciesId.DARMANITAN); - - await game.classicMode.startBattle([SpeciesId.DITTO]); - - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; - - // change form - enemy.species.forms[1]; - enemy.species.formIndex = 1; - - game.move.changeMoveset(player, MoveId.TRANSFORM); - game.move.changeMoveset(enemy, MoveId.MEMENTO); - - game.move.select(MoveId.TRANSFORM); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.toNextWave(); - - expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); - expect(game.scene.currentBattle.waveIndex).toBe(2); - - await game.reload.reloadSession(); - - const playerReloaded = game.scene.getPlayerPokemon()!; - - expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); - expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); - }); -}); diff --git a/test/moves/trick_or_treat.test.ts b/test/moves/trick_or_treat.test.ts index 861115e2503..4e563c97ddc 100644 --- a/test/moves/trick_or_treat.test.ts +++ b/test/moves/trick_or_treat.test.ts @@ -1,8 +1,8 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { SpeciesId } from "#enums/species-id"; import { PokemonType } from "#enums/pokemon-type"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/triple_arrows.test.ts b/test/moves/triple_arrows.test.ts index 89ccb4e5b04..a60c7106a21 100644 --- a/test/moves/triple_arrows.test.ts +++ b/test/moves/triple_arrows.test.ts @@ -1,10 +1,9 @@ -import type { FlinchAttr, StatStageChangeAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/data-lists"; +import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import type Move from "#app/data/moves/move"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { FlinchAttr, Move, StatStageChangeAttr } from "#moves/move"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/u_turn.test.ts b/test/moves/u_turn.test.ts index a2e29899ad8..cf8b91511ca 100644 --- a/test/moves/u_turn.test.ts +++ b/test/moves/u_turn.test.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/moves/upper_hand.test.ts b/test/moves/upper_hand.test.ts index fe38e75cf9a..f03a84397f2 100644 --- a/test/moves/upper_hand.test.ts +++ b/test/moves/upper_hand.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { MoveResult } from "#enums/move-result"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/moves/whirlwind.test.ts b/test/moves/whirlwind.test.ts index 00d7c16561c..9def19c4668 100644 --- a/test/moves/whirlwind.test.ts +++ b/test/moves/whirlwind.test.ts @@ -1,19 +1,19 @@ +import { globalScene } from "#app/global-scene"; +import { Status } from "#data/status-effect"; +import { AbilityId } from "#enums/ability-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Challenges } from "#enums/challenges"; -import { PokemonType } from "#enums/pokemon-type"; -import { MoveResult } from "#enums/move-result"; -import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { TrainerType } from "#enums/trainer-type"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { Status } from "#app/data/status-effect"; -import { StatusEffect } from "#enums/status-effect"; -import { globalScene } from "#app/global-scene"; -import { BattlerIndex } from "#enums/battler-index"; -import { BattleType } from "#enums/battle-type"; -import { TrainerType } from "#enums/trainer-type"; describe("Moves - Whirlwind", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/wide_guard.test.ts b/test/moves/wide_guard.test.ts index 07c02158e94..7de1faea900 100644 --- a/test/moves/wide_guard.test.ts +++ b/test/moves/wide_guard.test.ts @@ -1,12 +1,11 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; +import { BerryPhase } from "#phases/berry-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Moves - Wide Guard", () => { let phaserGame: Phaser.Game; @@ -41,9 +40,6 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.WIDE_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -59,9 +55,6 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.WIDE_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -77,9 +70,6 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); game.move.select(MoveId.WIDE_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -96,9 +86,6 @@ describe("Moves - Wide Guard", () => { const enemyPokemon = game.scene.getEnemyField(); game.move.select(MoveId.WIDE_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(MoveId.SURF, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/test/moves/will_o_wisp.test.ts b/test/moves/will_o_wisp.test.ts index 336e84129c5..6f878e1198c 100644 --- a/test/moves/will_o_wisp.test.ts +++ b/test/moves/will_o_wisp.test.ts @@ -1,9 +1,9 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts index 6954d6212cc..6b6a570d318 100644 --- a/test/mystery-encounter/encounter-test-utils.ts +++ b/test/mystery-encounter/encounter-test-utils.ts @@ -1,24 +1,24 @@ -// biome-ignore lint/style/noNamespaceImport: Necessary for mocks -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { Status } from "#app/data/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; +import { Status } from "#data/status-effect"; +import { Button } from "#enums/buttons"; +import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +// biome-ignore lint/performance/noNamespaceImport: Necessary for mocks +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { CommandPhase } from "#phases/command-phase"; +import { MessagePhase } from "#phases/message-phase"; import { MysteryEncounterBattlePhase, MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase, -} from "#app/phases/mystery-encounter-phases"; -import { VictoryPhase } from "#app/phases/victory-phase"; -import type MessageUiHandler from "#app/ui/message-ui-handler"; -import type MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; -import type PartyUiHandler from "#app/ui/party-ui-handler"; -import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { isNullOrUndefined } from "#app/utils/common"; -import { Button } from "#enums/buttons"; -import { StatusEffect } from "#enums/status-effect"; -import type GameManager from "#test/testUtils/gameManager"; +} from "#phases/mystery-encounter-phases"; +import { VictoryPhase } from "#phases/victory-phase"; +import type { GameManager } from "#test/testUtils/gameManager"; +import type { MessageUiHandler } from "#ui/message-ui-handler"; +import type { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler"; +import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; +import type { PartyUiHandler } from "#ui/party-ui-handler"; +import { isNullOrUndefined } from "#utils/common"; import { expect, vi } from "vitest"; /** diff --git a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index 82cac197fe9..a2832052611 100644 --- a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -1,25 +1,25 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { EggTier } from "#enums/egg-type"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { ATrainersTestEncounter } from "#mystery-encounters/a-trainers-test-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { PartyHealPhase } from "#phases/party-heal-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { ATrainersTestEncounter } from "#app/data/mystery-encounters/encounters/a-trainers-test-encounter"; -import { EggTier } from "#enums/egg-type"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/aTrainersTest"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index b6c4e4d85fb..969ac812907 100644 --- a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -1,25 +1,25 @@ +import type { BattleScene } from "#app/battle-scene"; +import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { BerryModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; +import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { BerryModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { BerryType } from "#enums/berry-type"; -import { AbsoluteAvariceEncounter } from "#app/data/mystery-encounters/encounters/absolute-avarice-encounter"; -import { MoveId } from "#enums/move-id"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/absoluteAvarice"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 4f986f58b88..97326e89a83 100644 --- a/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -1,24 +1,24 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { AbilityId } from "#enums/ability-id"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { AnOfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter"; +import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { ShinyRateBoosterModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import { AnOfferYouCantRefuseEncounter } from "#mystery-encounters/an-offer-you-cant-refuse-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { MoveId } from "#enums/move-id"; -import { ShinyRateBoosterModifier } from "#app/modifier/modifier"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { AbilityId } from "#enums/ability-id"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/anOfferYouCantRefuse"; /** Gyarados for Indimidate */ diff --git a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index 8c2c6c608cd..5af89ebc02c 100644 --- a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -1,26 +1,26 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { AbilityId } from "#enums/ability-id"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { UiMode } from "#enums/ui-mode"; +import { BerryModifier } from "#modifiers/modifier"; +import { BerriesAboundEncounter } from "#mystery-encounters/berries-abound-encounter"; +import * as EncounterDialogueUtils from "#mystery-encounters/encounter-dialogue-utils"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { BerryModifier } from "#app/modifier/modifier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { BerriesAboundEncounter } from "#app/data/mystery-encounters/encounters/berries-abound-encounter"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import * as EncounterDialogueUtils from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { AbilityId } from "#enums/ability-id"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/berriesAbound"; const defaultParty = [SpeciesId.PYUKUMUKU, SpeciesId.MAGIKARP, SpeciesId.PIKACHU]; diff --git a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts index 4da8ff7f643..80f4c00c2a6 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -1,29 +1,29 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { TrainerType } from "#enums/trainer-type"; +import { UiMode } from "#enums/ui-mode"; +import { ContactHeldItemTransferChanceModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter"; +import * as encounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { UiMode } from "#enums/ui-mode"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { TrainerType } from "#enums/trainer-type"; -import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; -import { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; -import { CommandPhase } from "#app/phases/command-phase"; -import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounters/bug-type-superfan-encounter"; -import * as encounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/bugTypeSuperfan"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.WEEDLE]; diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 85193d1ec72..af99834d082 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -1,42 +1,42 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import * as BattleAnims from "#data/battle-anims"; +import { modifierTypes } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { Button } from "#enums/buttons"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import * as BattleAnims from "#app/data/battle-anims"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { TrainerType } from "#enums/trainer-type"; +import { UiMode } from "#enums/ui-mode"; +import type { Pokemon } from "#field/pokemon"; +import type { PokemonHeldItemModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { NewBattlePhase } from "#phases/new-battle-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { UiMode } from "#enums/ui-mode"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { ModifierTier } from "#enums/modifier-tier"; -import { ClowningAroundEncounter } from "#app/data/mystery-encounters/encounters/clowning-around-encounter"; -import { TrainerType } from "#enums/trainer-type"; -import { AbilityId } from "#enums/ability-id"; -import { PostMysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { Button } from "#enums/buttons"; -import type PartyUiHandler from "#app/ui/party-ui-handler"; -import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { BerryType } from "#enums/berry-type"; -import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { PokemonType } from "#enums/pokemon-type"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; +import type { PartyUiHandler } from "#ui/party-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/clowningAround"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index e47c7cc1a42..8ef499fe7f4 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -1,28 +1,28 @@ +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { UiMode } from "#enums/ui-mode"; +import { PokemonMove } from "#moves/pokemon-move"; +import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { LearnMovePhase } from "#phases/learn-move-phase"; +import { MovePhase } from "#phases/move-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { MoveId } from "#enums/move-id"; -import { DancingLessonsEncounter } from "#app/data/mystery-encounters/encounters/dancing-lessons-encounter"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/dancingLessons"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts index 3ef8431cc2c..0fb6d2d09b8 100644 --- a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts +++ b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts @@ -1,19 +1,11 @@ +import type { BattleScene } from "#app/battle-scene"; +import { modifierTypes } from "#data/data-lists"; +import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { - runMysteryEncounterToEnd, - runSelectMysteryEncounterOption, -} from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import type { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; import { BerryModifier, HealingBoosterModifier, @@ -23,11 +15,19 @@ import { PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier, -} from "#app/modifier/modifier"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes } from "#app/data/data-lists"; -import { BerryType } from "#enums/berry-type"; +} from "#modifiers/modifier"; +import { DelibirdyEncounter } from "#mystery-encounters/delibirdy-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; +import type { MoneyRequirement } from "#mystery-encounters/mystery-encounter-requirements"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { + runMysteryEncounterToEnd, + runSelectMysteryEncounterOption, +} from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/delibirdy"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index a82734d0c03..807526d2683 100644 --- a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -1,19 +1,19 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale-encounter"; -import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { DepartmentStoreSaleEncounter } from "#mystery-encounters/department-store-sale-encounter"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/departmentStoreSale"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/test/mystery-encounter/encounters/field-trip-encounter.test.ts index f4b3c52eb65..4390c80d255 100644 --- a/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -1,20 +1,20 @@ +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; +import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { FieldTripEncounter } from "#app/data/mystery-encounters/encounters/field-trip-encounter"; -import { MoveId } from "#enums/move-id"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { FieldTripEncounter } from "#mystery-encounters/field-trip-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/fieldTrip"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 16adc47ff11..1fb1cbfdbc8 100644 --- a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -1,35 +1,35 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import * as BattleAnims from "#data/battle-anims"; +import { Gender } from "#data/gender"; +import { Status } from "#data/status-effect"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; -import { Gender } from "#app/data/gender"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import * as BattleAnims from "#app/data/battle-anims"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { StatusEffect } from "#enums/status-effect"; +import { AttackTypeBoosterModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import { AttackTypeBoosterModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { PokemonType } from "#enums/pokemon-type"; -import { Status } from "#app/data/status-effect"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { AbilityId } from "#enums/ability-id"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; -import { StatusEffect } from "#enums/status-effect"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/fieryFallout"; /** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */ diff --git a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts index 28db869004c..588c126852f 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -1,27 +1,27 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { UiMode } from "#enums/ui-mode"; +import { PokemonMove } from "#moves/pokemon-move"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { FightOrFlightEncounter } from "#app/data/mystery-encounters/encounters/fight-or-flight-encounter"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/fightOrFlight"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index ae7e269ea00..43102cbc80d 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -1,30 +1,30 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { Command } from "#enums/command"; +import { MoveId } from "#enums/move-id"; +import { MoveUseMode } from "#enums/move-use-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { UiMode } from "#enums/ui-mode"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { FunAndGamesEncounter } from "#mystery-encounters/fun-and-games-encounter"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { Nature } from "#enums/nature"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter"; -import { MoveId } from "#enums/move-id"; -import { Command } from "#enums/command"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { MoveUseMode } from "#enums/move-use-mode"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/funAndGames"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; @@ -154,11 +154,11 @@ describe("Fun And Games! - Mystery Encounter", () => { // Turn 1 (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); - await game.phaseInterceptor.to(CommandPhase); + await game.toNextTurn(); // Turn 2 (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); - await game.phaseInterceptor.to(CommandPhase); + await game.toNextTurn(); // Turn 3 (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); diff --git a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts index bb598f4ae6e..2a959df76f2 100644 --- a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts +++ b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts @@ -1,24 +1,24 @@ +import type { BattleScene } from "#app/battle-scene"; +import { modifierTypes } from "#data/data-lists"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; +import { ModifierTier } from "#enums/modifier-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { PokemonNatureWeightModifier } from "#app/modifier/modifier"; -import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes } from "#app/data/data-lists"; -import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; -import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { ModifierTier } from "#enums/modifier-tier"; -import * as Utils from "#app/utils/common"; +import { PokemonNatureWeightModifier } from "#modifiers/modifier"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; +import { GlobalTradeSystemEncounter } from "#mystery-encounters/global-trade-system-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import * as Utils from "#utils/common"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/globalTradeSystem"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index a55806e5f48..2186fe43e23 100644 --- a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -1,20 +1,23 @@ -import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost-at-sea-encounter"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption } from "../encounter-test-utils"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { LostAtSeaEncounter } from "#mystery-encounters/lost-at-sea-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { PartyExpPhase } from "#phases/party-exp-phase"; +import { + runMysteryEncounterToEnd, + runSelectMysteryEncounterOption, +} from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/lostAtSea"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index 478648d88a7..ee952f3809e 100644 --- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -1,30 +1,29 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PartyMemberStrength } from "#enums/party-member-strength"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { UiMode } from "#enums/ui-mode"; +import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-challengers-encounter"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { CommandPhase } from "#phases/command-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { ModifierTier } from "#enums/modifier-tier"; -import { MysteriousChallengersEncounter } from "#app/data/mystery-encounters/encounters/mysterious-challengers-encounter"; -import { TrainerConfig } from "#app/data/trainers/trainer-config"; -import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { TrainerConfig } from "#trainers/trainer-config"; +import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/mysteriousChallengers"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts index d36b387cae1..1bbda8b77e0 100644 --- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -1,22 +1,22 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { PokemonMove } from "#moves/pokemon-move"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { PartTimerEncounter } from "#mystery-encounters/part-timer-encounter"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { PartTimerEncounter } from "#app/data/mystery-encounters/encounters/part-timer-encounter"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { MoveId } from "#enums/move-id"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; +import { GameManager } from "#test/testUtils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/partTimer"; // Pyukumuku for lowest speed, Regieleki for highest speed, Feebas for lowest "bulk", Melmetal for highest "bulk" diff --git a/test/mystery-encounter/encounters/safari-zone.test.ts b/test/mystery-encounter/encounters/safari-zone.test.ts index 7ac26f48e59..252198139df 100644 --- a/test/mystery-encounter/encounters/safari-zone.test.ts +++ b/test/mystery-encounter/encounters/safari-zone.test.ts @@ -1,25 +1,22 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { NON_LEGEND_PARADOX_POKEMON } from "#balance/special-species-groups"; import { BiomeId } from "#enums/biome-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { getSafariSpeciesSpawn, SafariZoneEncounter } from "#mystery-encounters/safari-zone-encounter"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { - getSafariSpeciesSpawn, - SafariZoneEncounter, -} from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/safariZone"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 2138298ee2b..6ecf3162607 100644 --- a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -1,24 +1,24 @@ -import type BattleScene from "#app/battle-scene"; -import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { AbilityId } from "#enums/ability-id"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import GameManager from "#test/testUtils/gameManager"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import i18next from "i18next"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts index c9d6f540191..471a26aa532 100644 --- a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts @@ -1,27 +1,27 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#balance/starters"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { EggTier } from "#enums/egg-type"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { TrainerType } from "#enums/trainer-type"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; -import { TrainerType } from "#enums/trainer-type"; -import { EggTier } from "#enums/egg-type"; -import { PostMysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/theExpertPokemonBreeder"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index 990e39014e2..81329c5552f 100644 --- a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -1,25 +1,25 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { NON_LEGEND_PARADOX_POKEMON } from "#balance/special-species-groups"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { + getSalesmanSpeciesOffer, + ThePokemonSalesmanEncounter, +} from "#mystery-encounters/the-pokemon-salesman-encounter"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; -import { - getSalesmanSpeciesOffer, - ThePokemonSalesmanEncounter, -} from "#app/data/mystery-encounters/encounters/the-pokemon-salesman-encounter"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/thePokemonSalesman"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 96060e4114c..9a78cd99f93 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -1,34 +1,34 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import * as BattleAnims from "#data/battle-anims"; +import { CustomPokemonData } from "#data/pokemon-data"; +import { AbilityId } from "#enums/ability-id"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import * as BattleAnims from "#app/data/battle-anims"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { UiMode } from "#enums/ui-mode"; +import { BerryModifier, PokemonBaseStatTotalModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; -import { Nature } from "#enums/nature"; -import { BerryType } from "#enums/berry-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { AbilityId } from "#enums/ability-id"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/theStrongStuff"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts index 012b88bcd73..0541826e569 100644 --- a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -1,31 +1,31 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { Status } from "#data/status-effect"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; -import { TrainerType } from "#enums/trainer-type"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Nature } from "#enums/nature"; -import { MoveId } from "#enums/move-id"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { TheWinstrateChallengeEncounter } from "#app/data/mystery-encounters/encounters/the-winstrate-challenge-encounter"; -import { Status } from "#app/data/status-effect"; -import { MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; +import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; +import { TrainerType } from "#enums/trainer-type"; +import { UiMode } from "#enums/ui-mode"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; +import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases"; +import { PartyHealPhase } from "#phases/party-heal-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { VictoryPhase } from "#phases/victory-phase"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; +import { GameManager } from "#test/testUtils/gameManager"; +import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/theWinstrateChallenge"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; @@ -372,6 +372,6 @@ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false) if (isFinalBattle) { await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); } else { - await game.phaseInterceptor.to(CommandPhase); + await game.toNextTurn(); } } diff --git a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts index 9ab5f16d1b9..48b6ce79488 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -1,37 +1,37 @@ -import type BattleScene from "#app/battle-scene"; -import * as BattleAnims from "#app/data/battle-anims"; -import { TrashToTreasureEncounter } from "#app/data/mystery-encounters/encounters/trash-to-treasure-encounter"; -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import type { BattleScene } from "#app/battle-scene"; +import * as BattleAnims from "#data/battle-anims"; +import { modifierTypes } from "#data/data-lists"; +import { BiomeId } from "#enums/biome-id"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { HealShopCostModifier, HitHealModifier, TurnHealModifier } from "#modifiers/modifier"; +import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; +import { PokemonMove } from "#moves/pokemon-move"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import { type EnemyPartyConfig, type EnemyPokemonConfig, generateModifierType, -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { SpeciesId } from "#enums/species-id"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { HealShopCostModifier, HitHealModifier, TurnHealModifier } from "#app/modifier/modifier"; -import { ModifierTier } from "#enums/modifier-tier"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import * as Utils from "#app/utils/common"; -import { MoveId } from "#enums/move-id"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +} from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import * as Utils from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/trashToTreasure"; diff --git a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts index ec64a17d291..a9ebfda496a 100644 --- a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts +++ b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts @@ -1,33 +1,33 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; +import { speciesEggMoves } from "#balance/egg-moves"; +import { modifierTypes } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { BerryType } from "#enums/berry-type"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { MoveId } from "#enums/move-id"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { Stat } from "#enums/stat"; +import type { BerryModifier } from "#modifiers/modifier"; +import { PokemonMove } from "#moves/pokemon-move"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { MovePhase } from "#phases/move-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; -import { MoveId } from "#enums/move-id"; -import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; -import { MovePhase } from "#app/phases/move-phase"; -import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { BerryType } from "#enums/berry-type"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import { Stat } from "#enums/stat"; -import type { BerryModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/data/data-lists"; -import { AbilityId } from "#enums/ability-id"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/uncommonBreed"; const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index 528aea2c734..89ae300392f 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -1,25 +1,25 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { ModifierTier } from "#enums/modifier-tier"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { UiMode } from "#enums/ui-mode"; +import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; +import * as EncounterTransformationSequence from "#mystery-encounters/encounter-transformation-sequence"; +import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; +import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter"; +import { CommandPhase } from "#phases/command-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; -import type BattleScene from "#app/battle-scene"; -import { UiMode } from "#enums/ui-mode"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { WeirdDreamEncounter } from "#app/data/mystery-encounters/encounters/weird-dream-encounter"; -import * as EncounterTransformationSequence from "#app/data/mystery-encounters/utils/encounter-transformation-sequence"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { ModifierTier } from "#enums/modifier-tier"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/weirdDream"; const defaultParty = [SpeciesId.MAGBY, SpeciesId.HAUNTER, SpeciesId.ABRA]; diff --git a/test/mystery-encounter/mystery-encounter-utils.test.ts b/test/mystery-encounter/mystery-encounter-utils.test.ts index b775ce8df60..745b11b7e73 100644 --- a/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -1,26 +1,26 @@ -import type BattleScene from "#app/battle-scene"; -import { speciesStarterCosts } from "#app/data/balance/starters"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; +import type { BattleScene } from "#app/battle-scene"; +import { speciesStarterCosts } from "#balance/starters"; +import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; import { getEncounterText, queueEncounterMessage, showEncounterDialogue, showEncounterText, -} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +} from "#mystery-encounters/encounter-dialogue-utils"; import { getHighestLevelPlayerPokemon, getLowestLevelPlayerPokemon, getRandomPlayerPokemon, getRandomSpeciesByStarterCost, koPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { PokemonType } from "#enums/pokemon-type"; -import { MessagePhase } from "#app/phases/message-phase"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; -import { StatusEffect } from "#enums/status-effect"; +} from "#mystery-encounters/encounter-pokemon-utils"; +import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; +import { MessagePhase } from "#phases/message-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/mystery-encounter/mystery-encounter.test.ts b/test/mystery-encounter/mystery-encounter.test.ts index be1f153f8b1..c8ac7078788 100644 --- a/test/mystery-encounter/mystery-encounter.test.ts +++ b/test/mystery-encounter/mystery-encounter.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeAll, beforeEach, expect, describe, it } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { SpeciesId } from "#enums/species-id"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; +import type { BattleScene } from "#app/battle-scene"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import type BattleScene from "#app/battle-scene"; +import { SpeciesId } from "#enums/species-id"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Mystery Encounters", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/form-change-phase.test.ts b/test/phases/form-change-phase.test.ts index b025f72f5e4..4d3af280043 100644 --- a/test/phases/form-change-phase.test.ts +++ b/test/phases/form-change-phase.test.ts @@ -1,12 +1,12 @@ +import { modifierTypes } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; +import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { PokemonType } from "#enums/pokemon-type"; -import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes } from "#app/data/data-lists"; describe("Form Change Phase", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/frenzy-move-reset.test.ts b/test/phases/frenzy-move-reset.test.ts index 9c59e1da968..c922a121526 100644 --- a/test/phases/frenzy-move-reset.test.ts +++ b/test/phases/frenzy-move-reset.test.ts @@ -1,12 +1,12 @@ -import { BattlerIndex } from "#enums/battler-index"; import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatusEffect } from "#enums/status-effect"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Frenzy Move Reset", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/game-over-phase.test.ts b/test/phases/game-over-phase.test.ts index 008f9fb68e8..d679b812fc0 100644 --- a/test/phases/game-over-phase.test.ts +++ b/test/phases/game-over-phase.test.ts @@ -1,12 +1,12 @@ -import { BiomeId } from "#enums/biome-id"; import { AbilityId } from "#enums/ability-id"; +import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { Unlockables } from "#enums/unlockables"; +import { achvs } from "#system/achv"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { achvs } from "#app/system/achv"; -import { Unlockables } from "#enums/unlockables"; describe("Game Over Phase", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/learn-move-phase.test.ts b/test/phases/learn-move-phase.test.ts index 88b8187069b..b7e7175a46c 100644 --- a/test/phases/learn-move-phase.test.ts +++ b/test/phases/learn-move-phase.test.ts @@ -1,11 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { SpeciesId } from "#enums/species-id"; +import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import { Button } from "#app/enums/buttons"; +import { LearnMovePhase } from "#phases/learn-move-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Learn Move Phase", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/mystery-encounter-phase.test.ts b/test/phases/mystery-encounter-phase.test.ts index b17682d6c74..3c41028e4b7 100644 --- a/test/phases/mystery-encounter-phase.test.ts +++ b/test/phases/mystery-encounter-phase.test.ts @@ -1,15 +1,15 @@ -import { afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vitest"; -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { SpeciesId } from "#enums/species-id"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { UiMode } from "#enums/ui-mode"; import { Button } from "#enums/buttons"; -import type MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import type MessageUiHandler from "#app/ui/message-ui-handler"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { MessageUiHandler } from "#ui/message-ui-handler"; +import type { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler"; import i18next from "i18next"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Mystery Encounter Phases", () => { let phaserGame: Phaser.Game; diff --git a/test/phases/phases.test.ts b/test/phases/phases.test.ts index 9d1c1804615..daa63fe2adc 100644 --- a/test/phases/phases.test.ts +++ b/test/phases/phases.test.ts @@ -1,9 +1,9 @@ -import type BattleScene from "#app/battle-scene"; -import { LoginPhase } from "#app/phases/login-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { UnavailablePhase } from "#app/phases/unavailable-phase"; +import type { BattleScene } from "#app/battle-scene"; import { UiMode } from "#enums/ui-mode"; -import GameManager from "#test/testUtils/gameManager"; +import { LoginPhase } from "#phases/login-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { UnavailablePhase } from "#phases/unavailable-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/phases/select-modifier-phase.test.ts b/test/phases/select-modifier-phase.test.ts index b6c3089e236..565fbe8e439 100644 --- a/test/phases/select-modifier-phase.test.ts +++ b/test/phases/select-modifier-phase.test.ts @@ -1,20 +1,20 @@ -import type BattleScene from "#app/battle-scene"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { PlayerPokemon } from "#app/field/pokemon"; -import { ModifierTier } from "#enums/modifier-tier"; -import type { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { ModifierTypeOption } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; -import { shiftCharCodes } from "#app/utils/common"; +import type { BattleScene } from "#app/battle-scene"; +import { modifierTypes } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { Button } from "#enums/buttons"; +import { ModifierTier } from "#enums/modifier-tier"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { PlayerPokemon } from "#field/pokemon"; +import type { CustomModifierSettings } from "#modifiers/modifier-type"; +import { ModifierTypeOption } from "#modifiers/modifier-type"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { shiftCharCodes } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/plugins/api/pokerogue-account-api.test.ts b/test/plugins/api/pokerogue-account-api.test.ts index 3c37451960a..578b6ba041a 100644 --- a/test/plugins/api/pokerogue-account-api.test.ts +++ b/test/plugins/api/pokerogue-account-api.test.ts @@ -1,13 +1,13 @@ -import type { AccountInfoResponse } from "#app/@types/PokerogueAccountApi"; +import { PokerogueAccountApi } from "#api/pokerogue-account-api"; import { SESSION_ID_COOKIE_NAME } from "#app/constants"; -import { PokerogueAccountApi } from "#app/plugins/api/pokerogue-account-api"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import * as CookieUtils from "#app/utils/cookies"; -import * as cookies from "#app/utils/cookies"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; +import type { AccountInfoResponse } from "#types/PokerogueAccountApi"; +import * as CookieUtils from "#utils/cookies"; +import * as cookies from "#utils/cookies"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const accountApi = new PokerogueAccountApi(apiBase); diff --git a/test/plugins/api/pokerogue-admin-api.test.ts b/test/plugins/api/pokerogue-admin-api.test.ts index 08c4cf0dc45..e36f45801b6 100644 --- a/test/plugins/api/pokerogue-admin-api.test.ts +++ b/test/plugins/api/pokerogue-admin-api.test.ts @@ -1,3 +1,6 @@ +import { PokerogueAdminApi } from "#api/pokerogue-admin-api"; +import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; import type { LinkAccountToDiscordIdRequest, LinkAccountToGoogledIdRequest, @@ -5,13 +8,10 @@ import type { SearchAccountResponse, UnlinkAccountFromDiscordIdRequest, UnlinkAccountFromGoogledIdRequest, -} from "#app/@types/PokerogueAdminApi"; -import { PokerogueAdminApi } from "#app/plugins/api/pokerogue-admin-api"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +} from "#types/PokerogueAdminApi"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const adminApi = new PokerogueAdminApi(apiBase); diff --git a/test/plugins/api/pokerogue-api.test.ts b/test/plugins/api/pokerogue-api.test.ts index c53a38e23ab..b9ebbe0f685 100644 --- a/test/plugins/api/pokerogue-api.test.ts +++ b/test/plugins/api/pokerogue-api.test.ts @@ -1,10 +1,10 @@ -import type { TitleStatsResponse } from "#app/@types/PokerogueApi"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { pokerogueApi } from "#api/pokerogue-api"; import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; +import type { TitleStatsResponse } from "#types/PokerogueApi"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); let server: SetupServerApi; diff --git a/test/plugins/api/pokerogue-daily-api.test.ts b/test/plugins/api/pokerogue-daily-api.test.ts index 563e6d09009..6399c80781e 100644 --- a/test/plugins/api/pokerogue-daily-api.test.ts +++ b/test/plugins/api/pokerogue-daily-api.test.ts @@ -1,11 +1,11 @@ -import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#app/@types/PokerogueDailyApi"; -import { PokerogueDailyApi } from "#app/plugins/api/pokerogue-daily-api"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { ScoreboardCategory, type RankingEntry } from "#app/ui/daily-run-scoreboard"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { PokerogueDailyApi } from "#api/pokerogue-daily-api"; import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; +import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/PokerogueDailyApi"; +import { type RankingEntry, ScoreboardCategory } from "#ui/daily-run-scoreboard"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const dailyApi = new PokerogueDailyApi(apiBase); diff --git a/test/plugins/api/pokerogue-savedata-api.test.ts b/test/plugins/api/pokerogue-savedata-api.test.ts index 481ba62c19d..d465759bdfc 100644 --- a/test/plugins/api/pokerogue-savedata-api.test.ts +++ b/test/plugins/api/pokerogue-savedata-api.test.ts @@ -1,10 +1,10 @@ -import type { UpdateAllSavedataRequest } from "#app/@types/PokerogueSavedataApi"; -import { PokerogueSavedataApi } from "#app/plugins/api/pokerogue-savedata-api"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { PokerogueSavedataApi } from "#api/pokerogue-savedata-api"; import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; +import type { UpdateAllSavedataRequest } from "#types/PokerogueSavedataApi"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const savedataApi = new PokerogueSavedataApi(apiBase); diff --git a/test/plugins/api/pokerogue-session-savedata-api.test.ts b/test/plugins/api/pokerogue-session-savedata-api.test.ts index 4d4774f2283..e95697ab72e 100644 --- a/test/plugins/api/pokerogue-session-savedata-api.test.ts +++ b/test/plugins/api/pokerogue-session-savedata-api.test.ts @@ -1,3 +1,7 @@ +import { PokerogueSessionSavedataApi } from "#api/pokerogue-session-savedata-api"; +import type { SessionSaveData } from "#system/game-data"; +import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; import type { ClearSessionSavedataRequest, ClearSessionSavedataResponse, @@ -5,14 +9,10 @@ import type { GetSessionSavedataRequest, NewClearSessionSavedataRequest, UpdateSessionSavedataRequest, -} from "#app/@types/PokerogueSessionSavedataApi"; -import { PokerogueSessionSavedataApi } from "#app/plugins/api/pokerogue-session-savedata-api"; -import type { SessionSaveData } from "#app/system/game-data"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { http, HttpResponse } from "msw"; -import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +} from "#types/PokerogueSessionSavedataApi"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const sessionSavedataApi = new PokerogueSessionSavedataApi(apiBase); diff --git a/test/plugins/api/pokerogue-system-savedata-api.test.ts b/test/plugins/api/pokerogue-system-savedata-api.test.ts index 0c69ab8f922..0bec6dae16b 100644 --- a/test/plugins/api/pokerogue-system-savedata-api.test.ts +++ b/test/plugins/api/pokerogue-system-savedata-api.test.ts @@ -1,16 +1,16 @@ +import { PokerogueSystemSavedataApi } from "#api/pokerogue-system-savedata-api"; +import type { SystemSaveData } from "#system/game-data"; +import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; +import { getApiBaseUrl } from "#test/testUtils/testUtils"; import type { GetSystemSavedataRequest, UpdateSystemSavedataRequest, VerifySystemSavedataRequest, VerifySystemSavedataResponse, -} from "#app/@types/PokerogueSystemSavedataApi"; -import { PokerogueSystemSavedataApi } from "#app/plugins/api/pokerogue-system-savedata-api"; -import type { SystemSaveData } from "#app/system/game-data"; -import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; -import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import { http, HttpResponse } from "msw"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +} from "#types/PokerogueSystemSavedataApi"; +import { HttpResponse, http } from "msw"; import type { SetupServerApi } from "msw/node"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const apiBase = getApiBaseUrl(); const systemSavedataApi = new PokerogueSystemSavedataApi(getApiBaseUrl()); diff --git a/test/reload.test.ts b/test/reload.test.ts index 8e39df23f47..a1cdfcb392a 100644 --- a/test/reload.test.ts +++ b/test/reload.test.ts @@ -1,13 +1,13 @@ -import { GameModes } from "#enums/game-modes"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; +import { pokerogueApi } from "#api/pokerogue-api"; import { BiomeId } from "#enums/biome-id"; import { Button } from "#enums/buttons"; +import { GameModes } from "#enums/game-modes"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { GameManager } from "#test/testUtils/gameManager"; import type { MockClock } from "#test/testUtils/mocks/mockClock"; +import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Reload", () => { diff --git a/test/settingMenu/helpers/inGameManip.ts b/test/settingMenu/helpers/inGameManip.ts index ca1fac3bc65..c19c6745300 100644 --- a/test/settingMenu/helpers/inGameManip.ts +++ b/test/settingMenu/helpers/inGameManip.ts @@ -1,5 +1,5 @@ -import { getIconForLatestInput, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { getIconForLatestInput, getSettingNameWithKeycode } from "#inputs/configHandler"; +import { SettingKeyboard } from "#system/settings-keyboard"; import { expect } from "vitest"; export class InGameManip { diff --git a/test/settingMenu/helpers/menuManip.ts b/test/settingMenu/helpers/menuManip.ts index 1ddd842c864..37c0f823559 100644 --- a/test/settingMenu/helpers/menuManip.ts +++ b/test/settingMenu/helpers/menuManip.ts @@ -9,8 +9,8 @@ import { getKeyWithKeycode, getKeyWithSettingName, getSettingNameWithKeycode, -} from "#app/configs/inputs/configHandler"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +} from "#inputs/configHandler"; +import { SettingKeyboard } from "#system/settings-keyboard"; import { expect } from "vitest"; export class MenuManip { diff --git a/test/settingMenu/rebinding_setting.test.ts b/test/settingMenu/rebinding_setting.test.ts index 20a1fe51484..b738861c1ee 100644 --- a/test/settingMenu/rebinding_setting.test.ts +++ b/test/settingMenu/rebinding_setting.test.ts @@ -1,12 +1,12 @@ -import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; -import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; import type { InterfaceConfig } from "#app/inputs-controller"; -import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { deepCopy } from "#app/utils/data"; import { Button } from "#enums/buttons"; import { Device } from "#enums/devices"; +import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty"; +import { getKeyWithKeycode, getKeyWithSettingName } from "#inputs/configHandler"; +import { SettingKeyboard } from "#system/settings-keyboard"; import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; import { MenuManip } from "#test/settingMenu/helpers/menuManip"; +import { deepCopy } from "#utils/data"; import { beforeEach, describe, expect, it } from "vitest"; describe("Test Rebinding", () => { diff --git a/test/sprites/pokemonSprite.test.ts b/test/sprites/pokemonSprite.test.ts index a008b75b42e..019dc51a8e2 100644 --- a/test/sprites/pokemonSprite.test.ts +++ b/test/sprites/pokemonSprite.test.ts @@ -2,8 +2,8 @@ import { getAppRootDir } from "#test/sprites/spritesUtils"; import fs from "fs"; import path from "path"; import { beforeAll, describe, expect, it } from "vitest"; -import _masterlist from "../../public/images/pokemon/variant/_masterlist.json"; import _exp_masterlist from "../../public/images/pokemon/variant/_exp_masterlist.json"; +import _masterlist from "../../public/images/pokemon/variant/_masterlist.json"; type PokemonVariantMasterlist = typeof _masterlist; type PokemonExpVariantMasterlist = typeof _exp_masterlist; @@ -26,9 +26,9 @@ describe("check if every variant's sprite are correctly set", () => { femaleVariant = masterlist.female; backVariant = masterlist.back; - // @ts-ignore + // @ts-expect-error delete masterlist.female; // TODO: resolve ts-ignore - //@ts-ignore + //@ts-expect-error delete masterlist.back; //TODO: resolve ts-ignore }); diff --git a/test/system/game_data.test.ts b/test/system/game_data.test.ts index b9011cd934f..5daed0b2f1b 100644 --- a/test/system/game_data.test.ts +++ b/test/system/game_data.test.ts @@ -1,12 +1,12 @@ +import { pokerogueApi } from "#api/pokerogue-api"; +import * as account from "#app/account"; import * as bypassLoginModule from "#app/global-vars/bypass-login"; -import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import type { SessionSaveData } from "#app/system/game-data"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import type { SessionSaveData } from "#system/game-data"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as account from "#app/account"; describe("System - Game Data", () => { let phaserGame: Phaser.Game; diff --git a/test/testUtils/TextInterceptor.ts b/test/testUtils/TextInterceptor.ts index 4aaed458e44..36a5db4c78d 100644 --- a/test/testUtils/TextInterceptor.ts +++ b/test/testUtils/TextInterceptor.ts @@ -1,7 +1,7 @@ /** * Class will intercept any text or dialogue message calls and log them for test purposes */ -export default class TextInterceptor { +export class TextInterceptor { private scene; public logs: string[] = []; constructor(scene) { diff --git a/test/testUtils/errorInterceptor.ts b/test/testUtils/errorInterceptor.ts index a8fb3284b78..c253561a71c 100644 --- a/test/testUtils/errorInterceptor.ts +++ b/test/testUtils/errorInterceptor.ts @@ -1,4 +1,4 @@ -export default class ErrorInterceptor { +export class ErrorInterceptor { private static instance: ErrorInterceptor; public running; diff --git a/test/testUtils/gameManager.ts b/test/testUtils/gameManager.ts index b9f499d4e0c..b6d0da49902 100644 --- a/test/testUtils/gameManager.ts +++ b/test/testUtils/gameManager.ts @@ -1,46 +1,38 @@ import { updateUserInfo } from "#app/account"; -import { BattlerIndex } from "#enums/battler-index"; -import BattleScene from "#app/battle-scene"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import Trainer from "#app/field/trainer"; +import { BattleScene } from "#app/battle-scene"; import { getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; import { globalScene } from "#app/global-scene"; -import { ModifierTypeOption } from "#app/modifier/modifier-type"; -import { modifierTypes } from "#app/data/data-lists"; import overrides from "#app/overrides"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { LoginPhase } from "#app/phases/login-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import type { SelectTargetPhase } from "#app/phases/select-target-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; -import type BallUiHandler from "#app/ui/ball-ui-handler"; -import type BattleMessageUiHandler from "#app/ui/battle-message-ui-handler"; -import type CommandUiHandler from "#app/ui/command-ui-handler"; -import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import type PartyUiHandler from "#app/ui/party-ui-handler"; -import type StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import type TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; -import { isNullOrUndefined } from "#app/utils/common"; +import { modifierTypes } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; import { Button } from "#enums/buttons"; import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { ExpNotification } from "#enums/exp-notification"; +import { GameModes } from "#enums/game-modes"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PlayerGender } from "#enums/player-gender"; +import type { PokeballType } from "#enums/pokeball"; import type { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import ErrorInterceptor from "#test/testUtils/errorInterceptor"; +import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; +import { Trainer } from "#field/trainer"; +import { ModifierTypeOption } from "#modifiers/modifier-type"; +import { CheckSwitchPhase } from "#phases/check-switch-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { LoginPhase } from "#phases/login-phase"; +import { MovePhase } from "#phases/move-phase"; +import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; +import { NewBattlePhase } from "#phases/new-battle-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import type { SelectTargetPhase } from "#phases/select-target-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { ErrorInterceptor } from "#test/testUtils/errorInterceptor"; import { generateStarter, waitUntil } from "#test/testUtils/gameManagerUtils"; -import GameWrapper from "#test/testUtils/gameWrapper"; +import { GameWrapper } from "#test/testUtils/gameWrapper"; import { ChallengeModeHelper } from "#test/testUtils/helpers/challengeModeHelper"; import { ClassicModeHelper } from "#test/testUtils/helpers/classicModeHelper"; import { DailyModeHelper } from "#test/testUtils/helpers/dailyModeHelper"; @@ -50,18 +42,26 @@ import { MoveHelper } from "#test/testUtils/helpers/moveHelper"; import { OverridesHelper } from "#test/testUtils/helpers/overridesHelper"; import { ReloadHelper } from "#test/testUtils/helpers/reloadHelper"; import { SettingsHelper } from "#test/testUtils/helpers/settingsHelper"; -import type InputsHandler from "#test/testUtils/inputsHandler"; +import type { InputsHandler } from "#test/testUtils/inputsHandler"; import { MockFetch } from "#test/testUtils/mocks/mockFetch"; -import PhaseInterceptor from "#test/testUtils/phaseInterceptor"; -import TextInterceptor from "#test/testUtils/TextInterceptor"; -import { AES, enc } from "crypto-js"; +import { PhaseInterceptor } from "#test/testUtils/phaseInterceptor"; +import { TextInterceptor } from "#test/testUtils/TextInterceptor"; +import type { BallUiHandler } from "#ui/ball-ui-handler"; +import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler"; +import type { CommandUiHandler } from "#ui/command-ui-handler"; +import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import type { PartyUiHandler } from "#ui/party-ui-handler"; +import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler"; +import type { TargetSelectUiHandler } from "#ui/target-select-ui-handler"; +import { isNullOrUndefined } from "#utils/common"; import fs from "node:fs"; +import { AES, enc } from "crypto-js"; import { expect, vi } from "vitest"; /** * Class to manage the game state and transitions between phases. */ -export default class GameManager { +export class GameManager { public gameWrapper: GameWrapper; public scene: BattleScene; public phaseInterceptor: PhaseInterceptor; @@ -201,12 +201,10 @@ export default class GameManager { /** * Helper function to run to the final boss encounter as it's a bit tricky due to extra dialogue * Also handles Major/Minor bosses from endless modes - * @param game - The game manager - * @param species - * @param mode + * @param species - Array of {@linkcode SpeciesId}s to start the final battle with. + * @param mode - The {@linkcode GameModes} to spawn the final boss encounter in. */ async runToFinalBossEncounter(species: SpeciesId[], mode: GameModes) { - console.log("===to final boss encounter==="); await this.runToTitle(); this.onNextPrompt("TitlePhase", UiMode.TITLE, () => { @@ -224,15 +222,15 @@ export default class GameManager { this.removeEnemyHeldItems(); } - await this.phaseInterceptor.to(EncounterPhase); - console.log("===finished run to final boss encounter==="); + await this.phaseInterceptor.to("CommandPhase", false); + console.log("==================[Final Boss Encounter]=================="); } /** * Runs the game to a mystery encounter phase. - * @param encounterType if specified, will expect encounter to have been spawned - * @param species Optional array of species for party. - * @returns A promise that resolves when the EncounterPhase ends. + * @param encounterType - If specified, will expect encounter to be the given type. + * @param species - Optional array of species for party to start with. + * @returns A Promise that resolves when the EncounterPhase ends. */ async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: SpeciesId[]) { if (!isNullOrUndefined(encounterType)) { @@ -277,6 +275,7 @@ export default class GameManager { * Will trigger during the next {@linkcode SelectTargetPhase} * @param targetIndex - The {@linkcode BattlerIndex} of the attack target, or `undefined` for multi-target attacks * @param movePosition - The 0-indexed position of the move in the pokemon's moveset array + * @throws Immediately fails tests */ selectTarget(movePosition: number, targetIndex?: BattlerIndex) { this.onNextPrompt( @@ -292,7 +291,7 @@ export default class GameManager { handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); } if (move.isMultiTarget() && targetIndex !== undefined) { - throw new Error(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); + expect.fail(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); } handler.processInput(Button.ACTION); }, @@ -452,17 +451,14 @@ export default class GameManager { } /** - * Faints a player or enemy pokemon instantly by setting their HP to 0. + * Faint a player or enemy pokemon instantly by setting their HP to 0. * @param pokemon - The player/enemy pokemon being fainted - * @returns A promise that resolves once the fainted pokemon's FaintPhase finishes running. + * @returns A Promise that resolves once the fainted pokemon's FaintPhase finishes running. */ async killPokemon(pokemon: PlayerPokemon | EnemyPokemon) { - return new Promise(async (resolve, reject) => { - pokemon.hp = 0; - this.scene.phaseManager.pushPhase(new FaintPhase(pokemon.getBattlerIndex(), true)); - await this.phaseInterceptor.to(FaintPhase).catch(e => reject(e)); - resolve(); - }); + pokemon.hp = 0; + this.scene.phaseManager.pushNew("FaintPhase", pokemon.getBattlerIndex(), true); + await this.phaseInterceptor.to("FaintPhase"); } /** @@ -511,9 +507,9 @@ export default class GameManager { /** * Select the BALL option from the command menu, then press Action; in the BALL * menu, select a pokéball type and press Action again to throw it. - * @param ballIndex - The index of the pokeball to throw + * @param ballIndex - The {@linkcode PokeballType} to throw */ - public doThrowPokeball(ballIndex: number) { + public doThrowPokeball(ballIndex: PokeballType) { this.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { (this.scene.ui.getHandler() as CommandUiHandler).setCursor(1); (this.scene.ui.getHandler() as CommandUiHandler).processInput(Button.ACTION); diff --git a/test/testUtils/gameManagerUtils.ts b/test/testUtils/gameManagerUtils.ts index 57fd9b91d26..db758cfe64d 100644 --- a/test/testUtils/gameManagerUtils.ts +++ b/test/testUtils/gameManagerUtils.ts @@ -1,17 +1,17 @@ -import Battle from "#app/battle"; -import { BattleType } from "#enums/battle-type"; -import type BattleScene from "#app/battle-scene"; -import { getDailyRunStarters } from "#app/data/daily-run"; -import { Gender } from "#app/data/gender"; -import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { PlayerPokemon } from "#app/field/pokemon"; +import { Battle } from "#app/battle"; +import type { BattleScene } from "#app/battle-scene"; import { getGameMode } from "#app/game-mode"; +import { getDailyRunStarters } from "#data/daily-run"; +import { Gender } from "#data/gender"; +import { getPokemonSpeciesForm } from "#data/pokemon-species"; +import { BattleType } from "#enums/battle-type"; import { GameModes } from "#enums/game-modes"; -import type { StarterMoveset } from "#app/system/game-data"; -import type { Starter } from "#app/ui/starter-select-ui-handler"; -import { MoveId } from "#enums/move-id"; +import type { MoveId } from "#enums/move-id"; import type { SpeciesId } from "#enums/species-id"; +import { PlayerPokemon } from "#field/pokemon"; +import type { StarterMoveset } from "#system/game-data"; +import type { Starter } from "#ui/starter-select-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; /** Function to convert Blob to string */ export function blobToString(blob) { @@ -98,15 +98,6 @@ export function waitUntil(truth): Promise { }); } -/** Get the index of `move` from the moveset of the pokemon on the player's field at location `pokemonIndex`. */ -export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: MoveId): number { - const playerPokemon = scene.getPlayerField()[pokemonIndex]; - const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp()); - console.log(`Move position for ${MoveId[move]} (=${move}):`, index); - return index; -} - /** * Useful for populating party, wave index, etc. without having to spin up and run through an entire EncounterPhase */ diff --git a/test/testUtils/gameWrapper.ts b/test/testUtils/gameWrapper.ts index 9264b68d421..b9665ffe071 100644 --- a/test/testUtils/gameWrapper.ts +++ b/test/testUtils/gameWrapper.ts @@ -1,30 +1,32 @@ // @ts-nocheck - TODO: remove this -import BattleScene from "#app/battle-scene"; -import { MoveAnim } from "#app/data/battle-anims"; -import Pokemon from "#app/field/pokemon"; -import { sessionIdKey } from "#app/utils/common"; -import { setCookie } from "#app/utils/cookies"; + +import { BattleScene } from "#app/battle-scene"; +// biome-ignore lint/performance/noNamespaceImport: Necessary in order to mock the var +import * as bypassLoginModule from "#app/global-vars/bypass-login"; +import { MoveAnim } from "#data/battle-anims"; +import { Pokemon } from "#field/pokemon"; +import { version } from "#package.json"; import { blobToString } from "#test/testUtils/gameManagerUtils"; import { MockClock } from "#test/testUtils/mocks/mockClock"; import { MockFetch } from "#test/testUtils/mocks/mockFetch"; -import MockLoader from "#test/testUtils/mocks/mockLoader"; -import MockTextureManager from "#test/testUtils/mocks/mockTextureManager"; +import { MockGameObjectCreator } from "#test/testUtils/mocks/mockGameObjectCreator"; +import { MockLoader } from "#test/testUtils/mocks/mockLoader"; +import { MockContainer } from "#test/testUtils/mocks/mocksContainer/mockContainer"; +import { MockTextureManager } from "#test/testUtils/mocks/mockTextureManager"; +import { MockTimedEventManager } from "#test/testUtils/mocks/mockTimedEventManager"; +import { PokedexMonContainer } from "#ui/pokedex-mon-container"; +import { sessionIdKey } from "#utils/common"; +import { setCookie } from "#utils/cookies"; import fs from "node:fs"; import Phaser from "phaser"; import { vi } from "vitest"; -import { version } from "../../package.json"; -import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; -import { MockTimedEventManager } from "./mocks/mockTimedEventManager"; -import InputManager = Phaser.Input.InputManager; -import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; -import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; -import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; -import EventEmitter = Phaser.Events.EventEmitter; -import UpdateList = Phaser.GameObjects.UpdateList; -import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; -import MockContainer from "./mocks/mocksContainer/mockContainer"; -// biome-ignore lint/style/noNamespaceImport: Necessary in order to mock the var -import * as bypassLoginModule from "#app/global-vars/bypass-login"; + +const InputManager = Phaser.Input.InputManager; +const KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; +const KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; +const GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; +const EventEmitter = Phaser.Events.EventEmitter; +const UpdateList = Phaser.GameObjects.UpdateList; window.URL.createObjectURL = (blob: Blob) => { blobToString(blob).then((data: string) => { @@ -40,7 +42,7 @@ window.matchMedia = () => ({ matches: false, }); -export default class GameWrapper { +export class GameWrapper { public game: Phaser.Game; public scene: BattleScene; @@ -122,15 +124,20 @@ export default class GameWrapper { }, }; + // TODO: Replace this with a proper mock of phaser's TweenManager. this.scene.tweens = { add: data => { - if (data.onComplete) { - data.onComplete(); - } + // TODO: our mock of `add` should have the same signature as the real one, which returns the tween + data.onComplete?.(); }, getTweensOf: () => [], killTweensOf: () => [], - chain: () => null, + + chain: data => { + // TODO: our mock of `chain` should have the same signature as the real one, which returns the chain + data?.tweens?.forEach(tween => tween.onComplete?.()); + data.onComplete?.(); + }, addCounter: data => { if (data.onComplete) { data.onComplete(); diff --git a/test/testUtils/helpers/challengeModeHelper.ts b/test/testUtils/helpers/challengeModeHelper.ts index f0b4b151d22..31b7c062d99 100644 --- a/test/testUtils/helpers/challengeModeHelper.ts +++ b/test/testUtils/helpers/challengeModeHelper.ts @@ -1,15 +1,15 @@ -import { BattleStyle } from "#app/enums/battle-style"; -import type { SpeciesId } from "#enums/species-id"; import overrides from "#app/overrides"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { UiMode } from "#enums/ui-mode"; -import { generateStarter } from "../gameManagerUtils"; -import { GameManagerHelper } from "./gameManagerHelper"; -import type { Challenge } from "#app/data/challenge"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import type { Challenge } from "#data/challenge"; +import { BattleStyle } from "#enums/battle-style"; import type { Challenges } from "#enums/challenges"; +import type { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { CommandPhase } from "#phases/command-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { generateStarter } from "#test/testUtils/gameManagerUtils"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { copyChallenge } from "data/challenge"; /** diff --git a/test/testUtils/helpers/classicModeHelper.ts b/test/testUtils/helpers/classicModeHelper.ts index eff97483777..55b25623b5a 100644 --- a/test/testUtils/helpers/classicModeHelper.ts +++ b/test/testUtils/helpers/classicModeHelper.ts @@ -1,15 +1,16 @@ -import { BattleStyle } from "#app/enums/battle-style"; -import type { SpeciesId } from "#enums/species-id"; import { getGameMode } from "#app/game-mode"; -import { GameModes } from "#enums/game-modes"; import overrides from "#app/overrides"; -import { CommandPhase } from "#app/phases/command-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { BattleStyle } from "#enums/battle-style"; +import { GameModes } from "#enums/game-modes"; +import { Nature } from "#enums/nature"; +import type { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import { generateStarter } from "../gameManagerUtils"; -import { GameManagerHelper } from "./gameManagerHelper"; +import { CommandPhase } from "#phases/command-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { generateStarter } from "#test/testUtils/gameManagerUtils"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; /** * Helper to handle classic-mode specific operations. @@ -36,6 +37,12 @@ export class ClassicModeHelper extends GameManagerHelper { if (this.game.override.disableShinies) { this.game.override.shiny(false).enemyShiny(false); } + if (this.game.override.normalizeIVs) { + this.game.override.playerIVs(31).enemyIVs(31); + } + if (this.game.override.normalizeNatures) { + this.game.override.nature(Nature.HARDY).enemyNature(Nature.HARDY); + } this.game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { this.game.scene.gameMode = getGameMode(GameModes.CLASSIC); diff --git a/test/testUtils/helpers/dailyModeHelper.ts b/test/testUtils/helpers/dailyModeHelper.ts index 4672d4dc787..214b6e4c5a4 100644 --- a/test/testUtils/helpers/dailyModeHelper.ts +++ b/test/testUtils/helpers/dailyModeHelper.ts @@ -1,13 +1,13 @@ -import { BattleStyle } from "#app/enums/battle-style"; -import { Button } from "#app/enums/buttons"; import overrides from "#app/overrides"; -import { CommandPhase } from "#app/phases/command-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import type SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; +import { BattleStyle } from "#enums/battle-style"; +import { Button } from "#enums/buttons"; import { UiMode } from "#enums/ui-mode"; -import { GameManagerHelper } from "./gameManagerHelper"; +import { CommandPhase } from "#phases/command-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; +import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler"; /** * Helper to handle daily mode specifics diff --git a/test/testUtils/helpers/field-helper.ts b/test/testUtils/helpers/field-helper.ts index 2866c01209f..ca106804b85 100644 --- a/test/testUtils/helpers/field-helper.ts +++ b/test/testUtils/helpers/field-helper.ts @@ -1,16 +1,14 @@ -// -- start tsdoc imports -- -// biome-ignore lint/correctness/noUnusedImports: TSDoc import +/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ import type { globalScene } from "#app/global-scene"; -// -- end tsdoc imports -- +/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */ -import type { BattlerIndex } from "#enums/battler-index"; -import type { Ability } from "#app/data/abilities/ability"; -import { allAbilities } from "#app/data/data-lists"; -import type Pokemon from "#app/field/pokemon"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import type { Ability } from "#abilities/ability"; +import { allAbilities } from "#data/data-lists"; import type { AbilityId } from "#enums/ability-id"; +import type { BattlerIndex } from "#enums/battler-index"; import type { PokemonType } from "#enums/pokemon-type"; import { Stat } from "#enums/stat"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { expect, type MockInstance, vi } from "vitest"; @@ -70,7 +68,7 @@ export class FieldHelper extends GameManagerHelper { * @see {@linkcode vi.spyOn} * @see https://vitest.dev/api/mock#mockreturnvalue */ - public mockAbility(pokemon: Pokemon, ability: AbilityId): MockInstance<(baseOnly?: boolean) => Ability> { + public mockAbility(pokemon: Pokemon, ability: AbilityId): MockInstance<(ignoreOverride?: boolean) => Ability> { return vi.spyOn(pokemon, "getAbility").mockReturnValue(allAbilities[ability]); } diff --git a/test/testUtils/helpers/gameManagerHelper.ts b/test/testUtils/helpers/gameManagerHelper.ts index 85d7bc0df97..6db3819a5cb 100644 --- a/test/testUtils/helpers/gameManagerHelper.ts +++ b/test/testUtils/helpers/gameManagerHelper.ts @@ -1,4 +1,4 @@ -import type GameManager from "../gameManager"; +import type { GameManager } from "#test/testUtils/gameManager"; /** * Base class for defining all game helpers. diff --git a/test/testUtils/helpers/modifiersHelper.ts b/test/testUtils/helpers/modifiersHelper.ts index 22500c87906..ff6e78c8a2f 100644 --- a/test/testUtils/helpers/modifiersHelper.ts +++ b/test/testUtils/helpers/modifiersHelper.ts @@ -1,7 +1,7 @@ +import type { ModifierTypeKeys } from "#modifiers/modifier-type"; +import { itemPoolChecks } from "#modifiers/modifier-type"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { expect } from "vitest"; -import { GameManagerHelper } from "./gameManagerHelper"; -import type { ModifierTypeKeys } from "#app/modifier/modifier-type"; -import { itemPoolChecks } from "#app/modifier/modifier-type"; export class ModifierHelper extends GameManagerHelper { /** diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index ed1441a6a2f..98a1c1664b4 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -1,22 +1,23 @@ -import type { BattlerIndex } from "#enums/battler-index"; -import { getMoveTargets } from "#app/data/moves/move-utils"; -import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; import Overrides from "#app/overrides"; -import type { CommandPhase } from "#app/phases/command-phase"; -import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { allMoves } from "#data/data-lists"; +import { BattlerIndex } from "#enums/battler-index"; import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; -import { UiMode } from "#enums/ui-mode"; -import { getMovePosition } from "#test/testUtils/gameManagerUtils"; -import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; -import { vi } from "vitest"; -import { coerceArray } from "#app/utils/common"; import { MoveUseMode } from "#enums/move-use-mode"; +import { UiMode } from "#enums/ui-mode"; +import type { Pokemon } from "#field/pokemon"; +import { getMoveTargets } from "#moves/move-utils"; +import { PokemonMove } from "#moves/pokemon-move"; +import type { CommandPhase } from "#phases/command-phase"; +import type { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; +import { coerceArray, toReadableString } from "#utils/common"; +import type { MockInstance } from "vitest"; +import { expect, vi } from "vitest"; /** - * Helper to handle a Pokemon's move + * Helper to handle using a Pokemon's moves. */ export class MoveHelper extends GameManagerHelper { /** @@ -49,13 +50,31 @@ export class MoveHelper extends GameManagerHelper { } /** - * Select the move to be used by the given Pokemon(-index). Triggers during the next {@linkcode CommandPhase} - * @param move - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + * Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}. + * @param move - The {@linkcode MoveId} to use. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * If set to `null`, will forgo normal target selection entirely (useful for UI tests). + * @remarks + * Will fail the current test if the move being selected is not in the user's moveset. */ - public select(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { - const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + public select( + move: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex | null, + ) { + const movePosition = this.getMovePosition(pkmIndex, move); + if (movePosition === -1) { + expect.fail( + `MoveHelper.select called with move '${toReadableString(MoveId[move])}' not in moveset!` + + `\nBattler Index: ${toReadableString(BattlerIndex[pkmIndex])}` + + `\nMoveset: [${this.game.scene + .getPlayerParty() + [pkmIndex].getMoveset() + .map(pm => toReadableString(MoveId[pm.moveId])) + .join(", ")}]`, + ); + } this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { this.game.scene.ui.setMode( @@ -77,14 +96,30 @@ export class MoveHelper extends GameManagerHelper { } /** - * Select the move to be used by the given Pokemon(-index), **which will also terastallize on this turn**. - * Triggers during the next {@linkcode CommandPhase} - * @param move - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + * Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}, **which will also terastallize on this turn**. + * @param move - The {@linkcode MoveId} to use. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * If set to `null`, will forgo normal target selection entirely (useful for UI tests) */ - public selectWithTera(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { - const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + public selectWithTera( + move: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex | null, + ) { + const movePosition = this.getMovePosition(pkmIndex, move); + if (movePosition === -1) { + expect.fail( + `MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}' not in moveset!` + + `\nBattler Index: ${toReadableString(BattlerIndex[pkmIndex])}` + + `\nMoveset: [${this.game.scene + .getPlayerParty() + [pkmIndex].getMoveset() + .map(pm => toReadableString(MoveId[pm.moveId])) + .join(", ")}]`, + ); + } + this.game.scene.getPlayerParty()[pkmIndex].isTerastallized = false; this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { @@ -107,6 +142,15 @@ export class MoveHelper extends GameManagerHelper { } } + /** Helper function to get the index of the selected move in the selected part member's moveset. */ + private getMovePosition(pokemonIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2, move: MoveId): number { + const playerPokemon = this.game.scene.getPlayerField()[pokemonIndex]; + const moveset = playerPokemon.getMoveset(); + const index = moveset.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp()); + console.log(`Move position for ${MoveId[move]} (=${move}):`, index); + return index; + } + /** * Modifies a player pokemon's moveset to contain only the selected move and then * selects it to be used during the next {@linkcode CommandPhase}. @@ -116,14 +160,19 @@ export class MoveHelper extends GameManagerHelper { * Note: If you need to check for changes in the player's moveset as part of the test, it may be * best to use {@linkcode changeMoveset} and {@linkcode select} instead. * @param moveId - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - (optional) The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required - * @param useTera - If `true`, the Pokemon also chooses to Terastallize. This does not require a Tera Orb. Default: `false`. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * @param useTera - If `true`, the Pokemon will attempt to Terastallize even without a Tera Orb; default `false`. */ - public use(moveId: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null, useTera = false): void { + public use( + moveId: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex, + useTera = false, + ): void { if ([Overrides.MOVESET_OVERRIDE].flat().length > 0) { vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]); - console.warn("Warning: `use` overwrites the Pokemon's moveset and disables the player moveset override!"); + console.warn("Warning: `MoveHelper.use` overwriting player pokemon moveset and disabling moveset override!"); } const pokemon = this.game.scene.getPlayerField()[pkmIndex]; @@ -258,4 +307,20 @@ export class MoveHelper extends GameManagerHelper { */ await this.game.phaseInterceptor.to("EnemyCommandPhase"); } + + /** + * Force the move used by Metronome to be a specific move. + * @param move - The move to force metronome to use + * @param once - If `true`, uses {@linkcode MockInstance#mockReturnValueOnce} when mocking, else uses {@linkcode MockInstance#mockReturnValue}. + * @returns The spy that for Metronome that was mocked (Usually unneeded). + */ + public forceMetronomeMove(move: MoveId, once = false): MockInstance { + const spy = vi.spyOn(allMoves[MoveId.METRONOME].getAttrs("RandomMoveAttr")[0], "getMoveOverride"); + if (once) { + spy.mockReturnValueOnce(move); + } else { + spy.mockReturnValue(move); + } + return spy; + } } diff --git a/test/testUtils/helpers/overridesHelper.ts b/test/testUtils/helpers/overridesHelper.ts index 3bf0fbbda47..13e3dcc2f09 100644 --- a/test/testUtils/helpers/overridesHelper.ts +++ b/test/testUtils/helpers/overridesHelper.ts @@ -1,35 +1,54 @@ -import type { Variant } from "#app/sprites/variant"; -import { Weather } from "#app/data/weather"; -import { AbilityId } from "#enums/ability-id"; -import type { ModifierOverride } from "#app/modifier/modifier-type"; -import type { BattleStyle } from "#app/overrides"; +/** biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ +import type { NewArenaEvent } from "#events/battle-scene"; +/** biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */ + +import type { BattleStyle, RandomTrainerOverride } from "#app/overrides"; import Overrides, { defaultOverrides } from "#app/overrides"; -import type { Unlockables } from "#enums/unlockables"; +import { AbilityId } from "#enums/ability-id"; +import type { BattleType } from "#enums/battle-type"; import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; -import type { WeatherType } from "#enums/weather-type"; +import type { Unlockables } from "#enums/unlockables"; +import { WeatherType } from "#enums/weather-type"; +import type { ModifierOverride } from "#modifiers/modifier-type"; +import type { Variant } from "#sprites/variant"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; +import { coerceArray, shiftCharCodes } from "#utils/common"; import { expect, vi } from "vitest"; -import { GameManagerHelper } from "./gameManagerHelper"; -import { coerceArray, shiftCharCodes } from "#app/utils/common"; -import type { RandomTrainerOverride } from "#app/overrides"; -import type { BattleType } from "#enums/battle-type"; /** * Helper to handle overrides in tests */ export class OverridesHelper extends GameManagerHelper { - /** If `true`, removes the starting items from enemies at the start of each test; default `true` */ + /** + * If `true`, removes the starting items from enemies at the start of each test. + * @defaultValue `true` + */ public removeEnemyStartingItems = true; - /** If `true`, sets the shiny overrides to disable shinies at the start of each test; default `true` */ + /** + * If `true`, sets the shiny overrides to disable shinies at the start of each test. + * @defaultValue `true` + */ public disableShinies = true; + /** + * If `true`, will set the IV overrides for player and enemy pokemon to `31` at the start of each test. + * @defaultValue `true` + */ + public normalizeIVs = true; + /** + * If `true`, will set the Nature overrides for player and enemy pokemon to a neutral nature at the start of each test. + * @defaultValue `true` + */ + public normalizeNatures = true; /** * Override the starting biome - * @warning Any event listeners that are attached to [NewArenaEvent](events\battle-scene.ts) may need to be handled down the line + * @warning Any event listeners that are attached to {@linkcode NewArenaEvent} may need to be handled down the line * @param biome - The biome to set */ public startingBiome(biome: BiomeId): this { @@ -209,9 +228,9 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player pokemon's {@linkcode StatusEffect | status-effect} + * Override the player pokemon's initial {@linkcode StatusEffect | status-effect}, * @param statusEffect - The {@linkcode StatusEffect | status-effect} to set - * @returns + * @returns `this` */ public statusEffect(statusEffect: StatusEffect): this { vi.spyOn(Overrides, "STATUS_OVERRIDE", "get").mockReturnValue(statusEffect); @@ -219,6 +238,80 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Overrides the IVs of the player pokemon + * @param ivs - If set to a number, all IVs are set to the same value. Must be between `0` and `31`! + * + * If set to an array, that array is applied to the pokemon's IV field as-is. + * All values must be between `0` and `31`, and the array must be of exactly length `6`! + * + * If set to `null`, the override is disabled. + * @returns `this` + */ + public playerIVs(ivs: number | number[] | null): this { + this.normalizeIVs = false; + vi.spyOn(Overrides, "IVS_OVERRIDE", "get").mockReturnValue(ivs); + if (ivs === null) { + this.log("Player IVs override disabled!"); + } else { + this.log(`Player IVs set to ${ivs}!`); + } + return this; + } + + /** + * Overrides the nature of the player's pokemon + * @param nature - The nature to set, or `null` to disable the override. + * @returns `this` + */ + public nature(nature: Nature | null): this { + this.normalizeNatures = false; + vi.spyOn(Overrides, "NATURE_OVERRIDE", "get").mockReturnValue(nature); + if (nature === null) { + this.log("Player Nature override disabled!"); + } else { + this.log(`Player Nature set to ${Nature[nature]} (=${nature})!`); + } + return this; + } + + /** + * Overrides the IVs of the enemy pokemon + * @param ivs - If set to a number, all IVs are set to the same value. Must be between `0` and `31`! + * + * If set to an array, that array is applied to the pokemon's IV field as-is. + * All values must be between `0` and `31`, and the array must be of exactly length `6`! + * + * If set to `null`, the override is disabled. + * @returns `this` + */ + public enemyIVs(ivs: number | number[] | null): this { + this.normalizeIVs = false; + vi.spyOn(Overrides, "ENEMY_IVS_OVERRIDE", "get").mockReturnValue(ivs); + if (ivs === null) { + this.log("Enemy IVs override disabled!"); + } else { + this.log(`Enemy IVs set to ${ivs}!`); + } + return this; + } + + /** + * Overrides the nature of the enemy's pokemon + * @param nature - The nature to set, or `null` to disable the override. + * @returns `this` + */ + public enemyNature(nature: Nature | null): this { + this.normalizeNatures = false; + vi.spyOn(Overrides, "ENEMY_NATURE_OVERRIDE", "get").mockReturnValue(nature); + if (nature === null) { + this.log("Enemy Nature override disabled!"); + } else { + this.log(`Enemy Nature set to ${Nature[nature]} (=${nature})!`); + } + return this; + } + /** * Override each wave to not have standard trainer battles * @returns `this` @@ -262,7 +355,7 @@ export class OverridesHelper extends GameManagerHelper { */ public weather(type: WeatherType): this { vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type); - this.log(`Weather set to ${Weather[type]} (=${type})!`); + this.log(`Weather set to ${WeatherType[type]} (=${type})!`); return this; } @@ -401,9 +494,9 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy {@linkcode StatusEffect | status-effect} for enemy pokemon + * Override the enemy pokemon's initial {@linkcode StatusEffect | status-effect}. * @param statusEffect - The {@linkcode StatusEffect | status-effect} to set - * @returns + * @returns `this` */ public enemyStatusEffect(statusEffect: StatusEffect): this { vi.spyOn(Overrides, "OPP_STATUS_OVERRIDE", "get").mockReturnValue(statusEffect); diff --git a/test/testUtils/helpers/reloadHelper.ts b/test/testUtils/helpers/reloadHelper.ts index 4f9d6c810f8..bc8adae6f28 100644 --- a/test/testUtils/helpers/reloadHelper.ts +++ b/test/testUtils/helpers/reloadHelper.ts @@ -1,12 +1,12 @@ -import { GameManagerHelper } from "./gameManagerHelper"; -import { TitlePhase } from "#app/phases/title-phase"; +import { BattleStyle } from "#enums/battle-style"; import { UiMode } from "#enums/ui-mode"; +import { CommandPhase } from "#phases/command-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import type { SessionSaveData } from "#system/game-data"; +import type { GameManager } from "#test/testUtils/gameManager"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { vi } from "vitest"; -import { BattleStyle } from "#app/enums/battle-style"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import type { SessionSaveData } from "#app/system/game-data"; -import type GameManager from "../gameManager"; /** * Helper to allow reloading sessions in unit tests. diff --git a/test/testUtils/helpers/settingsHelper.ts b/test/testUtils/helpers/settingsHelper.ts index d5a60e6496a..76db93c7de5 100644 --- a/test/testUtils/helpers/settingsHelper.ts +++ b/test/testUtils/helpers/settingsHelper.ts @@ -1,7 +1,7 @@ -import { PlayerGender } from "#app/enums/player-gender"; -import { BattleStyle } from "#app/enums/battle-style"; -import { GameManagerHelper } from "./gameManagerHelper"; -import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; +import { BattleStyle } from "#enums/battle-style"; +import { ExpGainsSpeed } from "#enums/exp-gains-speed"; +import { PlayerGender } from "#enums/player-gender"; +import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; /** * Helper to handle settings for tests diff --git a/test/testUtils/inputsHandler.ts b/test/testUtils/inputsHandler.ts index 6e9dd453541..6a0db37c9b6 100644 --- a/test/testUtils/inputsHandler.ts +++ b/test/testUtils/inputsHandler.ts @@ -1,7 +1,7 @@ -import type BattleScene from "#app/battle-scene"; -import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; +import type { BattleScene } from "#app/battle-scene"; import type { InputsController } from "#app/inputs-controller"; -import TouchControl from "#app/touch-controls"; +import { TouchControl } from "#app/touch-controls"; +import pad_xbox360 from "#inputs/pad_xbox360"; import { holdOn } from "#test/testUtils/gameManagerUtils"; import fs from "node:fs"; import { JSDOM } from "jsdom"; @@ -12,7 +12,7 @@ interface LogEntry { button: any; } -export default class InputsHandler { +export class InputsHandler { private scene: BattleScene; private events: Phaser.Events.EventEmitter; private inputController: InputsController; @@ -89,7 +89,7 @@ class Fakepad extends Phaser.Input.Gamepad.Gamepad { public index: number; constructor(pad) { - //@ts-ignore + //@ts-expect-error super(undefined, { ...pad, buttons: pad.deviceMapping, axes: [] }); //TODO: resolve ts-ignore this.id = "xbox_360_fakepad"; this.index = 0; diff --git a/test/testUtils/mocks/mockClock.ts b/test/testUtils/mocks/mockClock.ts index 67f399ae41d..0d94a62824c 100644 --- a/test/testUtils/mocks/mockClock.ts +++ b/test/testUtils/mocks/mockClock.ts @@ -1,4 +1,6 @@ -import Clock = Phaser.Time.Clock; +import Phaser from "phaser"; + +const Clock = Phaser.Time.Clock; export class MockClock extends Clock { public overrideDelay: number | null = 1; diff --git a/test/testUtils/mocks/mockGameObjectCreator.ts b/test/testUtils/mocks/mockGameObjectCreator.ts index caf98b79a9f..cfb1051ec2f 100644 --- a/test/testUtils/mocks/mockGameObjectCreator.ts +++ b/test/testUtils/mocks/mockGameObjectCreator.ts @@ -1,5 +1,5 @@ -import MockGraphics from "./mocksContainer/mockGraphics"; -import type MockTextureManager from "./mockTextureManager"; +import { MockGraphics } from "#test/testUtils/mocks/mocksContainer/mockGraphics"; +import type { MockTextureManager } from "#test/testUtils/mocks/mockTextureManager"; export class MockGameObjectCreator { private readonly textureManager: MockTextureManager; diff --git a/test/testUtils/mocks/mockLoader.ts b/test/testUtils/mocks/mockLoader.ts index 7452f85a317..7bb3ffe7147 100644 --- a/test/testUtils/mocks/mockLoader.ts +++ b/test/testUtils/mocks/mockLoader.ts @@ -1,6 +1,8 @@ -import CacheManager = Phaser.Cache.CacheManager; +import Phaser from "phaser"; -export default class MockLoader { +const CacheManager = Phaser.Cache.CacheManager; + +export class MockLoader { public cacheManager; constructor(scene) { this.cacheManager = new CacheManager(scene); diff --git a/test/testUtils/mocks/mockTextureManager.ts b/test/testUtils/mocks/mockTextureManager.ts index 585ee0a674a..b61a804bf55 100644 --- a/test/testUtils/mocks/mockTextureManager.ts +++ b/test/testUtils/mocks/mockTextureManager.ts @@ -1,18 +1,20 @@ -import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import { MockBBCodeText } from "#test/testUtils/mocks/mocksContainer/mock-bbcode-text"; +import { MockInputText } from "#test/testUtils/mocks/mocksContainer/mock-input-text"; +import { MockContainer } from "#test/testUtils/mocks/mocksContainer/mockContainer"; import { MockImage } from "#test/testUtils/mocks/mocksContainer/mockImage"; -import MockNineslice from "#test/testUtils/mocks/mocksContainer/mockNineslice"; -import MockPolygon from "#test/testUtils/mocks/mocksContainer/mockPolygon"; -import MockRectangle from "#test/testUtils/mocks/mocksContainer/mockRectangle"; -import MockSprite from "#test/testUtils/mocks/mocksContainer/mockSprite"; -import MockText from "#test/testUtils/mocks/mocksContainer/mockText"; -import MockTexture from "#test/testUtils/mocks/mocksContainer/mockTexture"; -import type { MockGameObject } from "./mockGameObject"; -import { MockVideoGameObject } from "./mockVideoGameObject"; +import { MockNineslice } from "#test/testUtils/mocks/mocksContainer/mockNineslice"; +import { MockPolygon } from "#test/testUtils/mocks/mocksContainer/mockPolygon"; +import { MockRectangle } from "#test/testUtils/mocks/mocksContainer/mockRectangle"; +import { MockSprite } from "#test/testUtils/mocks/mocksContainer/mockSprite"; +import { MockText } from "#test/testUtils/mocks/mocksContainer/mockText"; +import { MockTexture } from "#test/testUtils/mocks/mocksContainer/mockTexture"; +import { MockVideoGameObject } from "#test/testUtils/mocks/mockVideoGameObject"; /** * Stub class for Phaser.Textures.TextureManager */ -export default class MockTextureManager { +export class MockTextureManager { private textures: Map; private scene; public add; @@ -33,6 +35,8 @@ export default class MockTextureManager { image: this.image.bind(this), polygon: this.polygon.bind(this), text: this.text.bind(this), + rexBBCodeText: this.rexBBCodeText.bind(this), + rexInputText: this.rexInputText.bind(this), bitmapText: this.text.bind(this), displayList: this.displayList, video: () => new MockVideoGameObject(), @@ -103,9 +107,25 @@ export default class MockTextureManager { return text; } + rexBBCodeText(x, y, content, styleOptions) { + const text = new MockBBCodeText(this, x, y, content, styleOptions); + this.list.push(text); + return text; + } + + rexInputText(x, y, w, h, content, styleOptions) { + const text = new MockInputText(this, x, y, w, h, content, styleOptions); + this.list.push(text); + return text; + } + polygon(x, y, content, fillColor, fillAlpha) { const polygon = new MockPolygon(this, x, y, content, fillColor, fillAlpha); this.list.push(polygon); return polygon; } + + exists(key: string): boolean { + return this.textures.has(key); + } } diff --git a/test/testUtils/mocks/mockTimedEventManager.ts b/test/testUtils/mocks/mockTimedEventManager.ts index 10f32fd4c8b..b6c84876783 100644 --- a/test/testUtils/mocks/mockTimedEventManager.ts +++ b/test/testUtils/mocks/mockTimedEventManager.ts @@ -1,5 +1,5 @@ -import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "#app/data/balance/starters"; import { TimedEventManager } from "#app/timed-event-manager"; +import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "#balance/starters"; /** Mock TimedEventManager so that ongoing events don't impact tests */ export class MockTimedEventManager extends TimedEventManager { diff --git a/test/testUtils/mocks/mockVideoGameObject.ts b/test/testUtils/mocks/mockVideoGameObject.ts index 1789229b1c7..40413f6f622 100644 --- a/test/testUtils/mocks/mockVideoGameObject.ts +++ b/test/testUtils/mocks/mockVideoGameObject.ts @@ -1,11 +1,11 @@ -import type { MockGameObject } from "./mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; /** Mocks video-related stuff */ export class MockVideoGameObject implements MockGameObject { public name: string; public active = true; - public play = () => null; + public play = () => this; public stop = () => this; public setOrigin = () => this; public setScale = () => this; diff --git a/test/testUtils/mocks/mocksContainer/mock-bbcode-text.ts b/test/testUtils/mocks/mocksContainer/mock-bbcode-text.ts new file mode 100644 index 00000000000..8df0e01a43e --- /dev/null +++ b/test/testUtils/mocks/mocksContainer/mock-bbcode-text.ts @@ -0,0 +1,6 @@ +import { MockText } from "#test/testUtils/mocks/mocksContainer/mockText"; + +export class MockBBCodeText extends MockText { + setMaxLines(_lines: number) {} + setWrapMode(_mode: 0 | 1 | 2 | 3 | "none" | "word" | "char" | "character" | "mix") {} +} diff --git a/test/testUtils/mocks/mocksContainer/mock-input-text.ts b/test/testUtils/mocks/mocksContainer/mock-input-text.ts new file mode 100644 index 00000000000..6826278aa1e --- /dev/null +++ b/test/testUtils/mocks/mocksContainer/mock-input-text.ts @@ -0,0 +1,24 @@ +import { MockText } from "#test/testUtils/mocks/mocksContainer/mockText"; + +export class MockInputText extends MockText { + public inputType: string; + public selectionStart: number; + public selectionEnd: number; + public selectedText: string; + + constructor(textureManager, x, y, _w, _h, content, styleOptions) { + super(textureManager, x, y, content, styleOptions); + } + + selectText(_selectionStart?: number, _selectionEnd?: number) {} + + selectAll() {} + + setCursorPosition(_value: number) {} + + scrollToBottom() {} + + resize(_width: number, _height: number) {} + + setElement(_element, _style, _innerText) {} +} diff --git a/test/testUtils/mocks/mocksContainer/mockContainer.ts b/test/testUtils/mocks/mocksContainer/mockContainer.ts index d31165bb847..df9aaab5888 100644 --- a/test/testUtils/mocks/mocksContainer/mockContainer.ts +++ b/test/testUtils/mocks/mocksContainer/mockContainer.ts @@ -1,8 +1,8 @@ -import { coerceArray } from "#app/utils/common"; -import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager"; -import type { MockGameObject } from "../mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import type { MockTextureManager } from "#test/testUtils/mocks/mockTextureManager"; +import { coerceArray } from "#utils/common"; -export default class MockContainer implements MockGameObject { +export class MockContainer implements MockGameObject { protected x: number; protected y: number; protected scene; diff --git a/test/testUtils/mocks/mocksContainer/mockGraphics.ts b/test/testUtils/mocks/mocksContainer/mockGraphics.ts index cd43bb3a877..6558cdf9fb1 100644 --- a/test/testUtils/mocks/mocksContainer/mockGraphics.ts +++ b/test/testUtils/mocks/mocksContainer/mockGraphics.ts @@ -1,6 +1,6 @@ -import type { MockGameObject } from "../mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; -export default class MockGraphics implements MockGameObject { +export class MockGraphics implements MockGameObject { private scene; public list: MockGameObject[] = []; public name: string; diff --git a/test/testUtils/mocks/mocksContainer/mockImage.ts b/test/testUtils/mocks/mocksContainer/mockImage.ts index d20b4663771..830a22a369c 100644 --- a/test/testUtils/mocks/mocksContainer/mockImage.ts +++ b/test/testUtils/mocks/mocksContainer/mockImage.ts @@ -1,6 +1,7 @@ -import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; +import { MockContainer } from "#test/testUtils/mocks/mocksContainer/mockContainer"; export class MockImage extends MockContainer { + // biome-ignore lint/correctness/noUnusedPrivateClassMembers: this is intentional (?) private texture; constructor(textureManager, x, y, texture) { diff --git a/test/testUtils/mocks/mocksContainer/mockNineslice.ts b/test/testUtils/mocks/mocksContainer/mockNineslice.ts index 90c0e13e725..11983075cf7 100644 --- a/test/testUtils/mocks/mocksContainer/mockNineslice.ts +++ b/test/testUtils/mocks/mocksContainer/mockNineslice.ts @@ -1,6 +1,6 @@ -import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; +import { MockContainer } from "#test/testUtils/mocks/mocksContainer/mockContainer"; -export default class MockNineslice extends MockContainer { +export class MockNineslice extends MockContainer { private texture; private leftWidth; private rightWidth; diff --git a/test/testUtils/mocks/mocksContainer/mockPolygon.ts b/test/testUtils/mocks/mocksContainer/mockPolygon.ts index 4a7f3baec78..2426231193a 100644 --- a/test/testUtils/mocks/mocksContainer/mockPolygon.ts +++ b/test/testUtils/mocks/mocksContainer/mockPolygon.ts @@ -1,6 +1,6 @@ -import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; +import { MockContainer } from "#test/testUtils/mocks/mocksContainer/mockContainer"; -export default class MockPolygon extends MockContainer { +export class MockPolygon extends MockContainer { constructor(textureManager, x, y, _content, _fillColor, _fillAlpha) { super(textureManager, x, y); } diff --git a/test/testUtils/mocks/mocksContainer/mockRectangle.ts b/test/testUtils/mocks/mocksContainer/mockRectangle.ts index a8eeb370115..1e38cd3af44 100644 --- a/test/testUtils/mocks/mocksContainer/mockRectangle.ts +++ b/test/testUtils/mocks/mocksContainer/mockRectangle.ts @@ -1,7 +1,7 @@ -import { coerceArray } from "#app/utils/common"; -import type { MockGameObject } from "../mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import { coerceArray } from "#utils/common"; -export default class MockRectangle implements MockGameObject { +export class MockRectangle implements MockGameObject { private fillColor; private scene; public list: MockGameObject[] = []; diff --git a/test/testUtils/mocks/mocksContainer/mockSprite.ts b/test/testUtils/mocks/mocksContainer/mockSprite.ts index ed1f1df6609..bea1db21629 100644 --- a/test/testUtils/mocks/mocksContainer/mockSprite.ts +++ b/test/testUtils/mocks/mocksContainer/mockSprite.ts @@ -1,10 +1,10 @@ -import { coerceArray } from "#app/utils/common"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import { coerceArray } from "#utils/common"; import Phaser from "phaser"; -import type { MockGameObject } from "../mockGameObject"; type Frame = Phaser.Textures.Frame; -export default class MockSprite implements MockGameObject { +export class MockSprite implements MockGameObject { private phaserSprite; public pipelineData; public texture; @@ -19,13 +19,13 @@ export default class MockSprite implements MockGameObject { constructor(textureManager, x, y, texture) { this.textureManager = textureManager; this.scene = textureManager.scene; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame; // Phaser.GameObjects.Sprite.prototype.disable = this.disable; diff --git a/test/testUtils/mocks/mocksContainer/mockText.ts b/test/testUtils/mocks/mocksContainer/mockText.ts index 2345ff70c0d..58ae3650411 100644 --- a/test/testUtils/mocks/mocksContainer/mockText.ts +++ b/test/testUtils/mocks/mocksContainer/mockText.ts @@ -1,7 +1,7 @@ -import UI from "#app/ui/ui"; -import type { MockGameObject } from "../mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import { UI } from "#ui/ui"; -export default class MockText implements MockGameObject { +export class MockText implements MockGameObject { private phaserText; private wordWrapWidth; private splitRegExp; diff --git a/test/testUtils/mocks/mocksContainer/mockTexture.ts b/test/testUtils/mocks/mocksContainer/mockTexture.ts index 3b01f9ab4ea..af25941094b 100644 --- a/test/testUtils/mocks/mocksContainer/mockTexture.ts +++ b/test/testUtils/mocks/mocksContainer/mockTexture.ts @@ -1,11 +1,11 @@ -import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager"; -import type { MockGameObject } from "../mockGameObject"; +import type { MockGameObject } from "#test/testUtils/mocks/mockGameObject"; +import type { MockTextureManager } from "#test/testUtils/mocks/mockTextureManager"; /** * Stub for Phaser.Textures.Texture object * Just mocks the function calls and data required for use in tests */ -export default class MockTexture implements MockGameObject { +export class MockTexture implements MockGameObject { public manager: MockTextureManager; public key: string; public source; diff --git a/test/testUtils/phaseInterceptor.ts b/test/testUtils/phaseInterceptor.ts index 9d046fc85ba..cdf151c01af 100644 --- a/test/testUtils/phaseInterceptor.ts +++ b/test/testUtils/phaseInterceptor.ts @@ -1,69 +1,69 @@ import { Phase } from "#app/phase"; -import ErrorInterceptor from "#test/testUtils/errorInterceptor"; -import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { EvolutionPhase } from "#app/phases/evolution-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { FormChangePhase } from "#app/phases/form-change-phase"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { LevelCapPhase } from "#app/phases/level-cap-phase"; -import { LoginPhase } from "#app/phases/login-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { SelectGenderPhase } from "#app/phases/select-gender-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { SelectTargetPhase } from "#app/phases/select-target-phase"; -import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { SwitchPhase } from "#app/phases/switch-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; -import { UnavailablePhase } from "#app/phases/unavailable-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import UI from "#app/ui/ui"; import { UiMode } from "#enums/ui-mode"; -import { SelectBiomePhase } from "#app/phases/select-biome-phase"; +import { AttemptRunPhase } from "#phases/attempt-run-phase"; +import { BattleEndPhase } from "#phases/battle-end-phase"; +import { BerryPhase } from "#phases/berry-phase"; +import { CheckSwitchPhase } from "#phases/check-switch-phase"; +import { CommandPhase } from "#phases/command-phase"; +import { DamageAnimPhase } from "#phases/damage-anim-phase"; +import { EggLapsePhase } from "#phases/egg-lapse-phase"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { EndEvolutionPhase } from "#phases/end-evolution-phase"; +import { EnemyCommandPhase } from "#phases/enemy-command-phase"; +import { EvolutionPhase } from "#phases/evolution-phase"; +import { ExpPhase } from "#phases/exp-phase"; +import { FaintPhase } from "#phases/faint-phase"; +import { FormChangePhase } from "#phases/form-change-phase"; +import { GameOverModifierRewardPhase } from "#phases/game-over-modifier-reward-phase"; +import { GameOverPhase } from "#phases/game-over-phase"; +import { LearnMovePhase } from "#phases/learn-move-phase"; +import { LevelCapPhase } from "#phases/level-cap-phase"; +import { LoginPhase } from "#phases/login-phase"; +import { MessagePhase } from "#phases/message-phase"; +import { ModifierRewardPhase } from "#phases/modifier-reward-phase"; +import { MoveEffectPhase } from "#phases/move-effect-phase"; +import { MoveEndPhase } from "#phases/move-end-phase"; +import { MovePhase } from "#phases/move-phase"; import { MysteryEncounterBattlePhase, MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase, PostMysteryEncounterPhase, -} from "#app/phases/mystery-encounter-phases"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; -import { PartyExpPhase } from "#app/phases/party-exp-phase"; -import { ExpPhase } from "#app/phases/exp-phase"; -import { GameOverPhase } from "#app/phases/game-over-phase"; -import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; -import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; -import { UnlockPhase } from "#app/phases/unlock-phase"; -import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; -import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; - -import type { PhaseClass, PhaseString } from "#app/@types/phase-types"; +} from "#phases/mystery-encounter-phases"; +import { NewBattlePhase } from "#phases/new-battle-phase"; +import { NewBiomeEncounterPhase } from "#phases/new-biome-encounter-phase"; +import { NextEncounterPhase } from "#phases/next-encounter-phase"; +import { PartyExpPhase } from "#phases/party-exp-phase"; +import { PartyHealPhase } from "#phases/party-heal-phase"; +import { PokemonTransformPhase } from "#phases/pokemon-transform-phase"; +import { PostGameOverPhase } from "#phases/post-game-over-phase"; +import { PostSummonPhase } from "#phases/post-summon-phase"; +import { QuietFormChangePhase } from "#phases/quiet-form-change-phase"; +import { RevivalBlessingPhase } from "#phases/revival-blessing-phase"; +import { RibbonModifierRewardPhase } from "#phases/ribbon-modifier-reward-phase"; +import { SelectBiomePhase } from "#phases/select-biome-phase"; +import { SelectGenderPhase } from "#phases/select-gender-phase"; +import { SelectModifierPhase } from "#phases/select-modifier-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import { SelectTargetPhase } from "#phases/select-target-phase"; +import { ShinySparklePhase } from "#phases/shiny-sparkle-phase"; +import { ShowAbilityPhase } from "#phases/show-ability-phase"; +import { StatStageChangePhase } from "#phases/stat-stage-change-phase"; +import { SummonPhase } from "#phases/summon-phase"; +import { SwitchPhase } from "#phases/switch-phase"; +import { SwitchSummonPhase } from "#phases/switch-summon-phase"; +import { TitlePhase } from "#phases/title-phase"; +import { ToggleDoublePositionPhase } from "#phases/toggle-double-position-phase"; +import { TurnEndPhase } from "#phases/turn-end-phase"; +import { TurnInitPhase } from "#phases/turn-init-phase"; +import { TurnStartPhase } from "#phases/turn-start-phase"; +import { UnavailablePhase } from "#phases/unavailable-phase"; +import { UnlockPhase } from "#phases/unlock-phase"; +import { VictoryPhase } from "#phases/victory-phase"; +import { ErrorInterceptor } from "#test/testUtils/errorInterceptor"; +import type { PhaseClass, PhaseString } from "#types/phase-types"; +import { UI } from "#ui/ui"; export interface PromptHandler { phaseTarget?: string; @@ -75,7 +75,7 @@ export interface PromptHandler { type PhaseInterceptorPhase = PhaseClass | PhaseString; -export default class PhaseInterceptor { +export class PhaseInterceptor { public scene; public phases = {}; public log: string[]; @@ -142,6 +142,7 @@ export default class PhaseInterceptor { [LevelCapPhase, this.startPhase], [AttemptRunPhase, this.startPhase], [SelectBiomePhase, this.startPhase], + [PokemonTransformPhase, this.startPhase], [MysteryEncounterPhase, this.startPhase], [MysteryEncounterOptionSelectedPhase, this.startPhase], [MysteryEncounterBattlePhase, this.startPhase], diff --git a/test/testUtils/testFileInitialization.ts b/test/testUtils/testFileInitialization.ts index da390870300..98b49159d98 100644 --- a/test/testUtils/testFileInitialization.ts +++ b/test/testUtils/testFileInitialization.ts @@ -1,29 +1,29 @@ -import { SESSION_ID_COOKIE_NAME } from "#app/constants"; +import { initAbilities } from "#abilities/ability"; import { initLoggedInUser } from "#app/account"; -import { initAbilities } from "#app/data/abilities/ability"; -import { initBiomes } from "#app/data/balance/biomes"; -import { initEggMoves } from "#app/data/balance/egg-moves"; -import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions"; -import { initMoves } from "#app/data/moves/move"; -import { initModifierPools } from "#app/modifier/init-modifier-pools"; -import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; -import { initPokemonForms } from "#app/data/pokemon-forms"; -import { initSpecies } from "#app/data/pokemon-species"; -import { initAchievements } from "#app/system/achv"; -import { initVouchers } from "#app/system/voucher"; -import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; -import { setCookie } from "#app/utils/cookies"; +import { SESSION_ID_COOKIE_NAME } from "#app/constants"; +import { initBiomes } from "#balance/biomes"; +import { initEggMoves } from "#balance/egg-moves"; +import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions"; +import { initPokemonForms } from "#data/pokemon-forms"; +import { initSpecies } from "#data/pokemon-species"; +import { initModifierPools } from "#modifiers/init-modifier-pools"; +import { initModifierTypes } from "#modifiers/modifier-type"; +import { initMoves } from "#moves/move"; +import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters"; +import { initI18n } from "#plugins/i18n"; +import { initAchievements } from "#system/achv"; +import { initVouchers } from "#system/voucher"; import { blobToString } from "#test/testUtils/gameManagerUtils"; +import { manageListeners } from "#test/testUtils/listenersManager"; import { MockConsoleLog } from "#test/testUtils/mocks/mockConsoleLog"; import { mockContext } from "#test/testUtils/mocks/mockContextCanvas"; import { mockLocalStorage } from "#test/testUtils/mocks/mockLocalStorage"; import { MockImage } from "#test/testUtils/mocks/mocksContainer/mockImage"; +import { initStatsKeys } from "#ui/game-stats-ui-handler"; +import { setCookie } from "#utils/cookies"; import Phaser from "phaser"; -import InputText from "phaser3-rex-plugins/plugins/inputtext"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { manageListeners } from "./listenersManager"; -import { initI18n } from "#app/plugins/i18n"; -import { initModifierTypes } from "#app/modifier/modifier-type"; +import InputText from "phaser3-rex-plugins/plugins/inputtext"; let wasInitialized = false; /** @@ -47,7 +47,7 @@ export function initTestFile() { }); BBCodeText.prototype.destroy = () => null; - // @ts-ignore + // @ts-expect-error BBCodeText.prototype.resize = () => null; InputText.prototype.setElement = () => null as any; InputText.prototype.resize = () => null as any; diff --git a/test/types/enum-types.test-d.ts b/test/types/enum-types.test-d.ts new file mode 100644 index 00000000000..396c479e85a --- /dev/null +++ b/test/types/enum-types.test-d.ts @@ -0,0 +1,104 @@ +import type { EnumOrObject, EnumValues, NormalEnum, TSNumericEnum } from "#app/@types/enum-types"; +import type { enumValueToKey, getEnumKeys, getEnumValues } from "#app/utils/enums"; +import { describe, expectTypeOf, it } from "vitest"; + +enum testEnumNum { + testN1 = 1, + testN2 = 2, +} + +enum testEnumString { + testS1 = "apple", + testS2 = "banana", +} + +const testObjNum = { testON1: 1, testON2: 2 } as const; + +const testObjString = { testOS1: "apple", testOS2: "banana" } as const; + +describe("Enum Type Helpers", () => { + describe("EnumValues", () => { + it("should go from enum object type to value type", () => { + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().branded.toEqualTypeOf<1 | 2>(); + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toMatchTypeOf<"apple" | "banana">(); + }); + + it("should produce union of const object values as type", () => { + expectTypeOf>().toEqualTypeOf<1 | 2>(); + + expectTypeOf>().toEqualTypeOf<"apple" | "banana">(); + }); + }); + + describe("TSNumericEnum", () => { + it("should match numeric enums", () => { + expectTypeOf>().toEqualTypeOf(); + }); + + it("should not match string enums or const objects", () => { + expectTypeOf>().toBeNever(); + expectTypeOf>().toBeNever(); + expectTypeOf>().toBeNever(); + }); + }); + + describe("NormalEnum", () => { + it("should match string enums or const objects", () => { + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + }); + it("should not match numeric enums", () => { + expectTypeOf>().toBeNever(); + }); + }); + + describe("EnumOrObject", () => { + it("should match any enum or const object", () => { + expectTypeOf().toMatchTypeOf(); + expectTypeOf().toMatchTypeOf(); + expectTypeOf().toMatchTypeOf(); + expectTypeOf().toMatchTypeOf(); + }); + + it("should not match an enum value union w/o typeof", () => { + expectTypeOf().not.toMatchTypeOf(); + expectTypeOf().not.toMatchTypeOf(); + }); + + it("should be equivalent to `TSNumericEnum | NormalEnum`", () => { + expectTypeOf().branded.toEqualTypeOf | NormalEnum>(); + }); + }); +}); + +describe("Enum Functions", () => { + describe("getEnumKeys", () => { + it("should retrieve keys of numeric enum", () => { + expectTypeOf>().returns.toEqualTypeOf<("testN1" | "testN2")[]>(); + }); + }); + + describe("getEnumValues", () => { + it("should retrieve values of numeric enum", () => { + expectTypeOf>().returns.branded.toEqualTypeOf<(1 | 2)[]>(); + }); + }); + + describe("enumValueToKey", () => { + it("should retrieve values for a given key", () => { + expectTypeOf< + typeof enumValueToKey + >().returns.toEqualTypeOf<"testS1">(); + expectTypeOf>().returns.toEqualTypeOf< + "testS1" | "testS2" + >(); + expectTypeOf>().returns.toEqualTypeOf<"testON1">(); + expectTypeOf>().returns.toEqualTypeOf<"testON1" | "testON2">(); + }); + }); +}); diff --git a/test/ui/battle_info.test.ts b/test/ui/battle_info.test.ts index 3049424e3d2..afc456c6430 100644 --- a/test/ui/battle_info.test.ts +++ b/test/ui/battle_info.test.ts @@ -1,9 +1,9 @@ -import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; -import { SpeciesId } from "#enums/species-id"; -import { ExpPhase } from "#app/phases/exp-phase"; import { AbilityId } from "#enums/ability-id"; +import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { MoveId } from "#enums/move-id"; -import GameManager from "#test/testUtils/gameManager"; +import { SpeciesId } from "#enums/species-id"; +import { ExpPhase } from "#phases/exp-phase"; +import { GameManager } from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/ui/pokedex.test.ts b/test/ui/pokedex.test.ts index 13f595e0c60..1d81f19052c 100644 --- a/test/ui/pokedex.test.ts +++ b/test/ui/pokedex.test.ts @@ -1,21 +1,19 @@ -import GameManager from "#test/testUtils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import PokedexUiHandler from "#app/ui/pokedex-ui-handler"; -import { FilterTextRow } from "#app/ui/filter-text"; -import { allAbilities } from "#app/data/data-lists"; +import { allAbilities, allSpecies } from "#data/data-lists"; +import type { PokemonForm, PokemonSpecies } from "#data/pokemon-species"; import { AbilityId } from "#enums/ability-id"; -import { SpeciesId } from "#enums/species-id"; -import type { PokemonForm } from "#app/data/pokemon-species"; -import { getPokemonSpecies } from "#app/utils/pokemon-utils"; -import { allSpecies } from "#app/data/data-lists"; import { Button } from "#enums/buttons"; import { DropDownColumn } from "#enums/drop-down-column"; -import type PokemonSpecies from "#app/data/pokemon-species"; import { PokemonType } from "#enums/pokemon-type"; +import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import PokedexPageUiHandler from "#app/ui/pokedex-page-ui-handler"; -import type { StarterAttributes } from "#app/system/game-data"; +import type { StarterAttributes } from "#system/game-data"; +import { GameManager } from "#test/testUtils/gameManager"; +import { FilterTextRow } from "#ui/filter-text"; +import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler"; +import { PokedexUiHandler } from "#ui/pokedex-ui-handler"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; /* Information for the `data_pokedex_tests.psrv`: diff --git a/test/ui/starter-select.test.ts b/test/ui/starter-select.test.ts index 8167ab17957..63abc99174e 100644 --- a/test/ui/starter-select.test.ts +++ b/test/ui/starter-select.test.ts @@ -1,19 +1,19 @@ -import { Gender } from "#app/data/gender"; -import { Nature } from "#enums/nature"; -import { allSpecies } from "#app/data/data-lists"; -import { GameModes } from "#enums/game-modes"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import type { TitlePhase } from "#app/phases/title-phase"; -import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import type SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; -import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import type StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import { UiMode } from "#enums/ui-mode"; +import { allSpecies } from "#data/data-lists"; +import { Gender } from "#data/gender"; import { AbilityId } from "#enums/ability-id"; import { Button } from "#enums/buttons"; +import { GameModes } from "#enums/game-modes"; +import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; -import GameManager from "#test/testUtils/gameManager"; +import { UiMode } from "#enums/ui-mode"; +import { EncounterPhase } from "#phases/encounter-phase"; +import { SelectStarterPhase } from "#phases/select-starter-phase"; +import type { TitlePhase } from "#phases/title-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; +import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler"; +import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/ui/transfer-item.test.ts b/test/ui/transfer-item.test.ts index 572d56c5903..f2b0a4f7d35 100644 --- a/test/ui/transfer-item.test.ts +++ b/test/ui/transfer-item.test.ts @@ -1,11 +1,11 @@ -import { BerryType } from "#app/enums/berry-type"; -import { Button } from "#app/enums/buttons"; +import { BerryType } from "#enums/berry-type"; +import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import { UiMode } from "#enums/ui-mode"; -import GameManager from "#test/testUtils/gameManager"; +import { GameManager } from "#test/testUtils/gameManager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import Phaser from "phaser"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/ui/type-hints.test.ts b/test/ui/type-hints.test.ts index 6b0bc6e5ea5..8359c50c35d 100644 --- a/test/ui/type-hints.test.ts +++ b/test/ui/type-hints.test.ts @@ -1,14 +1,14 @@ -import { Button } from "#app/enums/buttons"; +import { Button } from "#enums/buttons"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; -import { CommandPhase } from "#app/phases/command-phase"; -import FightUiHandler from "#app/ui/fight-ui-handler"; import { UiMode } from "#enums/ui-mode"; -import GameManager from "#test/testUtils/gameManager"; +import { CommandPhase } from "#phases/command-phase"; +import { GameManager } from "#test/testUtils/gameManager"; +import type { MockText } from "#test/testUtils/mocks/mocksContainer/mockText"; +import { FightUiHandler } from "#ui/fight-ui-handler"; +import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import type MockText from "#test/testUtils/mocks/mocksContainer/mockText"; -import i18next from "i18next"; describe("UI - Type Hints", () => { let phaserGame: Phaser.Game; diff --git a/test/utils.test.ts b/test/utils.test.ts index fe93bdd6970..aacd590a023 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,8 +1,7 @@ -import { expect, describe, it, beforeAll } from "vitest"; -import { randomString, padInt } from "#app/utils/common"; -import { deepMergeSpriteData } from "#app/utils/data"; - +import { padInt, randomString } from "#utils/common"; +import { deepMergeSpriteData } from "#utils/data"; import Phaser from "phaser"; +import { beforeAll, describe, expect, it } from "vitest"; describe("utils", () => { beforeAll(() => { diff --git a/test/vitest.setup.ts b/test/vitest.setup.ts index 93b439e540f..6cf20219408 100644 --- a/test/vitest.setup.ts +++ b/test/vitest.setup.ts @@ -1,8 +1,7 @@ import "vitest-canvas-mock"; +import { initTestFile } from "#test/testUtils/testFileInitialization"; import { afterAll, beforeAll, vi } from "vitest"; -import { initTestFile } from "./testUtils/testFileInitialization"; - /** Set the timezone to UTC for tests. */ /** Mock the override import to always return default values, ignoring any custom overrides. */ diff --git a/tsconfig.json b/tsconfig.json index 6af3e9ce650..b7ef84869e5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,17 @@ "compilerOptions": { "target": "ES2020", "module": "ES2020", + // Modifying this option requires all values to be set manually because the defaults get overridden + // Values other than "ES2024.Promise" taken from https://github.com/microsoft/TypeScript/blob/main/src/lib/es2020.full.d.ts + "lib": [ + "ES2020", + "ES2024.Promise", + "DOM", + "DOM.AsyncIterable", + "DOM.Iterable", + "ScriptHost", + "WebWorker.ImportScripts" + ], "moduleResolution": "bundler", "resolveJsonModule": true, "esModuleInterop": true, @@ -11,18 +22,41 @@ "rootDir": ".", "baseUrl": "./src", "paths": { + "#abilities/*": ["./data/abilities/*.ts"], + "#api/*": ["./plugins/api/*.ts"], + "#balance/*": ["./data/balance/*.ts"], "#enums/*": ["./enums/*.ts"], - "#app/*": ["*.ts"], - "#test/*": ["../test/*.ts"] + "#events/*": ["./events/*.ts"], + "#field/*": ["./field/*.ts"], + "#inputs/*": ["./configs/inputs/*.ts"], + "#modifiers/*": ["./modifier/*.ts"], + "#moves/*": ["./data/moves/*.ts"], + "#mystery-encounters/*": [ + "./data/mystery-encounters/utils/*.ts", + "./data/mystery-encounters/encounters/*.ts", + "./data/mystery-encounters/requirements/*.ts", + "./data/mystery-encounters/*.ts" + ], + "#package.json": ["../package.json"], + "#phases/*": ["./phases/*.ts"], + "#plugins/*": ["./plugins/vite/*.ts", "./plugins/*.ts"], + "#sprites/*": ["./sprites/*.ts"], + "#system/*": [ + "./system/settings/*.ts", + "./system/version_migration/versions/*.ts", + "./system/version_migration/*.ts", + "./system/*.ts" + ], + "#trainers/*": ["./data/trainers/*.ts"], + "#types/*": ["./@types/*.ts", "./typings/phaser/*.ts"], + "#ui/*": ["./ui/battle-info/*.ts", "./ui/settings/*.ts", "./ui/*.ts"], + "#utils/*": ["./utils/*.ts"], + "#data/*": ["./data/pokemon-forms/*.ts", "./data/pokemon/*.ts", "./data/*.ts"], + "#test/*": ["../test/*.ts"], + "#app/*": ["*.ts"] }, "outDir": "./build", "noEmit": true }, - "typedocOptions": { - "entryPoints": ["./src"], - "entryPointStrategy": "expand", - "exclude": "**/*+.test.ts", - "out": "typedoc" - }, "exclude": ["node_modules", "dist", "vite.config.ts", "vitest.config.ts", "vitest.workspace.ts"] } diff --git a/tsdoc.json b/tsdoc.json new file mode 100644 index 00000000000..b4cbc9a62a5 --- /dev/null +++ b/tsdoc.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": false, + "tagDefinitions": [ + { + "tagName": "@todo", + "syntaxKind": "block" + }, + { + "tagName": "@linkcode", + "syntaxKind": "inline" + } + ] +} diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 00000000000..c34e6190c1a --- /dev/null +++ b/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["./src"], + "entryPointStrategy": "expand", + "exclude": ["**/*+.test.ts"], + "out": "typedoc", + "highlightLanguages": ["javascript", "json", "jsonc", "json5", "tsx", "typescript", "markdown"] +} diff --git a/vitest.config.ts b/vitest.config.ts index c781bde97ed..3900bc7b047 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,16 +1,6 @@ import { defineProject } from "vitest/config"; -import { defaultConfig } from "./vite.config"; import { BaseSequencer, type TestSpecification } from "vitest/node"; - -function getTestOrder(testName: string): number { - if (testName.includes("battle-scene.test.ts")) { - return 1; - } - if (testName.includes("inputs.test.ts")) { - return 2; - } - return 3; -} +import { defaultConfig } from "./vite.config"; export default defineProject(({ mode }) => ({ ...defaultConfig, @@ -18,15 +8,7 @@ export default defineProject(({ mode }) => ({ testTimeout: 20000, setupFiles: ["./test/fontFace.setup.ts", "./test/vitest.setup.ts"], sequence: { - sequencer: class CustomSequencer extends BaseSequencer { - async sort(files: TestSpecification[]) { - // use default sorting at first. - files = await super.sort(files); - // Except, forcibly reorder - - return files.sort((a, b) => getTestOrder(a.moduleId) - getTestOrder(b.moduleId)); - } - }, + sequencer: MySequencer, }, environment: "jsdom" as const, environmentOptions: { @@ -34,6 +16,10 @@ export default defineProject(({ mode }) => ({ resources: "usable", }, }, + typecheck: { + tsconfig: "tsconfig.json", + include: ["./test/types/**/*.{test,spec}{-|.}d.ts"], + }, threads: false, trace: true, restoreMocks: true, @@ -51,3 +37,38 @@ export default defineProject(({ mode }) => ({ keepNames: true, }, })); + +//#region Helpers + +/** + * Class for sorting test files in the desired order. + */ +class MySequencer extends BaseSequencer { + async sort(files: TestSpecification[]) { + files = await super.sort(files); + + return files.sort((a, b) => { + const aTestOrder = getTestOrder(a.moduleId); + const bTestOrder = getTestOrder(b.moduleId); + return aTestOrder - bTestOrder; + }); + } +} + +/** + * A helper function for sorting test files in a desired order. + * + * A lower number means that a test file must be run earlier, + * or else it breaks due to running tests with `--no-isolate.` + */ +function getTestOrder(testName: string): number { + if (testName.includes("battle-scene.test.ts")) { + return 1; + } + if (testName.includes("inputs.test.ts")) { + return 2; + } + return 3; +} + +//#endregion diff --git a/vitest.workspace.ts b/vitest.workspace.ts deleted file mode 100644 index 2f5d1f1e2c8..00000000000 --- a/vitest.workspace.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineWorkspace } from "vitest/config"; -import { defaultConfig } from "./vite.config"; - -export default defineWorkspace([ - { - ...defaultConfig, - test: { - name: "pre", - include: ["./test/pre.test.ts"], - environment: "jsdom", - }, - }, - "./vitest.config.ts", -]);