This commit is contained in:
Dean 2025-07-15 20:00:33 -07:00
commit d2f8495c1f
823 changed files with 19369 additions and 20788 deletions

View File

@ -323,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.

View File

@ -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)?

View File

@ -18,15 +18,24 @@ jobs:
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 }}

View File

@ -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

View File

@ -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'

View File

@ -23,17 +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
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
run: pnpm depcruise

View File

@ -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' || '' }}

View File

@ -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:

View File

@ -2,7 +2,8 @@
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 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.
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
@ -11,7 +12,6 @@ Please make sure you understand everything relevant to your changes from the [Ta
- [Getting Started](#-getting-started)
- [Documentation](#-documentation)
- [Testing Your Changes](#-testing-your-changes)
- [Localization](#-localization)
- [Development Save File (Unlock Everything)](#-development-save-file)
## 🛠️ Development Basics
@ -24,18 +24,15 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
### Prerequisites
- node: >=22.14.0
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- 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. Clone the repo and in the root directory run `npm install`
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 `npm run start:dev` to locally run the project in `localhost:8000`
### Linting
Check out our [in-depth file](./docs/linting.md) on linting and formatting!
2. Run `pnpm start:dev` to locally run the project at `localhost:8000`
## 🚀 Getting Started
@ -60,8 +57,13 @@ You are free to comment on any issue so that you may be assigned to it and we ca
## 📚 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.
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!
@ -91,72 +93,16 @@ You can get help testing your specific changes, and you might have found a new o
> 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 `npm run test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `npm run test:silent dancer`.
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 `npm run create-test` 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.
- 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.
## 📜 Localization
The project intends for all text to be localized. That is, strings are pulled from translation files using keys (depending on the current language) and *never* hardcoded as a particular language. Note that there is a PDF in a message pinned in **#dev-corner** which gives the following information in greater detail.
### Setting Up and Updating the Locales Submodule
> The locales (translation) files are set up as a git submodule. A project-in-a-project, if you will.
To fetch translations when you first start development in your fork or to update them on your local branch, run:
```bash
git submodule update --progress --init --recursive
```
### How Localizations Work
> This project uses the [i18next](https://www.i18next.com/) library to integrate translations from public/locales
into the source code based on the user's settings or location. The basic process for
fetching translated text is as follows:
1. The source code fetches text by a given key, e.g.
```ts
i18next.t("fileName:keyName", { arg1: "Hello", arg2: "an example", ... })
```
2. The game looks up the key in the corresponding JSON file in the user's
language, e.g.
```ts
// from "en/file-name.json"...
"keyName": "{{arg1}}! This is {{arg2}} of translated text!"
```
If the key doesn't exist for the user's language, the game will default to the
corresponding English key (in the case of LATAM Spanish, it will first default to ES Spanish).
3. The game shows the text to the user:
```ts
"Hello! This is an example of translated text!"
```
### Adding Translated Text
> If your feature involves new or modified text in any form, then you will be modifying the [locales](https://github.com/pagefaultgames/pokerogue-locales) repository. ***Never hardcode new text in any language!***
The workflow is:
1. 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 pokerogue-locales repository.
2. Make another pull request -- this time to the [pokerogue-locales](https://github.com/pagefaultgames/pokerogue-locales)
repository -- adding a new entry to the English locale with text for each key
you added to your main PR. You *only* need to add the English key and value - the translation team will handle the rest.
3. If your feature is pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), add a source link for any added text within the locale PR.
[Poké Corpus](https://abcboy101.github.io/poke-corpus) is a great resource for finding text from the latest mainline games; otherwise, a YouTube video link showing the text in mainline is sufficient.
4. Ping @lugiadrien in **#dev-corner** or the current callout thread to make sure your locales PR is seen.
It'll be merged into the locales repository after any necessary corrections, at which point you can test it in your main PR (after updating locales from remote)
5. The Dev team will approve your main PR, and your changes will be in the beta environment!
## 😈 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).

View File

@ -27,6 +27,7 @@
"!**/.github/**/*",
"!**/node_modules/**/*",
"!**/.vscode/**/*",
"!**/typedoc/**/*",
// TODO: lint css and html?
"!**/*.css",
"!**/*.html",
@ -37,14 +38,13 @@
]
},
// TODO: Configure and enable import sorting
"assist": {
"actions": {
"source": {
"organizeImports": {
"level": "off",
"level": "on",
"options": {
"groups": []
"groups": [":ALIAS:", ":NODE:", ":PACKAGE_WITH_PROTOCOL:", ":PACKAGE:", ":PATH:"]
}
}
}
@ -69,7 +69,10 @@
},
"style": {
"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

View File

@ -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);

View File

@ -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`:

View File

@ -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?
<!-- TODO: Remove this if/when we perform a project wide linting spree -->
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?
<!-- Remove if/when we finally ditch eslint for good -->
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.

142
docs/localization.md Normal file
View File

@ -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).
<!-- Remember to update this everytime the head of translation changes! -->
> [!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.

View File

@ -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

6132
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,18 +21,17 @@
"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": "2.0.0",
"@hpcc-js/wasm": "^2.22.4",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.13.14",
"@vitest/coverage-istanbul": "^3.0.9",
"chalk": "^5.4.1",
"dependency-cruiser": "^16.3.10",
"inquirer": "^12.4.2",
"jsdom": "^26.0.0",

3899
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

6
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,6 @@
onlyBuiltDependencies:
- esbuild
- msw
- lefthook
shellEmulator: true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/fonts/pokemon-bw.ttf Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

View File

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 327 B

@ -1 +1 @@
Subproject commit fade123e20ff951e199d7c0466686fe8c5511643
Subproject commit 288ffa034d07a90ea227710703eb1331b99ffed0

View File

@ -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

View File

@ -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";

View File

@ -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 <encrypted-file> [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";

View File

@ -1,4 +1,4 @@
import type { UserInfo } from "#app/@types/UserInfo";
import type { UserInfo } from "#types/UserInfo";
export interface AccountInfoResponse extends UserInfo {}

View File

@ -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;

View File

@ -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;

View File

@ -1,4 +1,4 @@
import type { SystemSaveData } from "#app/system/game-data";
import type { SystemSaveData } from "#system/game-data";
export interface GetSystemSavedataRequest {
clientSessionId: string;

View File

@ -1,4 +1,4 @@
import type { SessionSaveData } from "#app/system/game-data";
import type { SessionSaveData } from "#system/game-data";
export interface SessionSaveMigrator {
version: string;

View File

@ -1,4 +1,4 @@
import type { SystemSaveData } from "#app/system/game-data";
import type { SystemSaveData } from "#system/game-data";
export interface SystemSaveMigrator {
version: string;

View File

@ -1,13 +1,13 @@
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 abilities to have this be the centralized place to import ability types
export type * from "#app/data/abilities/ability";
export type * from "#abilities/ability";
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
import type { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
import type { applyAbAttrs } from "#abilities/apply-ab-attrs";
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;

View File

@ -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;
}

View File

@ -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;
}

18
src/@types/enum-types.ts Normal file
View File

@ -0,0 +1,18 @@
/** Union type accepting any TS Enum or `const object`, with or without reverse mapping. */
export type EnumOrObject = Record<string | number, string | number>;
/**
* 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> = E[keyof E];
/**
* Generic type constraint representing a TS numeric enum with reverse mappings.
* @example
* TSNumericEnum<typeof WeatherType>
*/
export type TSNumericEnum<T extends EnumOrObject> = number extends EnumValues<T> ? T : never;
/** Generic type constraint representing a non reverse-mapped TS enum or `const object`. */
export type NormalEnum<T extends EnumOrObject> = Exclude<T, TSNumericEnum<T>>;

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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 { TrainerPartyTemplate } from "#trainers/TrainerPartyTemplate";
import type { TrainerConfig } from "#trainers/trainer-config";
export type PartyTemplateFunc = () => TrainerPartyTemplate;
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;

13
src/@types/turn-move.ts Normal file
View File

@ -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;
}

View File

@ -2,8 +2,11 @@
* A collection of custom utility types that aid in type checking and ensuring strict type conformity
*/
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
import type { AbAttr } from "./ability-types";
// 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.
@ -11,7 +14,7 @@ import type { AbAttr } from "./ability-types";
* 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 the apply method
* the type of its {@linkcode AbAttr.apply | apply} method.
*
* @typeParam T - The type to match exactly
*/
@ -26,9 +29,18 @@ export type Exact<T> = {
export type Closed<X> = X;
/**
* Remove `readonly` from all properties of the provided type
* @typeParam T - The type to make mutable
* Remove `readonly` from all properties of the provided type.
* @typeParam T - The type to make mutable.
*/
export type Mutable<T> = {
-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<O extends Record<keyof any, unknown>, V extends EnumValues<O>> = {
[K in keyof O]: O[K] extends V ? K : never;
}[keyof O];

View File

@ -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

View File

@ -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 type { 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 } 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;
@ -800,12 +791,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[] {
@ -885,8 +878,8 @@ export default class BattleScene extends SceneBase {
}
// 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
@ -934,9 +927,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;
}
@ -981,10 +997,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();
@ -2147,6 +2178,7 @@ export default class BattleScene extends SceneBase {
),
]
: allSpecies.filter(s => s.isCatchable());
// TODO: should this use `randSeedItem`?
return filteredSpecies[randSeedInt(filteredSpecies.length)];
}
@ -2172,6 +2204,7 @@ export default class BattleScene extends SceneBase {
}
}
// TODO: should this use `randSeedItem`?
return biomes[randSeedInt(biomes.length)];
}
@ -2488,6 +2521,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
@ -3691,6 +3728,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);

View File

@ -1,40 +1,40 @@
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 { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
import { ModifierTier } from "#enums/modifier-tier";
import type { MoveId } from "#enums/move-id";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PlayerGender } from "#enums/player-gender";
import { MusicPreference } from "#app/system/settings/settings";
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 +54,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 +94,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) {
@ -375,6 +381,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";

View File

@ -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",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,92 +1,80 @@
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";
import type { Closed, Exact } from "#app/@types/type-helpers";
} 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";
// biome-ignore-start lint/correctness/noUnusedImports: Used in TSDoc
import type BattleScene from "#app/battle-scene";
import type { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms/form-change-triggers";
// biome-ignore-end lint/correctness/noUnusedImports: Used in TSDoc
export class Ability implements Localizable {
public id: AbilityId;
@ -1194,7 +1182,16 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
}
}
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;
@ -1216,12 +1213,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 {
@ -1607,37 +1598,38 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
}
}
/** 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 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 apply({ simulated, pokemon, move }: AugmentMoveInteractionAbAttrParams): void {
@ -3047,61 +3039,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 canApply({ pokemon, simulated }: AbAttrBaseParams): 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;
}
// TODO: Consider moving the simulated check to the apply method
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 apply({ pokemon }: AbAttrBaseParams): void {
const target = this.getTarget(pokemon.getOpponents());
globalScene.phaseManager.unshiftNew(
"PokemonTransformPhase",
pokemon.getBattlerIndex(),
target.getBattlerIndex(),
true,
);
globalScene.phaseManager.unshiftNew("PokemonTransformPhase", pokemon.getBattlerIndex(), this.targetIndex, true);
}
}
@ -3164,7 +3135,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 {
@ -6417,7 +6388,7 @@ const AbilityAttrs = Object.freeze({
PostDefendContactApplyStatusEffectAbAttr,
EffectSporeAbAttr,
PostDefendContactApplyTagChanceAbAttr,
PostDefendCritStatStageChangeAbAttr,
PostReceiveCritStatStageChangeAbAttr,
PostDefendContactDamageAbAttr,
PostDefendPerishSongAbAttr,
PostDefendWeatherChangeAbAttr,
@ -6886,7 +6857,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
@ -7101,7 +7072,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
@ -7177,8 +7149,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(),
@ -7263,11 +7237,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()
@ -7333,12 +7310,11 @@ export function initAbilities() {
.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)
.attr(NoFusionAbilityAbAttr)
.uncopiable()
.unreplaceable()
@ -7429,8 +7405,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)),

View File

@ -1,6 +1,5 @@
import type { AbAttrParamMap } from "#app/@types/ability-types";
import type { AbAttrBaseParams, AbAttrString, CallableAbAttrString } from "#app/@types/ability-types";
import { globalScene } from "#app/global-scene";
import type { AbAttrBaseParams, AbAttrParamMap, AbAttrString, CallableAbAttrString } from "#types/ability-types";
function applySingleAbAttrs<T extends AbAttrString>(
attrType: T,

View File

@ -1,26 +1,26 @@
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, isNullOrUndefined, 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 { BooleanHolder, isNullOrUndefined, NumberHolder, toDmgValue } from "#utils/common";
import i18next from "i18next";
export abstract class ArenaTag {
constructor(

File diff suppressed because it is too large Load Diff

View File

@ -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,17 +584,24 @@ 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 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[] = [];
@ -602,14 +609,16 @@ function parseEggMoves(content: string): void {
for (let m = 0; m < 4; m++) {
const moveName = cols[m + 1].trim();
const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1;
eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE);
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`;
}
}

View File

@ -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 },

View File

@ -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,

View File

@ -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.

View File

@ -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,

View File

@ -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;
@ -880,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`
@ -1402,10 +1399,10 @@ 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 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 chargeAnimIds = getEnumValues(ChargeAnim);
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
const moveNameToId = {};
for (const move of getEnumValues(MoveId).slice(1)) {

View File

@ -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}.
@ -673,7 +671,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 {

View File

@ -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`);

View File

@ -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;

View File

@ -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<CustomPokemonData>) {
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;
}
}

View File

@ -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)];
}

View File

@ -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[] = [];

View File

@ -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];

View File

@ -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

View File

@ -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;
@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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, PendingHealTag } from "#data/arena-tag";
import { WeakenMoveTypeTag } from "#data/arena-tag";
import { MoveChargeAnim } from "#data/battle-anims";
import {
CommandedTag,
EncoreTag,
@ -11,31 +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, PendingHealTag } 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
} 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,
@ -43,52 +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 type { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types";
import { applyMoveAttrs } from "./apply-attrs";
import { frenzyMissFunc, getMoveTargets } from "./move-utils";
import { AbAttrBaseParams, AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "../abilities/ability";
/**
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
@ -97,7 +98,7 @@ import { AbAttrBaseParams, AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrPar
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;
@ -680,13 +681,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));
}
/**
@ -4090,30 +4085,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)) {
@ -7606,7 +7577,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 +7588,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)
}
}
/**
@ -8293,7 +8267,6 @@ const MoveAttrs = Object.freeze({
CompareWeightPowerAttr,
HpPowerAttr,
OpponentHighHpPowerAttr,
FirstAttackDoublePowerAttr,
TurnDamagedDoublePowerAttr,
MagnitudePowerAttr,
AntiSunlightPowerDecreaseAttr,
@ -8841,12 +8814,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 +9569,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 +10782,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 ]),

View File

@ -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.

View File

@ -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";

View File

@ -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";

View File

@ -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 */

View File

@ -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();

View File

@ -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 { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/TrainerPartyTemplate";
import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config";
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)]);
}

View File

@ -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 { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/TrainerPartyTemplate";
import { trainerConfigs } from "#trainers/trainer-config";
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";
@ -139,6 +138,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
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 };

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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;

View File

@ -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 {
TrainerPartyCompoundTemplate,
TrainerPartyTemplate,
trainerPartyTemplates,
} from "#trainers/TrainerPartyTemplate";
import { trainerConfigs } from "#trainers/trainer-config";
import { randSeedInt } from "#utils/common";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/mysteriousChallengers";

View File

@ -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";

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