diff --git a/.dependency-cruiser.cjs b/.dependency-cruiser.cjs
index 08daf98fca4..8b956a11a11 100644
--- a/.dependency-cruiser.cjs
+++ b/.dependency-cruiser.cjs
@@ -5,9 +5,7 @@ module.exports = {
name: "no-non-type-@type-exports",
severity: "error",
comment:
- "Files in @types should not export anything but types and interfaces. " +
- "The folder is intended to house imports that are removed at runtime, " +
- "and thus should not contain anything with a bearing on runtime code.",
+ "Files in @types should not export anything but types and interfaces. The folder is intended to house imports that are removed at runtime, and thus should not contain anything with a bearing on runtime code.",
from: {},
to: {
path: "(^|/)src/@types",
@@ -29,8 +27,7 @@ module.exports = {
name: "no-circular-at-runtime",
severity: "error",
comment:
- "This dependency is part of a circular relationship. You might want to revise " +
- "your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
+ "This dependency is part of a circular relationship. You might want to revise your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
from: {},
to: {
circular: true,
@@ -42,11 +39,7 @@ module.exports = {
{
name: "no-orphans",
comment:
- "This is an orphan module - it's likely not used (anymore?). Either use it or " +
- "remove it. If it's logical this module is an orphan (i.e. it's a config file), " +
- "add an exception for it in your dependency-cruiser configuration. By default " +
- "this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
- "files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
+ "This is an orphan module - it's likely not used (anymore?). Either use it or remove it. If it's logical this module is an orphan (i.e. it's a config file), add an exception for it in your dependency-cruiser configuration. By default this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
severity: "error",
from: {
orphan: true,
@@ -63,8 +56,7 @@ module.exports = {
{
name: "no-deprecated-core",
comment:
- "A module depends on a node core module that has been deprecated. Find an alternative - these are " +
- "bound to exist - node doesn't deprecate lightly.",
+ "A module depends on a node core module that has been deprecated. Find an alternative - these are bound to exist - node doesn't deprecate lightly.",
severity: "error",
from: {},
to: {
@@ -96,8 +88,7 @@ module.exports = {
{
name: "not-to-deprecated",
comment:
- "This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " +
- "version of that module, or find an alternative. Deprecated modules are a security risk.",
+ "This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later version of that module, or find an alternative. Deprecated modules are a security risk.",
severity: "error",
from: {},
to: {
@@ -108,10 +99,7 @@ module.exports = {
name: "no-non-package-json",
severity: "error",
comment:
- "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " +
- "That's problematic as the package either (1) won't be available on live (2 - worse) will be " +
- "available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " +
- "in your package.json.",
+ "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. That's problematic as the package either (1) won't be available on live (2 - worse) will be available on live with an non-guaranteed version. Fix it by adding the package to the dependencies in your package.json.",
from: {},
to: {
dependencyTypes: ["npm-no-pkg", "npm-unknown"],
@@ -120,8 +108,7 @@ module.exports = {
{
name: "not-to-unresolvable",
comment:
- "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " +
- "module: add it to your package.json. In all other cases you likely already know what to do.",
+ "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm module: add it to your package.json. In all other cases you likely already know what to do.",
severity: "error",
from: {},
to: {
@@ -131,9 +118,7 @@ module.exports = {
{
name: "no-duplicate-dep-types",
comment:
- "Likely this module depends on an external ('npm') package that occurs more than once " +
- "in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
- "maintenance problems later on.",
+ "Likely this module depends on an external ('npm') package that occurs more than once in your package.json i.e. bot as a devDependencies and in dependencies. This will cause maintenance problems later on.",
severity: "error",
from: {},
to: {
@@ -150,9 +135,7 @@ module.exports = {
{
name: "not-to-spec",
comment:
- "This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. " +
- "If there's something in a spec that's of use to other modules, it doesn't have that single " +
- "responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.",
+ "This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. If there's something in a spec that's of use to other modules, it doesn't have that single responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.",
severity: "error",
from: {},
to: {
@@ -163,11 +146,7 @@ module.exports = {
name: "not-to-dev-dep",
severity: "error",
comment:
- "This module depends on an npm package from the 'devDependencies' section of your " +
- "package.json. It looks like something that ships to production, though. To prevent problems " +
- "with npm packages that aren't there on production declare it (only!) in the 'dependencies'" +
- "section of your package.json. If this module is development only - add it to the " +
- "from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration",
+ "This module depends on an npm package from the 'devDependencies' section of your package.json. It looks like something that ships to production, though. To prevent problems with npm packages that aren't there on production declare it (only!) in the 'dependencies'section of your package.json. If this module is development only - add it to the from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration",
from: {
path: "^(src)",
pathNot: ["[.](?:spec|test|setup|script)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$", "./test"],
@@ -184,10 +163,7 @@ module.exports = {
name: "optional-deps-used",
severity: "info",
comment:
- "This module depends on an npm package that is declared as an optional dependency " +
- "in your package.json. As this makes sense in limited situations only, it's flagged here. " +
- "If you're using an optional dependency here by design - add an exception to your" +
- "dependency-cruiser configuration.",
+ "This module depends on an npm package that is declared as an optional dependency in your package.json. As this makes sense in limited situations only, it's flagged here. If you're using an optional dependency here by design - add an exception to yourdependency-cruiser configuration.",
from: {},
to: {
dependencyTypes: ["npm-optional"],
@@ -196,10 +172,7 @@ module.exports = {
{
name: "peer-deps-used",
comment:
- "This module depends on an npm package that is declared as a peer dependency " +
- "in your package.json. This makes sense if your package is e.g. a plugin, but in " +
- "other cases - maybe not so much. If the use of a peer dependency is intentional " +
- "add an exception to your dependency-cruiser configuration.",
+ "This module depends on an npm package that is declared as a peer dependency in your package.json. This makes sense if your package is e.g. a plugin, but in other cases - maybe not so much. If the use of a peer dependency is intentional add an exception to your dependency-cruiser configuration.",
severity: "error",
from: {},
to: {
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000000..219c096336b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+# .dockerignore
+node_modules
+*.log
+*.md
+.gitignore
+Dockerfile
+.env
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index be478a2b5fa..fc5cfbe61e3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,7 +3,7 @@
# top-most EditorConfig file
root = true
-[src/*.{js,ts}]
+[**/*.{js,ts,json,jsonc}]
indent_style = space
indent_size = 2
end_of_line = lf
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
index b1543b2cb44..fea857355a0 100644
--- a/.github/workflows/create-release.yml
+++ b/.github/workflows/create-release.yml
@@ -20,6 +20,7 @@ permissions:
jobs:
create-release:
if: github.repository == 'pagefaultgames/pokerogue' && (vars.BETA_DEPLOY_BRANCH == '' || ! startsWith(vars.BETA_DEPLOY_BRANCH, 'release'))
+ timeout-minutes: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for github cli commands
runs-on: ubuntu-latest
@@ -36,11 +37,13 @@ jobs:
exit 1
fi
shell: bash
+
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ secrets.PAGEFAULT_APP_ID }}
private-key: ${{ secrets.PAGEFAULT_APP_PRIVATE_KEY }}
+
- name: Check out code
uses: actions/checkout@v4
with:
@@ -48,21 +51,26 @@ jobs:
# Always base off of beta branch, regardless of the branch the workflow was triggered from.
ref: beta
token: ${{ steps.app-token.outputs.token }}
+
- name: Create release branch
run: git checkout -b release
- # In order to be able to open a PR into beta, we need the branch to have at least one change.
- - name: Overwrite RELEASE file
+
+ # In order to be able to open a PR into beta, we need the branch to have at least one commit.
+ # The first commit is _usually_ just bumping the version number, so we can kill 2 birds with 1 stone here
+ - name: Bump release version
run: |
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
- echo "Release v${{ github.event.inputs.versionName }}" > RELEASE
- git add RELEASE
- git commit -m "Stage release v${{ github.event.inputs.versionName }}"
+ pnpm --no-git-tag-version version ${{ github.events.inputs.versionName }}
+ git commit -am "Stage release for v${{ github.events.inputs.versionName }}"
+
- name: Push new branch
run: git push origin release
+
# The repository variable is used by the deploy-beta workflow to determine whether to deploy from beta or release.
- name: Set repository variable
run: GITHUB_TOKEN="${{ steps.app-token.outputs.token }}" gh variable set BETA_DEPLOY_BRANCH --body "release"
+
- name: Create pull request to main
run: |
gh pr create --base main \
@@ -70,6 +78,7 @@ jobs:
--title "Release v${{ github.event.inputs.versionName }} to main" \
--body "This PR is for the release of v${{ github.event.inputs.versionName }}, and was created automatically by the GitHub Actions workflow invoked by ${{ github.actor }}" \
--draft
+
- name: Create pull request to beta
run: |
gh pr create --base beta \
diff --git a/.github/workflows/deploy-beta.yml b/.github/workflows/deploy-beta.yml
index 0894032c8ad..5abba4488be 100644
--- a/.github/workflows/deploy-beta.yml
+++ b/.github/workflows/deploy-beta.yml
@@ -12,6 +12,7 @@ on:
jobs:
deploy:
if: github.repository == 'pagefaultgames/pokerogue' && github.ref_name == (vars.BETA_DEPLOY_BRANCH || 'beta')
+ timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -21,8 +22,6 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v4
- with:
- version: 10
- uses: actions/setup-node@v4
with:
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 0e7102a41dd..1f2c1259dd1 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -11,6 +11,7 @@ on:
jobs:
deploy:
if: github.repository == 'pagefaultgames/pokerogue'
+ timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -19,8 +20,6 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v4
- with:
- version: 10
- uses: actions/setup-node@v4
with:
diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml
index 84d5964064f..d8482cba83c 100644
--- a/.github/workflows/github-pages.yml
+++ b/.github/workflows/github-pages.yml
@@ -6,11 +6,13 @@ on:
- main
- beta
- release
+ - 'hotfix*'
pull_request:
branches:
- main
- beta
- release
+ - 'hotfix*'
merge_group:
types: [checks_requested]
@@ -18,31 +20,32 @@ jobs:
pages:
name: Github Pages
if: github.repository == 'pagefaultgames/pokerogue'
+ timeout-minutes: 10
runs-on: ubuntu-latest
env:
- api-dir: ./
-
- strategy:
- fail-fast: false
+ docs-dir: ./pokerogue_docs
+ # Only push docs when running on pushes to main/beta
+ DRY_RUN: ${{github.event_name != 'push' || (github.ref_name != 'beta' && github.ref_name != 'main')}}
steps:
- name: Checkout repository for Typedoc
uses: actions/checkout@v4
with:
- submodules: 'recursive'
path: pokerogue_docs
-
- - name: Install OS package
- run: |
- sudo apt update
- sudo apt install -y git openssh-client
+ sparse-checkout: |
+ /*
+ !/public/
+ /public/images/pokemon/variant/_exp_masterlist.json
+ /public/images/pokemon/variant/_masterlist.json
+ /public/images/logo.png
+ sparse-checkout-cone-mode: false
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- - name: Setup Node 22.14.1
+ - name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: "pokerogue_docs/.nvmrc"
@@ -55,26 +58,24 @@ jobs:
ref: gh-pages
- name: Install Node.js dependencies
- working-directory: ${{env.api-dir}}
- run: |
- cd pokerogue_docs
- pnpm i
+ working-directory: ${{env.docs-dir}}
+ run: pnpm i
- name: Generate Typedoc docs
- working-directory: ${{env.api-dir}}
- run: |
- cd pokerogue_docs
- pnpm exec typedoc --out /tmp/docs --githubPages false --entryPoints ./src/
+ working-directory: ${{env.docs-dir}}
+ env:
+ REF_NAME: ${{github.ref_name}}
+ DRY_RUN: ${{env.DRY_RUN}}
+ run: pnpm typedoc
- name: Commit & Push docs
- if: github.event_name == 'push'
+ # env vars are stored as strings instead of booleans (hence why an explicit check is required)
+ if: ${{ env.DRY_RUN == 'false'}}
run: |
cd pokerogue_gh
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
- mkdir -p $GITHUB_REF_NAME
- rm -rf $GITHUB_REF_NAME/*
- cp -r /tmp/docs/. $GITHUB_REF_NAME
+ rsync -rd --delete /tmp/docs/ $GITHUB_REF_NAME
git add $GITHUB_REF_NAME
- git commit --allow-empty -m "[skip ci] Deploy docs"
- git push
\ No newline at end of file
+ git commit -m "[skip ci] Deploy docs"
+ git push
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index a5e8f96961e..e1314c2cbd3 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -6,44 +6,104 @@ on:
- main
- beta
- release
+ - 'hotfix*'
pull_request:
branches:
- main
- beta
- release
+ - 'hotfix*'
merge_group:
types: [checks_requested]
jobs:
run-linters:
- name: Run linters
+ name: Run all linters
+ timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
with:
- submodules: 'recursive'
+ submodules: "recursive"
- name: Install pnpm
uses: pnpm/action-setup@v4
- with:
- version: 10
- - name: Set up Node.js
+ - name: Set up Node
uses: actions/setup-node@v4
with:
- node-version-file: '.nvmrc'
- cache: 'pnpm'
+ node-version-file: ".nvmrc"
+ cache: "pnpm"
- - name: Install Node.js dependencies
+ - name: Install Node modules
run: pnpm i
- - name: Lint with Biome
+ # Lint files with Biome-Lint - https://biomejs.dev/linter/
+ - name: Run Biome-Lint
run: pnpm biome-ci
+ id: biome_lint
+ continue-on-error: true
- - name: Check dependencies with depcruise
+ # Validate dependencies with dependency-cruiser - https://github.com/sverweij/dependency-cruiser
+ - name: Run Dependency-Cruise
run: pnpm depcruise
-
- - name: Lint with ls-lint
- run: pnpm ls-lint
\ No newline at end of file
+ id: depcruise
+ continue-on-error: true
+
+ # Validate types with tsc - https://www.typescriptlang.org/docs/handbook/compiler-options.html#using-the-cli
+ - name: Run Typecheck
+ run: pnpm typecheck
+ id: typecheck
+ continue-on-error: true
+
+ # The exact same thing
+ - name: Run Typecheck (scripts)
+ run: pnpm typecheck:scripts
+ id: typecheck-scripts
+ continue-on-error: true
+
+ - name: Evaluate for Errors
+ env:
+ BIOME_LINT_OUTCOME: ${{ steps.biome_lint.outcome }}
+ DEPCRUISE_OUTCOME: ${{ steps.depcruise.outcome }}
+ TYPECHECK_OUTCOME: ${{ steps.typecheck.outcome }}
+ TYPECHECK_SCRIPTS_OUTCOME: ${{ steps.typecheck-scripts.outcome }}
+ run: |
+ # Check for Errors
+
+ # Make text red.
+ red () {
+ printf "\e[31m%s\e[0m" "$1"
+ }
+
+ # Make text green.
+ green () {
+ printf "\e[32m%s\e[0m" "$1"
+ }
+
+ print_result() {
+ local name=$1
+ local outcome=$2
+ if [ "$outcome" == "success" ]; then
+ printf "$(green "✅ $name: $outcome")\n"
+ else
+ printf "$(red "❌ $name: $outcome")\n"
+ fi
+ }
+
+ print_result "Biome" "$BIOME_LINT_OUTCOME"
+ print_result "Depcruise" "$DEPCRUISE_OUTCOME"
+ print_result "Typecheck" "$TYPECHECK_OUTCOME"
+ print_result "Typecheck scripts" "$TYPECHECK_SCRIPTS_OUTCOME"
+
+ if [[ "$BIOME_LINT_OUTCOME" != "success" || \
+ "$DEPCRUISE_OUTCOME" != "success" || \
+ "$TYPECHECK_OUTCOME" != "success" || \
+ "$TYPECHECK_SCRIPTS_OUTCOME" != "success" ]]; then
+ printf "$(red "❌ One or more checks failed!")\n" >&2
+ exit 1
+ fi
+
+ printf "$(green "✅ All checks passed!")\n"
diff --git a/.github/workflows/post-release-deleted.yml b/.github/workflows/post-release-deleted.yml
index 65447e7826b..fe542365da4 100644
--- a/.github/workflows/post-release-deleted.yml
+++ b/.github/workflows/post-release-deleted.yml
@@ -6,6 +6,7 @@ jobs:
# Set the BETA_DEPLOY_BRANCH variable to beta when a release branch is deleted
update-release-var:
if: github.repository == 'pagefaultgames/pokerogue' && github.event.ref_type == 'branch' && github.event.ref == 'release'
+ timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Set BETA_DEPLOY_BRANCH to beta
diff --git a/.github/workflows/test-shard-template.yml b/.github/workflows/test-shard-template.yml
index 124004f380f..6f4728863b4 100644
--- a/.github/workflows/test-shard-template.yml
+++ b/.github/workflows/test-shard-template.yml
@@ -21,6 +21,7 @@ jobs:
test:
# We can't use dynmically named jobs until https://github.com/orgs/community/discussions/13261 is implemented
name: Shard
+ timeout-minutes: 10
runs-on: ubuntu-latest
if: ${{ !inputs.skip }}
steps:
@@ -31,8 +32,6 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v4
- with:
- version: 10
- name: Set up Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2b9f6dc9c0d..e1d6e3af60e 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -6,17 +6,20 @@ on:
- main
- beta
- release
+ - 'hotfix*'
pull_request:
branches:
- main
- beta
- release
+ - 'hotfix*'
merge_group:
types: [checks_requested]
workflow_dispatch:
jobs:
check-path-change-filter:
+ timeout-minutes: 5
runs-on: ubuntu-latest
permissions:
pull-requests: read
@@ -25,6 +28,10 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v4
+ with:
+ sparse-checkout: |
+ .github/test-filters.yml
+ sparse-checkout-cone-mode: false
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36
id: filter
@@ -35,6 +42,8 @@ jobs:
name: Run Tests
needs: check-path-change-filter
strategy:
+ # don't stop upon 1 shard failing
+ fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
uses: ./.github/workflows/test-shard-template.yml
diff --git a/.gitignore b/.gitignore
index 299767e742a..cdc0660d68c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
+build
# Editor directories and files (excluding `extensions.json` for devcontainer)
*.code-workspace
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 81abc8df2c0..2197ad03ef2 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -10,4 +10,4 @@
"aaron-bond.better-comments",
"MuTsunTsai.jsdoc-link"
]
-}
\ No newline at end of file
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 04ab7ff4faa..c24b648c490 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -80,7 +80,8 @@ 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)
+- [Enemy AI move selection](./docs/enemy-ai.md)
+- [Running with Podman](./docs/podman.md)
Again, if you have unanswered questions please feel free to ask!
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000000..ddb865b4831
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,47 @@
+# syntax=docker/dockerfile:1
+ARG NODE_VERSION=22.14
+ARG OS=alpine
+
+FROM node:${NODE_VERSION}-${OS}
+
+# Create non-root user
+RUN addgroup -S appgroup && adduser -S appuser -G appgroup
+
+# Install git (for potential runtime needs)
+RUN apk add --no-cache git
+
+# Set working directory
+WORKDIR /app
+
+# Enable and prepare pnpm
+RUN corepack enable && corepack prepare pnpm@10.14.0 --activate
+
+COPY . .
+
+# Copy package files
+COPY package.json pnpm-lock.yaml ./
+
+# Install all dependencies
+RUN --mount=type=cache,target=/home/appuser/.pnpm-store \
+ pnpm install --frozen-lockfile && \
+ rm -rf /home/appuser/.pnpm-store/*
+
+# Change ownership
+RUN chown -R appuser:appgroup /app
+
+# Switch to non-root user
+USER appuser
+
+# Set environment variables
+ENV VITE_BYPASS_LOGIN=1 \
+ VITE_BYPASS_TUTORIAL=0 \
+ NEXT_TELEMETRY_DISABLED=1 \
+ PNP_HOME=/home/appuser/.shrc \
+ NODE_ENV=development \
+ PORT=8000
+
+# Expose port
+EXPOSE $PORT
+
+# Start the app in development mode
+CMD ["pnpm", "run", "start:podman"]
diff --git a/README.md b/README.md
index 1bb8c7772f3..73477968bc0 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-
+
+
+[](https://discord.gg/pokerogue)
+[](https://pagefaultgames.github.io/pokerogue/beta)
+[](https://github.com/pagefaultgames/pokerogue/actions/workflows/tests.yml)
+[](https://www.gnu.org/licenses/agpl-3.0)
PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, fighting trainers, bosses, and more!
@@ -7,7 +12,7 @@ PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite
See [CONTRIBUTING.md](./CONTRIBUTING.md), this includes instructions on how to set up the game locally.
# 📝 Credits
->
+
> If this project contains assets you have produced and you do not see your name, **please** reach out, either [here on GitHub](https://github.com/pagefaultgames/pokerogue/issues/new) or via [Discord](https://discord.gg/pokerogue).
Thank you to all the wonderful people that have contributed to the PokéRogue project! You can find the credits [here](./CREDITS.md).
diff --git a/RELEASE b/RELEASE
deleted file mode 100644
index a1a9f30b0e8..00000000000
--- a/RELEASE
+++ /dev/null
@@ -1 +0,0 @@
-Release v1.10.0
diff --git a/biome.jsonc b/biome.jsonc
index a63ce0ee07d..e1aac032597 100644
--- a/biome.jsonc
+++ b/biome.jsonc
@@ -1,7 +1,7 @@
{
- "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.2.3/schema.json",
"vcs": {
- "enabled": false,
+ "enabled": true,
"clientKind": "git",
"useIgnoreFile": true,
"defaultBranch": "beta"
@@ -10,7 +10,7 @@
"enabled": true,
"useEditorconfig": true,
"indentStyle": "space",
- "includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"],
+ "includes": ["**", "!**/src/data/balance/**"],
"lineWidth": 120
},
"files": {
@@ -19,14 +19,12 @@
// and having to verify whether each individual file is ignored
"includes": [
"**",
- "!**/dist/**/*",
- "!**/build/**/*",
- "!**/coverage/**/*",
- "!**/public/**/*",
- "!**/.github/**/*",
- "!**/node_modules/**/*",
- "!**/.vscode/**/*",
- "!**/typedoc/**/*",
+ "!**/dist",
+ "!**/coverage",
+ "!**/public",
+ "!**/.github",
+ "!**/node_modules",
+ "!**/typedoc",
// TODO: lint css and html?
"!**/*.css",
"!**/*.html",
@@ -48,28 +46,75 @@
}
}
},
+ // TODO: Remove unneeded `options` blocks once biome's JSON schema is fixed to not require them
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
- "noUndeclaredVariables": "off",
+ "noUndeclaredVariables": "error",
"noUnusedVariables": "error",
"noSwitchDeclarations": "error",
"noVoidTypeReturn": "error",
"noUnusedImports": {
"level": "error",
- "fix": "safe"
+ "fix": "safe",
+ "options": {}
},
"noUnusedFunctionParameters": "error",
"noUnusedLabels": "error",
- "noPrivateImports": "error"
+ "noPrivateImports": "error",
+ "useSingleJsDocAsterisk": "error",
+ "useJsonImportAttributes": "off" // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)"
},
"style": {
- "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
+ "useExplicitLengthCheck": {
+ "level": "error",
+ "fix": "safe",
+ "options": {}
+ },
+ "useAtIndex": "error",
+ "noNegationElse": {
+ "level": "info", // TODO: Promote to error eventually
+ "fix": "unsafe", // duplicates else blocks
+ "options": {}
+ },
+ // TODO: Fix all instances of this and promote to `error` - this and enums are the 2 things
+ // barring us from `esModuleInterop`
+ "noParameterProperties": "warn",
+ "useConsistentBuiltinInstantiation": {
+ "level": "error",
+ "fix": "safe",
+ "options": {}
+ },
+ "noDefaultExport": "warn", // TODO: Fix `overrides.ts` and enable
+ "noShoutyConstants": "error",
+ "useThrowNewError": {
+ "level": "error",
+ "fix": "safe",
+ "options": {}
+ },
+ "useThrowOnlyError": "error",
+ "useTrimStartEnd": "error",
+ "useReadonlyClassProperties": {
+ "level": "info", // TODO: Graduate to error eventually
+ "options": { "checkAllProperties": true }
+ },
+ "useConsistentObjectDefinitions": {
+ "level": "error",
+ "options": { "syntax": "shorthand" }
+ },
+ "useCollapsedIf": "error",
+ "useCollapsedElseIf": "error",
+
+ "noSubstr": "error",
+ "noYodaExpression": "error",
+ "useForOf": "error",
+ "useEnumInitializers": "off", // large enums like MoveId/SpeciesId would make this cumbersome
"useBlockStatements": {
"level": "error",
- "fix": "safe"
+ "fix": "safe",
+ "options": {}
},
"useConst": "error",
"useImportType": "error",
@@ -80,9 +125,14 @@
// TODO: Fix spots in the codebase where this flag would be triggered
// and then set to "error" and re-enable the fixer
"level": "warn",
- "fix": "none"
+ "fix": "none",
+ "options": {}
+ },
+ "useSingleVarDeclarator": {
+ "level": "error",
+ "fix": "safe",
+ "options": {}
},
- "useSingleVarDeclarator": "off",
"useNodejsImportProtocol": "off",
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
"useAsConstAssertion": "error",
@@ -100,58 +150,80 @@
}
}
}
- }
+ },
+
+ // TODO: Wait until the rule gets options for ignoring doc comments and/or different parameter names,
+ // and THEN enable it codebase-wide
+ "useUnifiedTypeSignatures": {
+ "level": "info",
+ "fix": "none",
+ "options": {}
+ },
+ "useGroupedAccessorPairs": "error",
+ "useObjectSpread": "error",
+ "useNumericSeparators": "off" // TODO: Consider enabling?
},
"suspicious": {
+ "useErrorMessage": "error",
+ "noEvolvingTypes": "warn", // TODO: Review and enable ASAP - this is VERY VERY BAD
+ "useNumberToFixedDigitsArgument": "error",
+ "useGuardForIn": "warn", // TODO: Review and enable ASAP - this is EVEN FRICKING WORSE
"noDoubleEquals": "error",
// While this would be a nice rule to enable, the current structure of the codebase makes this infeasible
// due to being used for move/ability `args` params and save data-related code.
// This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off.
"noExplicitAny": "off",
"noAssignInExpressions": "off",
- "noPrototypeBuiltins": "off",
+ "noPrototypeBuiltins": "off", // TODO: enable this
"noFallthroughSwitchClause": "error", // Prevents accidental automatic fallthroughs in switch cases (use disable comment if needed)
"noImplicitAnyLet": "warn", // TODO: Refactor and make this an error
"noRedeclare": "info", // TODO: Refactor and make this an error
- "noGlobalIsNan": "off",
+ "noGlobalIsNan": "error",
"noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error
"noVar": "error",
- "noDocumentCookie": "off" // Firefox has minimal support for the "Cookie Store API"
+ "noDocumentCookie": "off", // Firefox has minimal support for the "Cookie Store API"
+ "noConstantBinaryExpressions": "error",
+ "noTsIgnore": "error",
+ "useIterableCallbackReturn": "warn" // TODO: Refactor and change to error
},
"complexity": {
+ "useWhile": "error",
+ "noVoid": "warn", // TODO: Review and enable ASAP - this is also bad
+ "noUselessStringConcat": "error",
"noExcessiveCognitiveComplexity": "info", // TODO: Refactor and make this an error
- "useLiteralKeys": "off",
+ "useLiteralKeys": "off", // TODO: enable?
"noForEach": "off", // Foreach vs for of is not that simple.
"noUselessSwitchCase": "off", // Explicit > Implicit
"noUselessConstructor": "error",
"noBannedTypes": "warn", // TODO: Refactor and make this an error
"noThisInStatic": "error",
"noUselessThisAlias": "error",
- "noUselessTernary": "error"
+ "noUselessTernary": "error",
+ "useIndexOf": "error"
},
"performance": {
"noNamespaceImport": "error",
- "noDelete": "error"
+ "noDelete": "error",
+ "noBarrelFile": "error"
},
"nursery": {
- "useAdjacentGetterSetter": "error",
- "noConstantBinaryExpression": "error",
- "noTsIgnore": "error",
- "noAwaitInLoop": "off",
- "useJsonImportAttribute": "off", // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)"
- "useIndexOf": "error",
- "useObjectSpread": "error",
- "useNumericSeparators": "off", // TODO: enable?
- "useIterableCallbackReturn": "warn", // TODO: refactor and make "error"
- "noShadow": "warn" // TODO: refactor and make "error"
+ "noUselessUndefined": "error",
+ "useMaxParams": {
+ "level": "warn", // TODO: Change to "error"... eventually...
+ "options": { "max": 4 } // A lot of stuff has a few params, but
+ },
+ "noShadow": "warn", // TODO: refactor and make "error"
+ "noNonNullAssertedOptionalChain": "warn" // TODO: refactor and make "error"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
- "arrowParentheses": "asNeeded"
+ "arrowParentheses": "asNeeded",
+ "operatorLinebreak": "before"
},
+ "globals": ["Phaser"],
"parser": {
"jsxEverywhere": false
}
@@ -166,7 +238,7 @@
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests
},
"style": {
- "noNonNullAssertion": "off"
+ "noNonNullAssertion": "off" // tedious in some tests
},
"nursery": {
"noFloatingPromises": "error"
@@ -175,10 +247,17 @@
}
},
- // Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes),
- // as well as in all TS files in `scripts/` (which are assumed to be boilerplate templates).
+ // Overrides to prevent unused import removal inside `overrides.ts`, enums & `.d.ts` files (for TSDoc linkcodes),
+ // as well as inside script boilerplate files.
{
- "includes": ["**/src/overrides.ts", "**/src/enums/**/*", "**/scripts/**/*.ts", "**/*.d.ts"],
+ // TODO: Rename existing boilerplates in the folder and remove this last alias
+ "includes": [
+ "**/src/overrides.ts",
+ "**/src/enums/**/*",
+ "**/*.d.ts",
+ "scripts/**/*.boilerplate.ts",
+ "**/boilerplates/*.ts"
+ ],
"linter": {
"rules": {
"correctness": {
@@ -188,7 +267,7 @@
}
},
{
- "includes": ["**/src/overrides.ts", "**/scripts/**/*.ts"],
+ "includes": ["**/src/overrides.ts"],
"linter": {
"rules": {
"style": {
diff --git a/docs/linting.md b/docs/linting.md
index d925b2f29af..9301917ae79 100644
--- a/docs/linting.md
+++ b/docs/linting.md
@@ -1,48 +1,57 @@
# Linting & Formatting
-Writing clean, readable code is important, and linters and formatters are an integral part of ensuring code quality and readability.
+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 `pnpm install` after cloning the repo. 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 that first.
# Using Biome
-For the most part, Biome attempts to stay "out of your hair", letting you write code while enforcing a consistent formatting standard and only notifying for errors it can't automatically fix.\
+For the most part, Biome attempts to stay "out of your hair", letting you write code while enforcing a consistent formatting standard and only notifying for errors it can't automatically fix. \
On the other hand, if Biome complains about a piece of code, **there's probably a good reason why**. Disable comments should be used sparingly or when readabilty demands it - your first instinct should be to fix the code in question, not disable the rule.
## Editor Integration
Biome has integration with many popular code editors. See [these](https://biomejs.dev/guides/editors/first-party-extensions/) [pages](https://biomejs.dev/guides/editors/third-party-extensions/) for information about enabling Biome in your editor of choice.
## Automated Runs
-Generally speaking, most users shouldn't need to run Biome directly; in addition to editor integration, [pre-commit hook](../lefthook.yml) will periodically run Biome before each commit.
-You will **not** be able to push code with `error`-level linting problems - fix them beforehand.
+Generally speaking, most users shouldn't need to run Biome directly; in addition to editor integration, a [pre-commit hook](../lefthook.yml) will automatically format and lint all staged files before each commit.
-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.
+> ![WARNING]
+> You will **not** be able to commit code if any staged files contain `error`-level linting problems. \
+> If you, for whatever reason, _absolutely need_ to bypass Lefthook for a given commit,
+> pass the `--no-verify` flag to `git commit`.
+
+We also have a [Github Action](../.github/workflows/linting.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. \
+These are effectively the same commands as run by Lefthook, merely on a project-wide scale.
## Running Biome via CLI
-If you want Biome to check your files manually, you can run it from the command line like so:
+To run you Biome on your files manually, you have 2 main options:
+1. Run the scripts included in `package.json` (`pnpm biome` and `pnpm biome:all`). \
+ These have sensible defaults for command-line options, but do not allow altering certain flags (as some cannot be specified twice in the same command)
-```sh
-pnpm exec biome check --[flags]
-```
+2. Execute the Biome executable manually from the command line like so:
+ ```sh
+ pnpm exec biome check --[flags]
+ ```
+ This allows customizing non-overridable flags like `--diagnostic-level` on a more granular level, but requires slightly more verbosity and specifying more options.
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:
-- `--write` will cause Biome to write all "safe" fixes and formatting changes directly to your files (rather than just complaining and doing nothing).
-- `--changed` and `--staged` will only perform checks on all changed or staged files respectively. Biome sources this info from the relevant version control system (in this case Git).
+- `--write` will cause Biome to write all "safe" fixes and formatting changes directly to your files (rather than just complaining and erroring out).
+- `--changed` and `--staged` will limit checking to all changed or staged files respectively. Biome sources this info from the relevant version control system (in this case `git`).
- `diagnostic-level=XXX` will only show diagnostics with at least the given severity level (`info/warn/error`). Useful to only focus on errors causing a failed workflow run or similar.
## Linting Rules
-We primarily use Biome's [recommended ruleset](https://biomejs.dev/linter/rules/) for linting JS/TS, with some customizations to better suit our project's needs[^1].
+We primarily use Biome's [recommended ruleset](https://biomejs.dev/linter/rules/) for linting JS/TS files, with some customizations to better suit our project's needs[^1].
Some things to consider:
- We have disabled rules that prioritize style over performance, such as `useTemplate`.
-- Some rules are currently disabled or marked as warnings (`warn`) to allow for gradual refactoring without blocking development. **Do not write new code that triggers these warnings.**
+- Some rules are currently marked as warnings (`warn`) to allow for gradual refactoring without blocking development. **Do not write new code that triggers these rules!**
- The linter is configured to ignore specific files and folders (such as excessively large files or ones in need of refactoring) to improve performance and focus on actionable areas.
-Any questions about linting rules should be brought up in the `#dev-corner` channel in the discord.
+Any questions about linting rules can be brought up in the `#dev-corner` channel in the community Discord.
-[^1]: A complete list of rules can be found in the `biome.jsonc` file in the project root.
+[^1]: A complete list of rules can be found in the [`biome.jsonc`](../biome.jsonc) file in the project root. Many rules are accompanied by comments explaining the reasons for their inclusion (or lack thereof).
diff --git a/docs/localization.md b/docs/localization.md
index c325aaf55a9..e153d070818 100644
--- a/docs/localization.md
+++ b/docs/localization.md
@@ -1,6 +1,6 @@
# 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.
+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.
@@ -19,11 +19,12 @@ This repository is integrated into the main one as a [git submodule](https://git
## 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.
+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:
+>
> 
>
> 
@@ -32,14 +33,14 @@ The parent repo (the "superproject") houses a cloned version of the 2nd reposito
The following command will initialize your branch's locales repository and update its HEAD:
```bash
-git submodule update --init --recursive
+pnpm update-locales
```
> [!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.
+> If you EVER run into issues with the `locales` submodule, try deleting the `.git/modules/public` and `public/locales` folders before re-initializing it again.
## How Are Translations Integrated?
@@ -65,7 +66,7 @@ The basic process for fetching translated text goes roughly as follows:
```
# 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.
+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]
@@ -73,29 +74,29 @@ Since these two PRs aren't _technically_ linked, it's important to coordinate wi
## 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.
+One perk of submodules is you don't actually _need_ to clone the locales repository to start contributing - `git` already does that for you on initialization.
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!
+> Make sure to checkout or rebase onto `upstream/main` (`pnpm update-locales:remote`) **BEFORE** creating a locales 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:
+When a new feature or enhancement requires adding a new locales key **without changing text in existing keys**, we have 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.
+ - 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. Your locales should use the following format:
- File names should be in `kebab-case`. Example: `trainer-names.json`
- Key names should be in `camelCase`. Example: `aceTrainer`
- If you make use of i18next's inbuilt [context support](https://www.i18next.com/translation-function/context), you need to use `snake_case` for the context key. Example: `aceTrainer_male`
4. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
-5. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
+5. The Translation Team will approve the locales PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
6. 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).
@@ -106,7 +107,7 @@ If a key is present in any language _except_ the master language, it won't appea
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**.
+- After your main PR is approved, 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
@@ -115,12 +116,12 @@ After making a PR involving any outwards-facing behavior (but _especially_ local
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
- ```
+ 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].
diff --git a/docs/podman.md b/docs/podman.md
new file mode 100644
index 00000000000..dea52131e92
--- /dev/null
+++ b/docs/podman.md
@@ -0,0 +1,27 @@
+# Using Podman
+
+## Requirements
+
+* `podman >=5.x`
+
+## Steps
+
+1. `podman build -t pokerogue -f Dockerfile .`
+2. `podman create --name temp-pokerogue localhost/pokerogue`
+3. `podman cp temp-pokerogue:/app/node_modules ./`
+4. `podman cp temp-pokerogue:/app/public/locales ./public/`
+5. `podman rm temp-pokerogue`
+6. `podman run --rm -p 8000:8000 -v $(pwd):/app:Z --userns=keep-id -u $(id -u):$(id -g) localhost/pokerogue`
+7. Visit `http://localhost:8000/`
+
+Note:
+
+1. Steps 2,3,4 are required because mounting working directory without installed `node_modules/` and assets locally will be empty,
+this way we prevent it by copying them from the container itself to local directory
+
+2. `podman run` may take a couple of minutes to mount the working directory
+
+### Running tests inside container
+
+`podman run --rm -p 8000:8000 -v $(pwd):/app:Z --userns=keep-id -u $(id -u):$(id -g) localhost/pokerogue pnpm test:silent
+`
\ No newline at end of file
diff --git a/global.d.ts b/global.d.ts
index 8b79d966e3c..92a883f40c9 100644
--- a/global.d.ts
+++ b/global.d.ts
@@ -18,3 +18,14 @@ declare global {
call(this: T, thisArg: ThisParameterType, ...argArray: Parameters): ReturnType;
}
}
+
+// Global augments for `typedoc` to prevent TS from erroring when editing the config JS file
+declare module "typedoc" {
+ export interface TypeDocOptionMap {
+ coverageLabel: string;
+ coverageColor: string;
+ coverageOutputPath: string;
+ coverageOutputType: "svg" | "json" | "all";
+ coverageSvgWidth: number;
+ }
+}
diff --git a/lefthook.yml b/lefthook.yml
index 8b5ad2234ed..c26ea8705b9 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -1,10 +1,12 @@
pre-commit:
- skip:
+ skip:
- merge
- rebase
commands:
biome-lint:
- run: pnpm exec biome check --write --reporter=summary --staged --no-errors-on-unmatched
+ # Disable colors as certain IDEs don't support it in the output pane.
+ # Summary mode looks decent in plain ASCII anyhow
+ run: pnpm exec biome check --write --colors=off --reporter=summary --staged --no-errors-on-unmatched --diagnostic-level=error
stage_fixed: true
ls-lint:
run: pnpm exec ls-lint
@@ -12,11 +14,11 @@ pre-commit:
post-merge:
commands:
update-submodules:
- run: git submodule update --init --recursive
+ run: pnpm update-locales
post-checkout:
commands:
update-submodules:
# cf https://git-scm.com/docs/githooks#_post_checkout:
# The 3rd argument is 1 for branch checkouts and 0 for file checkouts.
- run: if test {3} -eq "1"; then git submodule update --init --recursive; fi
\ No newline at end of file
+ run: if test {3} -eq "1"; then pnpm update-locales; fi
\ No newline at end of file
diff --git a/package.json b/package.json
index 93668f15c61..8b5635d3115 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,13 @@
{
"name": "pokemon-rogue-battle",
"private": true,
- "version": "1.11.0",
+ "version": "1.10.7",
"type": "module",
"scripts": {
- "start": "vite",
+ "start:prod": "vite --mode production",
+ "start:beta": "vite --mode beta",
"start:dev": "vite --mode development",
+ "start:podman": "vite --mode development --host 0.0.0.0 --port $PORT",
"build": "vite build",
"build:beta": "vite build --mode beta",
"preview": "vite preview",
@@ -14,25 +16,30 @@
"test:watch": "vitest watch --coverage --no-isolate",
"test:silent": "vitest run --silent='passed-only' --no-isolate",
"test:create": "node scripts/create-test/create-test.js",
+ "eggMoves:parse": "node scripts/parse-egg-moves/main.js",
"scrape-trainers": "node scripts/scrape-trainer-names/main.js",
"typecheck": "tsc --noEmit",
+ "typecheck:scripts": "tsc -p scripts/jsconfig.json",
"biome": "biome check --write --changed --no-errors-on-unmatched --diagnostic-level=error",
+ "biome:all": "biome check --write --no-errors-on-unmatched --diagnostic-level=error",
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
- "docs": "typedoc",
+ "typedoc": "typedoc",
"depcruise": "depcruise src test",
"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": "git submodule update --progress --init --recursive",
"update-locales:remote": "git submodule update --progress --init --recursive --force --remote"
},
"devDependencies": {
- "@biomejs/biome": "2.0.0",
+ "@biomejs/biome": "2.2.3",
"@ls-lint/ls-lint": "2.3.1",
"@types/crypto-js": "^4.2.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.16.5",
"@vitest/coverage-istanbul": "^3.2.4",
"@vitest/expect": "^3.2.4",
+ "@vitest/utils": "^3.2.4",
"chalk": "^5.4.1",
"dependency-cruiser": "^16.10.4",
"inquirer": "^12.8.2",
@@ -40,7 +47,10 @@
"lefthook": "^1.12.2",
"msw": "^2.10.4",
"phaser3spectorjs": "^0.0.8",
- "typedoc": "^0.28.8",
+ "typedoc": "^0.28.12",
+ "typedoc-github-theme": "^0.3.1",
+ "typedoc-plugin-coverage": "^4.0.1",
+ "typedoc-plugin-mdn-links": "^5.0.9",
"typescript": "^5.8.3",
"vite": "^7.0.6",
"vite-tsconfig-paths": "^5.1.4",
@@ -62,5 +72,6 @@
},
"engines": {
"node": ">=22.0.0"
- }
+ },
+ "packageManager": "pnpm@10.14.0"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c3b58a60f48..5bbe08bf0c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,8 +43,8 @@ importers:
version: 1.80.16(graphology-types@0.24.8)
devDependencies:
'@biomejs/biome':
- specifier: 2.0.0
- version: 2.0.0
+ specifier: 2.2.3
+ version: 2.2.3
'@ls-lint/ls-lint':
specifier: 2.3.1
version: 2.3.1
@@ -59,10 +59,13 @@ importers:
version: 22.16.5
'@vitest/coverage-istanbul':
specifier: ^3.2.4
- version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0))
+ version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1))
'@vitest/expect':
specifier: ^3.2.4
version: 3.2.4
+ '@vitest/utils':
+ specifier: ^3.2.4
+ version: 3.2.4
chalk:
specifier: ^5.4.1
version: 5.4.1
@@ -85,23 +88,32 @@ importers:
specifier: ^0.0.8
version: 0.0.8
typedoc:
- specifier: ^0.28.8
- version: 0.28.8(typescript@5.8.3)
+ specifier: ^0.28.12
+ version: 0.28.12(typescript@5.8.3)
+ typedoc-github-theme:
+ specifier: ^0.3.1
+ version: 0.3.1(typedoc@0.28.12(typescript@5.8.3))
+ typedoc-plugin-coverage:
+ specifier: ^4.0.1
+ version: 4.0.1(typedoc@0.28.12(typescript@5.8.3))
+ typedoc-plugin-mdn-links:
+ specifier: ^5.0.9
+ version: 5.0.9(typedoc@0.28.12(typescript@5.8.3))
typescript:
specifier: ^5.8.3
version: 5.8.3
vite:
specifier: ^7.0.6
- version: 7.0.6(@types/node@22.16.5)(yaml@2.8.0)
+ version: 7.0.6(@types/node@22.16.5)(yaml@2.8.1)
vite-tsconfig-paths:
specifier: ^5.1.4
- version: 5.1.4(typescript@5.8.3)(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.0))
+ version: 5.1.4(typescript@5.8.3)(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.1))
vitest:
specifier: ^3.2.4
- version: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0)
+ version: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1)
vitest-canvas-mock:
specifier: ^0.3.3
- version: 0.3.3(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0))
+ version: 0.3.3(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1))
packages:
@@ -183,55 +195,55 @@ packages:
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
engines: {node: '>=6.9.0'}
- '@biomejs/biome@2.0.0':
- resolution: {integrity: sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ==}
+ '@biomejs/biome@2.2.3':
+ resolution: {integrity: sha512-9w0uMTvPrIdvUrxazZ42Ib7t8Y2yoGLKLdNne93RLICmaHw7mcLv4PPb5LvZLJF3141gQHiCColOh/v6VWlWmg==}
engines: {node: '>=14.21.3'}
hasBin: true
- '@biomejs/cli-darwin-arm64@2.0.0':
- resolution: {integrity: sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw==}
+ '@biomejs/cli-darwin-arm64@2.2.3':
+ resolution: {integrity: sha512-OrqQVBpadB5eqzinXN4+Q6honBz+tTlKVCsbEuEpljK8ASSItzIRZUA02mTikl3H/1nO2BMPFiJ0nkEZNy3B1w==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
- '@biomejs/cli-darwin-x64@2.0.0':
- resolution: {integrity: sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw==}
+ '@biomejs/cli-darwin-x64@2.2.3':
+ resolution: {integrity: sha512-OCdBpb1TmyfsTgBAM1kPMXyYKTohQ48WpiN9tkt9xvU6gKVKHY4oVwteBebiOqyfyzCNaSiuKIPjmHjUZ2ZNMg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
- '@biomejs/cli-linux-arm64-musl@2.0.0':
- resolution: {integrity: sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA==}
+ '@biomejs/cli-linux-arm64-musl@2.2.3':
+ resolution: {integrity: sha512-q3w9jJ6JFPZPeqyvwwPeaiS/6NEszZ+pXKF+IczNo8Xj6fsii45a4gEEicKyKIytalV+s829ACZujQlXAiVLBQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
- '@biomejs/cli-linux-arm64@2.0.0':
- resolution: {integrity: sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw==}
+ '@biomejs/cli-linux-arm64@2.2.3':
+ resolution: {integrity: sha512-g/Uta2DqYpECxG+vUmTAmUKlVhnGEcY7DXWgKP8ruLRa8Si1QHsWknPY3B/wCo0KgYiFIOAZ9hjsHfNb9L85+g==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
- '@biomejs/cli-linux-x64-musl@2.0.0':
- resolution: {integrity: sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA==}
+ '@biomejs/cli-linux-x64-musl@2.2.3':
+ resolution: {integrity: sha512-y76Dn4vkP1sMRGPFlNc+OTETBhGPJ90jY3il6jAfur8XWrYBQV3swZ1Jo0R2g+JpOeeoA0cOwM7mJG6svDz79w==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
- '@biomejs/cli-linux-x64@2.0.0':
- resolution: {integrity: sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg==}
+ '@biomejs/cli-linux-x64@2.2.3':
+ resolution: {integrity: sha512-LEtyYL1fJsvw35CxrbQ0gZoxOG3oZsAjzfRdvRBRHxOpQ91Q5doRVjvWW/wepgSdgk5hlaNzfeqpyGmfSD0Eyw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
- '@biomejs/cli-win32-arm64@2.0.0':
- resolution: {integrity: sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw==}
+ '@biomejs/cli-win32-arm64@2.2.3':
+ resolution: {integrity: sha512-Ms9zFYzjcJK7LV+AOMYnjN3pV3xL8Prxf9aWdDVL74onLn5kcvZ1ZMQswE5XHtnd/r/0bnUd928Rpbs14BzVmA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
- '@biomejs/cli-win32-x64@2.0.0':
- resolution: {integrity: sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg==}
+ '@biomejs/cli-win32-x64@2.2.3':
+ resolution: {integrity: sha512-gvCpewE7mBwBIpqk1YrUqNR4mCiyJm6UI3YWQQXkedSSEwzRdodRpaKhbdbHw1/hmTWOVXQ+Eih5Qctf4TCVOQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
@@ -429,8 +441,8 @@ packages:
cpu: [x64]
os: [win32]
- '@gerrit0/mini-shiki@3.8.1':
- resolution: {integrity: sha512-HVZW+8pxoOExr5ZMPK15U79jQAZTO/S6i5byQyyZGjtNj+qaYd82cizTncwFzTQgiLo8uUBym6vh+/1tfJklTw==}
+ '@gerrit0/mini-shiki@3.12.2':
+ resolution: {integrity: sha512-HKZPmO8OSSAAo20H2B3xgJdxZaLTwtlMwxg0967scnrDlPwe6j5+ULGHyIqwgTbFCn9yv/ff8CmfWZLE9YKBzA==}
'@inquirer/checkbox@4.2.0':
resolution: {integrity: sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==}
@@ -700,17 +712,17 @@ packages:
cpu: [x64]
os: [win32]
- '@shikijs/engine-oniguruma@3.8.1':
- resolution: {integrity: sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==}
+ '@shikijs/engine-oniguruma@3.12.2':
+ resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==}
- '@shikijs/langs@3.8.1':
- resolution: {integrity: sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==}
+ '@shikijs/langs@3.12.2':
+ resolution: {integrity: sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==}
- '@shikijs/themes@3.8.1':
- resolution: {integrity: sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==}
+ '@shikijs/themes@3.12.2':
+ resolution: {integrity: sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==}
- '@shikijs/types@3.8.1':
- resolution: {integrity: sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==}
+ '@shikijs/types@3.12.2':
+ resolution: {integrity: sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
@@ -1789,12 +1801,29 @@ packages:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
- typedoc@0.28.8:
- resolution: {integrity: sha512-16GfLopc8icHfdvqZDqdGBoS2AieIRP2rpf9mU+MgN+gGLyEQvAO0QgOa6NJ5QNmQi0LFrDY9in4F2fUNKgJKA==}
+ typedoc-github-theme@0.3.1:
+ resolution: {integrity: sha512-j6PmkAGmf/MGCzYjQcUH6jS9djPsNl/IoTXooxC+MoeMkBhbmPyKJlpR6Lw12BLoe2OYpYA2J1KMktUJXp/8Sw==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ typedoc: ~0.28.0
+
+ typedoc-plugin-coverage@4.0.1:
+ resolution: {integrity: sha512-P1QBR5GJSfW3fDrpz4Vkd8z8lzWaBYjaHebRLk0u2Uga0oSFlPaqrCyiHzItBXxZX28aMlNlZwrUnsLgUgqA7g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ typedoc: 0.28.x
+
+ typedoc-plugin-mdn-links@5.0.9:
+ resolution: {integrity: sha512-kXssRKBhUd0JeHzFmxWVsGWVFR9WXafe70Y8Ed+MYH2Nu2647cqfGQN1OBKgvXpmAT8MTpACmUIQ7GnQnh1/iw==}
+ peerDependencies:
+ typedoc: 0.27.x || 0.28.x
+
+ typedoc@0.28.12:
+ resolution: {integrity: sha512-H5ODu4f7N+myG4MfuSp2Vh6wV+WLoZaEYxKPt2y8hmmqNEMVrH69DAjjdmYivF4tP/C2jrIZCZhPalZlTU/ipA==}
engines: {node: '>= 18', pnpm: '>= 10'}
hasBin: true
peerDependencies:
- typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x
+ typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
@@ -1991,8 +2020,8 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
- yaml@2.8.0:
- resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
engines: {node: '>= 14.6'}
hasBin: true
@@ -2125,39 +2154,39 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
- '@biomejs/biome@2.0.0':
+ '@biomejs/biome@2.2.3':
optionalDependencies:
- '@biomejs/cli-darwin-arm64': 2.0.0
- '@biomejs/cli-darwin-x64': 2.0.0
- '@biomejs/cli-linux-arm64': 2.0.0
- '@biomejs/cli-linux-arm64-musl': 2.0.0
- '@biomejs/cli-linux-x64': 2.0.0
- '@biomejs/cli-linux-x64-musl': 2.0.0
- '@biomejs/cli-win32-arm64': 2.0.0
- '@biomejs/cli-win32-x64': 2.0.0
+ '@biomejs/cli-darwin-arm64': 2.2.3
+ '@biomejs/cli-darwin-x64': 2.2.3
+ '@biomejs/cli-linux-arm64': 2.2.3
+ '@biomejs/cli-linux-arm64-musl': 2.2.3
+ '@biomejs/cli-linux-x64': 2.2.3
+ '@biomejs/cli-linux-x64-musl': 2.2.3
+ '@biomejs/cli-win32-arm64': 2.2.3
+ '@biomejs/cli-win32-x64': 2.2.3
- '@biomejs/cli-darwin-arm64@2.0.0':
+ '@biomejs/cli-darwin-arm64@2.2.3':
optional: true
- '@biomejs/cli-darwin-x64@2.0.0':
+ '@biomejs/cli-darwin-x64@2.2.3':
optional: true
- '@biomejs/cli-linux-arm64-musl@2.0.0':
+ '@biomejs/cli-linux-arm64-musl@2.2.3':
optional: true
- '@biomejs/cli-linux-arm64@2.0.0':
+ '@biomejs/cli-linux-arm64@2.2.3':
optional: true
- '@biomejs/cli-linux-x64-musl@2.0.0':
+ '@biomejs/cli-linux-x64-musl@2.2.3':
optional: true
- '@biomejs/cli-linux-x64@2.0.0':
+ '@biomejs/cli-linux-x64@2.2.3':
optional: true
- '@biomejs/cli-win32-arm64@2.0.0':
+ '@biomejs/cli-win32-arm64@2.2.3':
optional: true
- '@biomejs/cli-win32-x64@2.0.0':
+ '@biomejs/cli-win32-x64@2.2.3':
optional: true
'@bundled-es-modules/cookie@2.0.1':
@@ -2271,12 +2300,12 @@ snapshots:
'@esbuild/win32-x64@0.25.8':
optional: true
- '@gerrit0/mini-shiki@3.8.1':
+ '@gerrit0/mini-shiki@3.12.2':
dependencies:
- '@shikijs/engine-oniguruma': 3.8.1
- '@shikijs/langs': 3.8.1
- '@shikijs/themes': 3.8.1
- '@shikijs/types': 3.8.1
+ '@shikijs/engine-oniguruma': 3.12.2
+ '@shikijs/langs': 3.12.2
+ '@shikijs/themes': 3.12.2
+ '@shikijs/types': 3.12.2
'@shikijs/vscode-textmate': 10.0.2
'@inquirer/checkbox@4.2.0(@types/node@22.16.5)':
@@ -2505,20 +2534,20 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.46.1':
optional: true
- '@shikijs/engine-oniguruma@3.8.1':
+ '@shikijs/engine-oniguruma@3.12.2':
dependencies:
- '@shikijs/types': 3.8.1
+ '@shikijs/types': 3.12.2
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/langs@3.8.1':
+ '@shikijs/langs@3.12.2':
dependencies:
- '@shikijs/types': 3.8.1
+ '@shikijs/types': 3.12.2
- '@shikijs/themes@3.8.1':
+ '@shikijs/themes@3.12.2':
dependencies:
- '@shikijs/types': 3.8.1
+ '@shikijs/types': 3.12.2
- '@shikijs/types@3.8.1':
+ '@shikijs/types@3.12.2':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -2557,7 +2586,7 @@ snapshots:
'@types/unist@3.0.3': {}
- '@vitest/coverage-istanbul@3.2.4(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0))':
+ '@vitest/coverage-istanbul@3.2.4(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1))':
dependencies:
'@istanbuljs/schema': 0.1.3
debug: 4.4.1
@@ -2569,7 +2598,7 @@ snapshots:
magicast: 0.3.5
test-exclude: 7.0.1
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0)
+ vitest: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1)
transitivePeerDependencies:
- supports-color
@@ -2581,14 +2610,14 @@ snapshots:
chai: 5.2.1
tinyrainbow: 2.0.0
- '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.0))':
+ '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
msw: 2.10.4(@types/node@22.16.5)(typescript@5.8.3)
- vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.0)
+ vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.1)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -3634,14 +3663,26 @@ snapshots:
type-fest@4.41.0: {}
- typedoc@0.28.8(typescript@5.8.3):
+ typedoc-github-theme@0.3.1(typedoc@0.28.12(typescript@5.8.3)):
dependencies:
- '@gerrit0/mini-shiki': 3.8.1
+ typedoc: 0.28.12(typescript@5.8.3)
+
+ typedoc-plugin-coverage@4.0.1(typedoc@0.28.12(typescript@5.8.3)):
+ dependencies:
+ typedoc: 0.28.12(typescript@5.8.3)
+
+ typedoc-plugin-mdn-links@5.0.9(typedoc@0.28.12(typescript@5.8.3)):
+ dependencies:
+ typedoc: 0.28.12(typescript@5.8.3)
+
+ typedoc@0.28.12(typescript@5.8.3):
+ dependencies:
+ '@gerrit0/mini-shiki': 3.12.2
lunr: 2.3.9
markdown-it: 14.1.0
minimatch: 9.0.5
typescript: 5.8.3
- yaml: 2.8.0
+ yaml: 2.8.1
typescript@5.8.3: {}
@@ -3664,13 +3705,13 @@ snapshots:
util-deprecate@1.0.2: {}
- vite-node@3.2.4(@types/node@22.16.5)(yaml@2.8.0):
+ vite-node@3.2.4(@types/node@22.16.5)(yaml@2.8.1):
dependencies:
cac: 6.7.14
debug: 4.4.1
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.0)
+ vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.1)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -3685,18 +3726,18 @@ snapshots:
- tsx
- yaml
- vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.0)):
+ vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.1)):
dependencies:
debug: 4.4.1
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.8.3)
optionalDependencies:
- vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.0)
+ vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.1)
transitivePeerDependencies:
- supports-color
- typescript
- vite@7.0.6(@types/node@22.16.5)(yaml@2.8.0):
+ vite@7.0.6(@types/node@22.16.5)(yaml@2.8.1):
dependencies:
esbuild: 0.25.8
fdir: 6.4.6(picomatch@4.0.3)
@@ -3707,18 +3748,18 @@ snapshots:
optionalDependencies:
'@types/node': 22.16.5
fsevents: 2.3.3
- yaml: 2.8.0
+ yaml: 2.8.1
- vitest-canvas-mock@0.3.3(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0)):
+ vitest-canvas-mock@0.3.3(vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1)):
dependencies:
jest-canvas-mock: 2.5.2
- vitest: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0)
+ vitest: 3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1)
- vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.0):
+ vitest@3.2.4(@types/node@22.16.5)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.0))
+ '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(vite@7.0.6(@types/node@22.16.5)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -3736,8 +3777,8 @@ snapshots:
tinyglobby: 0.2.14
tinypool: 1.1.1
tinyrainbow: 2.0.0
- vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.0)
- vite-node: 3.2.4(@types/node@22.16.5)(yaml@2.8.0)
+ vite: 7.0.6(@types/node@22.16.5)(yaml@2.8.1)
+ vite-node: 3.2.4(@types/node@22.16.5)(yaml@2.8.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.16.5
@@ -3821,7 +3862,7 @@ snapshots:
yallist@3.1.1: {}
- yaml@2.8.0: {}
+ yaml@2.8.1: {}
yargs-parser@21.1.1: {}
diff --git a/public/images/arenas/beach_b_2.png b/public/images/arenas/beach_b_2.png
index 50c9be97b0d..6c3e89c1b25 100644
Binary files a/public/images/arenas/beach_b_2.png and b/public/images/arenas/beach_b_2.png differ
diff --git a/public/images/arenas/cave_b_2.png b/public/images/arenas/cave_b_2.png
index f76560e1f03..99c850ecdf6 100644
Binary files a/public/images/arenas/cave_b_2.png and b/public/images/arenas/cave_b_2.png differ
diff --git a/public/images/arenas/factory_b_3.png b/public/images/arenas/factory_b_3.png
index f2bb93128cf..e7716f0e168 100644
Binary files a/public/images/arenas/factory_b_3.png and b/public/images/arenas/factory_b_3.png differ
diff --git a/public/images/arenas/forest_b.png b/public/images/arenas/forest_b.png
index dcd1981a5e9..9f2cfc20c09 100644
Binary files a/public/images/arenas/forest_b.png and b/public/images/arenas/forest_b.png differ
diff --git a/public/images/arenas/ice_cave_b_2.png b/public/images/arenas/ice_cave_b_2.png
index d7e6fb1784e..636f4e95946 100644
Binary files a/public/images/arenas/ice_cave_b_2.png and b/public/images/arenas/ice_cave_b_2.png differ
diff --git a/public/images/arenas/island_bg.png b/public/images/arenas/island_bg.png
index e05c3afa775..8a8d703af5d 100644
Binary files a/public/images/arenas/island_bg.png and b/public/images/arenas/island_bg.png differ
diff --git a/public/images/arenas/jungle_bg.png b/public/images/arenas/jungle_bg.png
index e03de2a3223..4a8fa41fbb3 100644
Binary files a/public/images/arenas/jungle_bg.png and b/public/images/arenas/jungle_bg.png differ
diff --git a/public/images/arenas/laboratory_b_2.png b/public/images/arenas/laboratory_b_2.png
index fcd945b8b86..b5d73b8a9ed 100644
Binary files a/public/images/arenas/laboratory_b_2.png and b/public/images/arenas/laboratory_b_2.png differ
diff --git a/public/images/arenas/laboratory_bg.png b/public/images/arenas/laboratory_bg.png
index 0e64877c0b6..decb26240b3 100644
Binary files a/public/images/arenas/laboratory_bg.png and b/public/images/arenas/laboratory_bg.png differ
diff --git a/public/images/arenas/lake_b_1.png b/public/images/arenas/lake_b_1.png
index 5426dd1e8d9..8786fb6c728 100644
Binary files a/public/images/arenas/lake_b_1.png and b/public/images/arenas/lake_b_1.png differ
diff --git a/public/images/arenas/lake_b_2.png b/public/images/arenas/lake_b_2.png
index b08ccd7b8e4..454c9e89d29 100644
Binary files a/public/images/arenas/lake_b_2.png and b/public/images/arenas/lake_b_2.png differ
diff --git a/public/images/arenas/metropolis_bg.png b/public/images/arenas/metropolis_bg.png
index 374744f245e..b790221b323 100644
Binary files a/public/images/arenas/metropolis_bg.png and b/public/images/arenas/metropolis_bg.png differ
diff --git a/public/images/arenas/plains_b_2.png b/public/images/arenas/plains_b_2.png
index a35dbec9917..4d8d679d12f 100644
Binary files a/public/images/arenas/plains_b_2.png and b/public/images/arenas/plains_b_2.png differ
diff --git a/public/images/arenas/plains_bg.png b/public/images/arenas/plains_bg.png
index f2cbec9197c..9c0cff9b7dc 100644
Binary files a/public/images/arenas/plains_bg.png and b/public/images/arenas/plains_bg.png differ
diff --git a/public/images/arenas/snowy_forest_b_3.png b/public/images/arenas/snowy_forest_b_3.png
index 14c8f4c64aa..d8c89496d14 100644
Binary files a/public/images/arenas/snowy_forest_b_3.png and b/public/images/arenas/snowy_forest_b_3.png differ
diff --git a/public/images/arenas/snowy_forest_bg.png b/public/images/arenas/snowy_forest_bg.png
index b0dcc3ddfc5..62ff03f5b1c 100644
Binary files a/public/images/arenas/snowy_forest_bg.png and b/public/images/arenas/snowy_forest_bg.png differ
diff --git a/public/images/arenas/wasteland_b_1.png b/public/images/arenas/wasteland_b_1.png
index d10a7f27699..7f62b29f9a8 100644
Binary files a/public/images/arenas/wasteland_b_1.png and b/public/images/arenas/wasteland_b_1.png differ
diff --git a/public/images/arenas/wasteland_b_3.png b/public/images/arenas/wasteland_b_3.png
index cdc5a2bdd50..73a5b283861 100644
Binary files a/public/images/arenas/wasteland_b_3.png and b/public/images/arenas/wasteland_b_3.png differ
diff --git a/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png b/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png
index 44b50a6d385..4c55dd5f1c8 100644
Binary files a/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png and b/public/images/battle_anims/PRAS- Pulverizing Pancake BG2.png differ
diff --git a/public/images/battle_anims/PRAS- Pulverizing Pancake.png b/public/images/battle_anims/PRAS- Pulverizing Pancake.png
index 519a52c63c6..b284493c6da 100644
Binary files a/public/images/battle_anims/PRAS- Pulverizing Pancake.png and b/public/images/battle_anims/PRAS- Pulverizing Pancake.png differ
diff --git a/public/images/battle_anims/PRAS- Spectral thief BG.png b/public/images/battle_anims/PRAS- Spectral thief BG.png
index 35fc434fdca..92c356c5ec5 100644
Binary files a/public/images/battle_anims/PRAS- Spectral thief BG.png and b/public/images/battle_anims/PRAS- Spectral thief BG.png differ
diff --git a/public/images/egg/egg_crack.png b/public/images/egg/egg_crack.png
index 9d553ebf378..f5b5ee7b7e1 100644
Binary files a/public/images/egg/egg_crack.png and b/public/images/egg/egg_crack.png differ
diff --git a/public/images/items.png b/public/images/items.png
index a4ca454a153..ecc8d42daea 100644
Binary files a/public/images/items.png and b/public/images/items.png differ
diff --git a/public/images/items/common_egg.png b/public/images/items/common_egg.png
index 5eccff2f690..c449611d532 100644
Binary files a/public/images/items/common_egg.png and b/public/images/items/common_egg.png differ
diff --git a/public/images/items/great_ribbon.png b/public/images/items/great_ribbon.png
index 4db20f29c1f..284ee6a3b3b 100644
Binary files a/public/images/items/great_ribbon.png and b/public/images/items/great_ribbon.png differ
diff --git a/public/images/mystery-encounters/fun_and_games_wobbuffet.png b/public/images/mystery-encounters/fun_and_games_wobbuffet.png
index 71997be5692..5d61512ea78 100644
Binary files a/public/images/mystery-encounters/fun_and_games_wobbuffet.png and b/public/images/mystery-encounters/fun_and_games_wobbuffet.png differ
diff --git a/public/images/mystery-encounters/mysterious_chest_blue.png b/public/images/mystery-encounters/mysterious_chest_blue.png
index f959db13aa7..6fa3a7c5475 100644
Binary files a/public/images/mystery-encounters/mysterious_chest_blue.png and b/public/images/mystery-encounters/mysterious_chest_blue.png differ
diff --git a/public/images/ui/champion_ribbon.png b/public/images/ui/champion_ribbon.png
index b188f4c92d2..a19bb01279b 100644
Binary files a/public/images/ui/champion_ribbon.png and b/public/images/ui/champion_ribbon.png differ
diff --git a/public/images/ui/champion_ribbon_bronze.png b/public/images/ui/champion_ribbon_bronze.png
index b69efbb8f88..4bd1cb8ea2d 100644
Binary files a/public/images/ui/champion_ribbon_bronze.png and b/public/images/ui/champion_ribbon_bronze.png differ
diff --git a/public/images/ui/champion_ribbon_diamond.png b/public/images/ui/champion_ribbon_diamond.png
index fd0e1f77480..207ade56c19 100644
Binary files a/public/images/ui/champion_ribbon_diamond.png and b/public/images/ui/champion_ribbon_diamond.png differ
diff --git a/public/images/ui/champion_ribbon_emerald.png b/public/images/ui/champion_ribbon_emerald.png
index 4b0523f7f64..29a9503059d 100644
Binary files a/public/images/ui/champion_ribbon_emerald.png and b/public/images/ui/champion_ribbon_emerald.png differ
diff --git a/public/images/ui/champion_ribbon_silver.png b/public/images/ui/champion_ribbon_silver.png
index 92b6f4cf8c7..52cde028406 100644
Binary files a/public/images/ui/champion_ribbon_silver.png and b/public/images/ui/champion_ribbon_silver.png differ
diff --git a/public/images/ui/language_icon.png b/public/images/ui/language_icon.png
new file mode 100644
index 00000000000..ebe0671ca51
Binary files /dev/null and b/public/images/ui/language_icon.png differ
diff --git a/public/images/ui/legacy/champion_ribbon.png b/public/images/ui/legacy/champion_ribbon.png
index b188f4c92d2..a19bb01279b 100644
Binary files a/public/images/ui/legacy/champion_ribbon.png and b/public/images/ui/legacy/champion_ribbon.png differ
diff --git a/public/images/ui/legacy/champion_ribbon_bronze.png b/public/images/ui/legacy/champion_ribbon_bronze.png
index b69efbb8f88..4bd1cb8ea2d 100644
Binary files a/public/images/ui/legacy/champion_ribbon_bronze.png and b/public/images/ui/legacy/champion_ribbon_bronze.png differ
diff --git a/public/images/ui/legacy/champion_ribbon_diamond.png b/public/images/ui/legacy/champion_ribbon_diamond.png
index fd0e1f77480..207ade56c19 100644
Binary files a/public/images/ui/legacy/champion_ribbon_diamond.png and b/public/images/ui/legacy/champion_ribbon_diamond.png differ
diff --git a/public/images/ui/legacy/champion_ribbon_emerald.png b/public/images/ui/legacy/champion_ribbon_emerald.png
index 4b0523f7f64..29a9503059d 100644
Binary files a/public/images/ui/legacy/champion_ribbon_emerald.png and b/public/images/ui/legacy/champion_ribbon_emerald.png differ
diff --git a/public/images/ui/legacy/champion_ribbon_silver.png b/public/images/ui/legacy/champion_ribbon_silver.png
index 92b6f4cf8c7..52cde028406 100644
Binary files a/public/images/ui/legacy/champion_ribbon_silver.png and b/public/images/ui/legacy/champion_ribbon_silver.png differ
diff --git a/public/images/ui/legacy/language_icon.png b/public/images/ui/legacy/language_icon.png
new file mode 100644
index 00000000000..ebe0671ca51
Binary files /dev/null and b/public/images/ui/legacy/language_icon.png differ
diff --git a/public/images/ui/legacy/numbers_alt.png b/public/images/ui/legacy/numbers_alt.png
deleted file mode 100644
index fb510465471..00000000000
Binary files a/public/images/ui/legacy/numbers_alt.png and /dev/null differ
diff --git a/public/images/ui/legacy/numbers_red_alt.png b/public/images/ui/legacy/numbers_red_alt.png
deleted file mode 100644
index d9edb0468d8..00000000000
Binary files a/public/images/ui/legacy/numbers_red_alt.png and /dev/null differ
diff --git a/public/images/ui/legacy/overlay_lv.png b/public/images/ui/legacy/overlay_lv.png
deleted file mode 100644
index fdcbb1c7082..00000000000
Binary files a/public/images/ui/legacy/overlay_lv.png and /dev/null differ
diff --git a/public/images/ui/legacy/overlay_lv_alt.png b/public/images/ui/legacy/overlay_lv_alt.png
deleted file mode 100644
index 92ec684bf75..00000000000
Binary files a/public/images/ui/legacy/overlay_lv_alt.png and /dev/null differ
diff --git a/public/images/ui/legacy/party_slot_hp_bar.png b/public/images/ui/legacy/party_slot_hp_bar.png
index 6a37002744c..7ec53ae806b 100644
Binary files a/public/images/ui/legacy/party_slot_hp_bar.png and b/public/images/ui/legacy/party_slot_hp_bar.png differ
diff --git a/public/images/ui/legacy/party_slot_overlay_lv_alt.png b/public/images/ui/legacy/party_slot_overlay_lv_alt.png
deleted file mode 100644
index 7aa9a590a13..00000000000
Binary files a/public/images/ui/legacy/party_slot_overlay_lv_alt.png and /dev/null differ
diff --git a/public/images/ui/legacy/pbinfo_enemy_boss.png b/public/images/ui/legacy/pbinfo_enemy_boss.png
index 98b2f09f941..20eb74c59b3 100644
Binary files a/public/images/ui/legacy/pbinfo_enemy_boss.png and b/public/images/ui/legacy/pbinfo_enemy_boss.png differ
diff --git a/public/images/ui/legacy/pbinfo_enemy_mini.png b/public/images/ui/legacy/pbinfo_enemy_mini.png
index c1c002a4b21..7beddd98d55 100644
Binary files a/public/images/ui/legacy/pbinfo_enemy_mini.png and b/public/images/ui/legacy/pbinfo_enemy_mini.png differ
diff --git a/public/images/ui/legacy/pbinfo_player.png b/public/images/ui/legacy/pbinfo_player.png
index 60c92f886aa..f9e16a9aa0c 100644
Binary files a/public/images/ui/legacy/pbinfo_player.png and b/public/images/ui/legacy/pbinfo_player.png differ
diff --git a/public/images/ui/legacy/pbinfo_player_mini.png b/public/images/ui/legacy/pbinfo_player_mini.png
index 120e9592ab9..183db0d44f9 100644
Binary files a/public/images/ui/legacy/pbinfo_player_mini.png and b/public/images/ui/legacy/pbinfo_player_mini.png differ
diff --git a/public/images/ui/legacy/pbinfo_stat.png b/public/images/ui/legacy/pbinfo_stat.png
deleted file mode 100644
index 25c94f9a807..00000000000
Binary files a/public/images/ui/legacy/pbinfo_stat.png and /dev/null differ
diff --git a/public/images/ui/legacy/pokedex_summary_bg.png b/public/images/ui/legacy/pokedex_summary_bg.png
index 0f8fed843c9..62369eaa248 100644
Binary files a/public/images/ui/legacy/pokedex_summary_bg.png and b/public/images/ui/legacy/pokedex_summary_bg.png differ
diff --git a/public/images/ui/legacy/starter_select_bg.png b/public/images/ui/legacy/starter_select_bg.png
index 501e892969e..09d21d4a630 100644
Binary files a/public/images/ui/legacy/starter_select_bg.png and b/public/images/ui/legacy/starter_select_bg.png differ
diff --git a/public/images/ui/legacy/summary_bg.png b/public/images/ui/legacy/summary_bg.png
index 668a7438bf0..b08bd406a81 100644
Binary files a/public/images/ui/legacy/summary_bg.png and b/public/images/ui/legacy/summary_bg.png differ
diff --git a/public/images/ui/legacy/summary_moves.png b/public/images/ui/legacy/summary_moves.png
index ab909588160..abb499fdbb0 100644
Binary files a/public/images/ui/legacy/summary_moves.png and b/public/images/ui/legacy/summary_moves.png differ
diff --git a/public/images/ui/legacy/summary_moves_effect.png b/public/images/ui/legacy/summary_moves_effect.png
index 61567c9749b..f12e7e523b8 100644
Binary files a/public/images/ui/legacy/summary_moves_effect.png and b/public/images/ui/legacy/summary_moves_effect.png differ
diff --git a/public/images/ui/legacy/summary_profile.png b/public/images/ui/legacy/summary_profile.png
index d8cfebb000e..e2c678a3206 100644
Binary files a/public/images/ui/legacy/summary_profile.png and b/public/images/ui/legacy/summary_profile.png differ
diff --git a/public/images/ui/legacy/summary_profile_ability.png b/public/images/ui/legacy/summary_profile_ability.png
deleted file mode 100644
index f839a6249f8..00000000000
Binary files a/public/images/ui/legacy/summary_profile_ability.png and /dev/null differ
diff --git a/public/images/ui/legacy/summary_profile_passive.png b/public/images/ui/legacy/summary_profile_passive.png
deleted file mode 100644
index 1ff9ad8e09a..00000000000
Binary files a/public/images/ui/legacy/summary_profile_passive.png and /dev/null differ
diff --git a/public/images/ui/legacy/summary_stats.png b/public/images/ui/legacy/summary_stats.png
index 1da361f6157..773661cce07 100644
Binary files a/public/images/ui/legacy/summary_stats.png and b/public/images/ui/legacy/summary_stats.png differ
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/overlay_exp_label_ca.png b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_exp_label_ca.png
new file mode 100644
index 00000000000..acb04a84a31
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_exp_label_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_ca.png b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_ca.png
new file mode 100644
index 00000000000..4f388b70a75
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_hp_label_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/overlay_lv_ca.png b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_lv_ca.png
new file mode 100644
index 00000000000..7fdf26388bf
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/battle_ui/overlay_lv_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.json b/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.json
new file mode 100644
index 00000000000..4bae82011ea
--- /dev/null
+++ b/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ca.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.png b/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.png
new file mode 100644
index 00000000000..a6ccf90c284
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/battle_ui/pbinfo_stat_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_hp_ca.png b/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_hp_ca.png
new file mode 100644
index 00000000000..981d8573aca
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_hp_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_lv_ca.png b/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_lv_ca.png
new file mode 100644
index 00000000000..512db34fa85
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/party_ui/party_slot_overlay_lv_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_ca.png
new file mode 100644
index 00000000000..a457468d8d0
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png
new file mode 100644
index 00000000000..3277a28a59b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_moves_descriptions_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_moves_descriptions_title_ca.png
new file mode 100644
index 00000000000..a37aad953cc
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_moves_descriptions_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_moves_effect_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_moves_effect_title_ca.png
new file mode 100644
index 00000000000..ca66436eca6
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_moves_effect_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_moves_moves_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_moves_moves_title_ca.png
new file mode 100644
index 00000000000..71a6889da53
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_moves_moves_title_ca.png differ
diff --git a/public/images/ui/legacy/summary_moves_overlay_pp.png b/public/images/ui/legacy/text_images/ca/summary/summary_moves_overlay_pp_ca.png
similarity index 100%
rename from public/images/ui/legacy/summary_moves_overlay_pp.png
rename to public/images/ui/legacy/text_images/ca/summary/summary_moves_overlay_pp_ca.png
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_profile_ability_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_profile_ability_ca.png
new file mode 100644
index 00000000000..2d9e8ac317d
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_profile_ability_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_profile_memo_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_profile_memo_title_ca.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_profile_memo_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_profile_passive_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_profile_passive_ca.png
new file mode 100644
index 00000000000..a072b663d25
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_profile_passive_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_profile_profile_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_profile_profile_title_ca.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_profile_profile_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_stats_exp_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_stats_exp_title_ca.png
new file mode 100644
index 00000000000..e7ac7268b54
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_stats_exp_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_stats_expbar_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_stats_expbar_title_ca.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_stats_expbar_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_stats_item_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_stats_item_title_ca.png
new file mode 100644
index 00000000000..c13189fc816
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_stats_item_title_ca.png differ
diff --git a/public/images/ui/legacy/text_images/ca/summary/summary_stats_stats_title_ca.png b/public/images/ui/legacy/text_images/ca/summary/summary_stats_stats_title_ca.png
new file mode 100644
index 00000000000..426848af866
Binary files /dev/null and b/public/images/ui/legacy/text_images/ca/summary/summary_stats_stats_title_ca.png differ
diff --git a/public/images/ui/legacy/summary_tabs_1.png b/public/images/ui/legacy/text_images/ca/summary/summary_tabs_1_ca.png
similarity index 100%
rename from public/images/ui/legacy/summary_tabs_1.png
rename to public/images/ui/legacy/text_images/ca/summary/summary_tabs_1_ca.png
diff --git a/public/images/ui/legacy/summary_tabs_2.png b/public/images/ui/legacy/text_images/ca/summary/summary_tabs_2_ca.png
similarity index 100%
rename from public/images/ui/legacy/summary_tabs_2.png
rename to public/images/ui/legacy/text_images/ca/summary/summary_tabs_2_ca.png
diff --git a/public/images/ui/legacy/summary_tabs_3.png b/public/images/ui/legacy/text_images/ca/summary/summary_tabs_3_ca.png
similarity index 100%
rename from public/images/ui/legacy/summary_tabs_3.png
rename to public/images/ui/legacy/text_images/ca/summary/summary_tabs_3_ca.png
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/overlay_exp_label_da.png b/public/images/ui/legacy/text_images/da/battle_ui/overlay_exp_label_da.png
new file mode 100644
index 00000000000..4c456fff94e
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/battle_ui/overlay_exp_label_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_boss_da.png b/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_boss_da.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_boss_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_da.png b/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_da.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/battle_ui/overlay_hp_label_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/overlay_lv_da.png b/public/images/ui/legacy/text_images/da/battle_ui/overlay_lv_da.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/battle_ui/overlay_lv_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.json b/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.json
new file mode 100644
index 00000000000..eca2a9071ac
--- /dev/null
+++ b/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_da.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.png b/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/battle_ui/pbinfo_stat_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/party_ui/party_slot_overlay_hp_da.png b/public/images/ui/legacy/text_images/da/party_ui/party_slot_overlay_hp_da.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/party_ui/party_slot_overlay_hp_da.png differ
diff --git a/public/images/ui/legacy/party_slot_overlay_lv.png b/public/images/ui/legacy/text_images/da/party_ui/party_slot_overlay_lv_da.png
similarity index 100%
rename from public/images/ui/legacy/party_slot_overlay_lv.png
rename to public/images/ui/legacy/text_images/da/party_ui/party_slot_overlay_lv_da.png
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_da.png b/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_da.png
new file mode 100644
index 00000000000..ef081f493f3
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png b/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png
new file mode 100644
index 00000000000..92c2412f426
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_moves_descriptions_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_moves_descriptions_title_da.png
new file mode 100644
index 00000000000..79b16227bfe
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_moves_descriptions_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_moves_effect_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_moves_effect_title_da.png
new file mode 100644
index 00000000000..f2ac04e62c4
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_moves_effect_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_moves_moves_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_moves_moves_title_da.png
new file mode 100644
index 00000000000..f1732e3e2c0
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_moves_moves_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_moves_overlay_pp_da.png b/public/images/ui/legacy/text_images/da/summary/summary_moves_overlay_pp_da.png
new file mode 100644
index 00000000000..6cc0a2a6831
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_moves_overlay_pp_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_profile_ability_da.png b/public/images/ui/legacy/text_images/da/summary/summary_profile_ability_da.png
new file mode 100644
index 00000000000..206b65c6cdd
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_profile_ability_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_profile_memo_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_profile_memo_title_da.png
new file mode 100644
index 00000000000..48886d25e39
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_profile_memo_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_profile_passive_da.png b/public/images/ui/legacy/text_images/da/summary/summary_profile_passive_da.png
new file mode 100644
index 00000000000..681ae3f1b5e
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_profile_passive_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_profile_profile_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_profile_profile_title_da.png
new file mode 100644
index 00000000000..83b8a1d5e99
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_profile_profile_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_stats_exp_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_stats_exp_title_da.png
new file mode 100644
index 00000000000..feae0c9cb90
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_stats_exp_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_stats_expbar_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_stats_expbar_title_da.png
new file mode 100644
index 00000000000..78bfe5d9735
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_stats_expbar_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_stats_item_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_stats_item_title_da.png
new file mode 100644
index 00000000000..2cbce6c3dcf
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_stats_item_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_stats_stats_title_da.png b/public/images/ui/legacy/text_images/da/summary/summary_stats_stats_title_da.png
new file mode 100644
index 00000000000..ff97416c7ed
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_stats_stats_title_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_tabs_1_da.png b/public/images/ui/legacy/text_images/da/summary/summary_tabs_1_da.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_tabs_1_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_tabs_2_da.png b/public/images/ui/legacy/text_images/da/summary/summary_tabs_2_da.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_tabs_2_da.png differ
diff --git a/public/images/ui/legacy/text_images/da/summary/summary_tabs_3_da.png b/public/images/ui/legacy/text_images/da/summary/summary_tabs_3_da.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/da/summary/summary_tabs_3_da.png differ
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/overlay_exp_label_de.png b/public/images/ui/legacy/text_images/de/battle_ui/overlay_exp_label_de.png
new file mode 100644
index 00000000000..4c456fff94e
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/battle_ui/overlay_exp_label_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_boss_de.png b/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_boss_de.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_boss_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_de.png b/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_de.png
new file mode 100644
index 00000000000..b8404159bc9
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/battle_ui/overlay_hp_label_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/overlay_lv_de.png b/public/images/ui/legacy/text_images/de/battle_ui/overlay_lv_de.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/battle_ui/overlay_lv_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.json b/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.json
new file mode 100644
index 00000000000..c5ace776596
--- /dev/null
+++ b/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_de.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.png b/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.png
new file mode 100644
index 00000000000..8de02e9f16e
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/battle_ui/pbinfo_stat_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_hp_de.png b/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_hp_de.png
new file mode 100644
index 00000000000..8265dfda0d2
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_hp_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_lv_de.png b/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_lv_de.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/party_ui/party_slot_overlay_lv_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_de.png b/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_de.png
new file mode 100644
index 00000000000..ef081f493f3
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png b/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png
new file mode 100644
index 00000000000..92c2412f426
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_moves_descriptions_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_moves_descriptions_title_de.png
new file mode 100644
index 00000000000..ea4827c081d
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_moves_descriptions_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_moves_effect_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_moves_effect_title_de.png
new file mode 100644
index 00000000000..951e9281839
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_moves_effect_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_moves_moves_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_moves_moves_title_de.png
new file mode 100644
index 00000000000..d5e28d3dfe7
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_moves_moves_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_moves_overlay_pp_de.png b/public/images/ui/legacy/text_images/de/summary/summary_moves_overlay_pp_de.png
new file mode 100644
index 00000000000..ba23731775d
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_moves_overlay_pp_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_profile_ability_de.png b/public/images/ui/legacy/text_images/de/summary/summary_profile_ability_de.png
new file mode 100644
index 00000000000..2b6bd055555
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_profile_ability_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_profile_memo_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_profile_memo_title_de.png
new file mode 100644
index 00000000000..089668218fa
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_profile_memo_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_profile_passive_de.png b/public/images/ui/legacy/text_images/de/summary/summary_profile_passive_de.png
new file mode 100644
index 00000000000..681ae3f1b5e
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_profile_passive_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_profile_profile_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_profile_profile_title_de.png
new file mode 100644
index 00000000000..83b8a1d5e99
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_profile_profile_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_stats_exp_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_stats_exp_title_de.png
new file mode 100644
index 00000000000..fa702b4c223
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_stats_exp_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_stats_expbar_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_stats_expbar_title_de.png
new file mode 100644
index 00000000000..78bfe5d9735
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_stats_expbar_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_stats_item_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_stats_item_title_de.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_stats_item_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_stats_stats_title_de.png b/public/images/ui/legacy/text_images/de/summary/summary_stats_stats_title_de.png
new file mode 100644
index 00000000000..87438a5bd4f
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_stats_stats_title_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_tabs_1_de.png b/public/images/ui/legacy/text_images/de/summary/summary_tabs_1_de.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_tabs_1_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_tabs_2_de.png b/public/images/ui/legacy/text_images/de/summary/summary_tabs_2_de.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_tabs_2_de.png differ
diff --git a/public/images/ui/legacy/text_images/de/summary/summary_tabs_3_de.png b/public/images/ui/legacy/text_images/de/summary/summary_tabs_3_de.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/de/summary/summary_tabs_3_de.png differ
diff --git a/public/images/ui/legacy/text_images/en/battle_ui/overlay_exp_label.png b/public/images/ui/legacy/text_images/en/battle_ui/overlay_exp_label.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/battle_ui/overlay_exp_label.png differ
diff --git a/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label.png b/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label.png differ
diff --git a/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label_boss.png b/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label_boss.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/battle_ui/overlay_hp_label_boss.png differ
diff --git a/public/images/ui/legacy/text_images/en/battle_ui/overlay_lv.png b/public/images/ui/legacy/text_images/en/battle_ui/overlay_lv.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/battle_ui/overlay_lv.png differ
diff --git a/public/images/ui/legacy/pbinfo_stat.json b/public/images/ui/legacy/text_images/en/battle_ui/pbinfo_stat.json
similarity index 100%
rename from public/images/ui/legacy/pbinfo_stat.json
rename to public/images/ui/legacy/text_images/en/battle_ui/pbinfo_stat.json
diff --git a/public/images/ui/legacy/text_images/en/battle_ui/pbinfo_stat.png b/public/images/ui/legacy/text_images/en/battle_ui/pbinfo_stat.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/battle_ui/pbinfo_stat.png differ
diff --git a/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_hp.png b/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_hp.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_hp.png differ
diff --git a/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_lv.png b/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_lv.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/party_ui/party_slot_overlay_lv.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_dexnb_label.png b/public/images/ui/legacy/text_images/en/summary/summary_dexnb_label.png
new file mode 100644
index 00000000000..eab90a91c7f
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_dexnb_label.png differ
diff --git a/public/images/ui/legacy/summary_overlay_shiny.png b/public/images/ui/legacy/text_images/en/summary/summary_dexnb_label_overlay_shiny.png
similarity index 100%
rename from public/images/ui/legacy/summary_overlay_shiny.png
rename to public/images/ui/legacy/text_images/en/summary/summary_dexnb_label_overlay_shiny.png
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_moves_descriptions_title.png b/public/images/ui/legacy/text_images/en/summary/summary_moves_descriptions_title.png
new file mode 100644
index 00000000000..3d2b4d08376
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_moves_descriptions_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_moves_effect_title.png b/public/images/ui/legacy/text_images/en/summary/summary_moves_effect_title.png
new file mode 100644
index 00000000000..55fb0efd832
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_moves_effect_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_moves_moves_title.png b/public/images/ui/legacy/text_images/en/summary/summary_moves_moves_title.png
new file mode 100644
index 00000000000..d869ab4e311
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_moves_moves_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_moves_overlay_pp.png b/public/images/ui/legacy/text_images/en/summary/summary_moves_overlay_pp.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_moves_overlay_pp.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_profile_ability.png b/public/images/ui/legacy/text_images/en/summary/summary_profile_ability.png
new file mode 100644
index 00000000000..6600db26802
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_profile_ability.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_profile_memo_title.png b/public/images/ui/legacy/text_images/en/summary/summary_profile_memo_title.png
new file mode 100644
index 00000000000..14cdf099044
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_profile_memo_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_profile_passive.png b/public/images/ui/legacy/text_images/en/summary/summary_profile_passive.png
new file mode 100644
index 00000000000..66f56ff435e
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_profile_passive.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_profile_profile_title.png b/public/images/ui/legacy/text_images/en/summary/summary_profile_profile_title.png
new file mode 100644
index 00000000000..8d4f82df3b3
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_profile_profile_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_stats_exp_title.png b/public/images/ui/legacy/text_images/en/summary/summary_stats_exp_title.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_stats_exp_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_stats_expbar_title.png b/public/images/ui/legacy/text_images/en/summary/summary_stats_expbar_title.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_stats_expbar_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_stats_item_title.png b/public/images/ui/legacy/text_images/en/summary/summary_stats_item_title.png
new file mode 100644
index 00000000000..5752b28288c
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_stats_item_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_stats_stats_title.png b/public/images/ui/legacy/text_images/en/summary/summary_stats_stats_title.png
new file mode 100644
index 00000000000..5531819ef66
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_stats_stats_title.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_tabs_1.png b/public/images/ui/legacy/text_images/en/summary/summary_tabs_1.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_tabs_1.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_tabs_2.png b/public/images/ui/legacy/text_images/en/summary/summary_tabs_2.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_tabs_2.png differ
diff --git a/public/images/ui/legacy/text_images/en/summary/summary_tabs_3.png b/public/images/ui/legacy/text_images/en/summary/summary_tabs_3.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/en/summary/summary_tabs_3.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png
new file mode 100644
index 00000000000..acb04a84a31
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png
new file mode 100644
index 00000000000..6018011d75b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png
new file mode 100644
index 00000000000..4f388b70a75
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_lv_es-ES.png b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_lv_es-ES.png
new file mode 100644
index 00000000000..7fdf26388bf
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/battle_ui/overlay_lv_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.json b/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.json
new file mode 100644
index 00000000000..e7e6318b3b5
--- /dev/null
+++ b/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_es-ES.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.png b/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.png
new file mode 100644
index 00000000000..0a66f1a7137
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/battle_ui/pbinfo_stat_es_ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png b/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png
new file mode 100644
index 00000000000..981d8573aca
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png b/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png
new file mode 100644
index 00000000000..512db34fa85
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_es-ES.png
new file mode 100644
index 00000000000..a457468d8d0
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png
new file mode 100644
index 00000000000..3277a28a59b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png
new file mode 100644
index 00000000000..ffcae31894d
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png
new file mode 100644
index 00000000000..50ce2f51d6f
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png
new file mode 100644
index 00000000000..ffca8bdfa10
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_ability_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_ability_es-ES.png
new file mode 100644
index 00000000000..b1b1a84ddcf
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_ability_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png
new file mode 100644
index 00000000000..e837a58e4f9
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_passive_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_passive_es-ES.png
new file mode 100644
index 00000000000..885453e3e98
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_passive_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png
new file mode 100644
index 00000000000..51ba9300dab
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png
new file mode 100644
index 00000000000..7c8a80f0627
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_item_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_item_title_es-ES.png
new file mode 100644
index 00000000000..15fdb806125
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_item_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png
new file mode 100644
index 00000000000..2233461522c
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_1_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_1_es-ES.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_1_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_2_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_2_es-ES.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_2_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_3_es-ES.png b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_3_es-ES.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-ES/summary/summary_tabs_3_es-ES.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png
new file mode 100644
index 00000000000..acb04a84a31
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png
new file mode 100644
index 00000000000..6018011d75b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png
new file mode 100644
index 00000000000..4f388b70a75
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_lv_es-MX.png b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_lv_es-MX.png
new file mode 100644
index 00000000000..7fdf26388bf
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/battle_ui/overlay_lv_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.json b/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.json
new file mode 100644
index 00000000000..98bd43e7dd9
--- /dev/null
+++ b/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_es-MX.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.png b/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.png
new file mode 100644
index 00000000000..0a66f1a7137
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/battle_ui/pbinfo_stat_es_MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png b/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png
new file mode 100644
index 00000000000..981d8573aca
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png b/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png
new file mode 100644
index 00000000000..512db34fa85
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_es-MX.png
new file mode 100644
index 00000000000..a457468d8d0
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png
new file mode 100644
index 00000000000..3277a28a59b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png
new file mode 100644
index 00000000000..ffcae31894d
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png
new file mode 100644
index 00000000000..50ce2f51d6f
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png
new file mode 100644
index 00000000000..ffca8bdfa10
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_ability_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_ability_es-MX.png
new file mode 100644
index 00000000000..b1b1a84ddcf
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_ability_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png
new file mode 100644
index 00000000000..e837a58e4f9
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_passive_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_passive_es-MX.png
new file mode 100644
index 00000000000..885453e3e98
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_passive_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png
new file mode 100644
index 00000000000..51ba9300dab
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png
new file mode 100644
index 00000000000..7c8a80f0627
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_item_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_item_title_es-MX.png
new file mode 100644
index 00000000000..15fdb806125
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_item_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png
new file mode 100644
index 00000000000..2233461522c
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_1_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_1_es-MX.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_1_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_2_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_2_es-MX.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_2_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_3_es-MX.png b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_3_es-MX.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/es-MX/summary/summary_tabs_3_es-MX.png differ
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/overlay_exp_label_fr.png b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_exp_label_fr.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_exp_label_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_fr.png b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_fr.png
new file mode 100644
index 00000000000..3f7e12c3246
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_hp_label_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/overlay_lv_fr.png b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_lv_fr.png
new file mode 100644
index 00000000000..5a31182069c
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/battle_ui/overlay_lv_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.json b/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.json
new file mode 100644
index 00000000000..00cc9f7ea0e
--- /dev/null
+++ b/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_fr.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.png b/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.png
new file mode 100644
index 00000000000..1919561ddfe
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/battle_ui/pbinfo_stat_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_hp_fr.png b/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_hp_fr.png
new file mode 100644
index 00000000000..55468e4b1d1
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_hp_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_lv_fr.png b/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_lv_fr.png
new file mode 100644
index 00000000000..e7bbec23b07
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/party_ui/party_slot_overlay_lv_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_fr.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png
new file mode 100644
index 00000000000..1640e46caa0
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_moves_descriptions_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_moves_descriptions_title_fr.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_moves_descriptions_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_moves_effect_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_moves_effect_title_fr.png
new file mode 100644
index 00000000000..49f4e11096a
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_moves_effect_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_moves_moves_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_moves_moves_title_fr.png
new file mode 100644
index 00000000000..1fd7d72c307
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_moves_moves_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_moves_overlay_pp_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_moves_overlay_pp_fr.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_moves_overlay_pp_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_profile_ability_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_profile_ability_fr.png
new file mode 100644
index 00000000000..3354b40adfe
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_profile_ability_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_profile_memo_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_profile_memo_title_fr.png
new file mode 100644
index 00000000000..54c6beb4428
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_profile_memo_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_profile_passive_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_profile_passive_fr.png
new file mode 100644
index 00000000000..f5d2c98a21c
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_profile_passive_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_profile_profile_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_profile_profile_title_fr.png
new file mode 100644
index 00000000000..bccba2d87df
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_profile_profile_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_stats_exp_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_stats_exp_title_fr.png
new file mode 100644
index 00000000000..32d41d44be4
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_stats_exp_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_stats_expbar_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_stats_expbar_title_fr.png
new file mode 100644
index 00000000000..da999975932
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_stats_expbar_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_stats_item_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_stats_item_title_fr.png
new file mode 100644
index 00000000000..45c10e8c907
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_stats_item_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_stats_stats_title_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_stats_stats_title_fr.png
new file mode 100644
index 00000000000..63fe6a694af
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_stats_stats_title_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_tabs_1_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_1_fr.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_1_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_tabs_2_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_2_fr.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_2_fr.png differ
diff --git a/public/images/ui/legacy/text_images/fr/summary/summary_tabs_3_fr.png b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_3_fr.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/fr/summary/summary_tabs_3_fr.png differ
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/overlay_exp_label_it.png b/public/images/ui/legacy/text_images/it/battle_ui/overlay_exp_label_it.png
new file mode 100644
index 00000000000..3a888a5ffb0
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/battle_ui/overlay_exp_label_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_boss_it.png b/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_boss_it.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_boss_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_it.png b/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_it.png
new file mode 100644
index 00000000000..4f388b70a75
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/battle_ui/overlay_hp_label_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/overlay_lv_it.png b/public/images/ui/legacy/text_images/it/battle_ui/overlay_lv_it.png
new file mode 100644
index 00000000000..579c684d766
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/battle_ui/overlay_lv_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.json b/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.json
new file mode 100644
index 00000000000..1971bb61221
--- /dev/null
+++ b/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_it.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.png b/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.png
new file mode 100644
index 00000000000..ced53e91a6e
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/battle_ui/pbinfo_stat_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_hp_it.png b/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_hp_it.png
new file mode 100644
index 00000000000..981d8573aca
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_hp_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_lv_it.png b/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_lv_it.png
new file mode 100644
index 00000000000..5338254b4d0
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/party_ui/party_slot_overlay_lv_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_it.png b/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_it.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png b/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png
new file mode 100644
index 00000000000..1640e46caa0
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_moves_descriptions_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_moves_descriptions_title_it.png
new file mode 100644
index 00000000000..e0bf21396e5
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_moves_descriptions_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_moves_effect_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_moves_effect_title_it.png
new file mode 100644
index 00000000000..99f9dfeb2c3
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_moves_effect_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_moves_moves_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_moves_moves_title_it.png
new file mode 100644
index 00000000000..e56e3f4b336
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_moves_moves_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_moves_overlay_pp_it.png b/public/images/ui/legacy/text_images/it/summary/summary_moves_overlay_pp_it.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_moves_overlay_pp_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_profile_ability_it.png b/public/images/ui/legacy/text_images/it/summary/summary_profile_ability_it.png
new file mode 100644
index 00000000000..bb915da37ee
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_profile_ability_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_profile_memo_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_profile_memo_title_it.png
new file mode 100644
index 00000000000..0c81cd56f06
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_profile_memo_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_profile_passive_it.png b/public/images/ui/legacy/text_images/it/summary/summary_profile_passive_it.png
new file mode 100644
index 00000000000..a072b663d25
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_profile_passive_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_profile_profile_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_profile_profile_title_it.png
new file mode 100644
index 00000000000..169b0a7b8e7
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_profile_profile_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_stats_exp_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_stats_exp_title_it.png
new file mode 100644
index 00000000000..a3a1aa4c402
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_stats_exp_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_stats_expbar_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_stats_expbar_title_it.png
new file mode 100644
index 00000000000..aa8eee71613
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_stats_expbar_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_stats_item_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_stats_item_title_it.png
new file mode 100644
index 00000000000..a93695a48c8
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_stats_item_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_stats_stats_title_it.png b/public/images/ui/legacy/text_images/it/summary/summary_stats_stats_title_it.png
new file mode 100644
index 00000000000..998db7fc7e3
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_stats_stats_title_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_tabs_1_it.png b/public/images/ui/legacy/text_images/it/summary/summary_tabs_1_it.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_tabs_1_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_tabs_2_it.png b/public/images/ui/legacy/text_images/it/summary/summary_tabs_2_it.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_tabs_2_it.png differ
diff --git a/public/images/ui/legacy/text_images/it/summary/summary_tabs_3_it.png b/public/images/ui/legacy/text_images/it/summary/summary_tabs_3_it.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/it/summary/summary_tabs_3_it.png differ
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/overlay_exp_label_ja.png b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_exp_label_ja.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_exp_label_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_ja.png b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_ja.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_hp_label_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/overlay_lv_ja.png b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_lv_ja.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/battle_ui/overlay_lv_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.json b/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.json
new file mode 100644
index 00000000000..d8907e8e68c
--- /dev/null
+++ b/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ja.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.png b/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/battle_ui/pbinfo_stat_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_hp_ja.png b/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_hp_ja.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_hp_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_lv_ja.png b/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_lv_ja.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/party_ui/party_slot_overlay_lv_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_ja.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_moves_descriptions_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_moves_descriptions_title_ja.png
new file mode 100644
index 00000000000..d844e844244
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_moves_descriptions_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_moves_effect_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_moves_effect_title_ja.png
new file mode 100644
index 00000000000..a75452f1299
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_moves_effect_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_moves_moves_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_moves_moves_title_ja.png
new file mode 100644
index 00000000000..6492c3ecf7c
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_moves_moves_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_moves_overlay_pp_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_moves_overlay_pp_ja.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_moves_overlay_pp_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_profile_ability_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_profile_ability_ja.png
new file mode 100644
index 00000000000..6c82a863a69
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_profile_ability_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_profile_memo_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_profile_memo_title_ja.png
new file mode 100644
index 00000000000..3f5a4536817
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_profile_memo_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_profile_passive_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_profile_passive_ja.png
new file mode 100644
index 00000000000..3bfed88c7da
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_profile_passive_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_profile_profile_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_profile_profile_title_ja.png
new file mode 100644
index 00000000000..a05cba49247
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_profile_profile_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_stats_exp_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_stats_exp_title_ja.png
new file mode 100644
index 00000000000..0bc237a7e6b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_stats_exp_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_stats_expbar_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_stats_expbar_title_ja.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_stats_expbar_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_stats_item_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_stats_item_title_ja.png
new file mode 100644
index 00000000000..114b4d7fd3f
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_stats_item_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_stats_stats_title_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_stats_stats_title_ja.png
new file mode 100644
index 00000000000..c9f2e28fb07
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_stats_stats_title_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_tabs_1_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_1_ja.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_1_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_tabs_2_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_2_ja.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_2_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ja/summary/summary_tabs_3_ja.png b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_3_ja.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/ja/summary/summary_tabs_3_ja.png differ
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/overlay_exp_label_ko.png b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_exp_label_ko.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_exp_label_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_ko.png b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_ko.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_hp_label_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/overlay_lv_ko.png b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_lv_ko.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/battle_ui/overlay_lv_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.json b/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.json
new file mode 100644
index 00000000000..359e3a5b76f
--- /dev/null
+++ b/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ko.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.png b/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/battle_ui/pbinfo_stat_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_hp_ko.png b/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_hp_ko.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_hp_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_lv_ko.png b/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_lv_ko.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/party_ui/party_slot_overlay_lv_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_ko.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_moves_descriptions_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_moves_descriptions_title_ko.png
new file mode 100644
index 00000000000..350bd607aed
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_moves_descriptions_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_moves_effect_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_moves_effect_title_ko.png
new file mode 100644
index 00000000000..f90ac18fc4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_moves_effect_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_moves_moves_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_moves_moves_title_ko.png
new file mode 100644
index 00000000000..d020d8a77cb
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_moves_moves_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_moves_overlay_pp_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_moves_overlay_pp_ko.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_moves_overlay_pp_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_profile_ability_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_profile_ability_ko.png
new file mode 100644
index 00000000000..cdeafaf486e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_profile_ability_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_profile_memo_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_profile_memo_title_ko.png
new file mode 100644
index 00000000000..7af960fe1df
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_profile_memo_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_profile_passive_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_profile_passive_ko.png
new file mode 100644
index 00000000000..34a13ffe836
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_profile_passive_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_profile_profile_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_profile_profile_title_ko.png
new file mode 100644
index 00000000000..deb57b96715
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_profile_profile_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_stats_exp_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_stats_exp_title_ko.png
new file mode 100644
index 00000000000..af0cb3afab8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_stats_exp_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_stats_expbar_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_stats_expbar_title_ko.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_stats_expbar_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_stats_item_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_stats_item_title_ko.png
new file mode 100644
index 00000000000..6be5909e5f0
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_stats_item_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_stats_stats_title_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_stats_stats_title_ko.png
new file mode 100644
index 00000000000..371c2be4976
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_stats_stats_title_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_tabs_1_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_1_ko.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_1_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_tabs_2_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_2_ko.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_2_ko.png differ
diff --git a/public/images/ui/legacy/text_images/ko/summary/summary_tabs_3_ko.png b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_3_ko.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/ko/summary/summary_tabs_3_ko.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png
new file mode 100644
index 00000000000..acb04a84a31
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png
new file mode 100644
index 00000000000..4f388b70a75
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png
new file mode 100644
index 00000000000..7fdf26388bf
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json b/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json
new file mode 100644
index 00000000000..dc7eecf1970
--- /dev/null
+++ b/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_pt-BR.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png
new file mode 100644
index 00000000000..3dcd273bcc9
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png
new file mode 100644
index 00000000000..981d8573aca
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png
new file mode 100644
index 00000000000..512db34fa85
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png
new file mode 100644
index 00000000000..3277a28a59b
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png
new file mode 100644
index 00000000000..8d888143663
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png
new file mode 100644
index 00000000000..83190955f2c
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png
new file mode 100644
index 00000000000..0a2d1657750
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png
new file mode 100644
index 00000000000..28fbb5a8be9
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png
new file mode 100644
index 00000000000..11616a74287
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png
new file mode 100644
index 00000000000..d66fa829be1
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png
new file mode 100644
index 00000000000..b9ea9400a64
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png
new file mode 100644
index 00000000000..bc9dffc2887
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png differ
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/overlay_exp_label_ro.png b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_exp_label_ro.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_exp_label_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_ro.png b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_ro.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_hp_label_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/overlay_lv_ro.png b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_lv_ro.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/battle_ui/overlay_lv_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.json b/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.json
new file mode 100644
index 00000000000..8c268a77098
--- /dev/null
+++ b/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ro.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.png b/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/battle_ui/pbinfo_stat_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_hp_ro.png b/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_hp_ro.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_hp_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_lv_ro.png b/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_lv_ro.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/party_ui/party_slot_overlay_lv_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_ro.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_dexnb_label_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_moves_descriptions_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_moves_descriptions_title_ro.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_moves_descriptions_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_moves_effect_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_moves_effect_title_ro.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_moves_effect_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_moves_moves_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_moves_moves_title_ro.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_moves_moves_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_moves_overlay_pp_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_moves_overlay_pp_ro.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_moves_overlay_pp_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_profile_ability_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_profile_ability_ro.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_profile_ability_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_profile_memo_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_profile_memo_title_ro.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_profile_memo_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_profile_passive_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_profile_passive_ro.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_profile_passive_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_profile_profile_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_profile_profile_title_ro.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_profile_profile_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_stats_exp_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_stats_exp_title_ro.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_stats_exp_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_stats_expbar_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_stats_expbar_title_ro.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_stats_expbar_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_stats_item_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_stats_item_title_ro.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_stats_item_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_stats_stats_title_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_stats_stats_title_ro.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_stats_stats_title_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_tabs_1_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_1_ro.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_1_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_tabs_2_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_2_ro.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_2_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ro/summary/summary_tabs_3_ro.png b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_3_ro.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/ro/summary/summary_tabs_3_ro.png differ
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/overlay_exp_label_ru.png b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_exp_label_ru.png
new file mode 100644
index 00000000000..d8817108408
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_exp_label_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png
new file mode 100644
index 00000000000..26c4d570bb1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_ru.png b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_ru.png
new file mode 100644
index 00000000000..490c93f8d39
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_hp_label_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/overlay_lv_ru.png b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_lv_ru.png
new file mode 100644
index 00000000000..e8760560dfa
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/battle_ui/overlay_lv_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.json b/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.json
new file mode 100644
index 00000000000..e4e67b4da18
--- /dev/null
+++ b/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ru.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 7
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 7
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 7
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 7
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 7
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 7
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 7
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 7
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.png b/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.png
new file mode 100644
index 00000000000..0899fbdbfc1
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/battle_ui/pbinfo_stat_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_hp_ru.png b/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_hp_ru.png
new file mode 100644
index 00000000000..24bf5a8923f
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_hp_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_lv_ru.png b/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_lv_ru.png
new file mode 100644
index 00000000000..319d7a75fb8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/party_ui/party_slot_overlay_lv_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png
new file mode 100644
index 00000000000..1f8f6a1246e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_ru.png
new file mode 100644
index 00000000000..2a2e66426cb
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_dexnb_label_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_moves_descriptions_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_moves_descriptions_title_ru.png
new file mode 100644
index 00000000000..a435d6d845e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_moves_descriptions_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_moves_effect_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_moves_effect_title_ru.png
new file mode 100644
index 00000000000..8bd4b965247
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_moves_effect_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_moves_moves_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_moves_moves_title_ru.png
new file mode 100644
index 00000000000..94afd3226e8
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_moves_moves_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_moves_overlay_pp_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_moves_overlay_pp_ru.png
new file mode 100644
index 00000000000..4af1f833036
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_moves_overlay_pp_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_profile_ability_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_profile_ability_ru.png
new file mode 100644
index 00000000000..ddfcc6102dc
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_profile_ability_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_profile_memo_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_profile_memo_title_ru.png
new file mode 100644
index 00000000000..743a2f36454
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_profile_memo_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_profile_passive_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_profile_passive_ru.png
new file mode 100644
index 00000000000..29179b6d75b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_profile_passive_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_profile_profile_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_profile_profile_title_ru.png
new file mode 100644
index 00000000000..77edb538634
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_profile_profile_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_stats_exp_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_stats_exp_title_ru.png
new file mode 100644
index 00000000000..e058eaa917e
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_stats_exp_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_stats_expbar_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_stats_expbar_title_ru.png
new file mode 100644
index 00000000000..b885633d336
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_stats_expbar_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_stats_item_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_stats_item_title_ru.png
new file mode 100644
index 00000000000..95b01d062ab
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_stats_item_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_stats_stats_title_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_stats_stats_title_ru.png
new file mode 100644
index 00000000000..0c1c5dc9445
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_stats_stats_title_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_tabs_1_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_1_ru.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_1_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_tabs_2_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_2_ru.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_2_ru.png differ
diff --git a/public/images/ui/legacy/text_images/ru/summary/summary_tabs_3_ru.png b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_3_ru.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/ru/summary/summary_tabs_3_ru.png differ
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/overlay_exp_label_tl.png b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_exp_label_tl.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_exp_label_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_tl.png b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_tl.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_hp_label_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/overlay_lv_tl.png b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_lv_tl.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/battle_ui/overlay_lv_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.json b/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.json
new file mode 100644
index 00000000000..023a5ee45f9
--- /dev/null
+++ b/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_tl.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.png b/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/battle_ui/pbinfo_stat_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_hp_tl.png b/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_hp_tl.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_hp_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_lv_tl.png b/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_lv_tl.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/party_ui/party_slot_overlay_lv_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_tl.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_dexnb_label_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_moves_descriptions_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_moves_descriptions_title_tl.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_moves_descriptions_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_moves_effect_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_moves_effect_title_tl.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_moves_effect_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_moves_moves_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_moves_moves_title_tl.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_moves_moves_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_moves_overlay_pp_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_moves_overlay_pp_tl.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_moves_overlay_pp_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_profile_ability_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_profile_ability_tl.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_profile_ability_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_profile_memo_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_profile_memo_title_tl.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_profile_memo_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_profile_passive_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_profile_passive_tl.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_profile_passive_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_profile_profile_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_profile_profile_title_tl.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_profile_profile_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_stats_exp_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_stats_exp_title_tl.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_stats_exp_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_stats_expbar_title.png b/public/images/ui/legacy/text_images/tl/summary/summary_stats_expbar_title.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_stats_expbar_title.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_stats_item_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_stats_item_title_tl.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_stats_item_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_stats_stats_title_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_stats_stats_title_tl.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_stats_stats_title_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_tabs_1_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_1_tl.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_1_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_tabs_2_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_2_tl.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_2_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tl/summary/summary_tabs_3_tl.png b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_3_tl.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/tl/summary/summary_tabs_3_tl.png differ
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/overlay_exp_label_tr.png b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_exp_label_tr.png
new file mode 100644
index 00000000000..5b9bd882581
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_exp_label_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_tr.png b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_tr.png
new file mode 100644
index 00000000000..da11c11cb50
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_hp_label_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/overlay_lv_tr.png b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_lv_tr.png
new file mode 100644
index 00000000000..6d8ccbb3f3a
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/battle_ui/overlay_lv_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.json b/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.json
new file mode 100644
index 00000000000..6de50500767
--- /dev/null
+++ b/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_tr.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.png b/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/battle_ui/pbinfo_stat_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_hp_tr.png b/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_hp_tr.png
new file mode 100644
index 00000000000..2833fa27fb4
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_hp_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_lv_tr.png b/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_lv_tr.png
new file mode 100644
index 00000000000..958a750a665
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/party_ui/party_slot_overlay_lv_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_tr.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_dexnb_label_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_moves_descriptions_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_moves_descriptions_title_tr.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_moves_descriptions_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_moves_effect_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_moves_effect_title_tr.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_moves_effect_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_moves_moves_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_moves_moves_title_tr.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_moves_moves_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_moves_overlay_pp_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_moves_overlay_pp_tr.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_moves_overlay_pp_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_profile_ability_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_profile_ability_tr.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_profile_ability_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_profile_memo_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_profile_memo_title_tr.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_profile_memo_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_profile_passive_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_profile_passive_tr.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_profile_passive_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_profile_profile_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_profile_profile_title_tr.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_profile_profile_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_stats_exp_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_stats_exp_title_tr.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_stats_exp_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_stats_expbar_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_stats_expbar_title_tr.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_stats_expbar_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_stats_item_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_stats_item_title_tr.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_stats_item_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_stats_stats_title_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_stats_stats_title_tr.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_stats_stats_title_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_tabs_1_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_1_tr.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_1_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_tabs_2_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_2_tr.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_2_tr.png differ
diff --git a/public/images/ui/legacy/text_images/tr/summary/summary_tabs_3_tr.png b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_3_tr.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/tr/summary/summary_tabs_3_tr.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json b/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json
new file mode 100644
index 00000000000..49649bbc315
--- /dev/null
+++ b/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_zh-CN.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png
new file mode 100644
index 00000000000..eab90a91c7f
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png
new file mode 100644
index 00000000000..3d2b4d08376
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png
new file mode 100644
index 00000000000..55fb0efd832
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png
new file mode 100644
index 00000000000..d869ab4e311
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png
new file mode 100644
index 00000000000..6600db26802
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png
new file mode 100644
index 00000000000..14cdf099044
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png
new file mode 100644
index 00000000000..66f56ff435e
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png
new file mode 100644
index 00000000000..8d4f82df3b3
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png
new file mode 100644
index 00000000000..5752b28288c
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png
new file mode 100644
index 00000000000..5531819ef66
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png
new file mode 100644
index 00000000000..40b5e8925a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png
new file mode 100644
index 00000000000..283d63fb235
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png
new file mode 100644
index 00000000000..67824c7ee4e
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png
new file mode 100644
index 00000000000..ea32ac03ee2
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json b/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json
new file mode 100644
index 00000000000..5a2e0fe2c30
--- /dev/null
+++ b/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_zh-TW.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 112,
+ "h": 6
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 18,
+ "h": 6
+ },
+ "frame": {
+ "x": 18,
+ "y": 0,
+ "w": 18,
+ "h": 6
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 16,
+ "h": 6
+ },
+ "frame": {
+ "x": 36,
+ "y": 0,
+ "w": 16,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 52,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 64,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 76,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 88,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 12,
+ "h": 6
+ },
+ "frame": {
+ "x": 100,
+ "y": 0,
+ "w": 12,
+ "h": 6
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 1,
+ "y": 2,
+ "w": 8,
+ "h": 6
+ },
+ "frame": {
+ "x": 112,
+ "y": 0,
+ "w": 8,
+ "h": 6
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:40d30205ce8efd40dfa86cd11b0491d6:7076db6ed74199dcfb38fc8cd4d4a0e8:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png
new file mode 100644
index 00000000000..c729e7a2207
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png
new file mode 100644
index 00000000000..a78ac090d3b
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png
new file mode 100644
index 00000000000..11bb545c7af
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png
new file mode 100644
index 00000000000..bb2436738a8
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png
new file mode 100644
index 00000000000..eab90a91c7f
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png
new file mode 100644
index 00000000000..3d2b4d08376
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png
new file mode 100644
index 00000000000..55fb0efd832
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png
new file mode 100644
index 00000000000..d869ab4e311
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png
new file mode 100644
index 00000000000..9babffc0429
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png
new file mode 100644
index 00000000000..6600db26802
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png
new file mode 100644
index 00000000000..14cdf099044
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png
new file mode 100644
index 00000000000..66f56ff435e
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png
new file mode 100644
index 00000000000..8d4f82df3b3
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png
new file mode 100644
index 00000000000..e9dfb10e5d6
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png
new file mode 100644
index 00000000000..5752b28288c
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png
new file mode 100644
index 00000000000..5531819ef66
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png
new file mode 100644
index 00000000000..379768ea310
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png
new file mode 100644
index 00000000000..2a04879b32b
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png differ
diff --git a/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png
new file mode 100644
index 00000000000..ef65774f744
Binary files /dev/null and b/public/images/ui/legacy/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png differ
diff --git a/public/images/ui/overlay_lv.png b/public/images/ui/overlay_lv.png
deleted file mode 100644
index 6ced2da2660..00000000000
Binary files a/public/images/ui/overlay_lv.png and /dev/null differ
diff --git a/public/images/ui/party_slot_hp_bar.png b/public/images/ui/party_slot_hp_bar.png
index 181bd04c0ea..c1818439e75 100644
Binary files a/public/images/ui/party_slot_hp_bar.png and b/public/images/ui/party_slot_hp_bar.png differ
diff --git a/public/images/ui/pbinfo_enemy_boss.png b/public/images/ui/pbinfo_enemy_boss.png
index ce829bf4631..421edabba03 100644
Binary files a/public/images/ui/pbinfo_enemy_boss.png and b/public/images/ui/pbinfo_enemy_boss.png differ
diff --git a/public/images/ui/pbinfo_enemy_mini.png b/public/images/ui/pbinfo_enemy_mini.png
index ddf10776778..bf60bb0f964 100644
Binary files a/public/images/ui/pbinfo_enemy_mini.png and b/public/images/ui/pbinfo_enemy_mini.png differ
diff --git a/public/images/ui/pbinfo_player.png b/public/images/ui/pbinfo_player.png
index c7b2227e800..55c381bd436 100644
Binary files a/public/images/ui/pbinfo_player.png and b/public/images/ui/pbinfo_player.png differ
diff --git a/public/images/ui/pbinfo_player_mini.png b/public/images/ui/pbinfo_player_mini.png
index 90d5e03dae8..255ad00f8ca 100644
Binary files a/public/images/ui/pbinfo_player_mini.png and b/public/images/ui/pbinfo_player_mini.png differ
diff --git a/public/images/ui/pbinfo_stat.png b/public/images/ui/pbinfo_stat.png
deleted file mode 100644
index 1f86cc6a958..00000000000
Binary files a/public/images/ui/pbinfo_stat.png and /dev/null differ
diff --git a/public/images/ui/pokedex_summary_bg.png b/public/images/ui/pokedex_summary_bg.png
index bb55127edf2..423bc7528fd 100644
Binary files a/public/images/ui/pokedex_summary_bg.png and b/public/images/ui/pokedex_summary_bg.png differ
diff --git a/public/images/ui/starter_select_bg.png b/public/images/ui/starter_select_bg.png
index b11cd12a5e4..14e90dd0f85 100644
Binary files a/public/images/ui/starter_select_bg.png and b/public/images/ui/starter_select_bg.png differ
diff --git a/public/images/ui/summary_bg.png b/public/images/ui/summary_bg.png
index ebfe87b78b2..4bdb6d0db22 100644
Binary files a/public/images/ui/summary_bg.png and b/public/images/ui/summary_bg.png differ
diff --git a/public/images/ui/summary_moves.png b/public/images/ui/summary_moves.png
index 618d91934f2..7f53809d052 100644
Binary files a/public/images/ui/summary_moves.png and b/public/images/ui/summary_moves.png differ
diff --git a/public/images/ui/summary_moves_effect.png b/public/images/ui/summary_moves_effect.png
index 7993cd0ad57..f8ead3c5263 100644
Binary files a/public/images/ui/summary_moves_effect.png and b/public/images/ui/summary_moves_effect.png differ
diff --git a/public/images/ui/summary_overlay_shiny.png b/public/images/ui/summary_overlay_shiny.png
deleted file mode 100644
index 956f9fb93f3..00000000000
Binary files a/public/images/ui/summary_overlay_shiny.png and /dev/null differ
diff --git a/public/images/ui/summary_profile.png b/public/images/ui/summary_profile.png
index 2c0c93af6b4..6362148f9d9 100644
Binary files a/public/images/ui/summary_profile.png and b/public/images/ui/summary_profile.png differ
diff --git a/public/images/ui/summary_profile_ability.png b/public/images/ui/summary_profile_ability.png
deleted file mode 100644
index f839a6249f8..00000000000
Binary files a/public/images/ui/summary_profile_ability.png and /dev/null differ
diff --git a/public/images/ui/summary_profile_passive.png b/public/images/ui/summary_profile_passive.png
deleted file mode 100644
index 1ff9ad8e09a..00000000000
Binary files a/public/images/ui/summary_profile_passive.png and /dev/null differ
diff --git a/public/images/ui/summary_stats.png b/public/images/ui/summary_stats.png
index d808be92fd1..bcfc55fb6aa 100644
Binary files a/public/images/ui/summary_stats.png and b/public/images/ui/summary_stats.png differ
diff --git a/public/images/ui/summary_tabs_2.png b/public/images/ui/summary_tabs_2.png
deleted file mode 100644
index 250c3ce7960..00000000000
Binary files a/public/images/ui/summary_tabs_2.png and /dev/null differ
diff --git a/public/images/ui/summary_tabs_3.png b/public/images/ui/summary_tabs_3.png
deleted file mode 100644
index 1af80fa5c0d..00000000000
Binary files a/public/images/ui/summary_tabs_3.png and /dev/null differ
diff --git a/public/images/ui/text_images/ca/battle_ui/overlay_exp_label_ca.png b/public/images/ui/text_images/ca/battle_ui/overlay_exp_label_ca.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/ca/battle_ui/overlay_exp_label_ca.png differ
diff --git a/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png b/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_boss_ca.png differ
diff --git a/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_ca.png b/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_ca.png
new file mode 100644
index 00000000000..7c7ccba752f
Binary files /dev/null and b/public/images/ui/text_images/ca/battle_ui/overlay_hp_label_ca.png differ
diff --git a/public/images/ui/text_images/ca/battle_ui/overlay_lv_ca.png b/public/images/ui/text_images/ca/battle_ui/overlay_lv_ca.png
new file mode 100644
index 00000000000..869a1ee0051
Binary files /dev/null and b/public/images/ui/text_images/ca/battle_ui/overlay_lv_ca.png differ
diff --git a/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.json b/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.json
new file mode 100644
index 00000000000..488321effd7
--- /dev/null
+++ b/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ca.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 9
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 17,
+ "h": 9
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 9,
+ "h": 9
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 9
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.png b/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.png
new file mode 100644
index 00000000000..2b22b4f4d6f
Binary files /dev/null and b/public/images/ui/text_images/ca/battle_ui/pbinfo_stat_ca.png differ
diff --git a/public/images/ui/text_images/ca/party_ui/party_slot_overlay_hp_ca.png b/public/images/ui/text_images/ca/party_ui/party_slot_overlay_hp_ca.png
new file mode 100644
index 00000000000..4bc95e4f95e
Binary files /dev/null and b/public/images/ui/text_images/ca/party_ui/party_slot_overlay_hp_ca.png differ
diff --git a/public/images/ui/text_images/ca/party_ui/party_slot_overlay_lv_ca.png b/public/images/ui/text_images/ca/party_ui/party_slot_overlay_lv_ca.png
new file mode 100644
index 00000000000..c5971de4fd0
Binary files /dev/null and b/public/images/ui/text_images/ca/party_ui/party_slot_overlay_lv_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_dexnb_label_ca.png b/public/images/ui/text_images/ca/summary/summary_dexnb_label_ca.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_dexnb_label_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png b/public/images/ui/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_dexnb_label_overlay_shiny_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_moves_descriptions_title_ca.png b/public/images/ui/text_images/ca/summary/summary_moves_descriptions_title_ca.png
new file mode 100644
index 00000000000..a37aad953cc
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_moves_descriptions_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_moves_effect_title_ca.png b/public/images/ui/text_images/ca/summary/summary_moves_effect_title_ca.png
new file mode 100644
index 00000000000..ca66436eca6
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_moves_effect_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_moves_moves_title_ca.png b/public/images/ui/text_images/ca/summary/summary_moves_moves_title_ca.png
new file mode 100644
index 00000000000..71a6889da53
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_moves_moves_title_ca.png differ
diff --git a/public/images/ui/summary_moves_overlay_pp.png b/public/images/ui/text_images/ca/summary/summary_moves_overlay_pp_ca.png
similarity index 100%
rename from public/images/ui/summary_moves_overlay_pp.png
rename to public/images/ui/text_images/ca/summary/summary_moves_overlay_pp_ca.png
diff --git a/public/images/ui/text_images/ca/summary/summary_profile_ability_ca.png b/public/images/ui/text_images/ca/summary/summary_profile_ability_ca.png
new file mode 100644
index 00000000000..2d9e8ac317d
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_profile_ability_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_profile_memo_title_ca.png b/public/images/ui/text_images/ca/summary/summary_profile_memo_title_ca.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_profile_memo_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_profile_passive_ca.png b/public/images/ui/text_images/ca/summary/summary_profile_passive_ca.png
new file mode 100644
index 00000000000..a072b663d25
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_profile_passive_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_profile_profile_title_ca.png b/public/images/ui/text_images/ca/summary/summary_profile_profile_title_ca.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_profile_profile_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_stats_exp_title_ca.png b/public/images/ui/text_images/ca/summary/summary_stats_exp_title_ca.png
new file mode 100644
index 00000000000..e7ac7268b54
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_stats_exp_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_stats_expbar_title_ca.png b/public/images/ui/text_images/ca/summary/summary_stats_expbar_title_ca.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_stats_expbar_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_stats_item_title_ca.png b/public/images/ui/text_images/ca/summary/summary_stats_item_title_ca.png
new file mode 100644
index 00000000000..c13189fc816
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_stats_item_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_stats_stats_title_ca.png b/public/images/ui/text_images/ca/summary/summary_stats_stats_title_ca.png
new file mode 100644
index 00000000000..426848af866
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_stats_stats_title_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_tabs_1_ca.png b/public/images/ui/text_images/ca/summary/summary_tabs_1_ca.png
new file mode 100644
index 00000000000..1e1ad63672e
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_tabs_1_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_tabs_2_ca.png b/public/images/ui/text_images/ca/summary/summary_tabs_2_ca.png
new file mode 100644
index 00000000000..bf794e82d20
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_tabs_2_ca.png differ
diff --git a/public/images/ui/text_images/ca/summary/summary_tabs_3_ca.png b/public/images/ui/text_images/ca/summary/summary_tabs_3_ca.png
new file mode 100644
index 00000000000..6a0109997b2
Binary files /dev/null and b/public/images/ui/text_images/ca/summary/summary_tabs_3_ca.png differ
diff --git a/public/images/ui/text_images/da/battle_ui/overlay_exp_label_da.png b/public/images/ui/text_images/da/battle_ui/overlay_exp_label_da.png
new file mode 100644
index 00000000000..39a92725bc0
Binary files /dev/null and b/public/images/ui/text_images/da/battle_ui/overlay_exp_label_da.png differ
diff --git a/public/images/ui/text_images/da/battle_ui/overlay_hp_label_boss_da.png b/public/images/ui/text_images/da/battle_ui/overlay_hp_label_boss_da.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/da/battle_ui/overlay_hp_label_boss_da.png differ
diff --git a/public/images/ui/text_images/da/battle_ui/overlay_hp_label_da.png b/public/images/ui/text_images/da/battle_ui/overlay_hp_label_da.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/da/battle_ui/overlay_hp_label_da.png differ
diff --git a/public/images/ui/text_images/da/battle_ui/overlay_lv_da.png b/public/images/ui/text_images/da/battle_ui/overlay_lv_da.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/da/battle_ui/overlay_lv_da.png differ
diff --git a/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.json b/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.json
new file mode 100644
index 00000000000..5108fa59582
--- /dev/null
+++ b/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_da.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.png b/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/da/battle_ui/pbinfo_stat_da.png differ
diff --git a/public/images/ui/text_images/da/party_ui/party_slot_overlay_hp_da.png b/public/images/ui/text_images/da/party_ui/party_slot_overlay_hp_da.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/da/party_ui/party_slot_overlay_hp_da.png differ
diff --git a/public/images/ui/party_slot_overlay_lv.png b/public/images/ui/text_images/da/party_ui/party_slot_overlay_lv_da.png
similarity index 100%
rename from public/images/ui/party_slot_overlay_lv.png
rename to public/images/ui/text_images/da/party_ui/party_slot_overlay_lv_da.png
diff --git a/public/images/ui/text_images/da/summary/summary_dexnb_label_da.png b/public/images/ui/text_images/da/summary/summary_dexnb_label_da.png
new file mode 100644
index 00000000000..ef081f493f3
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_dexnb_label_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png b/public/images/ui/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png
new file mode 100644
index 00000000000..2c66a714e0c
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_dexnb_label_overlay_shiny_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_moves_descriptions_title_da.png b/public/images/ui/text_images/da/summary/summary_moves_descriptions_title_da.png
new file mode 100644
index 00000000000..79b16227bfe
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_moves_descriptions_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_moves_effect_title_da.png b/public/images/ui/text_images/da/summary/summary_moves_effect_title_da.png
new file mode 100644
index 00000000000..f2ac04e62c4
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_moves_effect_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_moves_moves_title_da.png b/public/images/ui/text_images/da/summary/summary_moves_moves_title_da.png
new file mode 100644
index 00000000000..f1732e3e2c0
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_moves_moves_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_moves_overlay_pp_da.png b/public/images/ui/text_images/da/summary/summary_moves_overlay_pp_da.png
new file mode 100644
index 00000000000..04430665f8f
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_moves_overlay_pp_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_profile_ability_da.png b/public/images/ui/text_images/da/summary/summary_profile_ability_da.png
new file mode 100644
index 00000000000..206b65c6cdd
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_profile_ability_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_profile_memo_title_da.png b/public/images/ui/text_images/da/summary/summary_profile_memo_title_da.png
new file mode 100644
index 00000000000..48886d25e39
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_profile_memo_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_profile_passive_da.png b/public/images/ui/text_images/da/summary/summary_profile_passive_da.png
new file mode 100644
index 00000000000..681ae3f1b5e
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_profile_passive_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_profile_profile_title_da.png b/public/images/ui/text_images/da/summary/summary_profile_profile_title_da.png
new file mode 100644
index 00000000000..83b8a1d5e99
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_profile_profile_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_stats_exp_title_da.png b/public/images/ui/text_images/da/summary/summary_stats_exp_title_da.png
new file mode 100644
index 00000000000..feae0c9cb90
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_stats_exp_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_stats_expbar_title_da.png b/public/images/ui/text_images/da/summary/summary_stats_expbar_title_da.png
new file mode 100644
index 00000000000..566dd844aa6
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_stats_expbar_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_stats_item_title_da.png b/public/images/ui/text_images/da/summary/summary_stats_item_title_da.png
new file mode 100644
index 00000000000..2cbce6c3dcf
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_stats_item_title_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_stats_stats_title_da.png b/public/images/ui/text_images/da/summary/summary_stats_stats_title_da.png
new file mode 100644
index 00000000000..ff97416c7ed
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_stats_stats_title_da.png differ
diff --git a/public/images/ui/summary_tabs_1.png b/public/images/ui/text_images/da/summary/summary_tabs_1_da.png
similarity index 100%
rename from public/images/ui/summary_tabs_1.png
rename to public/images/ui/text_images/da/summary/summary_tabs_1_da.png
diff --git a/public/images/ui/text_images/da/summary/summary_tabs_2_da.png b/public/images/ui/text_images/da/summary/summary_tabs_2_da.png
new file mode 100644
index 00000000000..e8dd00bc7e0
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_tabs_2_da.png differ
diff --git a/public/images/ui/text_images/da/summary/summary_tabs_3_da.png b/public/images/ui/text_images/da/summary/summary_tabs_3_da.png
new file mode 100644
index 00000000000..a274f79e4e2
Binary files /dev/null and b/public/images/ui/text_images/da/summary/summary_tabs_3_da.png differ
diff --git a/public/images/ui/text_images/de/battle_ui/overlay_exp_label_de.png b/public/images/ui/text_images/de/battle_ui/overlay_exp_label_de.png
new file mode 100644
index 00000000000..39a92725bc0
Binary files /dev/null and b/public/images/ui/text_images/de/battle_ui/overlay_exp_label_de.png differ
diff --git a/public/images/ui/text_images/de/battle_ui/overlay_hp_label_boss_de.png b/public/images/ui/text_images/de/battle_ui/overlay_hp_label_boss_de.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/de/battle_ui/overlay_hp_label_boss_de.png differ
diff --git a/public/images/ui/text_images/de/battle_ui/overlay_hp_label_de.png b/public/images/ui/text_images/de/battle_ui/overlay_hp_label_de.png
new file mode 100644
index 00000000000..8b227eebd7a
Binary files /dev/null and b/public/images/ui/text_images/de/battle_ui/overlay_hp_label_de.png differ
diff --git a/public/images/ui/text_images/de/battle_ui/overlay_lv_de.png b/public/images/ui/text_images/de/battle_ui/overlay_lv_de.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/de/battle_ui/overlay_lv_de.png differ
diff --git a/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.json b/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.json
new file mode 100644
index 00000000000..dbf39c94059
--- /dev/null
+++ b/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_de.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.png b/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.png
new file mode 100644
index 00000000000..b8bcb6138b9
Binary files /dev/null and b/public/images/ui/text_images/de/battle_ui/pbinfo_stat_de.png differ
diff --git a/public/images/ui/text_images/de/party_ui/party_slot_overlay_hp_de.png b/public/images/ui/text_images/de/party_ui/party_slot_overlay_hp_de.png
new file mode 100644
index 00000000000..47ac22e52cb
Binary files /dev/null and b/public/images/ui/text_images/de/party_ui/party_slot_overlay_hp_de.png differ
diff --git a/public/images/ui/text_images/de/party_ui/party_slot_overlay_lv_de.png b/public/images/ui/text_images/de/party_ui/party_slot_overlay_lv_de.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/de/party_ui/party_slot_overlay_lv_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_dexnb_label_de.png b/public/images/ui/text_images/de/summary/summary_dexnb_label_de.png
new file mode 100644
index 00000000000..ef081f493f3
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_dexnb_label_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png b/public/images/ui/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png
new file mode 100644
index 00000000000..2c66a714e0c
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_dexnb_label_overlay_shiny_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_moves_descriptions_title_de.png b/public/images/ui/text_images/de/summary/summary_moves_descriptions_title_de.png
new file mode 100644
index 00000000000..ea4827c081d
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_moves_descriptions_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_moves_effect_title_de.png b/public/images/ui/text_images/de/summary/summary_moves_effect_title_de.png
new file mode 100644
index 00000000000..951e9281839
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_moves_effect_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_moves_moves_title_de.png b/public/images/ui/text_images/de/summary/summary_moves_moves_title_de.png
new file mode 100644
index 00000000000..d5e28d3dfe7
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_moves_moves_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_moves_overlay_pp_de.png b/public/images/ui/text_images/de/summary/summary_moves_overlay_pp_de.png
new file mode 100644
index 00000000000..5db3861fbc9
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_moves_overlay_pp_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_profile_ability_de.png b/public/images/ui/text_images/de/summary/summary_profile_ability_de.png
new file mode 100644
index 00000000000..2b6bd055555
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_profile_ability_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_profile_memo_title_de.png b/public/images/ui/text_images/de/summary/summary_profile_memo_title_de.png
new file mode 100644
index 00000000000..089668218fa
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_profile_memo_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_profile_passive_de.png b/public/images/ui/text_images/de/summary/summary_profile_passive_de.png
new file mode 100644
index 00000000000..681ae3f1b5e
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_profile_passive_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_profile_profile_title_de.png b/public/images/ui/text_images/de/summary/summary_profile_profile_title_de.png
new file mode 100644
index 00000000000..83b8a1d5e99
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_profile_profile_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_stats_exp_title_de.png b/public/images/ui/text_images/de/summary/summary_stats_exp_title_de.png
new file mode 100644
index 00000000000..fa702b4c223
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_stats_exp_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_stats_expbar_title_de.png b/public/images/ui/text_images/de/summary/summary_stats_expbar_title_de.png
new file mode 100644
index 00000000000..566dd844aa6
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_stats_expbar_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_stats_item_title_de.png b/public/images/ui/text_images/de/summary/summary_stats_item_title_de.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_stats_item_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_stats_stats_title_de.png b/public/images/ui/text_images/de/summary/summary_stats_stats_title_de.png
new file mode 100644
index 00000000000..87438a5bd4f
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_stats_stats_title_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_tabs_1_de.png b/public/images/ui/text_images/de/summary/summary_tabs_1_de.png
new file mode 100644
index 00000000000..9c3e6c795b4
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_tabs_1_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_tabs_2_de.png b/public/images/ui/text_images/de/summary/summary_tabs_2_de.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_tabs_2_de.png differ
diff --git a/public/images/ui/text_images/de/summary/summary_tabs_3_de.png b/public/images/ui/text_images/de/summary/summary_tabs_3_de.png
new file mode 100644
index 00000000000..297df98c111
Binary files /dev/null and b/public/images/ui/text_images/de/summary/summary_tabs_3_de.png differ
diff --git a/public/images/ui/text_images/en/battle_ui/overlay_exp_label.png b/public/images/ui/text_images/en/battle_ui/overlay_exp_label.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/en/battle_ui/overlay_exp_label.png differ
diff --git a/public/images/ui/text_images/en/battle_ui/overlay_hp_label.png b/public/images/ui/text_images/en/battle_ui/overlay_hp_label.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/en/battle_ui/overlay_hp_label.png differ
diff --git a/public/images/ui/text_images/en/battle_ui/overlay_hp_label_boss.png b/public/images/ui/text_images/en/battle_ui/overlay_hp_label_boss.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/en/battle_ui/overlay_hp_label_boss.png differ
diff --git a/public/images/ui/text_images/en/battle_ui/overlay_lv.png b/public/images/ui/text_images/en/battle_ui/overlay_lv.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/en/battle_ui/overlay_lv.png differ
diff --git a/public/images/ui/pbinfo_stat.json b/public/images/ui/text_images/en/battle_ui/pbinfo_stat.json
similarity index 100%
rename from public/images/ui/pbinfo_stat.json
rename to public/images/ui/text_images/en/battle_ui/pbinfo_stat.json
diff --git a/public/images/ui/text_images/en/battle_ui/pbinfo_stat.png b/public/images/ui/text_images/en/battle_ui/pbinfo_stat.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/en/battle_ui/pbinfo_stat.png differ
diff --git a/public/images/ui/text_images/en/party_ui/party_slot_overlay_hp.png b/public/images/ui/text_images/en/party_ui/party_slot_overlay_hp.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/en/party_ui/party_slot_overlay_hp.png differ
diff --git a/public/images/ui/text_images/en/party_ui/party_slot_overlay_lv.png b/public/images/ui/text_images/en/party_ui/party_slot_overlay_lv.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/en/party_ui/party_slot_overlay_lv.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_dexnb_label.png b/public/images/ui/text_images/en/summary/summary_dexnb_label.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_dexnb_label.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_dexnb_label_overlay_shiny.png b/public/images/ui/text_images/en/summary/summary_dexnb_label_overlay_shiny.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_dexnb_label_overlay_shiny.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_moves_descriptions_title.png b/public/images/ui/text_images/en/summary/summary_moves_descriptions_title.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_moves_descriptions_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_moves_effect_title.png b/public/images/ui/text_images/en/summary/summary_moves_effect_title.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_moves_effect_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_moves_moves_title.png b/public/images/ui/text_images/en/summary/summary_moves_moves_title.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_moves_moves_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_moves_overlay_pp.png b/public/images/ui/text_images/en/summary/summary_moves_overlay_pp.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_moves_overlay_pp.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_profile_ability.png b/public/images/ui/text_images/en/summary/summary_profile_ability.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_profile_ability.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_profile_memo_title.png b/public/images/ui/text_images/en/summary/summary_profile_memo_title.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_profile_memo_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_profile_passive.png b/public/images/ui/text_images/en/summary/summary_profile_passive.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_profile_passive.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_profile_profile_title.png b/public/images/ui/text_images/en/summary/summary_profile_profile_title.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_profile_profile_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_stats_exp_title.png b/public/images/ui/text_images/en/summary/summary_stats_exp_title.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_stats_exp_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_stats_expbar_title.png b/public/images/ui/text_images/en/summary/summary_stats_expbar_title.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_stats_expbar_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_stats_item_title.png b/public/images/ui/text_images/en/summary/summary_stats_item_title.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_stats_item_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_stats_stats_title.png b/public/images/ui/text_images/en/summary/summary_stats_stats_title.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_stats_stats_title.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_tabs_1.png b/public/images/ui/text_images/en/summary/summary_tabs_1.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_tabs_1.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_tabs_2.png b/public/images/ui/text_images/en/summary/summary_tabs_2.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_tabs_2.png differ
diff --git a/public/images/ui/text_images/en/summary/summary_tabs_3.png b/public/images/ui/text_images/en/summary/summary_tabs_3.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/en/summary/summary_tabs_3.png differ
diff --git a/public/images/ui/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png b/public/images/ui/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/es-ES/battle_ui/overlay_exp_label_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png b/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png
new file mode 100644
index 00000000000..440ba6136dc
Binary files /dev/null and b/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_boss_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png b/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png
new file mode 100644
index 00000000000..7c7ccba752f
Binary files /dev/null and b/public/images/ui/text_images/es-ES/battle_ui/overlay_hp_label_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/battle_ui/overlay_lv_es-ES.png b/public/images/ui/text_images/es-ES/battle_ui/overlay_lv_es-ES.png
new file mode 100644
index 00000000000..869a1ee0051
Binary files /dev/null and b/public/images/ui/text_images/es-ES/battle_ui/overlay_lv_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.json b/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.json
new file mode 100644
index 00000000000..1af1d7108bc
--- /dev/null
+++ b/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_es-ES.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 9
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 17,
+ "h": 9
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 9,
+ "h": 9
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 9
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.png b/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.png
new file mode 100644
index 00000000000..63f8215113f
Binary files /dev/null and b/public/images/ui/text_images/es-ES/battle_ui/pbinfo_stat_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png b/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png
new file mode 100644
index 00000000000..4bc95e4f95e
Binary files /dev/null and b/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_hp_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png b/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png
new file mode 100644
index 00000000000..c5971de4fd0
Binary files /dev/null and b/public/images/ui/text_images/es-ES/party_ui/party_slot_overlay_lv_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_es-ES.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_dexnb_label_overlay_shiny_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png
new file mode 100644
index 00000000000..3a4e3c7c375
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_moves_descriptions_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png
new file mode 100644
index 00000000000..cf8d1309848
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_moves_effect_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png
new file mode 100644
index 00000000000..a601ae79e4f
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_moves_moves_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_moves_overlay_pp_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_profile_ability_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_profile_ability_es-ES.png
new file mode 100644
index 00000000000..71bffe95cfe
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_profile_ability_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_profile_memo_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_profile_passive_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_profile_passive_es-ES.png
new file mode 100644
index 00000000000..a50e3cacf58
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_profile_passive_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_profile_profile_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png
new file mode 100644
index 00000000000..7c8a80f0627
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_stats_exp_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_stats_expbar_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_stats_item_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_stats_item_title_es-ES.png
new file mode 100644
index 00000000000..9236aaa1ff8
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_stats_item_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png
new file mode 100644
index 00000000000..5b59c12984f
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_stats_stats_title_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_tabs_1_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_tabs_1_es-ES.png
new file mode 100644
index 00000000000..99f187c396f
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_tabs_1_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_tabs_2_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_tabs_2_es-ES.png
new file mode 100644
index 00000000000..c841de2c12c
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_tabs_2_es-ES.png differ
diff --git a/public/images/ui/text_images/es-ES/summary/summary_tabs_3_es-ES.png b/public/images/ui/text_images/es-ES/summary/summary_tabs_3_es-ES.png
new file mode 100644
index 00000000000..805fec323dd
Binary files /dev/null and b/public/images/ui/text_images/es-ES/summary/summary_tabs_3_es-ES.png differ
diff --git a/public/images/ui/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png b/public/images/ui/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/es-MX/battle_ui/overlay_exp_label_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png b/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png
new file mode 100644
index 00000000000..440ba6136dc
Binary files /dev/null and b/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_boss_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png b/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png
new file mode 100644
index 00000000000..7c7ccba752f
Binary files /dev/null and b/public/images/ui/text_images/es-MX/battle_ui/overlay_hp_label_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/battle_ui/overlay_lv_es-MX.png b/public/images/ui/text_images/es-MX/battle_ui/overlay_lv_es-MX.png
new file mode 100644
index 00000000000..869a1ee0051
Binary files /dev/null and b/public/images/ui/text_images/es-MX/battle_ui/overlay_lv_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.json b/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.json
new file mode 100644
index 00000000000..b372566656b
--- /dev/null
+++ b/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_es-MX.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 9
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 17,
+ "h": 9
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 9,
+ "h": 9
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 9
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.png b/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.png
new file mode 100644
index 00000000000..63f8215113f
Binary files /dev/null and b/public/images/ui/text_images/es-MX/battle_ui/pbinfo_stat_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png b/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png
new file mode 100644
index 00000000000..4bc95e4f95e
Binary files /dev/null and b/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_hp_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png b/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png
new file mode 100644
index 00000000000..c5971de4fd0
Binary files /dev/null and b/public/images/ui/text_images/es-MX/party_ui/party_slot_overlay_lv_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_es-MX.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_dexnb_label_overlay_shiny_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png
new file mode 100644
index 00000000000..3a4e3c7c375
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_moves_descriptions_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png
new file mode 100644
index 00000000000..cf8d1309848
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_moves_effect_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png
new file mode 100644
index 00000000000..a601ae79e4f
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_moves_moves_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_moves_overlay_pp_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_profile_ability_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_profile_ability_es-MX.png
new file mode 100644
index 00000000000..71bffe95cfe
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_profile_ability_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_profile_memo_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_profile_passive_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_profile_passive_es-MX.png
new file mode 100644
index 00000000000..a50e3cacf58
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_profile_passive_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_profile_profile_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png
new file mode 100644
index 00000000000..7c8a80f0627
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_stats_exp_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_stats_expbar_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_stats_item_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_stats_item_title_es-MX.png
new file mode 100644
index 00000000000..9236aaa1ff8
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_stats_item_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png
new file mode 100644
index 00000000000..5b59c12984f
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_stats_stats_title_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_tabs_1_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_tabs_1_es-MX.png
new file mode 100644
index 00000000000..99f187c396f
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_tabs_1_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_tabs_2_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_tabs_2_es-MX.png
new file mode 100644
index 00000000000..c841de2c12c
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_tabs_2_es-MX.png differ
diff --git a/public/images/ui/text_images/es-MX/summary/summary_tabs_3_es-MX.png b/public/images/ui/text_images/es-MX/summary/summary_tabs_3_es-MX.png
new file mode 100644
index 00000000000..805fec323dd
Binary files /dev/null and b/public/images/ui/text_images/es-MX/summary/summary_tabs_3_es-MX.png differ
diff --git a/public/images/ui/text_images/fr/battle_ui/overlay_exp_label_fr.png b/public/images/ui/text_images/fr/battle_ui/overlay_exp_label_fr.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/fr/battle_ui/overlay_exp_label_fr.png differ
diff --git a/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png b/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_boss_fr.png differ
diff --git a/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_fr.png b/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_fr.png
new file mode 100644
index 00000000000..e086339f047
Binary files /dev/null and b/public/images/ui/text_images/fr/battle_ui/overlay_hp_label_fr.png differ
diff --git a/public/images/ui/text_images/fr/battle_ui/overlay_lv_fr.png b/public/images/ui/text_images/fr/battle_ui/overlay_lv_fr.png
new file mode 100644
index 00000000000..9a9ccb6d02d
Binary files /dev/null and b/public/images/ui/text_images/fr/battle_ui/overlay_lv_fr.png differ
diff --git a/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.json b/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.json
new file mode 100644
index 00000000000..1275fe16ec3
--- /dev/null
+++ b/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_fr.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 9
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 17,
+ "h": 9
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 9,
+ "h": 9
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 9
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.png b/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.png
new file mode 100644
index 00000000000..baea5c8d93c
Binary files /dev/null and b/public/images/ui/text_images/fr/battle_ui/pbinfo_stat_fr.png differ
diff --git a/public/images/ui/text_images/fr/party_ui/party_slot_overlay_hp_fr.png b/public/images/ui/text_images/fr/party_ui/party_slot_overlay_hp_fr.png
new file mode 100644
index 00000000000..140c99eef68
Binary files /dev/null and b/public/images/ui/text_images/fr/party_ui/party_slot_overlay_hp_fr.png differ
diff --git a/public/images/ui/text_images/fr/party_ui/party_slot_overlay_lv_fr.png b/public/images/ui/text_images/fr/party_ui/party_slot_overlay_lv_fr.png
new file mode 100644
index 00000000000..ff489e33712
Binary files /dev/null and b/public/images/ui/text_images/fr/party_ui/party_slot_overlay_lv_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_dexnb_label_fr.png b/public/images/ui/text_images/fr/summary/summary_dexnb_label_fr.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_dexnb_label_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png b/public/images/ui/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_dexnb_label_overlay_shiny_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_moves_descriptions_title_fr.png b/public/images/ui/text_images/fr/summary/summary_moves_descriptions_title_fr.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_moves_descriptions_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_moves_effect_title_fr.png b/public/images/ui/text_images/fr/summary/summary_moves_effect_title_fr.png
new file mode 100644
index 00000000000..49f4e11096a
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_moves_effect_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_moves_moves_title_fr.png b/public/images/ui/text_images/fr/summary/summary_moves_moves_title_fr.png
new file mode 100644
index 00000000000..1fd7d72c307
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_moves_moves_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_moves_overlay_pp_fr.png b/public/images/ui/text_images/fr/summary/summary_moves_overlay_pp_fr.png
new file mode 100644
index 00000000000..8285fb09c45
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_moves_overlay_pp_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_profile_ability_fr.png b/public/images/ui/text_images/fr/summary/summary_profile_ability_fr.png
new file mode 100644
index 00000000000..3354b40adfe
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_profile_ability_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_profile_memo_title_fr.png b/public/images/ui/text_images/fr/summary/summary_profile_memo_title_fr.png
new file mode 100644
index 00000000000..54c6beb4428
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_profile_memo_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_profile_passive_fr.png b/public/images/ui/text_images/fr/summary/summary_profile_passive_fr.png
new file mode 100644
index 00000000000..f5d2c98a21c
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_profile_passive_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_profile_profile_title_fr.png b/public/images/ui/text_images/fr/summary/summary_profile_profile_title_fr.png
new file mode 100644
index 00000000000..bccba2d87df
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_profile_profile_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_stats_exp_title_fr.png b/public/images/ui/text_images/fr/summary/summary_stats_exp_title_fr.png
new file mode 100644
index 00000000000..32d41d44be4
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_stats_exp_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_stats_expbar_title_fr.png b/public/images/ui/text_images/fr/summary/summary_stats_expbar_title_fr.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_stats_expbar_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_stats_item_title_fr.png b/public/images/ui/text_images/fr/summary/summary_stats_item_title_fr.png
new file mode 100644
index 00000000000..45c10e8c907
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_stats_item_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_stats_stats_title_fr.png b/public/images/ui/text_images/fr/summary/summary_stats_stats_title_fr.png
new file mode 100644
index 00000000000..63fe6a694af
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_stats_stats_title_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_tabs_1_fr.png b/public/images/ui/text_images/fr/summary/summary_tabs_1_fr.png
new file mode 100644
index 00000000000..dd918e773e9
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_tabs_1_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_tabs_2_fr.png b/public/images/ui/text_images/fr/summary/summary_tabs_2_fr.png
new file mode 100644
index 00000000000..6e53366bec3
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_tabs_2_fr.png differ
diff --git a/public/images/ui/text_images/fr/summary/summary_tabs_3_fr.png b/public/images/ui/text_images/fr/summary/summary_tabs_3_fr.png
new file mode 100644
index 00000000000..d4c353084a3
Binary files /dev/null and b/public/images/ui/text_images/fr/summary/summary_tabs_3_fr.png differ
diff --git a/public/images/ui/text_images/it/battle_ui/overlay_exp_label_it.png b/public/images/ui/text_images/it/battle_ui/overlay_exp_label_it.png
new file mode 100644
index 00000000000..6ad5855cc46
Binary files /dev/null and b/public/images/ui/text_images/it/battle_ui/overlay_exp_label_it.png differ
diff --git a/public/images/ui/text_images/it/battle_ui/overlay_hp_label_boss_it.png b/public/images/ui/text_images/it/battle_ui/overlay_hp_label_boss_it.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/it/battle_ui/overlay_hp_label_boss_it.png differ
diff --git a/public/images/ui/text_images/it/battle_ui/overlay_hp_label_it.png b/public/images/ui/text_images/it/battle_ui/overlay_hp_label_it.png
new file mode 100644
index 00000000000..7c7ccba752f
Binary files /dev/null and b/public/images/ui/text_images/it/battle_ui/overlay_hp_label_it.png differ
diff --git a/public/images/ui/text_images/it/battle_ui/overlay_lv_it.png b/public/images/ui/text_images/it/battle_ui/overlay_lv_it.png
new file mode 100644
index 00000000000..987d806b93a
Binary files /dev/null and b/public/images/ui/text_images/it/battle_ui/overlay_lv_it.png differ
diff --git a/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.json b/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.json
new file mode 100644
index 00000000000..111a88ba365
--- /dev/null
+++ b/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_it.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.png b/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.png
new file mode 100644
index 00000000000..c2ac58da184
Binary files /dev/null and b/public/images/ui/text_images/it/battle_ui/pbinfo_stat_it.png differ
diff --git a/public/images/ui/text_images/it/party_ui/party_slot_overlay_hp_it.png b/public/images/ui/text_images/it/party_ui/party_slot_overlay_hp_it.png
new file mode 100644
index 00000000000..4bc95e4f95e
Binary files /dev/null and b/public/images/ui/text_images/it/party_ui/party_slot_overlay_hp_it.png differ
diff --git a/public/images/ui/text_images/it/party_ui/party_slot_overlay_lv_it.png b/public/images/ui/text_images/it/party_ui/party_slot_overlay_lv_it.png
new file mode 100644
index 00000000000..31a2a31dd41
Binary files /dev/null and b/public/images/ui/text_images/it/party_ui/party_slot_overlay_lv_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_dexnb_label_it.png b/public/images/ui/text_images/it/summary/summary_dexnb_label_it.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_dexnb_label_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png b/public/images/ui/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_dexnb_label_overlay_shiny_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_moves_descriptions_title_it.png b/public/images/ui/text_images/it/summary/summary_moves_descriptions_title_it.png
new file mode 100644
index 00000000000..e0bf21396e5
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_moves_descriptions_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_moves_effect_title_it.png b/public/images/ui/text_images/it/summary/summary_moves_effect_title_it.png
new file mode 100644
index 00000000000..99f9dfeb2c3
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_moves_effect_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_moves_moves_title_it.png b/public/images/ui/text_images/it/summary/summary_moves_moves_title_it.png
new file mode 100644
index 00000000000..e56e3f4b336
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_moves_moves_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_moves_overlay_pp_it.png b/public/images/ui/text_images/it/summary/summary_moves_overlay_pp_it.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_moves_overlay_pp_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_profile_ability_it.png b/public/images/ui/text_images/it/summary/summary_profile_ability_it.png
new file mode 100644
index 00000000000..bb915da37ee
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_profile_ability_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_profile_memo_title_it.png b/public/images/ui/text_images/it/summary/summary_profile_memo_title_it.png
new file mode 100644
index 00000000000..0c81cd56f06
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_profile_memo_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_profile_passive_it.png b/public/images/ui/text_images/it/summary/summary_profile_passive_it.png
new file mode 100644
index 00000000000..a072b663d25
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_profile_passive_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_profile_profile_title_it.png b/public/images/ui/text_images/it/summary/summary_profile_profile_title_it.png
new file mode 100644
index 00000000000..169b0a7b8e7
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_profile_profile_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_stats_exp_title_it.png b/public/images/ui/text_images/it/summary/summary_stats_exp_title_it.png
new file mode 100644
index 00000000000..a3a1aa4c402
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_stats_exp_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_stats_expbar_title_it.png b/public/images/ui/text_images/it/summary/summary_stats_expbar_title_it.png
new file mode 100644
index 00000000000..d6cd930c1c2
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_stats_expbar_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_stats_item_title_it.png b/public/images/ui/text_images/it/summary/summary_stats_item_title_it.png
new file mode 100644
index 00000000000..a93695a48c8
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_stats_item_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_stats_stats_title_it.png b/public/images/ui/text_images/it/summary/summary_stats_stats_title_it.png
new file mode 100644
index 00000000000..998db7fc7e3
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_stats_stats_title_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_tabs_1_it.png b/public/images/ui/text_images/it/summary/summary_tabs_1_it.png
new file mode 100644
index 00000000000..933a381dd50
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_tabs_1_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_tabs_2_it.png b/public/images/ui/text_images/it/summary/summary_tabs_2_it.png
new file mode 100644
index 00000000000..07c73fbdb90
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_tabs_2_it.png differ
diff --git a/public/images/ui/text_images/it/summary/summary_tabs_3_it.png b/public/images/ui/text_images/it/summary/summary_tabs_3_it.png
new file mode 100644
index 00000000000..57e8b176be1
Binary files /dev/null and b/public/images/ui/text_images/it/summary/summary_tabs_3_it.png differ
diff --git a/public/images/ui/text_images/ja/battle_ui/overlay_exp_label_ja.png b/public/images/ui/text_images/ja/battle_ui/overlay_exp_label_ja.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/ja/battle_ui/overlay_exp_label_ja.png differ
diff --git a/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png b/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_boss_ja.png differ
diff --git a/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_ja.png b/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_ja.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/ja/battle_ui/overlay_hp_label_ja.png differ
diff --git a/public/images/ui/text_images/ja/battle_ui/overlay_lv_ja.png b/public/images/ui/text_images/ja/battle_ui/overlay_lv_ja.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/ja/battle_ui/overlay_lv_ja.png differ
diff --git a/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.json b/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.json
new file mode 100644
index 00000000000..d8de5c788ef
--- /dev/null
+++ b/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ja.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.png b/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/ja/battle_ui/pbinfo_stat_ja.png differ
diff --git a/public/images/ui/text_images/ja/party_ui/party_slot_overlay_hp_ja.png b/public/images/ui/text_images/ja/party_ui/party_slot_overlay_hp_ja.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/ja/party_ui/party_slot_overlay_hp_ja.png differ
diff --git a/public/images/ui/text_images/ja/party_ui/party_slot_overlay_lv_ja.png b/public/images/ui/text_images/ja/party_ui/party_slot_overlay_lv_ja.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/ja/party_ui/party_slot_overlay_lv_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_dexnb_label_ja.png b/public/images/ui/text_images/ja/summary/summary_dexnb_label_ja.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_dexnb_label_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png b/public/images/ui/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_dexnb_label_overlay_shiny_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_moves_descriptions_title_ja.png b/public/images/ui/text_images/ja/summary/summary_moves_descriptions_title_ja.png
new file mode 100644
index 00000000000..d844e844244
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_moves_descriptions_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_moves_effect_title_ja.png b/public/images/ui/text_images/ja/summary/summary_moves_effect_title_ja.png
new file mode 100644
index 00000000000..a75452f1299
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_moves_effect_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_moves_moves_title_ja.png b/public/images/ui/text_images/ja/summary/summary_moves_moves_title_ja.png
new file mode 100644
index 00000000000..6492c3ecf7c
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_moves_moves_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_moves_overlay_pp_ja.png b/public/images/ui/text_images/ja/summary/summary_moves_overlay_pp_ja.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_moves_overlay_pp_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_profile_ability_ja.png b/public/images/ui/text_images/ja/summary/summary_profile_ability_ja.png
new file mode 100644
index 00000000000..6c82a863a69
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_profile_ability_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_profile_memo_title_ja.png b/public/images/ui/text_images/ja/summary/summary_profile_memo_title_ja.png
new file mode 100644
index 00000000000..3f5a4536817
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_profile_memo_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_profile_passive_ja.png b/public/images/ui/text_images/ja/summary/summary_profile_passive_ja.png
new file mode 100644
index 00000000000..3bfed88c7da
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_profile_passive_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_profile_profile_title_ja.png b/public/images/ui/text_images/ja/summary/summary_profile_profile_title_ja.png
new file mode 100644
index 00000000000..a05cba49247
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_profile_profile_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_stats_exp_title_ja.png b/public/images/ui/text_images/ja/summary/summary_stats_exp_title_ja.png
new file mode 100644
index 00000000000..0bc237a7e6b
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_stats_exp_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_stats_expbar_title_ja.png b/public/images/ui/text_images/ja/summary/summary_stats_expbar_title_ja.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_stats_expbar_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_stats_item_title_ja.png b/public/images/ui/text_images/ja/summary/summary_stats_item_title_ja.png
new file mode 100644
index 00000000000..114b4d7fd3f
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_stats_item_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_stats_stats_title_ja.png b/public/images/ui/text_images/ja/summary/summary_stats_stats_title_ja.png
new file mode 100644
index 00000000000..c9f2e28fb07
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_stats_stats_title_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_tabs_1_ja.png b/public/images/ui/text_images/ja/summary/summary_tabs_1_ja.png
new file mode 100644
index 00000000000..90f454dae75
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_tabs_1_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_tabs_2_ja.png b/public/images/ui/text_images/ja/summary/summary_tabs_2_ja.png
new file mode 100644
index 00000000000..006c33f23b1
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_tabs_2_ja.png differ
diff --git a/public/images/ui/text_images/ja/summary/summary_tabs_3_ja.png b/public/images/ui/text_images/ja/summary/summary_tabs_3_ja.png
new file mode 100644
index 00000000000..3306d0aba62
Binary files /dev/null and b/public/images/ui/text_images/ja/summary/summary_tabs_3_ja.png differ
diff --git a/public/images/ui/text_images/ko/battle_ui/overlay_exp_label_ko.png b/public/images/ui/text_images/ko/battle_ui/overlay_exp_label_ko.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/ko/battle_ui/overlay_exp_label_ko.png differ
diff --git a/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png b/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_boss_ko.png differ
diff --git a/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_ko.png b/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_ko.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/ko/battle_ui/overlay_hp_label_ko.png differ
diff --git a/public/images/ui/text_images/ko/battle_ui/overlay_lv_ko.png b/public/images/ui/text_images/ko/battle_ui/overlay_lv_ko.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/ko/battle_ui/overlay_lv_ko.png differ
diff --git a/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.json b/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.json
new file mode 100644
index 00000000000..a1d660dfca3
--- /dev/null
+++ b/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ko.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.png b/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/ko/battle_ui/pbinfo_stat_ko.png differ
diff --git a/public/images/ui/text_images/ko/party_ui/party_slot_overlay_hp_ko.png b/public/images/ui/text_images/ko/party_ui/party_slot_overlay_hp_ko.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/ko/party_ui/party_slot_overlay_hp_ko.png differ
diff --git a/public/images/ui/text_images/ko/party_ui/party_slot_overlay_lv_ko.png b/public/images/ui/text_images/ko/party_ui/party_slot_overlay_lv_ko.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/ko/party_ui/party_slot_overlay_lv_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_dexnb_label_ko.png b/public/images/ui/text_images/ko/summary/summary_dexnb_label_ko.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_dexnb_label_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png b/public/images/ui/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_dexnb_label_overlay_shiny_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_moves_descriptions_title_ko.png b/public/images/ui/text_images/ko/summary/summary_moves_descriptions_title_ko.png
new file mode 100644
index 00000000000..350bd607aed
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_moves_descriptions_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_moves_effect_title_ko.png b/public/images/ui/text_images/ko/summary/summary_moves_effect_title_ko.png
new file mode 100644
index 00000000000..f90ac18fc4e
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_moves_effect_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_moves_moves_title_ko.png b/public/images/ui/text_images/ko/summary/summary_moves_moves_title_ko.png
new file mode 100644
index 00000000000..d020d8a77cb
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_moves_moves_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_moves_overlay_pp_ko.png b/public/images/ui/text_images/ko/summary/summary_moves_overlay_pp_ko.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_moves_overlay_pp_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_profile_ability_ko.png b/public/images/ui/text_images/ko/summary/summary_profile_ability_ko.png
new file mode 100644
index 00000000000..cdeafaf486e
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_profile_ability_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_profile_memo_title_ko.png b/public/images/ui/text_images/ko/summary/summary_profile_memo_title_ko.png
new file mode 100644
index 00000000000..7af960fe1df
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_profile_memo_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_profile_passive_ko.png b/public/images/ui/text_images/ko/summary/summary_profile_passive_ko.png
new file mode 100644
index 00000000000..34a13ffe836
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_profile_passive_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_profile_profile_title_ko.png b/public/images/ui/text_images/ko/summary/summary_profile_profile_title_ko.png
new file mode 100644
index 00000000000..deb57b96715
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_profile_profile_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_stats_exp_title_ko.png b/public/images/ui/text_images/ko/summary/summary_stats_exp_title_ko.png
new file mode 100644
index 00000000000..af0cb3afab8
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_stats_exp_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_stats_expbar_title_ko.png b/public/images/ui/text_images/ko/summary/summary_stats_expbar_title_ko.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_stats_expbar_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_stats_item_title_ko.png b/public/images/ui/text_images/ko/summary/summary_stats_item_title_ko.png
new file mode 100644
index 00000000000..6be5909e5f0
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_stats_item_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_stats_stats_title_ko.png b/public/images/ui/text_images/ko/summary/summary_stats_stats_title_ko.png
new file mode 100644
index 00000000000..371c2be4976
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_stats_stats_title_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_tabs_1_ko.png b/public/images/ui/text_images/ko/summary/summary_tabs_1_ko.png
new file mode 100644
index 00000000000..487d4a628ca
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_tabs_1_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_tabs_2_ko.png b/public/images/ui/text_images/ko/summary/summary_tabs_2_ko.png
new file mode 100644
index 00000000000..304127ea2ef
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_tabs_2_ko.png differ
diff --git a/public/images/ui/text_images/ko/summary/summary_tabs_3_ko.png b/public/images/ui/text_images/ko/summary/summary_tabs_3_ko.png
new file mode 100644
index 00000000000..832acf4d5ba
Binary files /dev/null and b/public/images/ui/text_images/ko/summary/summary_tabs_3_ko.png differ
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png b/public/images/ui/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/battle_ui/overlay_exp_label_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png b/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_boss_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png b/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png
new file mode 100644
index 00000000000..7c7ccba752f
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/battle_ui/overlay_hp_label_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png b/public/images/ui/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png
new file mode 100644
index 00000000000..869a1ee0051
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/battle_ui/overlay_lv_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json b/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json
new file mode 100644
index 00000000000..ca97288795e
--- /dev/null
+++ b/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_pt-BR.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 9
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 19,
+ "h": 9
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 9
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 17,
+ "h": 9
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 13,
+ "h": 9
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 9
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": -1,
+ "w": 9,
+ "h": 9
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 9
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png b/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png
new file mode 100644
index 00000000000..c20f4441983
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/battle_ui/pbinfo_stat_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png b/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png
new file mode 100644
index 00000000000..4bc95e4f95e
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_hp_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png b/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png
new file mode 100644
index 00000000000..c5971de4fd0
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/party_ui/party_slot_overlay_lv_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png
new file mode 100644
index 00000000000..54f0c3799a0
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_overlay_shiny_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png
new file mode 100644
index 00000000000..5477e3385a8
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_dexnb_label_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png
new file mode 100644
index 00000000000..8d888143663
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_moves_descriptions_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png
new file mode 100644
index 00000000000..83190955f2c
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_moves_effect_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png
new file mode 100644
index 00000000000..0a2d1657750
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_moves_moves_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_moves_overlay_pp_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png
new file mode 100644
index 00000000000..28fbb5a8be9
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_profile_ability_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png
new file mode 100644
index 00000000000..b7ef7c91fc5
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_profile_memo_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png
new file mode 100644
index 00000000000..11616a74287
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_profile_passive_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png
new file mode 100644
index 00000000000..a5ed0e3e169
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_profile_profile_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png
new file mode 100644
index 00000000000..d66fa829be1
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_stats_exp_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_stats_expbar_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png
new file mode 100644
index 00000000000..b9ea9400a64
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_stats_item_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png
new file mode 100644
index 00000000000..bc9dffc2887
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_stats_stats_title_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png
new file mode 100644
index 00000000000..888e8042051
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_tabs_1_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png
new file mode 100644
index 00000000000..29cad435f2d
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_tabs_2_pt-BR.png differ
diff --git a/public/images/ui/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png b/public/images/ui/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png
new file mode 100644
index 00000000000..f4eebe1eb11
Binary files /dev/null and b/public/images/ui/text_images/pt-BR/summary/summary_tabs_3_pt-BR.png differ
diff --git a/public/images/ui/text_images/ro/battle_ui/overlay_exp_label_ro.png b/public/images/ui/text_images/ro/battle_ui/overlay_exp_label_ro.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/ro/battle_ui/overlay_exp_label_ro.png differ
diff --git a/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png b/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_boss_ro.png differ
diff --git a/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_ro.png b/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_ro.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/ro/battle_ui/overlay_hp_label_ro.png differ
diff --git a/public/images/ui/text_images/ro/battle_ui/overlay_lv_ro.png b/public/images/ui/text_images/ro/battle_ui/overlay_lv_ro.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/ro/battle_ui/overlay_lv_ro.png differ
diff --git a/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.json b/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.json
new file mode 100644
index 00000000000..b5f74fdd7cc
--- /dev/null
+++ b/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ro.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.png b/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/ro/battle_ui/pbinfo_stat_ro.png differ
diff --git a/public/images/ui/text_images/ro/party_ui/party_slot_overlay_hp_ro.png b/public/images/ui/text_images/ro/party_ui/party_slot_overlay_hp_ro.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/ro/party_ui/party_slot_overlay_hp_ro.png differ
diff --git a/public/images/ui/text_images/ro/party_ui/party_slot_overlay_lv_ro.png b/public/images/ui/text_images/ro/party_ui/party_slot_overlay_lv_ro.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/ro/party_ui/party_slot_overlay_lv_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png b/public/images/ui/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_dexnb_label_overlay_shiny_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_dexnb_label_ro.png b/public/images/ui/text_images/ro/summary/summary_dexnb_label_ro.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_dexnb_label_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_moves_descriptions_title_ro.png b/public/images/ui/text_images/ro/summary/summary_moves_descriptions_title_ro.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_moves_descriptions_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_moves_effect_title_ro.png b/public/images/ui/text_images/ro/summary/summary_moves_effect_title_ro.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_moves_effect_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_moves_moves_title_ro.png b/public/images/ui/text_images/ro/summary/summary_moves_moves_title_ro.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_moves_moves_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_moves_overlay_pp_ro.png b/public/images/ui/text_images/ro/summary/summary_moves_overlay_pp_ro.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_moves_overlay_pp_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_profile_ability_ro.png b/public/images/ui/text_images/ro/summary/summary_profile_ability_ro.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_profile_ability_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_profile_memo_title_ro.png b/public/images/ui/text_images/ro/summary/summary_profile_memo_title_ro.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_profile_memo_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_profile_passive_ro.png b/public/images/ui/text_images/ro/summary/summary_profile_passive_ro.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_profile_passive_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_profile_profile_title_ro.png b/public/images/ui/text_images/ro/summary/summary_profile_profile_title_ro.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_profile_profile_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_stats_exp_title_ro.png b/public/images/ui/text_images/ro/summary/summary_stats_exp_title_ro.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_stats_exp_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_stats_expbar_title_ro.png b/public/images/ui/text_images/ro/summary/summary_stats_expbar_title_ro.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_stats_expbar_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_stats_item_title_ro.png b/public/images/ui/text_images/ro/summary/summary_stats_item_title_ro.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_stats_item_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_stats_stats_title_ro.png b/public/images/ui/text_images/ro/summary/summary_stats_stats_title_ro.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_stats_stats_title_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_tabs_1_ro.png b/public/images/ui/text_images/ro/summary/summary_tabs_1_ro.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_tabs_1_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_tabs_2_ro.png b/public/images/ui/text_images/ro/summary/summary_tabs_2_ro.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_tabs_2_ro.png differ
diff --git a/public/images/ui/text_images/ro/summary/summary_tabs_3_ro.png b/public/images/ui/text_images/ro/summary/summary_tabs_3_ro.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/ro/summary/summary_tabs_3_ro.png differ
diff --git a/public/images/ui/text_images/ru/battle_ui/overlay_exp_label_ru.png b/public/images/ui/text_images/ru/battle_ui/overlay_exp_label_ru.png
new file mode 100644
index 00000000000..8342acb74fb
Binary files /dev/null and b/public/images/ui/text_images/ru/battle_ui/overlay_exp_label_ru.png differ
diff --git a/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png b/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png
new file mode 100644
index 00000000000..af85409a084
Binary files /dev/null and b/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_boss_ru.png differ
diff --git a/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_ru.png b/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_ru.png
new file mode 100644
index 00000000000..b040247cd64
Binary files /dev/null and b/public/images/ui/text_images/ru/battle_ui/overlay_hp_label_ru.png differ
diff --git a/public/images/ui/text_images/ru/battle_ui/overlay_lv_ru.png b/public/images/ui/text_images/ru/battle_ui/overlay_lv_ru.png
new file mode 100644
index 00000000000..65ec593d656
Binary files /dev/null and b/public/images/ui/text_images/ru/battle_ui/overlay_lv_ru.png differ
diff --git a/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.json b/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.json
new file mode 100644
index 00000000000..61618578d25
--- /dev/null
+++ b/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_ru.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 8
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 8
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 8
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 8
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 8
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 8
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 8
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 8
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 8
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 8
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 8
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 8
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 8
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 8
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 8
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 8
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 8
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.png b/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.png
new file mode 100644
index 00000000000..9688d78d425
Binary files /dev/null and b/public/images/ui/text_images/ru/battle_ui/pbinfo_stat_ru.png differ
diff --git a/public/images/ui/text_images/ru/party_ui/party_slot_overlay_hp_ru.png b/public/images/ui/text_images/ru/party_ui/party_slot_overlay_hp_ru.png
new file mode 100644
index 00000000000..5d9e62ac79a
Binary files /dev/null and b/public/images/ui/text_images/ru/party_ui/party_slot_overlay_hp_ru.png differ
diff --git a/public/images/ui/text_images/ru/party_ui/party_slot_overlay_lv_ru.png b/public/images/ui/text_images/ru/party_ui/party_slot_overlay_lv_ru.png
new file mode 100644
index 00000000000..65ec593d656
Binary files /dev/null and b/public/images/ui/text_images/ru/party_ui/party_slot_overlay_lv_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png b/public/images/ui/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png
new file mode 100644
index 00000000000..1ba0191881d
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_dexnb_label_overlay_shiny_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_dexnb_label_ru.png b/public/images/ui/text_images/ru/summary/summary_dexnb_label_ru.png
new file mode 100644
index 00000000000..2a2e66426cb
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_dexnb_label_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_moves_descriptions_title_ru.png b/public/images/ui/text_images/ru/summary/summary_moves_descriptions_title_ru.png
new file mode 100644
index 00000000000..a435d6d845e
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_moves_descriptions_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_moves_effect_title_ru.png b/public/images/ui/text_images/ru/summary/summary_moves_effect_title_ru.png
new file mode 100644
index 00000000000..8bd4b965247
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_moves_effect_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_moves_moves_title_ru.png b/public/images/ui/text_images/ru/summary/summary_moves_moves_title_ru.png
new file mode 100644
index 00000000000..94afd3226e8
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_moves_moves_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_moves_overlay_pp_ru.png b/public/images/ui/text_images/ru/summary/summary_moves_overlay_pp_ru.png
new file mode 100644
index 00000000000..dbdc2275129
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_moves_overlay_pp_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_profile_ability_ru.png b/public/images/ui/text_images/ru/summary/summary_profile_ability_ru.png
new file mode 100644
index 00000000000..ddfcc6102dc
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_profile_ability_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_profile_memo_title_ru.png b/public/images/ui/text_images/ru/summary/summary_profile_memo_title_ru.png
new file mode 100644
index 00000000000..743a2f36454
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_profile_memo_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_profile_passive_ru.png b/public/images/ui/text_images/ru/summary/summary_profile_passive_ru.png
new file mode 100644
index 00000000000..29179b6d75b
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_profile_passive_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_profile_profile_title_ru.png b/public/images/ui/text_images/ru/summary/summary_profile_profile_title_ru.png
new file mode 100644
index 00000000000..77edb538634
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_profile_profile_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_stats_exp_title_ru.png b/public/images/ui/text_images/ru/summary/summary_stats_exp_title_ru.png
new file mode 100644
index 00000000000..e058eaa917e
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_stats_exp_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_stats_expbar_title_ru.png b/public/images/ui/text_images/ru/summary/summary_stats_expbar_title_ru.png
new file mode 100644
index 00000000000..424a5378f7e
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_stats_expbar_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_stats_item_title_ru.png b/public/images/ui/text_images/ru/summary/summary_stats_item_title_ru.png
new file mode 100644
index 00000000000..95b01d062ab
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_stats_item_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_stats_stats_title_ru.png b/public/images/ui/text_images/ru/summary/summary_stats_stats_title_ru.png
new file mode 100644
index 00000000000..0c1c5dc9445
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_stats_stats_title_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_tabs_1_ru.png b/public/images/ui/text_images/ru/summary/summary_tabs_1_ru.png
new file mode 100644
index 00000000000..1fef6034ad7
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_tabs_1_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_tabs_2_ru.png b/public/images/ui/text_images/ru/summary/summary_tabs_2_ru.png
new file mode 100644
index 00000000000..0382fc880a9
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_tabs_2_ru.png differ
diff --git a/public/images/ui/text_images/ru/summary/summary_tabs_3_ru.png b/public/images/ui/text_images/ru/summary/summary_tabs_3_ru.png
new file mode 100644
index 00000000000..1395ac840af
Binary files /dev/null and b/public/images/ui/text_images/ru/summary/summary_tabs_3_ru.png differ
diff --git a/public/images/ui/text_images/tl/battle_ui/overlay_exp_label_tl.png b/public/images/ui/text_images/tl/battle_ui/overlay_exp_label_tl.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/tl/battle_ui/overlay_exp_label_tl.png differ
diff --git a/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png b/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_boss_tl.png differ
diff --git a/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_tl.png b/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_tl.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/tl/battle_ui/overlay_hp_label_tl.png differ
diff --git a/public/images/ui/text_images/tl/battle_ui/overlay_lv_tl.png b/public/images/ui/text_images/tl/battle_ui/overlay_lv_tl.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/tl/battle_ui/overlay_lv_tl.png differ
diff --git a/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.json b/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.json
new file mode 100644
index 00000000000..d2277c79f24
--- /dev/null
+++ b/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_tl.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.png b/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/tl/battle_ui/pbinfo_stat_tl.png differ
diff --git a/public/images/ui/text_images/tl/party_ui/party_slot_overlay_hp_tl.png b/public/images/ui/text_images/tl/party_ui/party_slot_overlay_hp_tl.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/tl/party_ui/party_slot_overlay_hp_tl.png differ
diff --git a/public/images/ui/text_images/tl/party_ui/party_slot_overlay_lv_tl.png b/public/images/ui/text_images/tl/party_ui/party_slot_overlay_lv_tl.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/tl/party_ui/party_slot_overlay_lv_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png b/public/images/ui/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_dexnb_label_overlay_shiny_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_dexnb_label_tl.png b/public/images/ui/text_images/tl/summary/summary_dexnb_label_tl.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_dexnb_label_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_moves_descriptions_title_tl.png b/public/images/ui/text_images/tl/summary/summary_moves_descriptions_title_tl.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_moves_descriptions_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_moves_effect_title_tl.png b/public/images/ui/text_images/tl/summary/summary_moves_effect_title_tl.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_moves_effect_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_moves_moves_title_tl.png b/public/images/ui/text_images/tl/summary/summary_moves_moves_title_tl.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_moves_moves_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_moves_overlay_pp_tl.png b/public/images/ui/text_images/tl/summary/summary_moves_overlay_pp_tl.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_moves_overlay_pp_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_profile_ability_tl.png b/public/images/ui/text_images/tl/summary/summary_profile_ability_tl.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_profile_ability_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_profile_memo_title_tl.png b/public/images/ui/text_images/tl/summary/summary_profile_memo_title_tl.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_profile_memo_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_profile_passive_tl.png b/public/images/ui/text_images/tl/summary/summary_profile_passive_tl.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_profile_passive_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_profile_profile_title_tl.png b/public/images/ui/text_images/tl/summary/summary_profile_profile_title_tl.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_profile_profile_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_stats_exp_title_tl.png b/public/images/ui/text_images/tl/summary/summary_stats_exp_title_tl.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_stats_exp_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_stats_expbar_title_tl.png b/public/images/ui/text_images/tl/summary/summary_stats_expbar_title_tl.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_stats_expbar_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_stats_item_title_tl.png b/public/images/ui/text_images/tl/summary/summary_stats_item_title_tl.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_stats_item_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_stats_stats_title_tl.png b/public/images/ui/text_images/tl/summary/summary_stats_stats_title_tl.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_stats_stats_title_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_tabs_1_tl.png b/public/images/ui/text_images/tl/summary/summary_tabs_1_tl.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_tabs_1_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_tabs_2_tl.png b/public/images/ui/text_images/tl/summary/summary_tabs_2_tl.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_tabs_2_tl.png differ
diff --git a/public/images/ui/text_images/tl/summary/summary_tabs_3_tl.png b/public/images/ui/text_images/tl/summary/summary_tabs_3_tl.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/tl/summary/summary_tabs_3_tl.png differ
diff --git a/public/images/ui/text_images/tr/battle_ui/overlay_exp_label_tr.png b/public/images/ui/text_images/tr/battle_ui/overlay_exp_label_tr.png
new file mode 100644
index 00000000000..0920a95fba2
Binary files /dev/null and b/public/images/ui/text_images/tr/battle_ui/overlay_exp_label_tr.png differ
diff --git a/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png b/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_boss_tr.png differ
diff --git a/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_tr.png b/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_tr.png
new file mode 100644
index 00000000000..e28aceaf0fa
Binary files /dev/null and b/public/images/ui/text_images/tr/battle_ui/overlay_hp_label_tr.png differ
diff --git a/public/images/ui/text_images/tr/battle_ui/overlay_lv_tr.png b/public/images/ui/text_images/tr/battle_ui/overlay_lv_tr.png
new file mode 100644
index 00000000000..5140444ec15
Binary files /dev/null and b/public/images/ui/text_images/tr/battle_ui/overlay_lv_tr.png differ
diff --git a/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.json b/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.json
new file mode 100644
index 00000000000..c3ccb4a5391
--- /dev/null
+++ b/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_tr.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.png b/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/tr/battle_ui/pbinfo_stat_tr.png differ
diff --git a/public/images/ui/text_images/tr/party_ui/party_slot_overlay_hp_tr.png b/public/images/ui/text_images/tr/party_ui/party_slot_overlay_hp_tr.png
new file mode 100644
index 00000000000..bb0017bcb58
Binary files /dev/null and b/public/images/ui/text_images/tr/party_ui/party_slot_overlay_hp_tr.png differ
diff --git a/public/images/ui/text_images/tr/party_ui/party_slot_overlay_lv_tr.png b/public/images/ui/text_images/tr/party_ui/party_slot_overlay_lv_tr.png
new file mode 100644
index 00000000000..12782430b9e
Binary files /dev/null and b/public/images/ui/text_images/tr/party_ui/party_slot_overlay_lv_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png b/public/images/ui/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_dexnb_label_overlay_shiny_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_dexnb_label_tr.png b/public/images/ui/text_images/tr/summary/summary_dexnb_label_tr.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_dexnb_label_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_moves_descriptions_title_tr.png b/public/images/ui/text_images/tr/summary/summary_moves_descriptions_title_tr.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_moves_descriptions_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_moves_effect_title_tr.png b/public/images/ui/text_images/tr/summary/summary_moves_effect_title_tr.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_moves_effect_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_moves_moves_title_tr.png b/public/images/ui/text_images/tr/summary/summary_moves_moves_title_tr.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_moves_moves_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_moves_overlay_pp_tr.png b/public/images/ui/text_images/tr/summary/summary_moves_overlay_pp_tr.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_moves_overlay_pp_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_profile_ability_tr.png b/public/images/ui/text_images/tr/summary/summary_profile_ability_tr.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_profile_ability_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_profile_memo_title_tr.png b/public/images/ui/text_images/tr/summary/summary_profile_memo_title_tr.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_profile_memo_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_profile_passive_tr.png b/public/images/ui/text_images/tr/summary/summary_profile_passive_tr.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_profile_passive_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_profile_profile_title_tr.png b/public/images/ui/text_images/tr/summary/summary_profile_profile_title_tr.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_profile_profile_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_stats_exp_title_tr.png b/public/images/ui/text_images/tr/summary/summary_stats_exp_title_tr.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_stats_exp_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_stats_expbar_title_tr.png b/public/images/ui/text_images/tr/summary/summary_stats_expbar_title_tr.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_stats_expbar_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_stats_item_title_tr.png b/public/images/ui/text_images/tr/summary/summary_stats_item_title_tr.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_stats_item_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_stats_stats_title_tr.png b/public/images/ui/text_images/tr/summary/summary_stats_stats_title_tr.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_stats_stats_title_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_tabs_1_tr.png b/public/images/ui/text_images/tr/summary/summary_tabs_1_tr.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_tabs_1_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_tabs_2_tr.png b/public/images/ui/text_images/tr/summary/summary_tabs_2_tr.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_tabs_2_tr.png differ
diff --git a/public/images/ui/text_images/tr/summary/summary_tabs_3_tr.png b/public/images/ui/text_images/tr/summary/summary_tabs_3_tr.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/tr/summary/summary_tabs_3_tr.png differ
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png b/public/images/ui/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/battle_ui/overlay_exp_label_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png b/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_boss_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png b/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/battle_ui/overlay_hp_label_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png b/public/images/ui/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/battle_ui/overlay_lv_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json b/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json
new file mode 100644
index 00000000000..22a1da0b536
--- /dev/null
+++ b/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_zh-CN.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png b/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/battle_ui/pbinfo_stat_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png b/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_hp_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png b/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/party_ui/party_slot_overlay_lv_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_overlay_shiny_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_dexnb_label_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_moves_descriptions_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_moves_effect_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_moves_moves_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_moves_overlay_pp_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_profile_ability_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_profile_memo_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_profile_passive_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_profile_profile_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_stats_exp_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_stats_expbar_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_stats_item_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_stats_stats_title_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_tabs_1_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_tabs_2_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png b/public/images/ui/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/zh-CN/summary/summary_tabs_3_zh-CN.png differ
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png b/public/images/ui/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png
new file mode 100644
index 00000000000..fde37213eff
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/battle_ui/overlay_exp_label_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png b/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png
new file mode 100644
index 00000000000..0bcec679e05
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_boss_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png b/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png
new file mode 100644
index 00000000000..38a34761381
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/battle_ui/overlay_hp_label_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png b/public/images/ui/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png
new file mode 100644
index 00000000000..9ad4312d561
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/battle_ui/overlay_lv_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json b/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json
new file mode 100644
index 00000000000..26c01d8bcc2
--- /dev/null
+++ b/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.json
@@ -0,0 +1,209 @@
+{
+ "textures": [
+ {
+ "image": "pbinfo_stat_zh-TW.png",
+ "format": "RGBA8888",
+ "size": {
+ "w": 120,
+ "h": 7
+ },
+ "scale": 1,
+ "frames": [
+ {
+ "filename": "SPATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 0,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPDEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 19,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 19,
+ "h": 7
+ },
+ "frame": {
+ "x": 19,
+ "y": 0,
+ "w": 19,
+ "h": 7
+ }
+ },
+ {
+ "filename": "CRIT",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 17,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 17,
+ "h": 7
+ },
+ "frame": {
+ "x": 38,
+ "y": 0,
+ "w": 17,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ACC",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 55,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "ATK",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 68,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "DEF",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 81,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "EVA",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 94,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "SPD",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 13,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 13,
+ "h": 7
+ },
+ "frame": {
+ "x": 107,
+ "y": 0,
+ "w": 13,
+ "h": 7
+ }
+ },
+ {
+ "filename": "HP",
+ "rotated": false,
+ "trimmed": true,
+ "sourceSize": {
+ "w": 9,
+ "h": 8
+ },
+ "spriteSourceSize": {
+ "x": 0,
+ "y": 1,
+ "w": 9,
+ "h": 7
+ },
+ "frame": {
+ "x": 120,
+ "y": 0,
+ "w": 9,
+ "h": 7
+ }
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "app": "https://www.codeandweb.com/texturepacker",
+ "version": "3.0",
+ "smartupdate": "$TexturePacker:SmartUpdate:86fbd1b45d46271597a7d9de482aaa74:df702dd9d88db50369f1a096f82fd915:05882267d3999884e0491134e98b1b53$"
+ }
+}
diff --git a/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png b/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png
new file mode 100644
index 00000000000..9656c5f04b9
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/battle_ui/pbinfo_stat_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png b/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png
new file mode 100644
index 00000000000..9be90b5c6bb
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_hp_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png b/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png
new file mode 100644
index 00000000000..122d3f7151c
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/party_ui/party_slot_overlay_lv_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png
new file mode 100644
index 00000000000..f35e327c29a
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_overlay_shiny_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png
new file mode 100644
index 00000000000..bf568c486aa
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_dexnb_label_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png
new file mode 100644
index 00000000000..e83e8cafbfc
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_moves_descriptions_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png
new file mode 100644
index 00000000000..fbbaac0b260
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_moves_effect_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png
new file mode 100644
index 00000000000..6bbb29c9c5f
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_moves_moves_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png
new file mode 100644
index 00000000000..a2bcd0bd021
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_moves_overlay_pp_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png
new file mode 100644
index 00000000000..a05c22b7d47
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_profile_ability_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png
new file mode 100644
index 00000000000..3d69c20e57f
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_profile_memo_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png
new file mode 100644
index 00000000000..c026e87a215
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_profile_passive_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png
new file mode 100644
index 00000000000..4170dccf682
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_profile_profile_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png
new file mode 100644
index 00000000000..d0d8d7f81a1
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_stats_exp_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png
new file mode 100644
index 00000000000..4889b64eca8
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_stats_expbar_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png
new file mode 100644
index 00000000000..42e08b3e52a
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_stats_item_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png
new file mode 100644
index 00000000000..f602a43c39d
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_stats_stats_title_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png
new file mode 100644
index 00000000000..750c407b075
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_tabs_1_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png
new file mode 100644
index 00000000000..50c1db1f34c
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_tabs_2_zh-TW.png differ
diff --git a/public/images/ui/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png b/public/images/ui/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png
new file mode 100644
index 00000000000..24676712fb9
Binary files /dev/null and b/public/images/ui/text_images/zh-TW/summary/summary_tabs_3_zh-TW.png differ
diff --git a/public/locales b/public/locales
index 58fa5f9b6e9..090bfefaf7e 160000
--- a/public/locales
+++ b/public/locales
@@ -1 +1 @@
-Subproject commit 58fa5f9b6e94469017bfbe69bef992ed48ef5343
+Subproject commit 090bfefaf7e9d4efcbca61fa78a9cdf5d701830b
diff --git a/public/service-worker.js b/public/service-worker.js
index b45d2484709..ff380adca73 100644
--- a/public/service-worker.js
+++ b/public/service-worker.js
@@ -1,3 +1,7 @@
self.addEventListener('install', function () {
console.log('Service worker installing...');
});
+
+self.addEventListener('activate', (event) => {
+ event.waitUntil(self.clients.claim());
+})
\ No newline at end of file
diff --git a/scripts/create-test/boilerplates/default.ts b/scripts/create-test/boilerplates/default.ts
index fa914b150c2..e644e740594 100644
--- a/scripts/create-test/boilerplates/default.ts
+++ b/scripts/create-test/boilerplates/default.ts
@@ -1,7 +1,10 @@
import { AbilityId } from "#enums/ability-id";
+import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id";
+import { MoveResult } from "#enums/move-result";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/test-utils/game-manager";
+import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -32,12 +35,18 @@ describe("{{description}}", () => {
.enemyLevel(100);
});
+ // Find more awesome utility functions inside `#test/test-utils`!
it("should do XYZ", async () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+ const feebas = game.field.getPlayerPokemon();
+
game.move.use(MoveId.SPLASH);
+ await game.move.forceEnemyMove(MoveId.CELEBRATE);
+ await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toEndOfTurn();
- expect(true).toBe(true);
+ expect(feebas).toHaveUsedMove({ move: MoveId.SPLASH, result: MoveResult.SUCCESS });
+ expect(game.textInterceptor.logs).toContain(i18next.t("moveTriggers:splash"));
});
});
diff --git a/scripts/create-test/create-test.js b/scripts/create-test/create-test.js
index 765993959d1..5e395783da7 100644
--- a/scripts/create-test/create-test.js
+++ b/scripts/create-test/create-test.js
@@ -156,7 +156,7 @@ async function runInteractive() {
console.log(chalk.green.bold(`✔ File created at: test/${localDir}/${fileName}.test.ts\n`));
console.groupEnd();
} catch (err) {
- console.error(chalk.red("✗ Error: ", err.message));
+ console.error(chalk.red("✗ Error: ", err));
}
}
diff --git a/scripts/decrypt-save.js b/scripts/decrypt-save.js
index e50f152f159..26b0a311378 100644
--- a/scripts/decrypt-save.js
+++ b/scripts/decrypt-save.js
@@ -1,7 +1,6 @@
// Usage: node decrypt-save.js [save-file]
-// biome-ignore lint/performance/noNamespaceImport: This is how you import fs from node
-import * as fs from "node:fs";
+import fs from "node:fs";
import crypto_js from "crypto-js";
const { AES, enc } = crypto_js;
@@ -60,6 +59,11 @@ function decryptSave(path) {
try {
fileData = fs.readFileSync(path, "utf8");
} catch (e) {
+ if (!(e instanceof Error)) {
+ console.error(`Unrecognized error: ${e}`);
+ process.exit(1);
+ }
+ // @ts-expect-error - e is usually a SystemError (all of which have codes)
switch (e.code) {
case "ENOENT":
console.error(`File not found: ${path}`);
@@ -104,6 +108,13 @@ function writeToFile(filePath, data) {
try {
fs.writeFileSync(filePath, data);
} catch (e) {
+ if (!(e instanceof Error)) {
+ console.error("Unknown error detected: ", e);
+ process.exitCode = 1;
+ return;
+ }
+
+ // @ts-expect-error - e is usually a SystemError (all of which have codes)
switch (e.code) {
case "EACCES":
console.error(`Could not open ${filePath}: Permission denied`);
@@ -114,7 +125,8 @@ function writeToFile(filePath, data) {
default:
console.error(`Error writing file: ${e.message}`);
}
- process.exit(1);
+ process.exitCode = 1;
+ return;
}
}
diff --git a/scripts/jsconfig.json b/scripts/jsconfig.json
new file mode 100644
index 00000000000..aed71f4f576
--- /dev/null
+++ b/scripts/jsconfig.json
@@ -0,0 +1,17 @@
+{
+ "include": ["**/*.js"],
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "rootDir": ".",
+ "target": "esnext",
+ "module": "nodenext",
+ "moduleResolution": "nodenext",
+ "erasableSyntaxOnly": true,
+ "strict": true,
+ "noEmit": true,
+ // Forcibly disable `node_modules` recursion to prevent TSC from typechecking random JS files.
+ // This is disabled by default in `tsconfig.json`, but needs to be explicitly disabled from the default of `2`
+ "maxNodeModuleJsDepth": 0
+ }
+}
diff --git a/scripts/parse-egg-moves/egg-move-template.boilerplate.ts b/scripts/parse-egg-moves/egg-move-template.boilerplate.ts
new file mode 100644
index 00000000000..bfac05f4bde
--- /dev/null
+++ b/scripts/parse-egg-moves/egg-move-template.boilerplate.ts
@@ -0,0 +1,10 @@
+//! DO NOT EDIT THIS FILE - CREATED BY THE `eggMoves:parse` script automatically
+import { MoveId } from "#enums/move-id";
+import { SpeciesId } from "#enums/species-id";
+
+/**
+ * An object mapping all base form {@linkcode SpeciesId}s to an array of {@linkcode MoveId}s corresponding
+ * to their current egg moves.
+ * Generated by the `eggMoves:parse` script using a CSV sourced from the current Balance Team spreadsheet.
+ */
+export const speciesEggMoves = "{{table}}";
diff --git a/scripts/parse-egg-moves/help-message.js b/scripts/parse-egg-moves/help-message.js
new file mode 100644
index 00000000000..b498acdf23c
--- /dev/null
+++ b/scripts/parse-egg-moves/help-message.js
@@ -0,0 +1,17 @@
+import chalk from "chalk";
+
+/** Show help/usage text for the `eggMoves:parse` CLI. */
+export function showHelpText() {
+ console.log(`
+Usage: ${chalk.cyan("pnpm eggMoves:parse [options]")}
+If given no options, assumes ${chalk.blue("`--interactive`")}.
+If given only a file path, assumes ${chalk.blue("`--file`")}.
+
+${chalk.hex("#ffa500")("Options:")}
+ ${chalk.blue("-h, --help")} Show this help message.
+ ${chalk.blue("-f, --file[=PATH]")} Specify a path to a CSV file to read, or provide one from stdin.
+ ${chalk.blue("-t, --text[=TEXT]")}
+ ${chalk.blue("-c, --console[=TEXT]")} Specify CSV text to read, or provide it from stdin.
+ ${chalk.blue("-i, --interactive")} Run in interactive mode (default)
+`);
+}
diff --git a/scripts/parse-egg-moves/interactive.js b/scripts/parse-egg-moves/interactive.js
new file mode 100644
index 00000000000..68ee41e7900
--- /dev/null
+++ b/scripts/parse-egg-moves/interactive.js
@@ -0,0 +1,108 @@
+import fs from "fs";
+import chalk from "chalk";
+import inquirer from "inquirer";
+import { showHelpText } from "./help-message.js";
+
+/**
+ * @import { Option } from "./main.js"
+ */
+
+/**
+ * Prompt the user to interactively select an option (console/file) to retrieve the egg move CSV.
+ * @returns {Promise} The selected option with value
+ */
+export async function runInteractive() {
+ /** @type {"Console" | "File" | "Help" | "Exit"} */
+ const answer = await inquirer
+ .prompt([
+ {
+ type: "list",
+ name: "type",
+ message: "Select the method to obtain egg moves.",
+ choices: ["Console", "File", "Help", "Exit"],
+ },
+ ])
+ .then(a => a.type);
+
+ if (answer === "Exit") {
+ console.log("Exiting...");
+ process.exitCode = 1;
+ return { type: "Exit" };
+ }
+
+ if (answer === "Help") {
+ showHelpText();
+ return { type: "Exit" };
+ }
+
+ if (!["Console", "File"].includes(answer)) {
+ console.error(chalk.red("Please provide a valid type!"));
+ return await runInteractive();
+ }
+
+ return { type: answer, value: await promptForValue(answer) };
+}
+
+/**
+ * Prompt the user to give a value (either the direct CSV or the file path).
+ * @param {"Console" | "File"} type - The input method
+ * @returns {Promise} A Promise resolving with the CSV/file path.
+ */
+function promptForValue(type) {
+ switch (type) {
+ case "Console":
+ return doPromptConsole();
+ case "File":
+ return getFilePath();
+ }
+}
+
+/**
+ * Prompt the user to enter a file path from the console.
+ * @returns {Promise} The file path inputted by the user.
+ */
+async function getFilePath() {
+ return await inquirer
+ .prompt([
+ {
+ type: "input",
+ name: "path",
+ message: "Please enter the path to the egg move CSV file.",
+ validate: input => {
+ if (input.trim() === "") {
+ return "File path cannot be empty!";
+ }
+ if (!fs.existsSync(input)) {
+ return "File does not exist!";
+ }
+ return true;
+ },
+ },
+ ])
+ .then(answer => answer.path);
+}
+
+/**
+ * Prompt the user for CSV input from the console.
+ * @returns {Promise} The CSV input from the user.
+ */
+async function doPromptConsole() {
+ return await inquirer
+ .prompt([
+ {
+ type: "input",
+ name: "csv",
+ message: "Please enter the egg move CSV text.",
+ validate: input => {
+ if (input.trim() === "") {
+ return "CSV text cannot be empty!";
+ }
+ if (!input.match(/^[^,]+(,[^,]+){4}$/gm)) {
+ return "CSV text malformed - should contain 5 consecutive comma-separated values per line!";
+ }
+ return true;
+ },
+ },
+ ])
+ .then(answer => answer.csv);
+}
diff --git a/scripts/parse-egg-moves/main.js b/scripts/parse-egg-moves/main.js
new file mode 100644
index 00000000000..a009c81a041
--- /dev/null
+++ b/scripts/parse-egg-moves/main.js
@@ -0,0 +1,168 @@
+/*
+ * This script accepts a CSV value or file path as input, parses the egg moves,
+ * and writes the output to a TypeScript file.
+ * It can be run interactively or with command line arguments.
+ * Usage: `pnpm eggMoves:parse`
+ */
+
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+import chalk from "chalk";
+import { showHelpText } from "./help-message.js";
+import { runInteractive } from "./interactive.js";
+import { parseEggMoves } from "./parse.js";
+
+const version = "1.0.0";
+
+// Get the directory name of the current module file
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const projectRoot = path.join(__dirname, "..", "..");
+const templatePath = path.join(__dirname, "egg-move-template.ts");
+// TODO: Do we want this to be configurable?
+const eggMoveTargetPath = path.join(projectRoot, "src/data/balance/egg-moves.ts");
+
+/**
+ * @typedef {{type: "Console" | "File", value: string} | {type: "Exit"}}
+ * Option
+ * An option selected by the user.
+ */
+
+/**
+ * Runs the interactive eggMoves:parse CLI.
+ * @returns {Promise}
+ */
+async function start() {
+ console.log(chalk.yellow(`🥚 Egg Move Parser - v${version}`));
+
+ if (process.argv.length > 4) {
+ console.error(
+ chalk.redBright.bold(
+ `✗ Error: Too many arguments provided!\nArgs: ${chalk.hex("#7310fdff")(process.argv.slice(2).join(" "))}`,
+ ),
+ );
+ showHelpText();
+ process.exitCode = 1;
+ return;
+ }
+
+ let csv = "";
+ const inputType = await parseArguments();
+ if (process.exitCode) {
+ // If exit code is non-zero, return to allow it to propagate up the chain.
+ return;
+ }
+ switch (inputType.type) {
+ case "Console":
+ csv = inputType.value;
+ break;
+ case "File":
+ csv = await fs.promises.readFile(inputType.value, "utf-8");
+ break;
+ case "Exit":
+ // Help screen triggered; break out
+ return;
+ }
+
+ await writeToFile(parseEggMoves(csv));
+}
+
+/**
+ * Handle the arguments passed to the script and obtain the CSV input type.
+ * @returns {Promise<{type: "Console" | "File", value: string} | {type: "Exit"}>} The input method selected by the user
+ */
+async function parseArguments() {
+ const args = process.argv.slice(2); // first 2 args are node and script name (irrelevant)
+
+ /** @type {string | undefined} */
+ const arg = args[0].split("=")[0]; // Yoink everything up to the first "=" to get the raw command
+ switch (arg) {
+ case "-f":
+ case "--file":
+ return { type: "File", value: getArgValue() };
+ case "-t":
+ case "--text":
+ case "-c":
+ case "--console":
+ return { type: "Console", value: getArgValue() };
+ case "-h":
+ case "--help":
+ showHelpText();
+ process.exitCode = 0;
+ return { type: "Exit" };
+ case "--interactive":
+ case "-i":
+ case undefined:
+ return await runInteractive();
+ default:
+ // If no arguments are found, check if it's a file path
+ if (fs.existsSync(arg)) {
+ console.log(chalk.green(`Using file path from stdin: ${chalk.blue(arg)}`));
+ return { type: "File", value: arg };
+ }
+ badArgs();
+ return { type: "Exit" };
+ }
+}
+
+/**
+ * Get the value of the argument provided.
+ * @returns {string} The CSV or file path from the arguments
+ * @throws {Error} If arguments are malformed
+ */
+function getArgValue() {
+ // If the user provided a value as argument 2, take that as the argument.
+ // Otherwise, check the 1st argument to see if it contains an `=` and extract everything afterwards.
+ /** @type {string | undefined} */
+ let filePath = process.argv[3];
+ const equalsIndex = process.argv[2].indexOf("=");
+ if (equalsIndex > -1) {
+ // If arg 3 was aleady existing and someone used `=` notation to assign a property, throw an error.
+ filePath = filePath ? undefined : process.argv[2].slice(equalsIndex + 1);
+ }
+
+ if (!filePath?.trim()) {
+ badArgs();
+ return "";
+ }
+ // NB: It doesn't really matter that this can be `undefined` - we'll always break out by lieu of setting the exit code
+ return filePath;
+}
+
+/**
+ * Write out the parsed CSV to a file.
+ * @param {string} moves - The parsed CSV
+ * @returns {Promise}
+ */
+export async function writeToFile(moves) {
+ try {
+ // Read the template file, replacing the placeholder with the move table.
+ const content = fs.readFileSync(templatePath, "utf8").replace(`"{{table}}"`, moves);
+
+ if (fs.existsSync(eggMoveTargetPath)) {
+ console.warn(chalk.hex("#ffa500")("\nEgg moves file already exists, overwriting...\n"));
+ }
+
+ // Write the template content to the file
+ fs.writeFileSync(eggMoveTargetPath, content, "utf8");
+
+ console.log(chalk.green.bold(`\n✔ Egg Moves written to ${eggMoveTargetPath}`));
+ console.groupEnd();
+ } catch (err) {
+ console.error(chalk.red(`✗ Error while writing egg moves: ${err}`));
+ process.exitCode = 1;
+ }
+}
+
+/**
+ * Do logging for incorrect or malformed CLI arguments.
+ * @returns {void}
+ */
+function badArgs() {
+ chalk.red.bold(`✗ Error: Malformed arguments!\nArgs: ${chalk.hex("#7310fdff")(process.argv.slice(2).join(" "))}`);
+ showHelpText();
+ process.exitCode = 1;
+}
+
+start();
diff --git a/scripts/parse-egg-moves/parse.js b/scripts/parse-egg-moves/parse.js
new file mode 100644
index 00000000000..fe7511f8b08
--- /dev/null
+++ b/scripts/parse-egg-moves/parse.js
@@ -0,0 +1,79 @@
+import chalk from "chalk";
+
+/**
+ * A single line of the inputted CSV.
+ * @typedef {[speciesName: string, move1: string, move2: string, move3: string, move4: string]}
+ * CSVLine
+ */
+
+/**
+ * Regex to determine if a string follows the required CSV format.
+ */
+const csvRegex = /^((?:[^,]+?,){4}(?:\w|\s)+?,?\n?)+$/g;
+
+/**
+ * Given a CSV string, parse it and return a structured table ready to be inputted into code.
+ * @param {string} csv - The formatted CSV string.
+ * @returns {string} The fully formatted table.
+ */
+export function parseEggMoves(csv) {
+ console.log(chalk.grey("⚙️ Parsing egg moves..."));
+ if (!csvRegex.test(csv)) {
+ console.error(chalk.redBright("! Input was not proper CSV!"));
+ process.exitCode = 1;
+ return "";
+ }
+
+ let output = "{\n";
+
+ const lines = csv.split(/\n/g);
+
+ for (const line of lines) {
+ /**
+ * The individual CSV column for this species.
+ */
+ const cols =
+ /** @type {CSVLine} */
+ (line.split(",").slice(0, 5));
+ const speciesName = toUpperSnakeCase(cols[0]);
+
+ const eggMoves =
+ /** @type {string[]} */
+ ([]);
+
+ for (let m = 1; m < 5; m++) {
+ const moveName = cols[m].trim();
+ if (!moveName || moveName === "N/A") {
+ console.warn(`Species ${speciesName} missing ${m}th egg move!`);
+ eggMoves.push("MoveId.NONE");
+ continue;
+ }
+
+ // Remove (N) and (P) from the ends of move names before UPPER_SNAKE_CASE-ing them
+ const moveNameTitle = toUpperSnakeCase(moveName.replace(/ \([A-Z]\)$/, ""));
+ eggMoves.push("MoveId." + moveNameTitle);
+ }
+
+ if (eggMoves.every(move => move === "MoveId.NONE")) {
+ console.warn(`Species ${speciesName} could not be parsed, excluding from output...`);
+ output += ` // [SpeciesId.${speciesName}]: [ MoveId.NONE, MoveId.NONE, MoveId.NONE, MoveId.NONE ],\n`;
+ } else {
+ output += ` [SpeciesId.${speciesName}]: [ ${eggMoves.join(", ")} ],\n`;
+ }
+ }
+
+ // NB: We omit the semicolon as it is contained in the template string itself
+ return output + "} satisfies Partial>";
+}
+
+/**
+ * Helper method to convert a string into `UPPER_SNAKE_CASE`.
+ * @param {string} str - The string being converted
+ * @returns {string} The result of converting `str` into upper snake case.
+ */
+function toUpperSnakeCase(str) {
+ return str
+ .split(/[_ -]+/g)
+ .map(word => word.toUpperCase())
+ .join("_");
+}
diff --git a/scripts/scrape-trainer-names/fetch-names.js b/scripts/scrape-trainer-names/fetch-names.js
index 9e6bd0c4d2b..3f7d0393f5b 100644
--- a/scripts/scrape-trainer-names/fetch-names.js
+++ b/scripts/scrape-trainer-names/fetch-names.js
@@ -31,11 +31,11 @@ export function fetchNames(trainerListHeader, knownFemale = false) {
// Grab all the trainer name tables sorted by generation
const tables = elements.slice(startChildIndex, endChildIndex).filter(
- /** @type {(t: ChildNode) => t is Element} */
+ /** @type {(t: ChildNode) => t is HTMLTableElement} */
(
t =>
// Only grab expandable tables within the header block
- t.nodeName === "TABLE" && t["className"] === "expandable"
+ t.nodeName === "TABLE" && /** @type {HTMLTableElement} */ (t)["className"] === "expandable"
),
);
@@ -48,7 +48,7 @@ export function fetchNames(trainerListHeader, knownFemale = false) {
/**
* Parse the table in question.
- * @param {Element[]} tables - The array of Elements forming the current table
+ * @param {HTMLTableElement[]} tables - The array of Elements forming the current table
* @param {boolean} isFemale - Whether the trainer is known to be female or not
* @param {Set} trainerNames A Set containing the male trainer names
* @param {Set} femaleTrainerNames - A Set containing the female trainer names
@@ -56,7 +56,7 @@ export function fetchNames(trainerListHeader, knownFemale = false) {
function parseTable(tables, isFemale, trainerNames, femaleTrainerNames) {
for (const table of tables) {
// Grab all rows past the first header with exactly 9 children in them (Name, Battle, Winnings, 6 party slots)
- const trainerRows = [...table.querySelectorAll("tr:not(:first-child)")].filter(r => r.children.length === 9);
+ const trainerRows = [...table.rows].slice(1).filter(r => r.children.length === 9);
for (const row of trainerRows) {
const content = row.firstElementChild?.innerHTML;
// Skip empty elements & ones without anchors
diff --git a/scripts/scrape-trainer-names/main.js b/scripts/scrape-trainer-names/main.js
index 43989eebab9..4d0f12a0e63 100644
--- a/scripts/scrape-trainer-names/main.js
+++ b/scripts/scrape-trainer-names/main.js
@@ -129,7 +129,7 @@ async function scrapeTrainerNames(classes) {
reason = `Server produced error code of ${+errCode}`;
}
throw new Error(
- chalk.red.bold(`Failed to parse URL for ${chalk.hex("#7fff00")(`\"${trainerClass}\"`)}!\nReason: ${reason}`),
+ chalk.red.bold(`Failed to parse URL for ${chalk.hex("#7fff00")(`"${trainerClass}"`)}!\nReason: ${reason}`),
);
}
}),
@@ -191,9 +191,7 @@ async function doFetch(trainerClass, seenClasses) {
const [female, counterpartURLs] = checkGenderAndType(document);
const names = fetchNames(trainerListHeader, female);
if (names === INVALID_URL) {
- return Promise.reject(
- new Error(chalk.red.bold(`URL \"${classURL}\" did not correspond to a valid trainer class!`)),
- );
+ return Promise.reject(new Error(chalk.red.bold(`URL "${classURL}" did not correspond to a valid trainer class!`)));
}
// Recurse into all unseen gender counterparts' URLs, using the first male name we find
@@ -285,7 +283,7 @@ async function promptExisting(outFile) {
{
type: "confirm",
name: "continue",
- message: `File ${chalk.blue(outFile)} already exists!` + "\nDo you want to replace it?",
+ message: `File ${chalk.blue(outFile)} already exists!\nDo you want to replace it?`,
default: false,
},
])
diff --git a/src/@types/api/pokerogue-daily-api.ts b/src/@types/api/pokerogue-daily-api.ts
index 838af2a2a34..862ff2f51a3 100644
--- a/src/@types/api/pokerogue-daily-api.ts
+++ b/src/@types/api/pokerogue-daily-api.ts
@@ -1,4 +1,4 @@
-import type { ScoreboardCategory } from "#ui/daily-run-scoreboard";
+import type { ScoreboardCategory } from "#ui/containers/daily-run-scoreboard";
export interface GetDailyRankingsRequest {
category: ScoreboardCategory;
diff --git a/src/@types/api/pokerogue-save-data-api.ts b/src/@types/api/pokerogue-save-data-api.ts
index ebc80ac4ce0..c33d775f114 100644
--- a/src/@types/api/pokerogue-save-data-api.ts
+++ b/src/@types/api/pokerogue-save-data-api.ts
@@ -1,4 +1,4 @@
-import type { SessionSaveData, SystemSaveData } from "#system/game-data";
+import type { SessionSaveData, SystemSaveData } from "#types/save-data";
export interface UpdateAllSavedataRequest {
system: SystemSaveData;
diff --git a/src/@types/api/pokerogue-system-save-data-api.ts b/src/@types/api/pokerogue-system-save-data-api.ts
index 133f9cda506..2e79b4fb92c 100644
--- a/src/@types/api/pokerogue-system-save-data-api.ts
+++ b/src/@types/api/pokerogue-system-save-data-api.ts
@@ -1,4 +1,4 @@
-import type { SystemSaveData } from "#system/game-data";
+import type { SystemSaveData } from "#types/save-data";
export interface GetSystemSavedataRequest {
clientSessionId: string;
diff --git a/src/@types/arena-tags.ts b/src/@types/arena-tags.ts
index 4ac7abf6f3d..73f26af052a 100644
--- a/src/@types/arena-tags.ts
+++ b/src/@types/arena-tags.ts
@@ -1,7 +1,8 @@
import type { ArenaTagTypeMap } from "#data/arena-tag";
import type { ArenaTagType } from "#enums/arena-tag-type";
// biome-ignore lint/correctness/noUnusedImports: TSDocs
-import type { SessionSaveData } from "#system/game-data";
+import type { SessionSaveData } from "#types/save-data";
+import type { ObjectValues } from "#types/type-helpers";
/** Subset of {@linkcode ArenaTagType}s that apply some negative effect to pokemon that switch in ({@link https://bulbapedia.bulbagarden.net/wiki/List_of_moves_that_cause_entry_hazards#List_of_traps | entry hazards} and Imprison. */
export type EntryHazardTagType =
@@ -24,22 +25,33 @@ export type TurnProtectArenaTagType =
/** Subset of {@linkcode ArenaTagType}s that create Trick Room-like effects which are removed upon overlap. */
export type RoomArenaTagType = ArenaTagType.TRICK_ROOM;
-/** Subset of {@linkcode ArenaTagType}s that cannot persist across turns, and thus should not be serialized in {@linkcode SessionSaveData}. */
+/** Subset of {@linkcode ArenaTagType}s that are **not** able to persist across turns, and should therefore not be serialized in {@linkcode SessionSaveData}. */
export type NonSerializableArenaTagType = ArenaTagType.NONE | TurnProtectArenaTagType | ArenaTagType.ION_DELUGE;
/** Subset of {@linkcode ArenaTagType}s that may persist across turns, and thus must be serialized in {@linkcode SessionSaveData}. */
export type SerializableArenaTagType = Exclude;
/**
- * Type-safe representation of an arbitrary, serialized Arena Tag
+ * Utility type containing all entries of {@linkcode ArenaTagTypeMap} corresponding to serializable tags.
*/
-export type ArenaTagTypeData = Parameters<
- ArenaTagTypeMap[keyof {
- [K in keyof ArenaTagTypeMap as K extends SerializableArenaTagType ? K : never]: ArenaTagTypeMap[K];
- }]["loadTag"]
->[0];
+type SerializableArenaTagTypeMap = Pick;
-/** Dummy, typescript-only declaration to ensure that
+/**
+ * Type mapping all `ArenaTag`s to type-safe representations of their serialized forms.
+ *
+ * @interface
+ */
+export type ArenaTagDataMap = {
+ [k in keyof SerializableArenaTagTypeMap]: Parameters[0];
+};
+
+/**
+ * Type-safe representation of an arbitrary, serialized `ArenaTag`.
+ */
+export type ArenaTagData = ObjectValues;
+
+/**
+ * Dummy, typescript-only declaration to ensure that
* {@linkcode ArenaTagTypeMap} has a map for all ArenaTagTypes.
*
* If an arena tag is missing from the map, typescript will throw an error on this statement.
diff --git a/src/@types/battler-tags.ts b/src/@types/battler-tags.ts
index 211eb25113d..e47b4f8cfc1 100644
--- a/src/@types/battler-tags.ts
+++ b/src/@types/battler-tags.ts
@@ -1,8 +1,11 @@
// biome-ignore-start lint/correctness/noUnusedImports: Used in a TSDoc comment
import type { AbilityBattlerTag, BattlerTagTypeMap, SerializableBattlerTag, TypeBoostTag } from "#data/battler-tags";
import type { AbilityId } from "#enums/ability-id";
-// biome-ignore-end lint/correctness/noUnusedImports: end
+import type { SessionSaveData } from "#types/save-data";
+// biome-ignore-end lint/correctness/noUnusedImports: Used in a TSDoc comment
+
import type { BattlerTagType } from "#enums/battler-tag-type";
+import type { InferKeys, ObjectValues } from "#types/type-helpers";
/**
* Subset of {@linkcode BattlerTagType}s that restrict the use of moves.
@@ -103,28 +106,35 @@ export type RemovedTypeTagType = BattlerTagType.DOUBLE_SHOCKED | BattlerTagType.
export type HighestStatBoostTagType =
| BattlerTagType.QUARK_DRIVE // formatting
| BattlerTagType.PROTOSYNTHESIS;
-/**
- * Subset of {@linkcode BattlerTagType}s that are able to persist between turns and should therefore be serialized
- */
-export type SerializableBattlerTagType = keyof {
- [K in keyof BattlerTagTypeMap as BattlerTagTypeMap[K] extends SerializableBattlerTag
- ? K
- : never]: BattlerTagTypeMap[K];
-};
/**
- * Subset of {@linkcode BattlerTagType}s that are not able to persist across waves and should therefore not be serialized
+ * Subset of {@linkcode BattlerTagType}s that are able to persist between turns, and should therefore be serialized.
+ */
+export type SerializableBattlerTagType = InferKeys;
+
+/**
+ * Subset of {@linkcode BattlerTagType}s that are **not** able to persist between turns,
+ * and should therefore not be serialized in {@linkcode SessionSaveData}.
*/
export type NonSerializableBattlerTagType = Exclude;
/**
- * Type-safe representation of an arbitrary, serialized Battler Tag
+ * Utility type containing all entries of {@linkcode BattlerTagTypeMap} corresponding to serializable tags.
*/
-export type BattlerTagTypeData = Parameters<
- BattlerTagTypeMap[keyof {
- [K in keyof BattlerTagTypeMap as K extends SerializableBattlerTagType ? K : never]: BattlerTagTypeMap[K];
- }]["loadTag"]
->[0];
+type SerializableBattlerTagTypeMap = Pick;
+
+/**
+ * Type mapping all `BattlerTag`s to type-safe representations of their serialized forms.
+ * @interface
+ */
+export type BattlerTagDataMap = {
+ [k in keyof SerializableBattlerTagTypeMap]: Parameters[0];
+};
+
+/**
+ * Type-safe representation of an arbitrary, serialized `BattlerTag`.
+ */
+export type BattlerTagData = ObjectValues;
/**
* Dummy, typescript-only declaration to ensure that
diff --git a/src/@types/helpers/type-helpers.ts b/src/@types/helpers/type-helpers.ts
index 0be391aa3c4..048a86ab489 100644
--- a/src/@types/helpers/type-helpers.ts
+++ b/src/@types/helpers/type-helpers.ts
@@ -36,15 +36,18 @@ export type Mutable = {
/**
* Type helper to obtain the keys associated with a given value inside an object.
+ * Acts similar to {@linkcode Pick}, except checking the object's values instead of its keys.
* @typeParam O - The type of the object
- * @typeParam V - The type of one of O's values
+ * @typeParam V - The type of one of O's values.
*/
-export type InferKeys> = {
- [K in keyof O]: O[K] extends V ? K : never;
-}[keyof O];
+export type InferKeys = V extends ObjectValues
+ ? {
+ [K in keyof O]: O[K] extends V ? K : never;
+ }[keyof O]
+ : never;
/**
- * Utility type to obtain the values of a given object. \
+ * Utility type to obtain a union of the values of a given object. \
* Functions similar to `keyof E`, except producing the values instead of the keys.
* @remarks
* This can be used to convert an `enum` interface produced by `typeof Enum` into the union type representing its members.
diff --git a/src/@types/save-data.ts b/src/@types/save-data.ts
new file mode 100644
index 00000000000..4c20d63da53
--- /dev/null
+++ b/src/@types/save-data.ts
@@ -0,0 +1,143 @@
+import type { PokeballCounts } from "#app/battle-scene";
+import type { Tutorial } from "#app/tutorial";
+import type { BattleType } from "#enums/battle-type";
+import type { GameModes } from "#enums/game-modes";
+import type { MoveId } from "#enums/move-id";
+import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
+import type { PlayerGender } from "#enums/player-gender";
+import type { PokemonType } from "#enums/pokemon-type";
+import type { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
+import type { Variant } from "#sprites/variant";
+import type { ArenaData } from "#system/arena-data";
+import type { ChallengeData } from "#system/challenge-data";
+import type { EggData } from "#system/egg-data";
+import type { GameStats } from "#system/game-stats";
+import type { ModifierData } from "#system/modifier-data";
+import type { PokemonData } from "#system/pokemon-data";
+import type { TrainerData } from "#system/trainer-data";
+import type { DexData } from "./dex-data";
+
+export interface SystemSaveData {
+ trainerId: number;
+ secretId: number;
+ gender: PlayerGender;
+ dexData: DexData;
+ starterData: StarterData;
+ gameStats: GameStats;
+ unlocks: Unlocks;
+ achvUnlocks: AchvUnlocks;
+ voucherUnlocks: VoucherUnlocks;
+ voucherCounts: VoucherCounts;
+ eggs: EggData[];
+ gameVersion: string;
+ timestamp: number;
+ eggPity: number[];
+ unlockPity: number[];
+}
+
+export interface SessionSaveData {
+ seed: string;
+ playTime: number;
+ gameMode: GameModes;
+ party: PokemonData[];
+ enemyParty: PokemonData[];
+ modifiers: ModifierData[];
+ enemyModifiers: ModifierData[];
+ arena: ArenaData;
+ pokeballCounts: PokeballCounts;
+ money: number;
+ score: number;
+ waveIndex: number;
+ battleType: BattleType;
+ trainer: TrainerData;
+ gameVersion: string;
+ /** The player-chosen name of the run */
+ name: string;
+ timestamp: number;
+ challenges: ChallengeData[];
+ mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
+ mysteryEncounterSaveData: MysteryEncounterSaveData;
+ /**
+ * Counts the amount of pokemon fainted in your party during the current arena encounter.
+ */
+ playerFaints: number;
+}
+
+export interface Unlocks {
+ [key: number]: boolean;
+}
+
+export interface AchvUnlocks {
+ [key: string]: number;
+}
+
+export interface VoucherUnlocks {
+ [key: string]: number;
+}
+
+export interface VoucherCounts {
+ [type: string]: number;
+}
+
+export type StarterMoveset = [MoveId] | [MoveId, MoveId] | [MoveId, MoveId, MoveId] | [MoveId, MoveId, MoveId, MoveId];
+
+export interface StarterFormMoveData {
+ [key: number]: StarterMoveset;
+}
+
+export interface StarterMoveData {
+ [key: number]: StarterMoveset | StarterFormMoveData;
+}
+
+export interface StarterAttributes {
+ nature?: number;
+ ability?: number;
+ variant?: number;
+ form?: number;
+ female?: boolean;
+ shiny?: boolean;
+ favorite?: boolean;
+ nickname?: string;
+ tera?: PokemonType;
+}
+
+export interface DexAttrProps {
+ shiny: boolean;
+ female: boolean;
+ variant: Variant;
+ formIndex: number;
+}
+
+export type RunHistoryData = Record;
+
+export interface RunEntry {
+ entry: SessionSaveData;
+ isVictory: boolean;
+ /*Automatically set to false at the moment - implementation TBD*/
+ isFavorite: boolean;
+}
+
+export interface StarterDataEntry {
+ moveset: StarterMoveset | StarterFormMoveData | null;
+ eggMoves: number;
+ candyCount: number;
+ friendship: number;
+ abilityAttr: number;
+ passiveAttr: number;
+ valueReduction: number;
+ classicWinCount: number;
+}
+
+export interface StarterData {
+ [key: number]: StarterDataEntry;
+}
+
+// TODO: Rework into a bitmask
+export type TutorialFlags = {
+ [key in Tutorial]: boolean;
+};
+
+// TODO: Rework into a bitmask
+export interface SeenDialogues {
+ [key: string]: boolean;
+}
diff --git a/src/@types/session-save-migrator.ts b/src/@types/session-save-migrator.ts
index 56518eaa8b7..c8f53236c63 100644
--- a/src/@types/session-save-migrator.ts
+++ b/src/@types/session-save-migrator.ts
@@ -1,4 +1,4 @@
-import type { SessionSaveData } from "#system/game-data";
+import type { SessionSaveData } from "./save-data";
export interface SessionSaveMigrator {
version: string;
diff --git a/src/@types/system-save-migrator.ts b/src/@types/system-save-migrator.ts
index 80fff9c7848..4cbcc4c3e15 100644
--- a/src/@types/system-save-migrator.ts
+++ b/src/@types/system-save-migrator.ts
@@ -1,4 +1,4 @@
-import type { SystemSaveData } from "#system/game-data";
+import type { SystemSaveData } from "./save-data";
export interface SystemSaveMigrator {
version: string;
diff --git a/src/battle-scene.ts b/src/battle-scene.ts
index be02962867c..50868e94c47 100644
--- a/src/battle-scene.ts
+++ b/src/battle-scene.ts
@@ -121,13 +121,13 @@ 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 { AbilityBar } from "#ui/containers/ability-bar";
+import { ArenaFlyout } from "#ui/containers/arena-flyout";
+import { CandyBar } from "#ui/containers/candy-bar";
+import { CharSprite } from "#ui/containers/char-sprite";
+import { PartyExpBar } from "#ui/containers/party-exp-bar";
+import { PokeballTray } from "#ui/containers/pokeball-tray";
+import { PokemonInfoContainer } from "#ui/containers/pokemon-info-container";
import { addTextObject, getTextColor } from "#ui/text";
import { UI } from "#ui/ui";
import { addUiThemeOverrides } from "#ui/ui-theme";
@@ -804,7 +804,7 @@ export class BattleScene extends SceneBase {
* @returns An array of {@linkcode Pokemon}, as described above.
*/
public getField(activeOnly = false): Pokemon[] {
- const ret = new Array(4).fill(null);
+ const ret: Pokemon[] = new Array(4).fill(null);
const playerField = this.getPlayerField();
const enemyField = this.getEnemyField();
ret.splice(0, playerField.length, ...playerField);
@@ -827,10 +827,10 @@ export class BattleScene extends SceneBase {
do {
targetingMovePhase = this.phaseManager.findPhase(
mp =>
- mp.is("MovePhase") &&
- mp.targets.length === 1 &&
- mp.targets[0] === removedPokemon.getBattlerIndex() &&
- mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
+ mp.is("MovePhase")
+ && mp.targets.length === 1
+ && mp.targets[0] === removedPokemon.getBattlerIndex()
+ && mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
) as MovePhase;
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex();
@@ -863,6 +863,8 @@ export class BattleScene extends SceneBase {
* @param pokemonId - The ID whose Pokemon will be retrieved.
* @returns The {@linkcode Pokemon} associated with the given id.
* Returns `null` if the ID is `undefined` or not present in either party.
+ * @todo Change the `null` to `undefined` and update callers' signatures -
+ * this is weird and causes a lot of random jank
*/
getPokemonById(pokemonId: number | undefined): Pokemon | null {
if (isNullOrUndefined(pokemonId)) {
@@ -1315,16 +1317,16 @@ export class BattleScene extends SceneBase {
}
} else {
if (
- !this.gameMode.hasTrainers ||
- Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD ||
- (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData))
+ !this.gameMode.hasTrainers
+ || Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD
+ || (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData))
) {
newBattleType = BattleType.WILD;
} else {
newBattleType =
- Overrides.BATTLE_TYPE_OVERRIDE ??
- battleType ??
- (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD);
+ Overrides.BATTLE_TYPE_OVERRIDE
+ ?? battleType
+ ?? (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD);
}
if (newBattleType === BattleType.TRAINER) {
@@ -1335,12 +1337,12 @@ export class BattleScene extends SceneBase {
doubleTrainer = true;
} else if (trainerConfigs[trainerType].hasDouble) {
doubleTrainer =
- Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble ||
- !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
+ Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble
+ || !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
if (
- trainerConfigs[trainerType].trainerTypeDouble &&
- ![TrainerType.TATE, TrainerType.LIZA].includes(trainerType)
+ trainerConfigs[trainerType].trainerTypeDouble
+ && ![TrainerType.TATE, TrainerType.LIZA].includes(trainerType)
) {
doubleTrainer = false;
}
@@ -1357,8 +1359,8 @@ export class BattleScene extends SceneBase {
// Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
if (
- !Overrides.BATTLE_TYPE_OVERRIDE &&
- (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER)
+ !Overrides.BATTLE_TYPE_OVERRIDE
+ && (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER)
) {
newBattleType = BattleType.MYSTERY_ENCOUNTER;
// Reset to base spawn weight
@@ -1448,9 +1450,9 @@ export class BattleScene extends SceneBase {
const isNewBiome = this.isNewBiome(lastBattle);
/** Whether to reset and recall pokemon */
const resetArenaState =
- isNewBiome ||
- [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) ||
- this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
+ isNewBiome
+ || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType)
+ || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
for (const enemyPokemon of this.getEnemyParty()) {
enemyPokemon.destroy();
@@ -1476,10 +1478,8 @@ export class BattleScene extends SceneBase {
pokemon.resetBattleAndWaveData();
pokemon.resetTera();
applyAbAttrs("PostBattleInitAbAttr", { pokemon });
- if (
- pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
- (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
- ) {
+ // Terapagos resets tera on each fight
+ if (pokemon.hasSpecies(SpeciesId.TERAPAGOS)) {
this.arena.playerTerasUsed = 0;
}
}
@@ -1524,8 +1524,8 @@ export class BattleScene extends SceneBase {
const fieldScale =
Math.floor(
Math.pow(
- 1 /
- this.getField(true)
+ 1
+ / this.getField(true)
.map(p => p.getSpriteScale())
.reduce((highestScale: number, scale: number) => (highestScale = Math.max(scale, highestScale)), 0),
0.7,
@@ -1549,10 +1549,10 @@ export class BattleScene extends SceneBase {
this.tweens.add({
targets: this.field,
- scale: scale,
+ scale,
x: (defaultWidth - scaledWidth) / 2,
y: defaultHeight - scaledHeight,
- duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0,
+ duration: instant ? 0 : fixedInt(Math.abs(this.field.scale - scale) * 200),
ease: "Sine.easeInOut",
onComplete: () => resolve(),
});
@@ -1560,20 +1560,20 @@ export class BattleScene extends SceneBase {
}
getSpeciesFormIndex(species: PokemonSpecies, gender?: Gender, nature?: Nature, ignoreArena?: boolean): number {
- if (!species.forms?.length) {
+ if (species.forms?.length === 0) {
return 0;
}
- const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
- this.phaseManager.getCurrentPhase()?.phaseName ?? "",
- );
+ const isEggPhase =
+ this.phaseManager.getCurrentPhase().is("EggLapsePhase")
+ || this.phaseManager.getCurrentPhase().is("EggHatchPhase");
if (
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
- !isEggPhase &&
- this.currentBattle?.battleType === BattleType.TRAINER &&
- !isNullOrUndefined(this.currentBattle.trainer) &&
- this.currentBattle.trainer.config.hasSpecialtyType()
+ !isEggPhase
+ && this.currentBattle?.battleType === BattleType.TRAINER
+ && !isNullOrUndefined(this.currentBattle.trainer)
+ && this.currentBattle.trainer.config.hasSpecialtyType()
) {
if (species.speciesId === SpeciesId.WORMADAM) {
switch (this.currentBattle.trainer.config.specialtyType) {
@@ -1665,9 +1665,9 @@ export class BattleScene extends SceneBase {
return randSeedInt(8);
case SpeciesId.EEVEE:
if (
- this.currentBattle?.battleType === BattleType.TRAINER &&
- this.currentBattle?.waveIndex < 30 &&
- !isEggPhase
+ this.currentBattle?.battleType === BattleType.TRAINER
+ && this.currentBattle?.waveIndex < 30
+ && !isEggPhase
) {
return 0; // No Partner Eevee for Wave 12 Preschoolers
}
@@ -1777,9 +1777,9 @@ export class BattleScene extends SceneBase {
} else {
this.executeWithSeedOffset(() => {
isBoss =
- waveIndex % 10 === 0 ||
- (this.gameMode.hasRandomBosses &&
- randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
+ waveIndex % 10 === 0
+ || (this.gameMode.hasRandomBosses
+ && randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
}, waveIndex << 2);
}
if (!isBoss) {
@@ -1791,10 +1791,8 @@ export class BattleScene extends SceneBase {
if (level >= 100) {
ret++;
}
- if (species) {
- if (species.baseTotal >= 670) {
- ret++;
- }
+ if (species && species.baseTotal >= 670) {
+ ret++;
}
ret += Math.floor(waveIndex / 250);
@@ -1838,12 +1836,7 @@ export class BattleScene extends SceneBase {
this.rngCounter = 0;
}
- executeWithSeedOffset(
- // biome-ignore lint/complexity/noBannedTypes: Refactor to not use Function
- func: Function,
- offset: number,
- seedOverride?: string,
- ): void {
+ executeWithSeedOffset(func: () => void, offset: number, seedOverride?: string): void {
if (!func) {
return;
}
@@ -1900,8 +1893,8 @@ export class BattleScene extends SceneBase {
): Phaser.GameObjects.Sprite {
sprite.setPipeline(this.spritePipeline, {
tone: [0.0, 0.0, 0.0, 0.0],
- hasShadow: hasShadow,
- ignoreOverride: ignoreOverride,
+ hasShadow,
+ ignoreOverride,
teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined,
isTerastallized: pokemon ? pokemon.isTerastallized : false,
});
@@ -1922,7 +1915,7 @@ export class BattleScene extends SceneBase {
targets: this.fieldOverlay,
alpha: 0.5,
ease: "Sine.easeOut",
- duration: duration,
+ duration,
onComplete: () => resolve(),
});
});
@@ -1933,7 +1926,7 @@ export class BattleScene extends SceneBase {
this.tweens.add({
targets: this.fieldOverlay,
alpha: 0,
- duration: duration,
+ duration,
ease: "Cubic.easeIn",
onComplete: () => resolve(),
});
@@ -1967,7 +1960,7 @@ export class BattleScene extends SceneBase {
this.tweens.add({
targets: this.shopOverlay,
alpha: 0,
- duration: duration,
+ duration,
ease: "Cubic.easeIn",
onComplete: () => resolve(),
});
@@ -2044,7 +2037,7 @@ export class BattleScene extends SceneBase {
this.luckLabelText.setX(this.scaledCanvas.width - 2 - (this.luckText.displayWidth + 2));
this.tweens.add({
targets: labels,
- duration: duration,
+ duration,
alpha: 1,
onComplete: () => {
for (const label of labels) {
@@ -2061,7 +2054,7 @@ export class BattleScene extends SceneBase {
const labels = [this.luckLabelText, this.luckText];
this.tweens.add({
targets: labels,
- duration: duration,
+ duration,
alpha: 0,
onComplete: () => {
for (const label of labels) {
@@ -2075,9 +2068,9 @@ export class BattleScene extends SceneBase {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length;
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY(
- -this.scaledCanvas.height +
- (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) +
- biomeWaveTextHeight / 2,
+ -this.scaledCanvas.height
+ + (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0)
+ + biomeWaveTextHeight / 2,
);
this.moneyText.setY(this.biomeWaveText.y + 10);
this.scoreText.setY(this.moneyText.y + 10);
@@ -2101,9 +2094,9 @@ export class BattleScene extends SceneBase {
addFaintedEnemyScore(enemy: EnemyPokemon): void {
let scoreIncrease =
- enemy.getSpeciesForm().getBaseExp() *
- (enemy.level / this.getMaxExpLevel()) *
- ((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8);
+ enemy.getSpeciesForm().getBaseExp()
+ * (enemy.level / this.getMaxExpLevel())
+ * ((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8);
this.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id, false).map(
m => (scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier()),
);
@@ -2318,7 +2311,7 @@ export class BattleScene extends SceneBase {
});
}
- playSound(sound: string | AnySound, config?: object): AnySound {
+ playSound(sound: string | AnySound, config?: object): AnySound | null {
const key = typeof sound === "string" ? sound : sound.key;
config = config ?? {};
try {
@@ -2354,16 +2347,19 @@ export class BattleScene extends SceneBase {
this.sound.play(key, config);
return this.sound.get(key) as AnySound;
} catch {
- console.log(`${key} not found`);
- return sound as AnySound;
+ console.warn(`${key} not found`);
+ return null;
}
}
- playSoundWithoutBgm(soundName: string, pauseDuration?: number): AnySound {
+ playSoundWithoutBgm(soundName: string, pauseDuration?: number): AnySound | null {
this.bgmCache.add(soundName);
const resumeBgm = this.pauseBgm();
this.playSound(soundName);
- const sound = this.sound.get(soundName) as AnySound;
+ const sound = this.sound.get(soundName);
+ if (!sound) {
+ return sound;
+ }
if (this.bgmResumeTimer) {
this.bgmResumeTimer.destroy();
}
@@ -2373,7 +2369,7 @@ export class BattleScene extends SceneBase {
this.bgmResumeTimer = null;
});
}
- return sound;
+ return sound as AnySound;
}
/** The loop point of any given battle, mystery encounter, or title track, read as seconds and milliseconds. */
@@ -2621,8 +2617,8 @@ export class BattleScene extends SceneBase {
const waveIndex = this.currentBattle.waveIndex;
const waveSetIndex = Math.ceil(waveIndex / 10) - 1;
const moneyValue =
- Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex) *
- moneyMultiplier;
+ Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex)
+ * moneyMultiplier;
return Math.floor(moneyValue / 10) * 10;
}
@@ -2736,10 +2732,8 @@ export class BattleScene extends SceneBase {
}
if (!ignoreUpdate) {
this.updateModifiers(false, instant);
- resolve();
- } else {
- resolve();
}
+ resolve();
});
}
@@ -2936,8 +2930,8 @@ export class BattleScene extends SceneBase {
}
} else {
const isBoss =
- enemyPokemon.isBoss() ||
- (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
+ enemyPokemon.isBoss()
+ || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
let upgradeChance = 32;
if (isBoss) {
upgradeChance /= 2;
@@ -3006,15 +3000,15 @@ export class BattleScene extends SceneBase {
for (let m = 0; m < modifiers.length; m++) {
const modifier = modifiers[m];
if (
- modifier instanceof PokemonHeldItemModifier &&
- !this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId)
+ modifier instanceof PokemonHeldItemModifier
+ && !this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId)
) {
modifiers.splice(m--, 1);
}
if (
- modifier instanceof PokemonHeldItemModifier &&
- !isNullOrUndefined(modifier.getSpecies()) &&
- !this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
+ modifier instanceof PokemonHeldItemModifier
+ && !isNullOrUndefined(modifier.getSpecies())
+ && !this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
) {
modifiers.splice(m--, 1);
}
@@ -3114,7 +3108,7 @@ export class BattleScene extends SceneBase {
* Apply all modifiers that match `modifierType` in a random order
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
- * @param ...args The list of arguments needed to invoke `modifierType.apply`
+ * @param args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyShuffledModifiers(
@@ -3128,7 +3122,7 @@ export class BattleScene extends SceneBase {
this.executeWithSeedOffset(
() => {
const shuffleModifiers = mods => {
- if (mods.length < 1) {
+ if (mods.length === 0) {
return mods;
}
const rand = randSeedInt(mods.length);
@@ -3146,7 +3140,7 @@ export class BattleScene extends SceneBase {
* Apply all modifiers that match `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
- * @param ...args The list of arguments needed to invoke `modifierType.apply`
+ * @param args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyModifiers(
@@ -3181,7 +3175,7 @@ export class BattleScene extends SceneBase {
* Apply the first modifier that matches `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
- * @param ...args The list of arguments needed to invoke `modifierType.apply`
+ * @param args The list of arguments needed to invoke `modifierType.apply`
* @returns the first modifier that matches `modifierType` and was applied; return `null` if none matched
*/
applyModifier(
@@ -3278,8 +3272,8 @@ export class BattleScene extends SceneBase {
validateAchv(achv: Achv, args?: unknown[]): boolean {
if (
- (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) &&
- achv.validate(args)
+ (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE)
+ && achv.validate(args)
) {
this.gameData.achvUnlocks[achv.id] = Date.now();
this.ui.achvBar.showAchv(achv);
@@ -3329,7 +3323,7 @@ export class BattleScene extends SceneBase {
/**
* This function retrieves the sprite and audio keys for active Pokemon.
* Active Pokemon include both enemy and player Pokemon of the current wave.
- * Note: Questions on garbage collection go to @frutescens
+ * Note: Questions on garbage collection go to `@frutescens`
* @returns a string array of active sprite and audio keys that should not be deleted
*/
getActiveKeys(): string[] {
@@ -3417,8 +3411,8 @@ export class BattleScene extends SceneBase {
if (participantIds.size > 0) {
if (
- this.currentBattle.battleType === BattleType.TRAINER ||
- this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
+ this.currentBattle.battleType === BattleType.TRAINER
+ || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) {
expValue = Math.floor(expValue * 1.5);
} else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) {
@@ -3514,12 +3508,12 @@ export class BattleScene extends SceneBase {
isMysteryEncounterValidForWave(battleType: BattleType, waveIndex: number): boolean {
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
return (
- this.gameMode.hasMysteryEncounters &&
- battleType === BattleType.WILD &&
- !this.gameMode.isBoss(waveIndex) &&
- waveIndex % 10 !== 1 &&
- waveIndex < highestMysteryEncounterWave &&
- waveIndex > lowestMysteryEncounterWave
+ this.gameMode.hasMysteryEncounters
+ && battleType === BattleType.WILD
+ && !this.gameMode.isBoss(waveIndex)
+ && waveIndex % 10 !== 1
+ && waveIndex < highestMysteryEncounterWave
+ && waveIndex > lowestMysteryEncounterWave
);
}
@@ -3541,18 +3535,17 @@ export class BattleScene extends SceneBase {
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
// Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256
const expectedEncountersByFloor =
- (AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave)) *
- (waveIndex - lowestMysteryEncounterWave);
+ (AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave))
+ * (waveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate =
- sessionEncounterRate +
- Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
+ sessionEncounterRate
+ + Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate;
// MEs can only spawn 3 or more waves after the previous ME, barring overrides
- const canSpawn =
- encounteredEvents.length === 0 || waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3;
+ const canSpawn = encounteredEvents.length === 0 || waveIndex - encounteredEvents.at(-1)!.waveIndex > 3;
if (canSpawn || Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE !== null) {
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
@@ -3580,8 +3573,8 @@ export class BattleScene extends SceneBase {
// Loading override or session encounter
let encounter: MysteryEncounter | null;
if (
- !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) &&
- allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
+ !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
+ && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
if (canBypass) {
@@ -3596,9 +3589,9 @@ export class BattleScene extends SceneBase {
// Check for queued encounters first
if (
- !encounter &&
- this.mysteryEncounterSaveData?.queuedEncounters &&
- this.mysteryEncounterSaveData.queuedEncounters.length > 0
+ !encounter
+ && this.mysteryEncounterSaveData?.queuedEncounters
+ && this.mysteryEncounterSaveData.queuedEncounters.length > 0
) {
let i = 0;
while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) {
@@ -3655,11 +3648,7 @@ export class BattleScene extends SceneBase {
}
let availableEncounters: MysteryEncounter[] = [];
- const previousEncounter =
- this.mysteryEncounterSaveData.encounteredEvents.length > 0
- ? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1]
- .type
- : null;
+ const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.at(-1)?.type ?? null; // TODO: This being `null` is a bit weird
const disabledEncounters = this.eventManager.getEventMysteryEncountersDisabled();
const biomeMysteryEncounters =
mysteryEncountersByBiome.get(this.arena.biomeType)?.filter(enc => !disabledEncounters.includes(enc)) ?? [];
@@ -3678,9 +3667,9 @@ export class BattleScene extends SceneBase {
}
const disallowedGameModes = encounterCandidate.disallowedGameModes;
if (
- disallowedGameModes &&
- disallowedGameModes.length > 0 &&
- disallowedGameModes.includes(this.gameMode.modeId)
+ disallowedGameModes
+ && disallowedGameModes.length > 0
+ && disallowedGameModes.includes(this.gameMode.modeId)
) {
return false;
}
@@ -3694,11 +3683,11 @@ export class BattleScene extends SceneBase {
return false;
}
return !(
- this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
- encounterCandidate.maxAllowedEncounters &&
- encounterCandidate.maxAllowedEncounters > 0 &&
- this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
- encounterCandidate.maxAllowedEncounters
+ this.mysteryEncounterSaveData.encounteredEvents.length > 0
+ && encounterCandidate.maxAllowedEncounters
+ && encounterCandidate.maxAllowedEncounters > 0
+ && this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length
+ >= encounterCandidate.maxAllowedEncounters
);
})
.map(m => allMysteryEncounters[m]);
diff --git a/src/battle.ts b/src/battle.ts
index 7b6a58cbaca..1b789707806 100644
--- a/src/battle.ts
+++ b/src/battle.ts
@@ -237,11 +237,11 @@ export class Battle {
return null;
}
if (
- this.battleType === BattleType.TRAINER ||
- this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
+ this.battleType === BattleType.TRAINER
+ || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) {
- if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
- return `encounter_${this.trainer?.getEncounterBgm()}`;
+ if (!this.started && this.trainer?.config.encounterBgm && this.trainer.getEncounterMessages().length > 0) {
+ return `encounter_${this.trainer.getEncounterBgm()}`;
}
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
return this.trainer?.getBattleBgm() ?? null;
diff --git a/src/configs/inputs/config-handler.ts b/src/configs/inputs/config-handler.ts
index 227c2b964b9..11af770f969 100644
--- a/src/configs/inputs/config-handler.ts
+++ b/src/configs/inputs/config-handler.ts
@@ -116,8 +116,8 @@ export function getIconForLatestInput(configs, source, devices, settingName) {
export function assign(config, settingNameTarget, keycode): boolean {
// first, we need to check if this keycode is already used on another settingName
if (
- !canIAssignThisKey(config, getKeyWithKeycode(config, keycode)) ||
- !canIOverrideThisSetting(config, settingNameTarget)
+ !canIAssignThisKey(config, getKeyWithKeycode(config, keycode))
+ || !canIOverrideThisSetting(config, settingNameTarget)
) {
return false;
}
diff --git a/src/constants.ts b/src/constants.ts
index 589a091153c..17cf08aa7e2 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -53,12 +53,6 @@ export const defaultStarterSpecies: SpeciesId[] = [
SpeciesId.QUAXLY,
];
-export const defaultStarterSpeciesAndEvolutions: SpeciesId[] = defaultStarterSpecies.flatMap(id => [
- id,
- (id + 1) as SpeciesId,
- (id + 2) as SpeciesId,
-]);
-
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
/**
diff --git a/src/constants/colors.ts b/src/constants/colors.ts
new file mode 100644
index 00000000000..717c5fa5f0d
--- /dev/null
+++ b/src/constants/colors.ts
@@ -0,0 +1,25 @@
+/**
+ * @module
+ * A big file storing colors used in logging.
+ * Minified by Terser during production builds, so has no overhead.
+ */
+
+// Colors used in prod
+/** Color used for "Start Phase " logs */
+export const PHASE_START_COLOR = "green" as const;
+/** Color used for logs in `MovePhase` */
+export const MOVE_COLOR = "orchid" as const;
+
+// Colors used for testing code
+export const NEW_TURN_COLOR = "#ffad00ff" as const;
+export const UI_MSG_COLOR = "#009dffff" as const;
+export const OVERRIDES_COLOR = "#b0b01eff" as const;
+export const SETTINGS_COLOR = "#008844ff" as const;
+
+// Colors used for Vitest-related test utils
+export const TEST_NAME_COLOR = "#008886ff" as const;
+export const VITEST_PINK_COLOR = "#c162de" as const;
+
+// Mock console log stuff
+export const TRACE_COLOR = "#b700ff" as const;
+export const DEBUG_COLOR = "#874600ff" as const;
diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts
index afac857395c..8bb2f30b243 100644
--- a/src/data/abilities/ability.ts
+++ b/src/data/abilities/ability.ts
@@ -396,7 +396,23 @@ export abstract class AbAttr {
}
}
-export class BlockRecoilDamageAttr extends AbAttr {
+/**
+ * Abstract class for ability attributes that simply cancel an interaction
+ *
+ * @remarks
+ * Abilities that have simple cancel interactions (e.g. {@linkcode BlockRecoilDamageAttr}) can extend this class to reuse the `canApply` and `apply` logic
+ */
+abstract class CancelInteractionAbAttr extends AbAttr {
+ override canApply({ cancelled }: AbAttrParamsWithCancel): boolean {
+ return !cancelled.value;
+ }
+
+ override apply({ cancelled }: AbAttrParamsWithCancel): void {
+ cancelled.value = true;
+ }
+}
+
+export class BlockRecoilDamageAttr extends CancelInteractionAbAttr {
private declare readonly _: never;
constructor() {
super(false);
@@ -491,7 +507,7 @@ export class ClearWeatherAbAttr extends AbAttr {
private weather: WeatherType[];
/**
- * @param weather {@linkcode WeatherType[]} - the weather to be removed
+ * @param weather - The weather to be removed
*/
constructor(weather: WeatherType[]) {
super(true);
@@ -521,7 +537,7 @@ export class ClearTerrainAbAttr extends AbAttr {
private terrain: TerrainType[];
/**
- * @param terrain {@linkcode TerrainType[]} - the terrain to be removed
+ * @param terrain - the terrain to be removed
*/
constructor(terrain: TerrainType[]) {
super(true);
@@ -575,13 +591,10 @@ export abstract class PreDefendAbAttr extends AbAttr {
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean {
return (
- pokemon.isFullHp() &&
- // Checks if pokemon has wonder_guard (which forces 1hp)
- pokemon.getMaxHp() > 1 &&
- // Damage >= hp
- damage.value >= pokemon.hp &&
- // Cannot apply if the pokemon already has sturdy from some other source
- !pokemon.getTag(BattlerTagType.STURDY)
+ pokemon.isFullHp() // Checks if pokemon has wonder_guard (which forces 1hp)
+ && pokemon.getMaxHp() > 1 // Damage >= hp
+ && damage.value >= pokemon.hp // Cannot apply if the pokemon already has sturdy from some other source
+ && !pokemon.getTag(BattlerTagType.STURDY)
);
}
@@ -592,11 +605,7 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
}
}
-export class BlockItemTheftAbAttr extends AbAttr {
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
-
+export class BlockItemTheftAbAttr extends CancelInteractionAbAttr {
getTriggerMessage({ pokemon }: AbAttrBaseParams, abilityName: string) {
return i18next.t("abilityTriggers:blockItemTheft", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
@@ -646,7 +655,6 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
/**
* Reduces the damage dealt to an allied Pokemon. Used by Friend Guard.
- * @see {@linkcode applyPreDefend}
*/
export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr {
private damageMultiplier: number;
@@ -682,8 +690,6 @@ export interface TypeMultiplierAbAttrParams extends AugmentMoveInteractionAbAttr
/**
* Determines whether a Pokemon is immune to a move because of an ability.
- * @see {@linkcode applyPreDefend}
- * @see {@linkcode getCondition}
*/
export class TypeImmunityAbAttr extends PreDefendAbAttr {
private immuneType: PokemonType | null;
@@ -699,9 +705,9 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean {
return (
- ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) &&
- attacker !== pokemon &&
- attacker.getMoveType(move) === this.immuneType
+ ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget)
+ && attacker !== pokemon
+ && attacker.getMoveType(move) === this.immuneType
);
}
@@ -727,9 +733,9 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr {
override canApply(params: TypeMultiplierAbAttrParams): boolean {
const { move } = params;
return (
- move.category !== MoveCategory.STATUS &&
- !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") &&
- super.canApply(params)
+ move.category !== MoveCategory.STATUS
+ && !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr")
+ && super.canApply(params)
);
}
}
@@ -833,7 +839,6 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
/**
* Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Tera_Shell_(Ability) | Tera Shell}
* When the source is at full HP, incoming attacks will have a maximum 0.5x type effectiveness multiplier.
- * @extends PreDefendAbAttr
*/
export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
/**
@@ -841,10 +846,10 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
*/
override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean {
return (
- typeMultiplier instanceof NumberHolder &&
- !move?.hasAttr("FixedDamageAttr") &&
- pokemon.isFullHp() &&
- typeMultiplier.value > 0.5
+ typeMultiplier instanceof NumberHolder
+ && !move?.hasAttr("FixedDamageAttr")
+ && pokemon.isFullHp()
+ && typeMultiplier.value > 0.5
);
}
@@ -869,11 +874,12 @@ export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveIntera
}
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
- override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean {
+ override canApply({ move, opponent: attacker, cancelled }: FieldPriorityMoveImmunityAbAttrParams): boolean {
return (
- !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) &&
- move.getPriority(attacker) > 0 &&
- !move.isMultiTarget()
+ !cancelled.value
+ && !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY)
+ && move.getPriority(attacker) > 0
+ && !move.isMultiTarget()
);
}
@@ -897,10 +903,8 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr {
this.immuneCondition = immuneCondition;
}
- override canApply({ pokemon, opponent: attacker, move }: MoveImmunityAbAttrParams): boolean {
- // TODO: Investigate whether this method should be checking against `cancelled`, specifically
- // if not checking this results in multiple flyouts showing when multiple abilities block the move.
- return this.immuneCondition(pokemon, attacker, move);
+ override canApply({ pokemon, opponent: attacker, move, cancelled }: MoveImmunityAbAttrParams): boolean {
+ return !cancelled.value && this.immuneCondition(pokemon, attacker, move);
}
override apply({ cancelled }: MoveImmunityAbAttrParams): void {
@@ -970,6 +974,8 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr {
export interface PostMoveInteractionAbAttrParams extends AugmentMoveInteractionAbAttrParams {
/** Stores the hit result of the move used in the interaction */
readonly hitResult: HitResult;
+ /** The amount of damage dealt in the interaction */
+ readonly damage: number;
}
export class PostDefendAbAttr extends AbAttr {
@@ -1081,18 +1087,18 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr {
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate);
- const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1];
- const damageReceived = lastAttackReceived?.damage || 0;
+ const lastAttackReceived = pokemon.turnData.attacksReceived.at(-1);
+ const damageReceived = lastAttackReceived?.damage ?? 0;
return (
this.condition(pokemon, attacker, move) && pokemon.hp <= hpGateFlat && pokemon.hp + damageReceived > hpGateFlat
);
}
- override apply({ simulated, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): void {
+ override apply({ simulated, pokemon, opponent }: PostMoveInteractionAbAttrParams): void {
if (!simulated) {
globalScene.phaseManager.unshiftNew(
"StatStageChangePhase",
- (this.selfTarget ? pokemon : attacker).getBattlerIndex(),
+ (this.selfTarget ? pokemon : opponent).getBattlerIndex(),
true,
this.stats,
this.stages,
@@ -1115,8 +1121,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
const tag = globalScene.arena.getTag(this.arenaTagType) as EntryHazardTag;
return (
- this.condition(pokemon, attacker, move) &&
- (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)
+ this.condition(pokemon, attacker, move)
+ && (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)
);
}
@@ -1224,10 +1230,10 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
const effect =
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
return (
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- !attacker.status &&
- (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) &&
- attacker.canSetStatus(effect, true, false, pokemon)
+ move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && !attacker.status
+ && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
+ && attacker.canSetStatus(effect, true, false, pokemon)
);
}
@@ -1263,17 +1269,17 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
this.turnCount = turnCount;
}
- override canApply({ move, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean {
+ override canApply({ move, pokemon, opponent }: PostMoveInteractionAbAttrParams): boolean {
return (
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- pokemon.randBattleSeedInt(100) < this.chance &&
- attacker.canAddTag(this.tagType)
+ move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })
+ && pokemon.randBattleSeedInt(100) < this.chance
+ && opponent.canAddTag(this.tagType)
);
}
- override apply({ simulated, opponent: attacker, move }: PostMoveInteractionAbAttrParams): void {
+ override apply({ pokemon, simulated, opponent, move }: PostMoveInteractionAbAttrParams): void {
if (!simulated) {
- attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id);
+ opponent.addTag(this.tagType, this.turnCount, move.id, pokemon.id);
}
}
}
@@ -1322,9 +1328,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return (
- !simulated &&
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- !attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
+ !simulated
+ && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && !attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
);
}
@@ -1341,11 +1347,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
}
}
/**
- * @description: This ability applies the Perish Song tag to the attacking pokemon
+ * This ability applies the Perish Song tag to the attacking pokemon
* and the defending pokemon if the move makes physical contact and neither pokemon
* already has the Perish Song tag.
- * @class PostDefendPerishSongAbAttr
- * @extends {PostDefendAbAttr}
*/
export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
private turns: number;
@@ -1358,8 +1362,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return (
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- !attacker.getTag(BattlerTagType.PERISH_SONG)
+ move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && !attacker.getTag(BattlerTagType.PERISH_SONG)
);
}
@@ -1373,7 +1377,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string {
return i18next.t("abilityTriggers:perishBody", {
pokemonName: getPokemonNameWithAffix(pokemon),
- abilityName: abilityName,
+ abilityName,
});
}
}
@@ -1391,9 +1395,9 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
return (
- !(this.condition && !this.condition(pokemon, attacker, move)) &&
- !globalScene.arena.weather?.isImmutable() &&
- globalScene.arena.canSetWeather(this.weatherType)
+ !(this.condition && !this.condition(pokemon, attacker, move))
+ && !globalScene.arena.weather?.isImmutable()
+ && globalScene.arena.canSetWeather(this.weatherType)
);
}
@@ -1407,8 +1411,8 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return (
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- attacker.getAbility().isSwappable
+ move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && attacker.getAbility().isSwappable
);
}
@@ -1437,9 +1441,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return (
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- attacker.getAbility().isSuppressable &&
- !attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr")
+ move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && attacker.getAbility().isSuppressable
+ && !attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr")
);
}
@@ -1460,7 +1464,6 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
private chance: number;
private attacker: Pokemon;
- private move: Move;
constructor(chance: number) {
super();
@@ -1470,17 +1473,15 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return (
- isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) &&
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
- (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
+ isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED))
+ && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
);
}
- override apply({ simulated, opponent: attacker, move, pokemon }: PostMoveInteractionAbAttrParams): void {
- // TODO: investigate why this is setting properties
+ override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void {
if (!simulated) {
this.attacker = attacker;
- this.move = move;
this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id);
}
}
@@ -1593,12 +1594,7 @@ export interface FieldPreventExplosiveMovesAbAttrParams extends AbAttrBaseParams
cancelled: BooleanHolder;
}
-export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
- // TODO: investigate whether we need to check against `cancelled` in a `canApply` method
- override apply({ cancelled }: FieldPreventExplosiveMovesAbAttrParams): void {
- cancelled.value = true;
- }
-}
+export class FieldPreventExplosiveMovesAbAttr extends CancelInteractionAbAttr {}
export interface FieldMultiplyStatAbAttrParams extends AbAttrBaseParams {
/** The kind of stat that is being checked for modification */
@@ -1639,10 +1635,10 @@ export class FieldMultiplyStatAbAttr extends AbAttr {
canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean {
return (
- this.canStack ||
- (!hasApplied.value &&
- this.stat === stat &&
- target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat))
+ this.canStack
+ || (!hasApplied.value
+ && this.stat === stat
+ && target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat))
);
}
@@ -1684,14 +1680,14 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
*/
override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean {
return (
- (!this.condition || this.condition(pokemon, target, move)) &&
- !noAbilityTypeOverrideMoves.has(move.id) &&
- !(
- pokemon.isTerastallized &&
- (move.id === MoveId.TERA_BLAST ||
- (move.id === MoveId.TERA_STARSTORM &&
- pokemon.getTeraType() === PokemonType.STELLAR &&
- pokemon.hasSpecies(SpeciesId.TERAPAGOS)))
+ (!this.condition || this.condition(pokemon, target, move))
+ && !noAbilityTypeOverrideMoves.has(move.id)
+ && !(
+ pokemon.isTerastallized
+ && (move.id === MoveId.TERA_BLAST
+ || (move.id === MoveId.TERA_STARSTORM
+ && pokemon.getTeraType() === PokemonType.STELLAR
+ && pokemon.hasSpecies(SpeciesId.TERAPAGOS)))
)
);
}
@@ -1714,14 +1710,13 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
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: https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves
*/
- move.hasAttr("CallMoveAttr") ||
- move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr`
+ || move.hasAttr("CallMoveAttr")
+ || move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr`
) {
return false;
}
@@ -1960,19 +1955,16 @@ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostA
/**
* Boosts the power of a specific type of move for all Pokemon in the field.
- * @extends PreAttackFieldMoveTypePowerBoostAbAttr
*/
export class FieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr {}
/**
* Boosts the power of a specific type of move for the user and its allies.
- * @extends PreAttackFieldMoveTypePowerBoostAbAttr
*/
export class UserFieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr {}
/**
* Boosts the power of moves in specified categories.
- * @extends FieldMovePowerBoostAbAttr
*/
export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr {
/**
@@ -2149,13 +2141,13 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
// The PostAttackAbAttr should should only be invoked in cases where the move successfully connected,
// calling `super.canApply` already checks that the move was a damage move and not a status move.
if (
- super.canApply(params) &&
- !simulated &&
- hitResult < HitResult.NO_EFFECT &&
- (!this.stealCondition || this.stealCondition(pokemon, opponent, move))
+ super.canApply(params)
+ && !simulated
+ && hitResult < HitResult.NO_EFFECT
+ && (!this.stealCondition || this.stealCondition(pokemon, opponent, move))
) {
const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable);
- if (heldItems.length) {
+ if (heldItems.length > 0) {
// Ensure that the stolen item in testing is the same as when the effect is applied
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
@@ -2208,14 +2200,14 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
override canApply(params: PostMoveInteractionAbAttrParams): boolean {
const { simulated, pokemon, move, opponent } = params;
if (
- super.canApply(params) &&
- (simulated ||
- (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
- pokemon !== opponent &&
- (!this.contactRequired ||
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) &&
- pokemon.randBattleSeedInt(100) < this.chance &&
- !pokemon.status))
+ super.canApply(params)
+ && (simulated
+ || (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr")
+ && pokemon !== opponent
+ && (!this.contactRequired
+ || move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent }))
+ && pokemon.randBattleSeedInt(100) < this.chance
+ && !pokemon.status))
) {
const effect =
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
@@ -2259,13 +2251,13 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
const { pokemon, move, opponent } = params;
/**Battler tags inflicted by abilities post attacking are also considered additional effects.*/
return (
- super.canApply(params) &&
- !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
- pokemon !== opponent &&
- (!this.contactRequired ||
- move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) &&
- pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) &&
- !pokemon.status
+ super.canApply(params)
+ && !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr")
+ && pokemon !== opponent
+ && (!this.contactRequired
+ || move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon }))
+ && pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move)
+ && !pokemon.status
);
}
@@ -2291,7 +2283,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
override canApply({ simulated, pokemon, opponent, move, hitResult }: PostMoveInteractionAbAttrParams): boolean {
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, opponent, move))) {
const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable);
- if (heldItems.length) {
+ if (heldItems.length > 0) {
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
return true;
@@ -2537,15 +2529,11 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr {
* Abilities with this attribute prevent the user from being affected by Intimidate.
* @sealed
*/
-export class IntimidateImmunityAbAttr extends AbAttr {
+export class IntimidateImmunityAbAttr extends CancelInteractionAbAttr {
constructor() {
super(false);
}
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
-
getTriggerMessage({ pokemon }: AbAttrParamsWithCancel, abilityName: string, ..._args: any[]): string {
return i18next.t("abilityTriggers:intimidateImmunity", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
@@ -2582,7 +2570,6 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr {
/**
* Base class for defining all {@linkcode Ability} Attributes post summon
- * @see {@linkcode applyPostSummon()}
*/
export abstract class PostSummonAbAttr extends AbAttr {
/** Should the ability activate when gained in battle? This will almost always be true */
@@ -2622,7 +2609,7 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr {
private arenaTags: ArenaTagType[];
/**
- * @param arenaTags {@linkcode ArenaTagType[]} - the arena tags to be removed
+ * @param arenaTags - The arena tags to be removed
*/
constructor(arenaTags: ArenaTagType[]) {
super(true);
@@ -2922,10 +2909,10 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
override canApply(_params: AbAttrBaseParams): boolean {
const weatherReplaceable =
- this.weatherType === WeatherType.HEAVY_RAIN ||
- this.weatherType === WeatherType.HARSH_SUN ||
- this.weatherType === WeatherType.STRONG_WINDS ||
- !globalScene.arena.weather?.isImmutable();
+ this.weatherType === WeatherType.HEAVY_RAIN
+ || this.weatherType === WeatherType.HARSH_SUN
+ || this.weatherType === WeatherType.STRONG_WINDS
+ || !globalScene.arena.weather?.isImmutable();
return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType);
}
@@ -2973,7 +2960,7 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
public override canApply({ pokemon }: AbAttrBaseParams): boolean {
const status = pokemon.status?.effect;
- return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status));
+ return !isNullOrUndefined(status) && (this.immuneEffects.length === 0 || this.immuneEffects.includes(status));
}
public override apply({ pokemon }: AbAttrBaseParams): void {
@@ -3014,41 +3001,44 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr {
}
}
-/** Attempts to copy a pokemon's ability */
+/**
+ * Attempts to copy a pokemon's ability
+ *
+ * @remarks
+ * Hardcodes idiosyncrasies specific to trace, so should not be used for other abilities
+ * that might copy abilities in the future
+ * @sealed
+ */
export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
private target: Pokemon;
private targetAbilityName: string;
- override canApply({ pokemon }: AbAttrBaseParams): boolean {
- const targets = pokemon.getOpponents();
- if (!targets.length) {
+ override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean {
+ const targets = pokemon
+ .getOpponents()
+ .filter(t => t.getAbility().isCopiable || t.getAbility().id === AbilityId.WONDER_GUARD);
+ if (targets.length === 0) {
return false;
}
let target: Pokemon;
- if (targets.length > 1) {
- globalScene.executeWithSeedOffset(() => (target = randSeedItem(targets)), globalScene.currentBattle.waveIndex);
+ // simulated call always chooses first target so as to not advance RNG
+ if (targets.length > 1 && !simulated) {
+ target = targets[randSeedInt(targets.length)];
} else {
target = targets[0];
}
- if (
- !target!.getAbility().isCopiable &&
- // Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it
- !(pokemon.hasAbility(AbilityId.TRACE) && target!.getAbility().id === AbilityId.WONDER_GUARD)
- ) {
- return false;
- }
-
- this.target = target!;
- this.targetAbilityName = allAbilities[target!.getAbility().id].name;
+ this.target = target;
+ this.targetAbilityName = allAbilities[target.getAbility().id].name;
return true;
}
override apply({ pokemon, simulated }: AbAttrBaseParams): void {
- if (!simulated) {
- pokemon.setTempAbility(this.target!.getAbility());
- setAbilityRevealed(this.target!);
+ // Protect against this somehow being called before canApply by ensuring target is defined
+ if (!simulated && this.target) {
+ pokemon.setTempAbility(this.target.getAbility());
+ setAbilityRevealed(this.target);
pokemon.updateInfo();
}
}
@@ -3158,7 +3148,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
// 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;
+ return;
}
const mon = targets[user.randBattleSeedInt(targets.length)];
@@ -3180,7 +3170,6 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
/**
* Reverts weather-based forms to their normal forms when the user is summoned.
* Used by Cloud Nine and Air Lock.
- * @extends PostSummonAbAttr
*/
export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAttr {
override canApply(_params: AbAttrBaseParams): boolean {
@@ -3200,7 +3189,6 @@ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAtt
/**
* Triggers weather-based form change when summoned into an active weather.
* Used by Forecast and Flower Gift.
- * @extends PostSummonAbAttr
*/
export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
private ability: AbilityId;
@@ -3251,10 +3239,10 @@ export class CommanderAbAttr extends AbAttr {
// TODO: Should this work with X + Dondozo fusions?
const ally = pokemon.getAlly();
return (
- globalScene.currentBattle?.double &&
- !isNullOrUndefined(ally) &&
- ally.species.speciesId === SpeciesId.DONDOZO &&
- !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
+ globalScene.currentBattle?.double
+ && !isNullOrUndefined(ally)
+ && ally.species.speciesId === SpeciesId.DONDOZO
+ && !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
);
}
@@ -3320,8 +3308,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
switch (weatherType) {
case WeatherType.HARSH_SUN:
if (
- pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
- globalScene
+ pokemon.hasAbility(AbilityId.DESOLATE_LAND)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
@@ -3331,8 +3319,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
break;
case WeatherType.HEAVY_RAIN:
if (
- pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
- globalScene
+ pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
@@ -3342,8 +3330,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
break;
case WeatherType.STRONG_WINDS:
if (
- pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
- globalScene
+ pokemon.hasAbility(AbilityId.DELTA_STREAM)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
@@ -3382,7 +3370,6 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
/**
* Attribute for form changes that occur on switching out
- * @see {@linkcode applyPreSwitchOut}
*/
export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr {
private formFunc: (p: Pokemon) => number;
@@ -3428,8 +3415,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
switch (weatherType) {
case WeatherType.HARSH_SUN:
if (
- pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
- globalScene
+ pokemon.hasAbility(AbilityId.DESOLATE_LAND)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
@@ -3439,8 +3426,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
break;
case WeatherType.HEAVY_RAIN:
if (
- pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
- globalScene
+ pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
@@ -3450,8 +3437,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
break;
case WeatherType.STRONG_WINDS:
if (
- pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
- globalScene
+ pokemon.hasAbility(AbilityId.DELTA_STREAM)
+ && globalScene
.getField(true)
.filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
@@ -3576,8 +3563,8 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
this.protectedStat = protectedStat;
}
- override canApply({ stat }: PreStatStageChangeAbAttrParams): boolean {
- return isNullOrUndefined(this.protectedStat) || stat === this.protectedStat;
+ override canApply({ stat, cancelled }: PreStatStageChangeAbAttrParams): boolean {
+ return !cancelled.value && (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat);
}
/**
@@ -3668,8 +3655,11 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
this.immuneEffects = immuneEffects;
}
- override canApply({ effect }: PreSetStatusAbAttrParams): boolean {
- return (this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) || this.immuneEffects.includes(effect);
+ override canApply({ effect, cancelled }: PreSetStatusAbAttrParams): boolean {
+ return (
+ !cancelled.value
+ && ((this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) || this.immuneEffects.includes(effect))
+ );
}
/**
@@ -3680,7 +3670,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
}
override getTriggerMessage({ pokemon, effect }: PreSetStatusAbAttrParams, abilityName: string): string {
- return this.immuneEffects.length
+ return this.immuneEffects.length > 0
? i18next.t("abilityTriggers:statusEffectImmunityWithName", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
@@ -3719,7 +3709,8 @@ export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBasePar
/**
* Provides immunity to status effects to the user's field.
*/
-export class UserFieldStatusEffectImmunityAbAttr extends AbAttr {
+export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr {
+ private declare readonly _: never;
protected immuneEffects: StatusEffect[];
/**
@@ -3734,17 +3725,13 @@ export class UserFieldStatusEffectImmunityAbAttr extends AbAttr {
override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean {
return (
- (!cancelled.value && this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) ||
- this.immuneEffects.includes(effect)
+ (!cancelled.value && this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT)
+ || this.immuneEffects.includes(effect)
);
}
- /**
- * Set the `cancelled` value to true, indicating that the status effect is prevented.
- */
- override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void {
- cancelled.value = true;
- }
+ // declare here to allow typescript to allow us to override `canApply` method without adjusting params
+ declare apply: (params: UserFieldStatusEffectImmunityAbAttrParams) => void;
}
/**
@@ -3775,14 +3762,7 @@ export class ConditionalUserFieldStatusEffectImmunityAbAttr extends UserFieldSta
* @returns Whether the ability can be applied to cancel the status effect.
*/
override canApply(params: UserFieldStatusEffectImmunityAbAttrParams): boolean {
- return this.condition(params.target, params.source) && super.canApply(params);
- }
-
- /**
- * Set the `cancelled` value to true, indicating that the status effect is prevented.
- */
- override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void {
- cancelled.value = true;
+ return !params.cancelled.value && this.condition(params.target, params.source) && super.canApply(params);
}
}
@@ -3821,9 +3801,9 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA
return false;
}
return (
- !cancelled.value &&
- (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) &&
- this.condition(target)
+ !cancelled.value
+ && (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat)
+ && this.condition(target)
);
}
@@ -4018,20 +3998,16 @@ export class ConditionalCritAbAttr extends AbAttr {
}
}
-export class BlockNonDirectDamageAbAttr extends AbAttr {
+export class BlockNonDirectDamageAbAttr extends CancelInteractionAbAttr {
constructor() {
super(false);
}
-
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
}
/**
* This attribute will block any status damage that you put in the parameter.
*/
-export class BlockStatusDamageAbAttr extends AbAttr {
+export class BlockStatusDamageAbAttr extends CancelInteractionAbAttr {
private effects: StatusEffect[];
/**
@@ -4043,20 +4019,12 @@ export class BlockStatusDamageAbAttr extends AbAttr {
this.effects = effects;
}
- override canApply({ pokemon }: AbAttrParamsWithCancel): boolean {
- return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect);
- }
-
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
+ override canApply({ pokemon, cancelled }: AbAttrParamsWithCancel): boolean {
+ return !cancelled.value && !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect);
}
}
-export class BlockOneHitKOAbAttr extends AbAttr {
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
-}
+export class BlockOneHitKOAbAttr extends CancelInteractionAbAttr {}
export interface ChangeMovePriorityAbAttrParams extends AbAttrBaseParams {
/** The move being used */
@@ -4130,12 +4098,12 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr {
this.weatherTypes = weatherTypes;
}
- override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean {
- if (!weather) {
+ override canApply({ weather, cancelled }: PreWeatherEffectAbAttrParams): boolean {
+ if (!weather || cancelled.value) {
return false;
}
const weatherType = weather.weatherType;
- return !this.weatherTypes.length || this.weatherTypes.includes(weatherType);
+ return this.weatherTypes.length === 0 || this.weatherTypes.includes(weatherType);
}
override apply({ cancelled }: PreWeatherEffectAbAttrParams): void {
@@ -4152,8 +4120,8 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
this.affectsImmutable = affectsImmutable;
}
- override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean {
- if (!weather) {
+ override canApply({ weather, cancelled }: PreWeatherEffectAbAttrParams): boolean {
+ if (!weather || cancelled.value) {
return false;
}
return this.affectsImmutable || weather.isImmutable();
@@ -4213,8 +4181,8 @@ function getAnticipationCondition(): AbAttrCondition {
}
// the move's base type (not accounting for variable type changes) is super effective
if (
- move.getMove().is("AttackMove") &&
- pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
+ move.getMove().is("AttackMove")
+ && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
) {
return true;
}
@@ -4225,14 +4193,14 @@ function getAnticipationCondition(): AbAttrCondition {
// edge case for hidden power, type is computed
if (move.getMove().id === MoveId.HIDDEN_POWER) {
const iv_val = Math.floor(
- (((opponent.ivs[Stat.HP] & 1) +
- (opponent.ivs[Stat.ATK] & 1) * 2 +
- (opponent.ivs[Stat.DEF] & 1) * 4 +
- (opponent.ivs[Stat.SPD] & 1) * 8 +
- (opponent.ivs[Stat.SPATK] & 1) * 16 +
- (opponent.ivs[Stat.SPDEF] & 1) * 32) *
- 15) /
- 63,
+ (((opponent.ivs[Stat.HP] & 1)
+ + (opponent.ivs[Stat.ATK] & 1) * 2
+ + (opponent.ivs[Stat.DEF] & 1) * 4
+ + (opponent.ivs[Stat.SPD] & 1) * 8
+ + (opponent.ivs[Stat.SPATK] & 1) * 16
+ + (opponent.ivs[Stat.SPDEF] & 1) * 32)
+ * 15)
+ / 63,
);
const type = [
@@ -4268,8 +4236,8 @@ function getAnticipationCondition(): AbAttrCondition {
* Creates an ability condition that causes the ability to fail if that ability
* has already been used by that pokemon that battle. It requires an ability to
* be specified due to current limitations in how conditions on abilities work.
- * @param {AbilityId} ability The ability to check if it's already been applied
- * @returns {AbAttrCondition} The condition
+ * @param ability The ability to check if it's already been applied
+ * @returns The condition
*/
function getOncePerBattleCondition(ability: AbilityId): AbAttrCondition {
return (pokemon: Pokemon) => {
@@ -4299,9 +4267,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
} else if (move?.getMove().hasAttr("OneHitKOAttr")) {
movePower = 150;
} else if (
- move?.getMove().id === MoveId.COUNTER ||
- move?.getMove().id === MoveId.MIRROR_COAT ||
- move?.getMove().id === MoveId.METAL_BURST
+ move?.getMove().id === MoveId.COUNTER
+ || move?.getMove().id === MoveId.MIRROR_COAT
+ || move?.getMove().id === MoveId.METAL_BURST
) {
movePower = 120;
} else if (move?.getMove().power === -1) {
@@ -4660,7 +4628,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
/**
* @param procChance - function providing chance to restore an item
- * @see {@linkcode createEatenBerry()}
+ * @see {@linkcode createEatenBerry}
*/
constructor(private procChance: (pokemon: Pokemon) => number) {
super();
@@ -4677,7 +4645,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
this.berriesUnderCap = pokemon.battleData.berriesEaten.filter(bt => !cappedBerries.has(bt));
- if (!this.berriesUnderCap.length) {
+ if (this.berriesUnderCap.length === 0) {
return false;
}
@@ -4743,7 +4711,7 @@ export class CudChewConsumeBerryAbAttr extends AbAttr {
* @returns `true` if the pokemon ate anything last turn
*/
override canApply({ pokemon }: AbAttrBaseParams): boolean {
- return !!pokemon.summonData.berriesEatenLast.length;
+ return pokemon.summonData.berriesEatenLast.length > 0;
}
override apply({ pokemon }: AbAttrBaseParams): void {
@@ -4812,7 +4780,7 @@ export class MoodyAbAttr extends PostTurnAbAttr {
if (!simulated) {
if (canRaise.length > 0) {
const raisedStat = canRaise[pokemon.randBattleSeedInt(canRaise.length)];
- canLower = canRaise.filter(s => s !== raisedStat);
+ canLower = canLower.filter(s => s !== raisedStat);
globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [raisedStat], 2);
}
if (canLower.length > 0) {
@@ -4892,9 +4860,9 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
.getOpponents()
.some(
opp =>
- (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) &&
- !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") &&
- !opp.switchOutStatus,
+ (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE))
+ && !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
+ && !opp.switchOutStatus,
);
}
@@ -4925,7 +4893,6 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
/**
* Grabs the last failed Pokeball used
* @sealed
- * @see {@linkcode applyPostTurn}
*/
export class FetchBallAbAttr extends PostTurnAbAttr {
override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean {
@@ -5015,7 +4982,6 @@ export class PostMoveUsedAbAttr extends AbAttr {
/**
* Triggers after a dance move is used either by the opponent or the player
- * @extends PostMoveUsedAbAttr
*/
export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
override canApply({ source, pokemon }: PostMoveUsedAbAttrParams): boolean {
@@ -5028,8 +4994,8 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
];
// The move to replicate cannot come from the Dancer
return (
- source.getBattlerIndex() !== pokemon.getBattlerIndex() &&
- !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))
+ source.getBattlerIndex() !== pokemon.getBattlerIndex()
+ && !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))
);
}
@@ -5074,7 +5040,6 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
/**
* Triggers after the Pokemon loses or consumes an item
- * @extends AbAttr
*/
export class PostItemLostAbAttr extends AbAttr {
canApply(_params: Closed): boolean {
@@ -5150,15 +5115,11 @@ export class StatStageChangeCopyAbAttr extends AbAttr {
}
}
-export class BypassBurnDamageReductionAbAttr extends AbAttr {
+export class BypassBurnDamageReductionAbAttr extends CancelInteractionAbAttr {
private declare readonly _: never;
constructor() {
super(false);
}
-
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
}
export interface ReduceBurnDamageAbAttrParams extends AbAttrBaseParams {
@@ -5198,14 +5159,7 @@ export class DoubleBerryEffectAbAttr extends AbAttr {
* Attribute to prevent opposing berry use while on the field.
* Used by {@linkcode AbilityId.UNNERVE}, {@linkcode AbilityId.AS_ONE_GLASTRIER} and {@linkcode AbilityId.AS_ONE_SPECTRIER}
*/
-export class PreventBerryUseAbAttr extends AbAttr {
- /**
- * Prevent use of opposing berries.
- */
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
-}
+export class PreventBerryUseAbAttr extends CancelInteractionAbAttr {}
/**
* A Pokemon with this ability heals by a percentage of their maximum hp after eating a berry
@@ -5256,7 +5210,6 @@ type ArenaTrapCondition = (user: Pokemon, target: Pokemon) => boolean;
/**
* Base class for checking if a Pokemon is trapped by arena trap
- * @extends AbAttr
* @field {@linkcode arenaTrapCondition} Conditional for trapping abilities.
* For example, Magnet Pull will only activate if opponent is Steel type.
* @see {@linkcode applyCheckTrapped}
@@ -5289,12 +5242,12 @@ export interface CheckTrappedAbAttrParams extends AbAttrBaseParams {
export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean {
return (
- this.arenaTrapCondition(pokemon, opponent) &&
- !(
- opponent.getTypes(true).includes(PokemonType.GHOST) ||
- (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST))
- ) &&
- !opponent.hasAbility(AbilityId.RUN_AWAY)
+ this.arenaTrapCondition(pokemon, opponent)
+ && !(
+ opponent.getTypes(true).includes(PokemonType.GHOST)
+ || (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST))
+ )
+ && !opponent.hasAbility(AbilityId.RUN_AWAY)
);
}
@@ -5355,7 +5308,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
override canApply({ simulated, victory, pokemon }: PostBattleAbAttrParams): boolean {
const postBattleLoot = globalScene.currentBattle.postBattleLoot;
- if (!simulated && postBattleLoot.length && victory) {
+ if (!simulated && postBattleLoot.length > 0 && victory) {
this.randItem = randSeedItem(postBattleLoot);
return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1);
}
@@ -5454,9 +5407,9 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean {
if (
- move === undefined ||
- attacker === undefined ||
- !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
+ move === undefined
+ || attacker === undefined
+ || !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
) {
return false;
}
@@ -5592,7 +5545,6 @@ export interface ReduceStatusEffectDurationAbAttrParams extends AbAttrBaseParams
/**
* Used by Early Bird, makes the pokemon wake up faster
* @param statusEffect - The {@linkcode StatusEffect} to check for
- * @see {@linkcode apply}
* @sealed
*/
export class ReduceStatusEffectDurationAbAttr extends AbAttr {
@@ -5663,11 +5615,7 @@ export class IncreasePpAbAttr extends AbAttr {
}
/** @sealed */
-export class ForceSwitchOutImmunityAbAttr extends AbAttr {
- override apply({ cancelled }: AbAttrParamsWithCancel): void {
- cancelled.value = true;
- }
-}
+export class ForceSwitchOutImmunityAbAttr extends CancelInteractionAbAttr {}
export interface ReduceBerryUseThresholdAbAttrParams extends AbAttrBaseParams {
/** Holds the hp ratio for the berry to proc, which may be modified by ability application */
@@ -5746,8 +5694,8 @@ export class MoveAbilityBypassAbAttr extends AbAttr {
this.moveIgnoreFunc = moveIgnoreFunc || ((_pokemon, _move) => true);
}
- override canApply({ pokemon, move }: MoveAbilityBypassAbAttrParams): boolean {
- return this.moveIgnoreFunc(pokemon, move);
+ override canApply({ pokemon, move, cancelled }: MoveAbilityBypassAbAttrParams): boolean {
+ return !cancelled.value && this.moveIgnoreFunc(pokemon, move);
}
override apply({ cancelled }: MoveAbilityBypassAbAttrParams): void {
@@ -5841,8 +5789,8 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr {
this.allowedMoveTypes = allowedMoveTypes;
}
- override canApply({ moveType, defenderType }: IgnoreTypeImmunityAbAttrParams): boolean {
- return this.defenderType === defenderType && this.allowedMoveTypes.includes(moveType);
+ override canApply({ moveType, defenderType, cancelled }: IgnoreTypeImmunityAbAttrParams): boolean {
+ return !cancelled.value && this.defenderType === defenderType && this.allowedMoveTypes.includes(moveType);
}
override apply({ cancelled }: IgnoreTypeImmunityAbAttrParams): void {
@@ -5883,8 +5831,6 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr {
/**
* Gives money to the user after the battle.
- *
- * @extends PostBattleAbAttr
*/
export class MoneyAbAttr extends PostBattleAbAttr {
override canApply({ simulated, victory }: PostBattleAbAttrParams): boolean {
@@ -5988,7 +5934,6 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
/**
* Base class for defining {@linkcode Ability} attributes before summon
* (should use {@linkcode PostSummonAbAttr} for most ability)
- * @see {@linkcode applyPreSummon()}
*/
export class PreSummonAbAttr extends AbAttr {
private declare readonly _: never;
@@ -6031,9 +5976,9 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr {
// If the last conscious Pokémon in the party is a Terastallized Ogerpon or Terapagos, Illusion will not activate.
// Illusion will also not activate if the Pokémon with Illusion is Terastallized and the last Pokémon in the party is Ogerpon or Terapagos.
if (
- lastPokemon === pokemon ||
- ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) &&
- (lastPokemon.isTerastallized || pokemon.isTerastallized))
+ lastPokemon === pokemon
+ || ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS)
+ && (lastPokemon.isTerastallized || pokemon.isTerastallized))
) {
return false;
}
@@ -6187,7 +6132,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr {
override apply({ pokemon }: AbAttrBaseParams): void {
const currentTerrain = globalScene.arena.getTerrainType();
const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain);
- if (typeChange.length !== 0) {
+ if (typeChange.length > 0) {
if (pokemon.summonData.addedType && typeChange.includes(pokemon.summonData.addedType)) {
pokemon.summonData.addedType = null;
}
@@ -6257,7 +6202,7 @@ class ForceSwitchOutHelper {
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
*/
if (switchOutTarget.isPlayer()) {
- if (globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
+ if (globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length === 0) {
return false;
}
@@ -6278,7 +6223,7 @@ class ForceSwitchOutHelper {
* If yes, the Pokémon leaves the field and a new SwitchSummonPhase is initiated.
*/
} else if (globalScene.currentBattle.battleType !== BattleType.WILD) {
- if (globalScene.getEnemyParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
+ if (globalScene.getEnemyParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length === 0) {
return false;
}
if (switchOutTarget.hp > 0) {
@@ -6355,28 +6300,31 @@ class ForceSwitchOutHelper {
return !blockedByAbility.value;
}
- if (!player && globalScene.currentBattle.battleType === BattleType.WILD) {
- if (!globalScene.currentBattle.waveIndex && globalScene.currentBattle.waveIndex % 10 === 0) {
- return false;
- }
+ if (
+ !player
+ && globalScene.currentBattle.battleType === BattleType.WILD
+ && !globalScene.currentBattle.waveIndex
+ && globalScene.currentBattle.waveIndex % 10 === 0
+ ) {
+ return false;
}
if (
- !player &&
- globalScene.currentBattle.isBattleMysteryEncounter() &&
- !globalScene.currentBattle.mysteryEncounter?.fleeAllowed
+ !player
+ && globalScene.currentBattle.isBattleMysteryEncounter()
+ && !globalScene.currentBattle.mysteryEncounter?.fleeAllowed
) {
return false;
}
const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
return (
- (!player && globalScene.currentBattle.battleType === BattleType.WILD) ||
- party.filter(
+ (!player && globalScene.currentBattle.battleType === BattleType.WILD)
+ || party.filter(
p =>
- p.isAllowedInBattle() &&
- !p.isOnField() &&
- (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot),
+ p.isAllowedInBattle()
+ && !p.isOnField()
+ && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot),
).length > 0
);
}
@@ -6437,8 +6385,6 @@ export class PostDamageAbAttr extends AbAttr {
* and its opponents, and determines whether a forced switch-out should occur.
*
* Used by Wimp Out and Emergency Exit
- *
- * @see {@linkcode applyPostDamage}
* @sealed
*/
export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
@@ -6452,26 +6398,22 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
// TODO: Refactor to use more early returns
public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean {
- const moveHistory = pokemon.getMoveHistory();
// Will not activate when the Pokémon's HP is lowered by cutting its own HP
const forbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT];
- if (moveHistory.length > 0) {
- const lastMoveUsed = moveHistory[moveHistory.length - 1];
- if (forbiddenAttackingMoves.includes(lastMoveUsed.move)) {
- return false;
- }
+ const lastMoveUsed = pokemon.getLastXMoves()[0];
+ if (forbiddenAttackingMoves.includes(lastMoveUsed?.move)) {
+ return false;
}
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
const forbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW];
if (source) {
- const enemyMoveHistory = source.getMoveHistory();
- if (enemyMoveHistory.length > 0) {
- const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
+ const enemyLastMoveUsed = source.getLastXMoves()[0];
+ if (enemyLastMoveUsed) {
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
if (
- forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) ||
- (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
+ forbiddenDefendingMoves.includes(enemyLastMoveUsed.move)
+ || (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
) {
return false;
// Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force.
@@ -6760,8 +6702,8 @@ function getPokemonWithWeatherBasedForms() {
.getField(true)
.filter(
p =>
- (p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) ||
- (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM),
+ (p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM)
+ || (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM),
);
}
@@ -7061,6 +7003,7 @@ export function initAbilities() {
.attr(StatMultiplierAbAttr, Stat.SPATK, 1.5)
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
new Ability(AbilityId.QUICK_FEET, 4)
+ // TODO: This should ignore the speed drop, not manually undo it
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, StatMultiplierAbAttr, Stat.SPD, 2)
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(AbilityId.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5),
new Ability(AbilityId.NORMALIZE, 4)
@@ -7460,7 +7403,7 @@ export function initAbilities() {
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
.attr(FormBlockDamageAbAttr,
(target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE,
- (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
+ (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }),
(pokemon) => toDmgValue(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.attr(PostFaintFormChangeAbAttr, () => 0)
@@ -7633,7 +7576,7 @@ export function initAbilities() {
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
.attr(FormBlockDamageAbAttr,
(target, _user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE,
- (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }))
+ (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.uncopiable()
.unreplaceable()
diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts
index b03cc5b951a..22955e0a9ac 100644
--- a/src/data/arena-tag.ts
+++ b/src/data/arena-tag.ts
@@ -23,7 +23,7 @@ import type { Arena } from "#field/arena";
import type { Pokemon } from "#field/pokemon";
import type {
ArenaScreenTagType,
- ArenaTagTypeData,
+ ArenaTagData,
EntryHazardTagType,
RoomArenaTagType,
SerializableArenaTagType,
@@ -471,7 +471,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
const move = allMoves[moveId];
const effectPhase = globalScene.phaseManager.getCurrentPhase();
- if (effectPhase?.is("MoveEffectPhase")) {
+ if (effectPhase.is("MoveEffectPhase")) {
const attacker = effectPhase.getUserPokemon();
if (attacker) {
return move.getPriority(attacker) > 0;
@@ -572,10 +572,10 @@ class MatBlockTag extends ConditionalProtectTag {
const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
const move = allMoves[moveId];
return (
- move.category === MoveCategory.STATUS &&
- move.moveTarget !== MoveTarget.ENEMY_SIDE &&
- move.moveTarget !== MoveTarget.BOTH_SIDES &&
- move.moveTarget !== MoveTarget.ALL
+ move.category === MoveCategory.STATUS
+ && move.moveTarget !== MoveTarget.ENEMY_SIDE
+ && move.moveTarget !== MoveTarget.BOTH_SIDES
+ && move.moveTarget !== MoveTarget.ALL
);
};
@@ -765,9 +765,10 @@ export abstract class EntryHazardTag extends SerializableArenaTag {
const source = this.getSourcePokemon();
if (!source) {
console.warn(
- "Failed to get source Pokemon for AernaTrapTag on add message!" +
- `\nTag type: ${this.tagType}` +
- `\nPID: ${this.sourceId}`,
+ // biome-ignore lint/complexity/noUselessStringConcat: Rule bugs out with operator linebreaks set to `before`
+ "Failed to get source Pokemon for AernaTrapTag on add message!"
+ + `\nTag type: ${this.tagType}`
+ + `\nPID: ${this.sourceId}`,
);
return;
}
@@ -937,7 +938,7 @@ class StealthRockTag extends DamagingTrapTag {
protected override getTriggerMessage(pokemon: Pokemon): string {
return i18next.t("arenaTag:stealthRockActivateTrap", {
- pokemonName: getPokemonNameWithAffix(pokemon),
+ pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
});
}
@@ -1131,7 +1132,7 @@ class ImprisonTag extends EntryHazardTag {
/**
* This applies the effects of Imprison to any opposing Pokemon that switch into the field while the source Pokemon is still active
- * @param {Pokemon} pokemon the Pokemon Imprison is applied to
+ * @param pokemon the Pokemon Imprison is applied to
* @returns `true`
*/
override activateTrap(pokemon: Pokemon): boolean {
@@ -1663,7 +1664,7 @@ export function getArenaTag(
* @param source - An arena tag
* @returns The valid arena tag
*/
-export function loadArenaTag(source: ArenaTag | ArenaTagTypeData | { tagType: ArenaTagType.NONE }): ArenaTag {
+export function loadArenaTag(source: ArenaTag | ArenaTagData | { tagType: ArenaTagType.NONE }): ArenaTag {
if (source.tagType === ArenaTagType.NONE) {
return new NoneTag();
}
diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts
index c0c57cb15fb..b253b0ded6e 100644
--- a/src/data/balance/biomes.ts
+++ b/src/data/balance/biomes.ts
@@ -7649,7 +7649,7 @@ export function initBiomes() {
? pokemonEvolutions[speciesId]
: [];
- if (!biomeEntries.filter(b => b[0] !== BiomeId.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== BiomeId.END).length).length) { // TODO: is the bang on the `find()` correct?
+ if (biomeEntries.filter(b => b[0] !== BiomeId.END).length === 0&& speciesEvolutions.filter(es => ((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== BiomeId.END).length > 0).length === 0) { // TODO: is the bang on the `find()` correct?
uncatchableSpecies.push(speciesId);
}
diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts
index 2d2e981219d..1184de70b07 100644
--- a/src/data/balance/egg-moves.ts
+++ b/src/data/balance/egg-moves.ts
@@ -1,9 +1,12 @@
-import { allMoves } from "#data/data-lists";
+//! DO NOT EDIT THIS FILE - CREATED BY THE `eggMoves:parse` script automatically
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
-import { getEnumKeys, getEnumValues } from "#utils/enums";
-import { toTitleCase } from "#utils/strings";
+/**
+ * An object mapping all base form {@linkcode SpeciesId}s to an array of {@linkcode MoveId}s corresponding
+ * to their current egg moves.
+ * Generated by the `eggMoves:parse` script using a CSV sourced from the current Balance Team spreadsheet.
+ */
export const speciesEggMoves = {
[SpeciesId.BULBASAUR]: [ MoveId.SAPPY_SEED, MoveId.MALIGNANT_CHAIN, MoveId.EARTH_POWER, MoveId.MATCHA_GOTCHA ],
[SpeciesId.CHARMANDER]: [ MoveId.DRAGON_DANCE, MoveId.AEROBLAST, MoveId.EARTH_POWER, MoveId.BITTER_BLADE ],
@@ -187,7 +190,7 @@ export const speciesEggMoves = {
[SpeciesId.WYNAUT]: [ MoveId.RECOVER, MoveId.SHED_TAIL, MoveId.TAUNT, MoveId.COMEUPPANCE ],
[SpeciesId.SNORUNT]: [ MoveId.SPARKLY_SWIRL, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.BLOOD_MOON ],
[SpeciesId.SPHEAL]: [ MoveId.FLIP_TURN, MoveId.FREEZE_DRY, MoveId.SLACK_OFF, MoveId.STEAM_ERUPTION ],
- [SpeciesId.CLAMPERL]: [ MoveId.SHELL_SIDE_ARM, MoveId.BOUNCY_BUBBLE, MoveId.FREEZE_DRY, MoveId.STEAM_ERUPTION ],
+ [SpeciesId.CLAMPERL]: [ MoveId.SHELL_SIDE_ARM, MoveId.SNIPE_SHOT, MoveId.GIGA_DRAIN, MoveId.BOUNCY_BUBBLE ],
[SpeciesId.RELICANTH]: [ MoveId.DRAGON_DANCE, MoveId.SHORE_UP, MoveId.WAVE_CRASH, MoveId.DIAMOND_STORM ],
[SpeciesId.LUVDISC]: [ MoveId.BATON_PASS, MoveId.HEART_SWAP, MoveId.GLITZY_GLOW, MoveId.REVIVAL_BLESSING ],
[SpeciesId.BAGON]: [ MoveId.HEADLONG_RUSH, MoveId.FIRE_LASH, MoveId.DRAGON_DANCE, MoveId.DRAGON_DARTS ],
@@ -582,55 +585,4 @@ export const speciesEggMoves = {
[SpeciesId.PALDEA_TAUROS]: [ MoveId.NO_RETREAT, MoveId.BLAZING_TORQUE, MoveId.AQUA_STEP, MoveId.THUNDEROUS_KICK ],
[SpeciesId.PALDEA_WOOPER]: [ MoveId.STONE_AXE, MoveId.RECOVER, MoveId.BANEFUL_BUNKER, MoveId.BARB_BARRAGE ],
[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 enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_") as keyof typeof SpeciesId;
- // TODO: This should use reverse mapping instead of `indexOf`
- const species = speciesValues[speciesNames.indexOf(enumSpeciesName)];
-
- const eggMoves: MoveId[] = [];
-
- for (let m = 0; m < 4; m++) {
- const moveName = cols[m + 1].trim();
- const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1;
- if (moveIndex === -1) {
- console.warn(moveName, "could not be parsed");
- }
-
- eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE);
- }
-
- if (eggMoves.every(m => m === MoveId.NONE)) {
- console.warn(`Species ${toTitleCase(SpeciesId[species])} could not be parsed, excluding from output...`)
- } else {
- output += `[SpeciesId.${SpeciesId[species]}]: [ ${eggMoves.map(m => `MoveId.${MoveId[m]}`).join(", ")} ],\n`;
- }
- }
-
- console.log(output);
-}
-
-export function initEggMoves() {
- const eggMovesStr = "";
- if (eggMovesStr) {
- setTimeout(() => {
- parseEggMoves(eggMovesStr);
- }, 1000);
- }
-}
+} satisfies Partial>;
diff --git a/src/data/balance/passives.ts b/src/data/balance/passives.ts
index 0e34917fd80..1297ad71c36 100644
--- a/src/data/balance/passives.ts
+++ b/src/data/balance/passives.ts
@@ -402,7 +402,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.SPHEAL]: { 0: AbilityId.UNAWARE },
[SpeciesId.SEALEO]: { 0: AbilityId.UNAWARE },
[SpeciesId.WALREIN]: { 0: AbilityId.UNAWARE },
- [SpeciesId.CLAMPERL]: { 0: AbilityId.DAUNTLESS_SHIELD },
+ [SpeciesId.CLAMPERL]: { 0: AbilityId.OVERCOAT },
[SpeciesId.GOREBYSS]: { 0: AbilityId.ARENA_TRAP },
[SpeciesId.HUNTAIL]: { 0: AbilityId.ARENA_TRAP },
[SpeciesId.RELICANTH]: { 0: AbilityId.PRIMORDIAL_SEA },
diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts
index d42bce041c2..d364dc036b1 100644
--- a/src/data/balance/pokemon-evolutions.ts
+++ b/src/data/balance/pokemon-evolutions.ts
@@ -1,3 +1,4 @@
+import { defaultStarterSpecies } from "#app/constants";
import { globalScene } from "#app/global-scene";
import { speciesStarterCosts } from "#balance/starters";
import { allMoves } from "#data/data-lists";
@@ -76,7 +77,8 @@ export enum EvolutionItem {
LEADERS_CREST
}
-type TyrogueMove = MoveId.LOW_SWEEP | MoveId.MACH_PUNCH | MoveId.RAPID_SPIN;
+const tyrogueMoves = [MoveId.LOW_SWEEP, MoveId.MACH_PUNCH, MoveId.RAPID_SPIN] as const;
+type TyrogueMove = (typeof tyrogueMoves)[number];
/**
* Pokemon Evolution tuple type consisting of:
@@ -134,7 +136,7 @@ export class SpeciesEvolutionCondition {
case EvoCondKey.FRIENDSHIP:
return i18next.t("pokemonEvolutions:friendship");
case EvoCondKey.TIME:
- return i18next.t(`pokemonEvolutions:timeOfDay.${toCamelCase(TimeOfDay[cond.time[cond.time.length - 1]])}`); // For Day and Night evos, the key we want goes last
+ return i18next.t(`pokemonEvolutions:timeOfDay.${toCamelCase(TimeOfDay[cond.time.at(-1)!])}`); // For Day and Night evos, the key we want goes last
case EvoCondKey.MOVE_TYPE:
return i18next.t("pokemonEvolutions:moveType", {type: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[cond.pkmnType])}`)});
case EvoCondKey.PARTY_TYPE:
@@ -191,7 +193,10 @@ export class SpeciesEvolutionCondition {
case EvoCondKey.WEATHER:
return cond.weather.includes(globalScene.arena.getWeatherType());
case EvoCondKey.TYROGUE:
- return pokemon.getMoveset(true).find(m => m.moveId as TyrogueMove)?.moveId === cond.move;
+ return (
+ pokemon.getMoveset(true).find(m => (tyrogueMoves as readonly MoveId[]).includes(m.moveId))?.moveId
+ === cond.move
+ );
case EvoCondKey.NATURE:
return cond.nature.includes(pokemon.getNature());
case EvoCondKey.RANDOM_FORM: {
@@ -654,18 +659,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null),
new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}, SpeciesWildEvolutionDelay.LONG),
],
- [SpeciesId.SURSKIT]: [
- new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)
- ],
- [SpeciesId.SHROOMISH]: [
- new SpeciesEvolution(SpeciesId.BRELOOM, 23, null, null)
- ],
- [SpeciesId.SLAKOTH]: [
- new SpeciesEvolution(SpeciesId.VIGOROTH, 18, null, null)
- ],
- [SpeciesId.VIGOROTH]: [
- new SpeciesEvolution(SpeciesId.SLAKING, 36, null, null)
- ],
+ [SpeciesId.SURSKIT]: [new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)],
+ [SpeciesId.SHROOMISH]: [new SpeciesEvolution(SpeciesId.BRELOOM, 23, null, null)],
+ [SpeciesId.SLAKOTH]: [new SpeciesEvolution(SpeciesId.VIGOROTH, 18, null, null)],
+ [SpeciesId.VIGOROTH]: [new SpeciesEvolution(SpeciesId.SLAKING, 36, null, null)],
[SpeciesId.NINCADA]: [
new SpeciesEvolution(SpeciesId.NINJASK, 20, null, null),
new SpeciesEvolution(SpeciesId.SHEDINJA, 20, null, {key: EvoCondKey.SHEDINJA})
@@ -743,66 +740,26 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null),
new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}, SpeciesWildEvolutionDelay.LONG),
],
- [SpeciesId.SPHEAL]: [
- new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)
- ],
- [SpeciesId.SEALEO]: [
- new SpeciesEvolution(SpeciesId.WALREIN, 44, null, null)
- ],
- [SpeciesId.BAGON]: [
- new SpeciesEvolution(SpeciesId.SHELGON, 30, null, null)
- ],
- [SpeciesId.SHELGON]: [
- new SpeciesEvolution(SpeciesId.SALAMENCE, 50, null, null)
- ],
- [SpeciesId.BELDUM]: [
- new SpeciesEvolution(SpeciesId.METANG, 20, null, null)
- ],
- [SpeciesId.METANG]: [
- new SpeciesEvolution(SpeciesId.METAGROSS, 45, null, null)
- ],
- [SpeciesId.TURTWIG]: [
- new SpeciesEvolution(SpeciesId.GROTLE, 18, null, null)
- ],
- [SpeciesId.GROTLE]: [
- new SpeciesEvolution(SpeciesId.TORTERRA, 32, null, null)
- ],
- [SpeciesId.CHIMCHAR]: [
- new SpeciesEvolution(SpeciesId.MONFERNO, 14, null, null)
- ],
- [SpeciesId.MONFERNO]: [
- new SpeciesEvolution(SpeciesId.INFERNAPE, 36, null, null)
- ],
- [SpeciesId.PIPLUP]: [
- new SpeciesEvolution(SpeciesId.PRINPLUP, 16, null, null)
- ],
- [SpeciesId.PRINPLUP]: [
- new SpeciesEvolution(SpeciesId.EMPOLEON, 36, null, null)
- ],
- [SpeciesId.STARLY]: [
- new SpeciesEvolution(SpeciesId.STARAVIA, 14, null, null)
- ],
- [SpeciesId.STARAVIA]: [
- new SpeciesEvolution(SpeciesId.STARAPTOR, 34, null, null)
- ],
- [SpeciesId.BIDOOF]: [
- new SpeciesEvolution(SpeciesId.BIBAREL, 15, null, null)
- ],
- [SpeciesId.KRICKETOT]: [
- new SpeciesEvolution(SpeciesId.KRICKETUNE, 10, null, null)
- ],
- [SpeciesId.SHINX]: [
- new SpeciesEvolution(SpeciesId.LUXIO, 15, null, null)
- ],
- [SpeciesId.LUXIO]: [
- new SpeciesEvolution(SpeciesId.LUXRAY, 30, null, null)
- ],
- [SpeciesId.CRANIDOS]: [
- new SpeciesEvolution(SpeciesId.RAMPARDOS, 30, null, null)
- ],
- [SpeciesId.SHIELDON]: [
- new SpeciesEvolution(SpeciesId.BASTIODON, 30, null, null)
- ],
+ [SpeciesId.SPHEAL]: [new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)],
+ [SpeciesId.SEALEO]: [new SpeciesEvolution(SpeciesId.WALREIN, 44, null, null)],
+ [SpeciesId.BAGON]: [new SpeciesEvolution(SpeciesId.SHELGON, 30, null, null)],
+ [SpeciesId.SHELGON]: [new SpeciesEvolution(SpeciesId.SALAMENCE, 50, null, null)],
+ [SpeciesId.BELDUM]: [new SpeciesEvolution(SpeciesId.METANG, 20, null, null)],
+ [SpeciesId.METANG]: [new SpeciesEvolution(SpeciesId.METAGROSS, 45, null, null)],
+ [SpeciesId.TURTWIG]: [new SpeciesEvolution(SpeciesId.GROTLE, 18, null, null)],
+ [SpeciesId.GROTLE]: [new SpeciesEvolution(SpeciesId.TORTERRA, 32, null, null)],
+ [SpeciesId.CHIMCHAR]: [new SpeciesEvolution(SpeciesId.MONFERNO, 14, null, null)],
+ [SpeciesId.MONFERNO]: [new SpeciesEvolution(SpeciesId.INFERNAPE, 36, null, null)],
+ [SpeciesId.PIPLUP]: [new SpeciesEvolution(SpeciesId.PRINPLUP, 16, null, null)],
+ [SpeciesId.PRINPLUP]: [new SpeciesEvolution(SpeciesId.EMPOLEON, 36, null, null)],
+ [SpeciesId.STARLY]: [new SpeciesEvolution(SpeciesId.STARAVIA, 14, null, null)],
+ [SpeciesId.STARAVIA]: [new SpeciesEvolution(SpeciesId.STARAPTOR, 34, null, null)],
+ [SpeciesId.BIDOOF]: [new SpeciesEvolution(SpeciesId.BIBAREL, 15, null, null)],
+ [SpeciesId.KRICKETOT]: [new SpeciesEvolution(SpeciesId.KRICKETUNE, 10, null, null)],
+ [SpeciesId.SHINX]: [new SpeciesEvolution(SpeciesId.LUXIO, 15, null, null)],
+ [SpeciesId.LUXIO]: [new SpeciesEvolution(SpeciesId.LUXRAY, 30, null, null)],
+ [SpeciesId.CRANIDOS]: [new SpeciesEvolution(SpeciesId.RAMPARDOS, 30, null, null)],
+ [SpeciesId.SHIELDON]: [new SpeciesEvolution(SpeciesId.BASTIODON, 30, null, null)],
[SpeciesId.BURMY]: [
new SpeciesEvolution(SpeciesId.MOTHIM, 20, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}),
new SpeciesEvolution(SpeciesId.WORMADAM, 20, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE})
@@ -1868,7 +1825,7 @@ export const pokemonPrevolutions: PokemonPrevolutions = {};
export function initPokemonPrevolutions(): void {
// TODO: Why do we have empty strings in our array?
- const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ];
+ const megaFormKeys = [SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y];
for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) {
for (const ev of evolutions) {
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
@@ -1883,6 +1840,15 @@ export function initPokemonPrevolutions(): void {
// TODO: This may cause funny business for double starters such as Pichu/Pikachu
export const pokemonStarters: PokemonPrevolutions = {};
+/**
+ * The default species and all their evolutions
+ */
+export const defaultStarterSpeciesAndEvolutions: SpeciesId[] = defaultStarterSpecies.flatMap(id => {
+ const stage2ids = pokemonEvolutions[id]?.map(e => e.speciesId) ?? [];
+ const stage3ids = stage2ids.flatMap(s2id => pokemonEvolutions[s2id]?.map(e => e.speciesId) ?? []);
+ return [id, ...stage2ids, ...stage3ids];
+});
+
export function initPokemonStarters(): void {
const starterKeys = Object.keys(pokemonPrevolutions);
starterKeys.forEach(pk => {
diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts
index bd7cf401ad1..f7c330e3d95 100644
--- a/src/data/balance/tms.ts
+++ b/src/data/balance/tms.ts
@@ -68875,27 +68875,27 @@ interface TmPoolTiers {
export const tmPoolTiers: TmPoolTiers = {
[MoveId.MEGA_PUNCH]: ModifierTier.GREAT,
- [MoveId.PAY_DAY]: ModifierTier.ULTRA,
+ [MoveId.PAY_DAY]: ModifierTier.COMMON,
[MoveId.FIRE_PUNCH]: ModifierTier.GREAT,
[MoveId.ICE_PUNCH]: ModifierTier.GREAT,
[MoveId.THUNDER_PUNCH]: ModifierTier.GREAT,
- [MoveId.SWORDS_DANCE]: ModifierTier.COMMON,
+ [MoveId.SWORDS_DANCE]: ModifierTier.GREAT,
[MoveId.CUT]: ModifierTier.COMMON,
- [MoveId.FLY]: ModifierTier.COMMON,
+ [MoveId.FLY]: ModifierTier.GREAT,
[MoveId.MEGA_KICK]: ModifierTier.GREAT,
[MoveId.BODY_SLAM]: ModifierTier.GREAT,
[MoveId.TAKE_DOWN]: ModifierTier.GREAT,
[MoveId.DOUBLE_EDGE]: ModifierTier.ULTRA,
- [MoveId.PIN_MISSILE]: ModifierTier.COMMON,
+ [MoveId.PIN_MISSILE]: ModifierTier.GREAT,
[MoveId.ROAR]: ModifierTier.COMMON,
[MoveId.FLAMETHROWER]: ModifierTier.ULTRA,
[MoveId.HYDRO_PUMP]: ModifierTier.ULTRA,
[MoveId.SURF]: ModifierTier.ULTRA,
[MoveId.ICE_BEAM]: ModifierTier.ULTRA,
[MoveId.BLIZZARD]: ModifierTier.ULTRA,
- [MoveId.PSYBEAM]: ModifierTier.GREAT,
+ [MoveId.PSYBEAM]: ModifierTier.COMMON,
[MoveId.HYPER_BEAM]: ModifierTier.ULTRA,
- [MoveId.LOW_KICK]: ModifierTier.COMMON,
+ [MoveId.LOW_KICK]: ModifierTier.GREAT,
[MoveId.COUNTER]: ModifierTier.COMMON,
[MoveId.STRENGTH]: ModifierTier.GREAT,
[MoveId.SOLAR_BEAM]: ModifierTier.ULTRA,
@@ -68907,9 +68907,9 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.DIG]: ModifierTier.GREAT,
[MoveId.TOXIC]: ModifierTier.GREAT,
[MoveId.PSYCHIC]: ModifierTier.ULTRA,
- [MoveId.AGILITY]: ModifierTier.COMMON,
+ [MoveId.AGILITY]: ModifierTier.GREAT,
[MoveId.NIGHT_SHADE]: ModifierTier.COMMON,
- [MoveId.SCREECH]: ModifierTier.COMMON,
+ [MoveId.SCREECH]: ModifierTier.GREAT,
[MoveId.DOUBLE_TEAM]: ModifierTier.COMMON,
[MoveId.CONFUSE_RAY]: ModifierTier.COMMON,
[MoveId.LIGHT_SCREEN]: ModifierTier.COMMON,
@@ -68921,7 +68921,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.FIRE_BLAST]: ModifierTier.ULTRA,
[MoveId.WATERFALL]: ModifierTier.GREAT,
[MoveId.SWIFT]: ModifierTier.COMMON,
- [MoveId.AMNESIA]: ModifierTier.COMMON,
+ [MoveId.AMNESIA]: ModifierTier.GREAT,
[MoveId.DREAM_EATER]: ModifierTier.GREAT,
[MoveId.LEECH_LIFE]: ModifierTier.ULTRA,
[MoveId.FLASH]: ModifierTier.COMMON,
@@ -68933,11 +68933,11 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.SUBSTITUTE]: ModifierTier.COMMON,
[MoveId.THIEF]: ModifierTier.GREAT,
[MoveId.SNORE]: ModifierTier.COMMON,
- [MoveId.CURSE]: ModifierTier.COMMON,
+ [MoveId.CURSE]: ModifierTier.GREAT,
[MoveId.REVERSAL]: ModifierTier.COMMON,
[MoveId.SPITE]: ModifierTier.COMMON,
[MoveId.PROTECT]: ModifierTier.COMMON,
- [MoveId.SCARY_FACE]: ModifierTier.COMMON,
+ [MoveId.SCARY_FACE]: ModifierTier.GREAT,
[MoveId.SLUDGE_BOMB]: ModifierTier.GREAT,
[MoveId.MUD_SLAP]: ModifierTier.COMMON,
[MoveId.SPIKES]: ModifierTier.COMMON,
@@ -68979,8 +68979,8 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.TORMENT]: ModifierTier.COMMON,
[MoveId.WILL_O_WISP]: ModifierTier.COMMON,
[MoveId.FACADE]: ModifierTier.GREAT,
- [MoveId.FOCUS_PUNCH]: ModifierTier.COMMON,
- [MoveId.NATURE_POWER]: ModifierTier.COMMON,
+ [MoveId.FOCUS_PUNCH]: ModifierTier.GREAT,
+ [MoveId.NATURE_POWER]: ModifierTier.GREAT,
[MoveId.CHARGE]: ModifierTier.COMMON,
[MoveId.TAUNT]: ModifierTier.COMMON,
[MoveId.HELPING_HAND]: ModifierTier.COMMON,
@@ -68993,7 +68993,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.ENDEAVOR]: ModifierTier.COMMON,
[MoveId.SKILL_SWAP]: ModifierTier.COMMON,
[MoveId.IMPRISON]: ModifierTier.COMMON,
- [MoveId.SECRET_POWER]: ModifierTier.COMMON,
+ [MoveId.SECRET_POWER]: ModifierTier.GREAT,
[MoveId.DIVE]: ModifierTier.GREAT,
[MoveId.FEATHER_DANCE]: ModifierTier.COMMON,
[MoveId.BLAZE_KICK]: ModifierTier.GREAT,
@@ -69001,12 +69001,12 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.BLAST_BURN]: ModifierTier.ULTRA,
[MoveId.HYDRO_CANNON]: ModifierTier.ULTRA,
[MoveId.WEATHER_BALL]: ModifierTier.COMMON,
- [MoveId.FAKE_TEARS]: ModifierTier.COMMON,
+ [MoveId.FAKE_TEARS]: ModifierTier.GREAT,
[MoveId.AIR_CUTTER]: ModifierTier.GREAT,
[MoveId.OVERHEAT]: ModifierTier.ULTRA,
[MoveId.ROCK_TOMB]: ModifierTier.GREAT,
- [MoveId.METAL_SOUND]: ModifierTier.COMMON,
- [MoveId.COSMIC_POWER]: ModifierTier.COMMON,
+ [MoveId.METAL_SOUND]: ModifierTier.GREAT,
+ [MoveId.COSMIC_POWER]: ModifierTier.GREAT,
[MoveId.SIGNAL_BEAM]: ModifierTier.GREAT,
[MoveId.SAND_TOMB]: ModifierTier.COMMON,
[MoveId.MUDDY_WATER]: ModifierTier.GREAT,
@@ -69016,10 +69016,10 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.IRON_DEFENSE]: ModifierTier.GREAT,
[MoveId.DRAGON_CLAW]: ModifierTier.ULTRA,
[MoveId.FRENZY_PLANT]: ModifierTier.ULTRA,
- [MoveId.BULK_UP]: ModifierTier.COMMON,
+ [MoveId.BULK_UP]: ModifierTier.GREAT,
[MoveId.BOUNCE]: ModifierTier.GREAT,
[MoveId.MUD_SHOT]: ModifierTier.GREAT,
- [MoveId.POISON_TAIL]: ModifierTier.GREAT,
+ [MoveId.POISON_TAIL]: ModifierTier.COMMON,
[MoveId.COVET]: ModifierTier.GREAT,
[MoveId.MAGICAL_LEAF]: ModifierTier.GREAT,
[MoveId.CALM_MIND]: ModifierTier.GREAT,
@@ -69047,7 +69047,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.TOXIC_SPIKES]: ModifierTier.GREAT,
[MoveId.FLARE_BLITZ]: ModifierTier.ULTRA,
[MoveId.AURA_SPHERE]: ModifierTier.GREAT,
- [MoveId.ROCK_POLISH]: ModifierTier.COMMON,
+ [MoveId.ROCK_POLISH]: ModifierTier.GREAT,
[MoveId.POISON_JAB]: ModifierTier.GREAT,
[MoveId.DARK_PULSE]: ModifierTier.GREAT,
[MoveId.AQUA_TAIL]: ModifierTier.GREAT,
@@ -69063,8 +69063,8 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.ENERGY_BALL]: ModifierTier.GREAT,
[MoveId.BRAVE_BIRD]: ModifierTier.ULTRA,
[MoveId.EARTH_POWER]: ModifierTier.ULTRA,
- [MoveId.GIGA_IMPACT]: ModifierTier.GREAT,
- [MoveId.NASTY_PLOT]: ModifierTier.COMMON,
+ [MoveId.GIGA_IMPACT]: ModifierTier.ULTRA,
+ [MoveId.NASTY_PLOT]: ModifierTier.GREAT,
[MoveId.AVALANCHE]: ModifierTier.GREAT,
[MoveId.SHADOW_CLAW]: ModifierTier.GREAT,
[MoveId.THUNDER_FANG]: ModifierTier.GREAT,
@@ -69084,7 +69084,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.IRON_HEAD]: ModifierTier.GREAT,
[MoveId.STONE_EDGE]: ModifierTier.ULTRA,
[MoveId.STEALTH_ROCK]: ModifierTier.COMMON,
- [MoveId.GRASS_KNOT]: ModifierTier.ULTRA,
+ [MoveId.GRASS_KNOT]: ModifierTier.GREAT,
[MoveId.BUG_BITE]: ModifierTier.GREAT,
[MoveId.CHARGE_BEAM]: ModifierTier.GREAT,
[MoveId.HONE_CLAWS]: ModifierTier.COMMON,
@@ -69102,7 +69102,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.FOUL_PLAY]: ModifierTier.ULTRA,
[MoveId.ROUND]: ModifierTier.COMMON,
[MoveId.ECHOED_VOICE]: ModifierTier.COMMON,
- [MoveId.STORED_POWER]: ModifierTier.COMMON,
+ [MoveId.STORED_POWER]: ModifierTier.GREAT,
[MoveId.ALLY_SWITCH]: ModifierTier.COMMON,
[MoveId.SCALD]: ModifierTier.GREAT,
[MoveId.HEX]: ModifierTier.GREAT,
@@ -69130,7 +69130,7 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.SNARL]: ModifierTier.COMMON,
[MoveId.PHANTOM_FORCE]: ModifierTier.ULTRA,
[MoveId.PETAL_BLIZZARD]: ModifierTier.GREAT,
- [MoveId.DISARMING_VOICE]: ModifierTier.GREAT,
+ [MoveId.DISARMING_VOICE]: ModifierTier.COMMON,
[MoveId.DRAINING_KISS]: ModifierTier.GREAT,
[MoveId.GRASSY_TERRAIN]: ModifierTier.COMMON,
[MoveId.MISTY_TERRAIN]: ModifierTier.COMMON,
@@ -69161,12 +69161,12 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.BREAKING_SWIPE]: ModifierTier.GREAT,
[MoveId.STEEL_BEAM]: ModifierTier.ULTRA,
[MoveId.EXPANDING_FORCE]: ModifierTier.GREAT,
- [MoveId.STEEL_ROLLER]: ModifierTier.COMMON,
+ [MoveId.STEEL_ROLLER]: ModifierTier.GREAT,
[MoveId.SCALE_SHOT]: ModifierTier.ULTRA,
[MoveId.METEOR_BEAM]: ModifierTier.GREAT,
- [MoveId.MISTY_EXPLOSION]: ModifierTier.COMMON,
+ [MoveId.MISTY_EXPLOSION]: ModifierTier.GREAT,
[MoveId.GRASSY_GLIDE]: ModifierTier.COMMON,
- [MoveId.RISING_VOLTAGE]: ModifierTier.COMMON,
+ [MoveId.RISING_VOLTAGE]: ModifierTier.GREAT,
[MoveId.TERRAIN_PULSE]: ModifierTier.COMMON,
[MoveId.SKITTER_SMACK]: ModifierTier.GREAT,
[MoveId.BURNING_JEALOUSY]: ModifierTier.GREAT,
@@ -69175,20 +69175,20 @@ export const tmPoolTiers: TmPoolTiers = {
[MoveId.CORROSIVE_GAS]: ModifierTier.COMMON,
[MoveId.COACHING]: ModifierTier.COMMON,
[MoveId.FLIP_TURN]: ModifierTier.COMMON,
- [MoveId.TRIPLE_AXEL]: ModifierTier.COMMON,
- [MoveId.DUAL_WINGBEAT]: ModifierTier.COMMON,
+ [MoveId.TRIPLE_AXEL]: ModifierTier.ULTRA,
+ [MoveId.DUAL_WINGBEAT]: ModifierTier.GREAT,
[MoveId.SCORCHING_SANDS]: ModifierTier.GREAT,
[MoveId.TERA_BLAST]: ModifierTier.GREAT,
[MoveId.ICE_SPINNER]: ModifierTier.GREAT,
[MoveId.SNOWSCAPE]: ModifierTier.COMMON,
[MoveId.POUNCE]: ModifierTier.COMMON,
- [MoveId.TRAILBLAZE]: ModifierTier.COMMON,
+ [MoveId.TRAILBLAZE]: ModifierTier.GREAT,
[MoveId.CHILLING_WATER]: ModifierTier.COMMON,
[MoveId.HARD_PRESS]: ModifierTier.GREAT,
[MoveId.DRAGON_CHEER]: ModifierTier.COMMON,
[MoveId.ALLURING_VOICE]: ModifierTier.GREAT,
[MoveId.TEMPER_FLARE]: ModifierTier.GREAT,
- [MoveId.SUPERCELL_SLAM]: ModifierTier.GREAT,
+ [MoveId.SUPERCELL_SLAM]: ModifierTier.ULTRA,
[MoveId.PSYCHIC_NOISE]: ModifierTier.GREAT,
[MoveId.UPPER_HAND]: ModifierTier.COMMON,
};
diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts
index aa4951f3263..1dcb7d7eebf 100644
--- a/src/data/battle-anims.ts
+++ b/src/data/battle-anims.ts
@@ -139,8 +139,8 @@ class AnimFrame {
focus: AnimFocus,
init?: boolean,
) {
- this.x = !init ? ((x || 0) - 128) * 0.5 : x;
- this.y = !init ? ((y || 0) - 224) * 0.5 : y;
+ this.x = init ? x : ((x || 0) - 128) * 0.5;
+ this.y = init ? y : ((y || 0) - 224) * 0.5;
if (zoomX) {
this.zoomX = zoomX;
} else if (init) {
@@ -291,9 +291,17 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
} catch (err) {
console.error(err);
}
- return Math.ceil((globalScene.sound.get(`battle_anims/${this.resourceName}`).totalDuration * 1000) / 33.33);
+ const sound = globalScene.sound.get(`battle_anims/${this.resourceName}`);
+ if (!sound) {
+ return 0;
+ }
+ return Math.ceil((sound.totalDuration * 1000) / 33.33);
}
- return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct?
+ const cry = battleAnim.user!.cry(soundConfig); // TODO: is the bang behind user correct?
+ if (!cry) {
+ return 0;
+ }
+ return Math.ceil((cry.totalDuration * 1000) / 33.33);
}
getEventType(): string {
@@ -352,7 +360,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
if (this.opacity !== undefined) {
tweenProps["alpha"] = (this.opacity || 0) / 255;
}
- if (Object.keys(tweenProps).length) {
+ if (Object.keys(tweenProps).length > 0) {
globalScene.tweens.add({
targets: moveAnim.bgSprite,
duration: getFrameMs(this.duration * 3),
@@ -617,7 +625,7 @@ function loadAnimAssets(anims: AnimConfig[], startLoad?: boolean): Promise
const backgrounds = new Set();
const sounds = new Set();
for (const a of anims) {
- if (!a.frames?.length) {
+ if (a.frames?.length === 0) {
continue;
}
const animSounds = a.getSoundResourceNames();
@@ -808,8 +816,8 @@ export abstract class BattleAnim {
x = point[0];
y = point[1];
if (
- frame.target === AnimFrameTarget.GRAPHIC &&
- isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])
+ frame.target === AnimFrameTarget.GRAPHIC
+ && isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])
) {
scaleX = scaleX * -1;
}
@@ -818,7 +826,7 @@ export abstract class BattleAnim {
}
const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
- ret.get(frame.target)!.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); // TODO: is the bang correct?
+ ret.get(frame.target)!.set(key, { x, y, scaleX, scaleY, angle }); // TODO: is the bang correct?
}
return ret;
@@ -827,8 +835,8 @@ export abstract class BattleAnim {
// biome-ignore lint/complexity/noBannedTypes: callback is used liberally
play(onSubstitute?: boolean, callback?: Function) {
const isOppAnim = this.isOppAnim();
- const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
- const target = !isOppAnim ? this.target! : this.user!;
+ const user = isOppAnim ? this.target! : this.user!;
+ const target = isOppAnim ? this.user! : this.target!; // TODO: These bangs are LITERALLY not correct at all
if (!target?.isOnField() && !this.playRegardlessOfIssues) {
if (callback) {
@@ -880,8 +888,8 @@ export abstract class BattleAnim {
* and `this.target` prevent the target's Substitute doll from disappearing
* after being the target of an animation.
*/
- const userSpriteToShow = !isOppAnim ? userSprite : targetSprite;
- const targetSpriteToShow = !isOppAnim ? targetSprite : userSprite;
+ const userSpriteToShow = isOppAnim ? targetSprite : userSprite;
+ const targetSpriteToShow = isOppAnim ? userSprite : targetSprite;
if (!this.isHideUser() && userSpriteToShow) {
userSpriteToShow.setVisible(true);
}
@@ -1134,18 +1142,18 @@ export abstract class BattleAnim {
for (const frame of frames) {
let { x, y } = frame;
- const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
+ const scaleX = (frame.zoomX / 100) * (frame.mirror ? -1 : 1);
const scaleY = frame.zoomY / 100;
x += targetInitialX;
y += targetInitialY;
const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
ret.get(frame.target)?.set(key, {
- x: x,
- y: y,
- scaleX: scaleX,
- scaleY: scaleY,
- angle: angle,
+ x,
+ y,
+ scaleX,
+ scaleY,
+ angle,
});
}
diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts
index 104eca0e407..c495cdaa604 100644
--- a/src/data/battler-tags.ts
+++ b/src/data/battler-tags.ts
@@ -34,7 +34,7 @@ import type { StatStageChangeCallback } from "#phases/stat-stage-change-phase";
import i18next from "#plugins/i18n";
import type {
AbilityBattlerTagType,
- BattlerTagTypeData,
+ BattlerTagData,
ContactSetStatusProtectedTagType,
ContactStatStageChangeProtectedTagType,
CritStageBoostTagType,
@@ -228,26 +228,27 @@ interface GenericSerializableBattlerTag extends Serial
* Descendants can override {@linkcode isMoveRestricted} to restrict moves that
* match a condition. A restricted move gets cancelled before it is used.
* Players and enemies should not be allowed to select restricted moves.
+ * @todo Require descendant subclasses to inherit a `PRE_MOVE` lapse type
*/
export abstract class MoveRestrictionBattlerTag extends SerializableBattlerTag {
public declare readonly tagType: MoveRestrictionBattlerTagType;
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
- if (lapseType === BattlerTagLapseType.PRE_MOVE) {
- // Cancel the affected pokemon's selected move
- const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase;
- const move = phase.move;
-
- if (this.isMoveRestricted(move.moveId, pokemon)) {
- if (this.interruptedText(pokemon, move.moveId)) {
- globalScene.phaseManager.queueMessage(this.interruptedText(pokemon, move.moveId));
- }
- phase.cancel();
- }
-
- return true;
+ if (lapseType !== BattlerTagLapseType.PRE_MOVE) {
+ return super.lapse(pokemon, lapseType);
}
- return super.lapse(pokemon, lapseType);
+ // Cancel the affected pokemon's selected move
+ const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase;
+ const move = phase.move;
+
+ if (this.isMoveRestricted(move.moveId, pokemon)) {
+ if (this.interruptedText(pokemon, move.moveId)) {
+ globalScene.phaseManager.queueMessage(this.interruptedText(pokemon, move.moveId));
+ }
+ phase.cancel();
+ }
+
+ return true;
}
/**
@@ -1058,8 +1059,7 @@ export class SeedTag extends SerializableBattlerTag {
// Check which opponent to restore HP to
const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex);
if (!source) {
- console.warn(`Failed to get source Pokemon for SeedTag lapse; id: ${this.sourceId}`);
- return false;
+ return true;
}
const cancelled = new BooleanHolder(false);
@@ -1135,8 +1135,8 @@ export class PowderTag extends BattlerTag {
const move = movePhase.move.getMove();
const weather = globalScene.arena.weather;
if (
- pokemon.getMoveType(move) !== PokemonType.FIRE ||
- (weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder
+ pokemon.getMoveType(move) !== PokemonType.FIRE
+ || (weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder
) {
return true;
}
@@ -1793,9 +1793,9 @@ export abstract class ContactProtectedTag extends ProtectedTag {
const moveData = getMoveEffectPhaseData(pokemon);
if (
- lapseType === BattlerTagLapseType.CUSTOM &&
- moveData &&
- moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon })
+ lapseType === BattlerTagLapseType.CUSTOM
+ && moveData
+ && moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon })
) {
this.onContact(moveData.attacker, pokemon);
}
@@ -2159,7 +2159,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag {
null,
false,
null,
- true,
+ false,
);
}
@@ -2474,10 +2474,7 @@ export class RemovedTypeTag extends SerializableBattlerTag {
}
}
-/**
- * Battler tag for effects that ground the source, allowing Ground-type moves to hit them.
- * @description `IGNORE_FLYING`: Persistent grounding effects (i.e. from Smack Down and Thousand Waves)
- */
+/** Battler tag for effects that ground the source, allowing Ground-type moves to hit them. */
export class GroundedTag extends SerializableBattlerTag {
public override readonly tagType = BattlerTagType.IGNORE_FLYING;
constructor(tagType: BattlerTagType.IGNORE_FLYING, lapseType: BattlerTagLapseType, sourceMove: MoveId) {
@@ -2485,11 +2482,7 @@ export class GroundedTag extends SerializableBattlerTag {
}
}
-/**
- * @description `ROOSTED`: Tag for temporary grounding if only source of ungrounding is flying and pokemon uses Roost.
- * Roost removes flying type from a pokemon for a single turn.
- */
-
+/** Removes flying type from a pokemon for a single turn */
export class RoostedTag extends BattlerTag {
private isBaseFlying: boolean;
private isBasePureFlying: boolean;
@@ -2538,12 +2531,10 @@ export class RoostedTag extends BattlerTag {
let modifiedTypes: PokemonType[];
if (this.isBasePureFlying && !isCurrentlyDualType) {
modifiedTypes = [PokemonType.NORMAL];
+ } else if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
+ modifiedTypes = [PokemonType.UNKNOWN];
} else {
- if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
- modifiedTypes = [PokemonType.UNKNOWN];
- } else {
- modifiedTypes = currentTypes.filter(type => type !== PokemonType.FLYING);
- }
+ modifiedTypes = currentTypes.filter(type => type !== PokemonType.FLYING);
}
pokemon.summonData.types = modifiedTypes;
pokemon.updateInfo();
@@ -2570,7 +2561,7 @@ export class FormBlockDamageTag extends SerializableBattlerTag {
/**
* Applies the tag to the Pokémon.
* Triggers a form change if the Pokémon is not in its defense form.
- * @param {Pokemon} pokemon The Pokémon to which the tag is added.
+ * @param pokemon The Pokémon to which the tag is added.
*/
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
@@ -2819,9 +2810,9 @@ export class GulpMissileTag extends SerializableBattlerTag {
// Bang here is OK as if sourceMove was undefined, this would just evaluate to false
const isSurfOrDive = [MoveId.SURF, MoveId.DIVE].includes(this.sourceMove!);
const isNormalForm =
- pokemon.formIndex === 0 &&
- !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA) &&
- !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU);
+ pokemon.formIndex === 0
+ && !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)
+ && !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU);
const isCramorant = pokemon.species.speciesId === SpeciesId.CRAMORANT;
return isSurfOrDive && isNormalForm && isCramorant;
@@ -3843,7 +3834,7 @@ export function getBattlerTag(
* @param source - An object containing the data necessary to reconstruct the BattlerTag.
* @returns The valid battler tag
*/
-export function loadBattlerTag(source: BattlerTag | BattlerTagTypeData): BattlerTag {
+export function loadBattlerTag(source: BattlerTag | BattlerTagData): BattlerTag {
// TODO: Remove this bang by fixing the signature of `getBattlerTag`
// to allow undefined sourceIds and sourceMoves (with appropriate fallback for tags that require it)
const tag = getBattlerTag(source.tagType, source.turnCount, source.sourceMove!, source.sourceId!);
@@ -3862,7 +3853,7 @@ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; at
const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("MoveEffectPhase")) {
return {
- phase: phase,
+ phase,
attacker: phase.getPokemon(),
move: phase.move,
};
diff --git a/src/data/berry.ts b/src/data/berry.ts
index 762423799fe..1f1364f2f6b 100644
--- a/src/data/berry.ts
+++ b/src/data/berry.ts
@@ -28,7 +28,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
case BerryType.ENIGMA:
return (pokemon: Pokemon) =>
- !!pokemon.turnData.attacksReceived.filter(a => a.result === HitResult.SUPER_EFFECTIVE).length;
+ pokemon.turnData.attacksReceived.filter(a => a.result === HitResult.SUPER_EFFECTIVE).length > 0;
case BerryType.LIECHI:
case BerryType.GANLON:
case BerryType.PETAYA:
@@ -141,8 +141,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
{
// Pick the first move completely out of PP, or else the first one that has any PP missing
const ppRestoreMove =
- consumer.getMoveset().find(m => m.ppUsed === m.getMovePp()) ??
- consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
+ consumer.getMoveset().find(m => m.ppUsed === m.getMovePp())
+ ?? consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
if (ppRestoreMove) {
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
globalScene.phaseManager.queueMessage(
diff --git a/src/data/challenge.ts b/src/data/challenge.ts
index 3c282e7640e..64a88bc8be2 100644
--- a/src/data/challenge.ts
+++ b/src/data/challenge.ts
@@ -1,6 +1,6 @@
import type { FixedBattleConfig } from "#app/battle";
import { getRandomTrainerFunc } from "#app/battle";
-import { defaultStarterSpeciesAndEvolutions } from "#app/constants";
+import { defaultStarterSpeciesAndEvolutions } from "#balance/pokemon-evolutions";
import { speciesStarterCosts } from "#balance/starters";
import type { PokemonSpecies } from "#data/pokemon-species";
import { AbilityAttr } from "#enums/ability-attr";
@@ -21,13 +21,14 @@ import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer";
import type { ModifierTypeOption } from "#modifiers/modifier-type";
import { PokemonMove } from "#moves/pokemon-move";
-import type { DexAttrProps, GameData, StarterDataEntry } from "#system/game-data";
+import type { GameData } from "#system/game-data";
import { RibbonData, type RibbonFlag } from "#system/ribbons/ribbon-data";
import type { DexEntry } from "#types/dex-data";
+import type { DexAttrProps, StarterDataEntry } from "#types/save-data";
import { type BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common";
import { deepCopy } from "#utils/data";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
-import { toCamelCase, toSnakeCase } from "#utils/strings";
+import { toCamelCase } from "#utils/strings";
import i18next from "i18next";
/** A constant for the default max cost of the starting party before a run */
@@ -56,7 +57,7 @@ export abstract class Challenge {
}
/**
- * @param id {@link Challenges} The enum value for the challenge
+ * @param id - The enum value for the challenge
*/
constructor(id: Challenges, maxValue: number = Number.MAX_SAFE_INTEGER) {
this.id = id;
@@ -85,9 +86,9 @@ export abstract class Challenge {
}
/**
- * Used for unlockable challenges to check if they're unlocked.
- * @param data {@link GameData} The save data.
- * @returns {@link boolean} Whether this challenge is unlocked.
+ * Check if an unlockable challenge is unlocked
+ * @param data - The save data
+ * @returns Whether this challenge is unlocked
*/
isUnlocked(data: GameData): boolean {
return this.conditions.every(f => f(data));
@@ -95,8 +96,8 @@ export abstract class Challenge {
/**
* Adds an unlock condition to this challenge.
- * @param condition {@link ChallengeCondition} The condition to add.
- * @returns {@link Challenge} This challenge
+ * @param condition - The condition to add
+ * @returns This challenge
*/
condition(condition: ChallengeCondition): Challenge {
this.conditions.push(condition);
@@ -105,7 +106,7 @@ export abstract class Challenge {
}
/**
- * @returns {@link string} The localised name of this challenge.
+ * @returns The localised name of this challenge.
*/
getName(): string {
return i18next.t(`challenges:${this.geti18nKey()}.name`);
@@ -132,7 +133,7 @@ export abstract class Challenge {
/**
* Increase the value of the challenge
- * @returns {@link boolean} Returns true if the value changed
+ * @returns Returns true if the value changed
*/
increaseValue(): boolean {
if (this.value < this.maxValue) {
@@ -144,7 +145,7 @@ export abstract class Challenge {
/**
* Decrease the value of the challenge
- * @returns {@link boolean} Returns true if the value changed
+ * @returns Returns true if the value changed
*/
decreaseValue(): boolean {
if (this.value > 0) {
@@ -163,7 +164,7 @@ export abstract class Challenge {
/**
* Decrease the severity of the challenge
- * @returns {@link boolean} Returns true if the value changed
+ * @returns Returns true if the value changed
*/
decreaseSeverity(): boolean {
if (this.severity > 0) {
@@ -175,7 +176,7 @@ export abstract class Challenge {
/**
* Increase the severity of the challenge
- * @returns {@link boolean} Returns true if the value changed
+ * @returns Returns true if the value changed
*/
increaseSeverity(): boolean {
if (this.severity < this.maxSeverity) {
@@ -187,7 +188,7 @@ export abstract class Challenge {
/**
* Gets the "difficulty" value of this challenge.
- * @returns {@link integer} The difficulty value.
+ * @returns The difficulty value.
*/
getDifficulty(): number {
return this.value;
@@ -195,7 +196,7 @@ export abstract class Challenge {
/**
* Gets the minimum difficulty added by this challenge.
- * @returns {@link integer} The difficulty value.
+ * @returns The difficulty value.
*/
getMinDifficulty(): number {
return 0;
@@ -203,7 +204,7 @@ export abstract class Challenge {
/**
* Clones a challenge, either from another challenge or json. Chainable.
- * @param source The source challenge or json.
+ * @param _source - The source challenge or json.
* @returns This challenge.
*/
static loadChallenge(_source: Challenge | any): Challenge {
@@ -212,10 +213,10 @@ export abstract class Challenge {
/**
* An apply function for STARTER_CHOICE challenges. Derived classes should alter this.
- * @param _pokemon {@link PokemonSpecies} The pokemon to check the validity of.
- * @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
- * @param _dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The Pokémon to check the validity of
+ * @param _valid - Holder for whether the Pokémon is valid or not
+ * @param _dexAttr - The dex attributes of the Pokémon
+ * @returns Whether this function did anything.
*/
applyStarterChoice(_pokemon: PokemonSpecies, _valid: BooleanHolder, _dexAttr: DexAttrProps): boolean {
return false;
@@ -223,8 +224,8 @@ export abstract class Challenge {
/**
* An apply function for STARTER_POINTS challenges. Derived classes should alter this.
- * @param _points {@link NumberHolder} The amount of points you have available.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _points - Holder for amount of starter points the user has to spend
+ * @returns Whether this function did anything
*/
applyStarterPoints(_points: NumberHolder): boolean {
return false;
@@ -232,9 +233,9 @@ export abstract class Challenge {
/**
* An apply function for STARTER_COST challenges. Derived classes should alter this.
- * @param _species {@link SpeciesId} The pokemon to change the cost of.
- * @param _cost {@link NumberHolder} The cost of the starter.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _species - The pokémon to change the cost of
+ * @param _cost - Holder for the cost of the starter Pokémon
+ * @returns Whether this function did anything.
*/
applyStarterCost(_species: SpeciesId, _cost: NumberHolder): boolean {
return false;
@@ -242,8 +243,8 @@ export abstract class Challenge {
/**
* An apply function for STARTER_SELECT_MODIFY challenges. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} The starter pokemon to modify.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The starter Pokémon to modify.
+ * @returns Whether this function did anything.
*/
applyStarterSelectModify(_speciesId: SpeciesId, _dexEntry: DexEntry, _starterDataEntry: StarterDataEntry): boolean {
return false;
@@ -251,8 +252,8 @@ export abstract class Challenge {
/**
* An apply function for STARTER_MODIFY challenges. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} The starter pokemon to modify.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The starter Pokémon to modify.
+ * @returns Whether this function did anything.
*/
applyStarterModify(_pokemon: Pokemon): boolean {
return false;
@@ -260,9 +261,9 @@ export abstract class Challenge {
/**
* An apply function for POKEMON_IN_BATTLE challenges. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} The pokemon to check the validity of.
- * @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The Pokémon to check the validity of
+ * @param _valid - Holds a boolean that will be set to false if the Pokémon isn't allowed
+ * @returns Whether this function did anything
*/
applyPokemonInBattle(_pokemon: Pokemon, _valid: BooleanHolder): boolean {
return false;
@@ -270,9 +271,9 @@ export abstract class Challenge {
/**
* An apply function for FIXED_BATTLE challenges. Derived classes should alter this.
- * @param _waveIndex {@link Number} The current wave index.
- * @param _battleConfig {@link FixedBattleConfig} The battle config to modify.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _waveIndex The current wave index
+ * @param _battleConfig - The battle config to modify
+ * @returns Whether this function did anything
*/
applyFixedBattle(_waveIndex: number, _battleConfig: FixedBattleConfig): boolean {
return false;
@@ -280,8 +281,8 @@ export abstract class Challenge {
/**
* An apply function for TYPE_EFFECTIVENESS challenges. Derived classes should alter this.
- * @param _effectiveness {@linkcode NumberHolder} The current effectiveness of the move.
- * @returns Whether this function did anything.
+ * @param _effectiveness - The current effectiveness of the move
+ * @returns Whether this function did anything
*/
applyTypeEffectiveness(_effectiveness: NumberHolder): boolean {
return false;
@@ -289,11 +290,11 @@ export abstract class Challenge {
/**
* An apply function for AI_LEVEL challenges. Derived classes should alter this.
- * @param _level {@link NumberHolder} The generated level.
- * @param _levelCap {@link Number} The current level cap.
- * @param _isTrainer {@link Boolean} Whether this is a trainer pokemon.
- * @param _isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _level - The generated level.
+ * @param _levelCap - The current level cap.
+ * @param _isTrainer - Whether this is a trainer Pokémon
+ * @param _isBoss - Whether this is a non-trainer boss Pokémon
+ * @returns - Whether this function did anything
*/
applyLevelChange(_level: NumberHolder, _levelCap: number, _isTrainer: boolean, _isBoss: boolean): boolean {
return false;
@@ -301,9 +302,9 @@ export abstract class Challenge {
/**
* An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this.
- * @param pokemon {@link Pokemon} The pokemon that is being considered.
- * @param moveSlots {@link NumberHolder} The amount of move slots.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The Pokémon that is being considered
+ * @param _moveSlots - The amount of move slots
+ * @returns Whether this function did anything
*/
applyMoveSlot(_pokemon: Pokemon, _moveSlots: NumberHolder): boolean {
return false;
@@ -311,9 +312,9 @@ export abstract class Challenge {
/**
* An apply function for PASSIVE_ACCESS challenges. Derived classes should alter this.
- * @param pokemon {@link Pokemon} The pokemon to change.
- * @param hasPassive {@link BooleanHolder} Whether it should have its passive.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - The Pokémon to change
+ * @param _hasPassive - Whether it should have its passive
+ * @returns Whether this function did anything
*/
applyPassiveAccess(_pokemon: Pokemon, _hasPassive: BooleanHolder): boolean {
return false;
@@ -321,7 +322,7 @@ export abstract class Challenge {
/**
* An apply function for GAME_MODE_MODIFY challenges. Derived classes should alter this.
- * @returns {@link boolean} Whether this function did anything.
+ * @returns Whether this function did anything
*/
applyGameModeModify(): boolean {
return false;
@@ -329,11 +330,11 @@ export abstract class Challenge {
/**
* An apply function for MOVE_ACCESS. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} What pokemon would learn the move.
- * @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
- * @param _move {@link MoveId} The move in question.
- * @param _level {@link NumberHolder} The level threshold for access.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - What Pokémon would learn the move
+ * @param _moveSource - What source the Pokémon would get the move from
+ * @param _move - The move in question
+ * @param _level - The level threshold for access
+ * @returns Whether this function did anything
*/
applyMoveAccessLevel(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: MoveId, _level: NumberHolder): boolean {
return false;
@@ -341,21 +342,21 @@ export abstract class Challenge {
/**
* An apply function for MOVE_WEIGHT. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} What pokemon would learn the move.
- * @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
- * @param _move {@link MoveId} The move in question.
- * @param _weight {@link NumberHolder} The base weight of the move
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - What Pokémon would learn the move
+ * @param _moveSource - What source the Pokémon would get the move from
+ * @param _move - The move in question.
+ * @param _weight - The base weight of the move
+ * @returns Whether this function did anything
*/
- applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: MoveId, _level: NumberHolder): boolean {
+ applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: MoveId, _weight: NumberHolder): boolean {
return false;
}
/**
* An apply function for FlipStats. Derived classes should alter this.
- * @param _pokemon {@link Pokemon} What pokemon would learn the move.
- * @param _baseStats What are the stats to flip.
- * @returns {@link boolean} Whether this function did anything.
+ * @param _pokemon - What Pokémon would learn the move
+ * @param _baseStats What are the stats to flip
+ * @returns Whether this function did anything
*/
applyFlipStat(_pokemon: Pokemon, _baseStats: number[]) {
return false;
@@ -381,9 +382,9 @@ export abstract class Challenge {
/**
* An apply function for POKEMON_ADD_TO_PARTY. Derived classes should alter this.
- * @param _pokemon - The pokemon being caught
- * @param _status - Whether the pokemon can be added to the party or not
- * @return Whether this function did anything
+ * @param _pokemon - The Pokémon being caught
+ * @param _status - Whether the Pokémon can be added to the party or not
+ * @returns Whether this function did anything
*/
applyPokemonAddToParty(_pokemon: EnemyPokemon, _status: BooleanHolder): boolean {
return false;
@@ -391,8 +392,8 @@ export abstract class Challenge {
/**
* An apply function for POKEMON_FUSION. Derived classes should alter this.
- * @param _pokemon - The pokemon being checked
- * @param _status - Whether the selected pokemon is allowed to fuse or not
+ * @param _pokemon - The Pokémon being checked
+ * @param _status - Whether the selected Pokémon is allowed to fuse or not
* @returns Whether this function did anything
*/
applyPokemonFusion(_pokemon: PlayerPokemon, _status: BooleanHolder): boolean {
@@ -467,8 +468,8 @@ export class SingleGenerationChallenge extends Challenge {
const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0;
if (
- pokemon.isPlayer() &&
- (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))
+ pokemon.isPlayer()
+ && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))
) {
valid.value = false;
return true;
@@ -670,10 +671,7 @@ export class SingleGenerationChallenge extends Challenge {
return false;
}
- /**
- * @overrides
- */
- getDifficulty(): number {
+ override getDifficulty(): number {
return this.value > 0 ? 1 : 0;
}
@@ -741,12 +739,12 @@ export class SingleTypeChallenge extends Challenge {
applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean {
if (
- pokemon.isPlayer() &&
- !pokemon.isOfType(this.value - 1, false, false, true) &&
- !SingleTypeChallenge.TYPE_OVERRIDES.some(
+ pokemon.isPlayer()
+ && !pokemon.isOfType(this.value - 1, false, false, true)
+ && !SingleTypeChallenge.TYPE_OVERRIDES.some(
o =>
- o.type === this.value - 1 &&
- (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species,
+ o.type === this.value - 1
+ && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species,
)
) {
// TODO: is the bang on fusionSpecies correct?
@@ -756,15 +754,12 @@ export class SingleTypeChallenge extends Challenge {
return false;
}
- /**
- * @overrides
- */
- getDifficulty(): number {
+ override getDifficulty(): number {
return this.value > 0 ? 1 : 0;
}
getValue(overrideValue: number = this.value): string {
- return toSnakeCase(PokemonType[overrideValue - 1]);
+ return PokemonType[overrideValue - 1].toLowerCase();
}
getDescription(overrideValue: number = this.value): string {
@@ -823,11 +818,11 @@ export class FreshStartChallenge extends Challenge {
// Remove natures except for the default ones
const neutralNaturesAttr =
- (1 << (Nature.HARDY + 1)) |
- (1 << (Nature.DOCILE + 1)) |
- (1 << (Nature.SERIOUS + 1)) |
- (1 << (Nature.BASHFUL + 1)) |
- (1 << (Nature.QUIRKY + 1));
+ (1 << (Nature.HARDY + 1))
+ | (1 << (Nature.DOCILE + 1))
+ | (1 << (Nature.SERIOUS + 1))
+ | (1 << (Nature.BASHFUL + 1))
+ | (1 << (Nature.QUIRKY + 1));
dexEntry.natureAttr &= neutralNaturesAttr;
// Cap all ivs at 15
@@ -882,8 +877,8 @@ export class FreshStartChallenge extends Challenge {
if (pokemon.species.speciesId === SpeciesId.ZYGARDE && pokemon.formIndex >= 2) {
pokemon.formIndex -= 2; // Sets 10%-PC to 10%-AB and 50%-PC to 50%-AB
} else if (
- pokemon.formIndex > 0 &&
- [
+ pokemon.formIndex > 0
+ && [
SpeciesId.PIKACHU,
SpeciesId.EEVEE,
SpeciesId.PICHU,
diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts
index f0a20a0b02b..a0d3358ecb0 100644
--- a/src/data/daily-run.ts
+++ b/src/data/daily-run.ts
@@ -6,7 +6,7 @@ import { PokemonSpecies } from "#data/pokemon-species";
import { BiomeId } from "#enums/biome-id";
import { PartyMemberStrength } from "#enums/party-member-strength";
import { SpeciesId } from "#enums/species-id";
-import type { Starter } from "#ui/starter-select-ui-handler";
+import type { Starter } from "#ui/handlers/starter-select-ui-handler";
import { isNullOrUndefined, randSeedGauss, randSeedInt, randSeedItem } from "#utils/common";
import { getEnumValues } from "#utils/enums";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
@@ -42,10 +42,9 @@ export function getDailyRunStarters(seed: string): Starter[] {
starterCosts.push(randSeedInt(9 - starterCosts[0], 1));
starterCosts.push(10 - (starterCosts[0] + starterCosts[1]));
- for (let c = 0; c < starterCosts.length; c++) {
- const cost = starterCosts[c];
+ for (const cost of starterCosts) {
const costSpecies = Object.keys(speciesStarterCosts)
- .map(s => Number.parseInt(s) as SpeciesId)
+ .map(s => Number.parseInt(s) as SpeciesId) // TODO: Remove
.filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(
diff --git a/src/data/egg-hatch-data.ts b/src/data/egg-hatch-data.ts
index e78dc4d7984..efe06d5a504 100644
--- a/src/data/egg-hatch-data.ts
+++ b/src/data/egg-hatch-data.ts
@@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import type { PlayerPokemon } from "#field/pokemon";
-import type { StarterDataEntry } from "#system/game-data";
import type { DexEntry } from "#types/dex-data";
+import type { StarterDataEntry } from "#types/save-data";
/**
* Stores data associated with a specific egg and the hatched pokemon
diff --git a/src/data/egg.ts b/src/data/egg.ts
index 5fab276f327..d4d37071d11 100644
--- a/src/data/egg.ts
+++ b/src/data/egg.ts
@@ -220,9 +220,9 @@ export class Egg {
public isManaphyEgg(): boolean {
return (
- this._species === SpeciesId.PHIONE ||
- this._species === SpeciesId.MANAPHY ||
- (this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
+ this._species === SpeciesId.PHIONE
+ || this._species === SpeciesId.MANAPHY
+ || (this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
);
}
@@ -324,15 +324,15 @@ export class Egg {
switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG:
return (
- this._eggDescriptor ??
- i18next.t("egg:sameSpeciesEgg", {
+ this._eggDescriptor
+ ?? i18next.t("egg:sameSpeciesEgg", {
species: getPokemonSpecies(this._species).getName(),
})
);
case EggSourceType.GACHA_LEGENDARY:
return (
- this._eggDescriptor ??
- `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`
+ this._eggDescriptor
+ ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`
);
case EggSourceType.GACHA_SHINY:
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
@@ -419,10 +419,8 @@ export class Egg {
const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
return rand ? SpeciesId.PHIONE : SpeciesId.MANAPHY;
}
- if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
- if (!randSeedInt(2)) {
- return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
- }
+ if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY && !randSeedInt(2)) {
+ return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
}
let minStarterValue: number;
@@ -454,9 +452,9 @@ export class Egg {
.map(s => Number.parseInt(s) as SpeciesId)
.filter(
s =>
- !pokemonPrevolutions.hasOwnProperty(s) &&
- getPokemonSpecies(s).isObtainable() &&
- ignoredSpecies.indexOf(s) === -1,
+ !pokemonPrevolutions.hasOwnProperty(s)
+ && getPokemonSpecies(s).isObtainable()
+ && ignoredSpecies.indexOf(s) === -1,
);
// If this is the 10th egg without unlocking something new, attempt to force it.
@@ -464,7 +462,7 @@ export class Egg {
const lockedPool = speciesPool.filter(
s => !globalScene.gameData.dexData[s].caughtAttr && !globalScene.gameData.eggs.some(e => e.species === s),
);
- if (lockedPool.length) {
+ if (lockedPool.length > 0) {
// Skip this if everything is unlocked
speciesPool = lockedPool;
}
@@ -510,8 +508,8 @@ export class Egg {
species = species!; // tell TS compiled it's defined now!
if (
- globalScene.gameData.dexData[species].caughtAttr ||
- globalScene.gameData.eggs.some(e => e.species === species)
+ globalScene.gameData.dexData[species].caughtAttr
+ || globalScene.gameData.eggs.some(e => e.species === species)
) {
globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
} else {
@@ -567,8 +565,8 @@ export class Egg {
globalScene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
if (
- globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD &&
- this._tier === EggTier.COMMON
+ globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD
+ && this._tier === EggTier.COMMON
) {
this._tier = EggTier.LEGENDARY;
} else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
diff --git a/src/data/moves/move-utils.ts b/src/data/moves/move-utils.ts
index 241144599e5..eedeea53087 100644
--- a/src/data/moves/move-utils.ts
+++ b/src/data/moves/move-utils.ts
@@ -126,7 +126,7 @@ export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: Move
}
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
- while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
+ while (user.getMoveQueue().length > 0 && user.getMoveQueue()[0].move === move.id) {
user.getMoveQueue().shift();
}
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts
index ebaf5e03322..1cd64afb5d7 100644
--- a/src/data/moves/move.ts
+++ b/src/data/moves/move.ts
@@ -94,6 +94,7 @@ import { getEnumValues } from "#utils/enums";
import { toCamelCase, toTitleCase } from "#utils/strings";
import i18next from "i18next";
import { applyChallenges } from "#utils/challenge-utils";
+import type { AbstractConstructor } from "#types/type-helpers";
/**
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
@@ -643,9 +644,9 @@ export abstract class Move implements Localizable {
* will not consider {@linkcode AbilityId.WIND_RIDER | Wind Rider }.
*
* To simply check whether the move has a flag, use {@linkcode hasFlag}.
- * @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target
- * @param user {@linkcode Pokemon} the Pokemon using the move
- * @param target {@linkcode Pokemon} the Pokemon receiving the move
+ * @param flag - MoveFlag to check on user and/or target
+ * @param user - the Pokemon using the move
+ * @param target - the Pokemon receiving the move
* @param isFollowUp (defaults to `false`) `true` if the move was used as a follow up
* @returns boolean
* @see {@linkcode hasFlag}
@@ -1056,16 +1057,11 @@ export class SelfStatusMove extends Move {
}
}
-// TODO: Figure out how to improve the signature of this so that
-// the `ChargeMove` function knows that the argument `Base` is a specific subclass of move that cannot
-// be abstract.
-// Right now, I only know how to do this by using the type conjunction (the & operators)
-type SubMove = new (...args: any[]) => Move & {
- is(moveKind: K): this is MoveClassMap[K];
-};
+type SubMove = AbstractConstructor
function ChargeMove(Base: TBase, nameAppend: string) {
- return class extends Base {
+ // NB: This cannot be made into a oneline return
+ abstract class Charging extends Base {
/** The animation to play during the move's charging phase */
public readonly chargeAnim: ChargeAnim = ChargeAnim[`${MoveId[this.id]}_CHARGING`];
/** The message to show during the move's charging phase */
@@ -1142,16 +1138,13 @@ function ChargeMove(Base: TBase, nameAppend: string) {
return this;
}
};
+ return Charging;
}
export class ChargingAttackMove extends ChargeMove(AttackMove, "ChargingAttackMove") {}
export class ChargingSelfStatusMove extends ChargeMove(SelfStatusMove, "ChargingSelfStatusMove") {}
-/**
- * Base class defining all {@linkcode Move} Attributes
- * @abstract
- * @see {@linkcode apply}
- */
+/** Base class defining all {@linkcode Move} Attributes */
export abstract class MoveAttr {
/** Should this {@linkcode Move} target the user? */
public selfTarget: boolean;
@@ -1247,8 +1240,6 @@ interface MoveEffectAttrOptions {
/**
* Base class defining all Move Effect Attributes
- * @extends MoveAttr
- * @see {@linkcode apply}
*/
export class MoveEffectAttr extends MoveAttr {
/**
@@ -1461,7 +1452,6 @@ export class PreMoveMessageAttr extends MoveAttr {
* Attribute for moves that can be conditionally interrupted to be considered to
* have failed before their "useMove" message is displayed. Currently used by
* Focus Punch.
- * @extends MoveAttr
*/
export class PreUseInterruptAttr extends MoveAttr {
protected message: string | MoveMessageFunc;
@@ -1505,7 +1495,6 @@ export class PreUseInterruptAttr extends MoveAttr {
/**
* Attribute for Status moves that take attack type effectiveness
* into consideration (i.e. {@linkcode https://bulbapedia.bulbagarden.net/wiki/Thunder_Wave_(move) | Thunder Wave})
- * @extends MoveAttr
*/
export class RespectAttackTypeImmunityAttr extends MoveAttr { }
@@ -1789,9 +1778,7 @@ export class RecoilAttr extends MoveEffectAttr {
/**
* Attribute used for moves which self KO the user regardless if the move hits a target
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
- **/
+ */
export class SacrificialAttr extends MoveEffectAttr {
constructor() {
super(true, { trigger: MoveEffectTrigger.POST_TARGET });
@@ -1822,9 +1809,7 @@ export class SacrificialAttr extends MoveEffectAttr {
/**
* Attribute used for moves which self KO the user but only if the move hits a target
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
- **/
+ */
export class SacrificialAttrOnHit extends MoveEffectAttr {
constructor() {
super(true);
@@ -1861,8 +1846,6 @@ export class SacrificialAttrOnHit extends MoveEffectAttr {
/**
* Attribute used for moves which cut the user's Max HP in half.
* Triggers using {@linkcode MoveEffectTrigger.POST_TARGET}.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class HalfSacrificialAttr extends MoveEffectAttr {
constructor() {
@@ -1962,8 +1945,6 @@ export class AddSubstituteAttr extends MoveEffectAttr {
/**
* Heals the user or target by {@linkcode healRatio} depending on the value of {@linkcode selfTarget}
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class HealAttr extends MoveEffectAttr {
constructor(
@@ -2052,8 +2033,6 @@ export class RestAttr extends HealAttr {
/**
* Cures the user's party of non-volatile status conditions, ie. Heal Bell, Aromatherapy
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class PartyStatusCureAttr extends MoveEffectAttr {
/** Message to display after using move */
@@ -2112,7 +2091,6 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
/**
* Applies damage to the target's ally equal to 1/16 of that ally's max HP.
- * @extends MoveEffectAttr
*/
export class FlameBurstAttr extends MoveEffectAttr {
constructor() {
@@ -2201,8 +2179,6 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
/**
* Attribute used for moves which ignore type-based debuffs from weather, namely Hydro Steam.
* Called during damage calculation after getting said debuff from getAttackTypeMultiplier in the Pokemon class.
- * @extends MoveAttr
- * @see {@linkcode apply}
*/
export class IgnoreWeatherTypeDebuffAttr extends MoveAttr {
/** The {@linkcode WeatherType} this move ignores */
@@ -2281,8 +2257,6 @@ export class SandHealAttr extends WeatherHealAttr {
/**
* Heals the target or the user by either {@linkcode normalHealRatio} or {@linkcode boostedHealRatio}
* depending on the evaluation of {@linkcode condition}
- * @extends HealAttr
- * @see {@linkcode apply}
*/
export class BoostHealAttr extends HealAttr {
/** Healing received when {@linkcode condition} is false */
@@ -2315,22 +2289,24 @@ export class BoostHealAttr extends HealAttr {
/**
* Heals the target only if it is the ally
- * @extends HealAttr
- * @see {@linkcode apply}
*/
export class HealOnAllyAttr extends HealAttr {
override canApply(user: Pokemon, target: Pokemon, _move: Move, _args?: any[]): boolean {
// Don't trigger if not targeting an ally
return target === user.getAlly() && super.canApply(user, target, _move, _args);
}
+
+ override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
+ if (user.isOpponent(target)) {
+ return false;
+ }
+ return super.apply(user, target, _move, _args);
+ }
}
/**
* Heals user as a side effect of a move that hits a target.
* Healing is based on {@linkcode healRatio} * the amount of damage dealt or a stat of the target.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
- * @see {@linkcode getUserBenefitScore}
*/
// TODO: Make Strength Sap its own attribute that extends off of this one
export class HitHealAttr extends MoveEffectAttr {
@@ -2400,8 +2376,6 @@ export class HitHealAttr extends MoveEffectAttr {
* Attribute used for moves that change priority in a turn given a condition,
* e.g. Grassy Glide
* Called when move order is calculated in {@linkcode TurnStartPhase}.
- * @extends MoveAttr
- * @see {@linkcode apply}
*/
export class IncrementMovePriorityAttr extends MoveAttr {
/** The condition for a move's priority being incremented */
@@ -2437,10 +2411,8 @@ export class IncrementMovePriorityAttr extends MoveAttr {
/**
* Attribute used for attack moves that hit multiple times per use, e.g. Bullet Seed.
*
+ * @remarks
* Applied at the beginning of {@linkcode MoveEffectPhase}.
- *
- * @extends MoveAttr
- * @see {@linkcode apply}
*/
export class MultiHitAttr extends MoveAttr {
/** This move's intrinsic multi-hit type. It should never be modified. */
@@ -2950,8 +2922,6 @@ export class StealEatBerryAttr extends EatBerryAttr {
/**
* Move attribute that signals that the move should cure a status effect
- * @extends MoveEffectAttr
- * @see {@linkcode apply()}
*/
export class HealStatusEffectAttr extends MoveEffectAttr {
/** List of Status Effects to cure */
@@ -3034,8 +3004,6 @@ export class BypassSleepAttr extends MoveAttr {
/**
* Attribute used for moves that bypass the burn damage reduction of physical moves, currently only facade
* Called during damage calculation
- * @extends MoveAttr
- * @see {@linkcode apply}
*/
export class BypassBurnDamageReductionAttr extends MoveAttr {
/** Prevents the move's damage from being reduced by burn
@@ -3144,7 +3112,6 @@ export class OneHitKOAttr extends MoveAttr {
/**
* Attribute that allows charge moves to resolve in 1 turn under a given condition.
* Should only be used for {@linkcode ChargingMove | ChargingMoves} as a `chargeAttr`.
- * @extends MoveAttr
*/
export class InstantChargeAttr extends MoveAttr {
/** The condition in which the move with this attribute instantly charges */
@@ -3181,7 +3148,6 @@ export class InstantChargeAttr extends MoveAttr {
/**
* Attribute that allows charge moves to resolve in 1 turn while specific {@linkcode WeatherType | Weather}
* is active. Should only be used for {@linkcode ChargingMove | ChargingMoves} as a `chargeAttr`.
- * @extends InstantChargeAttr
*/
export class WeatherInstantChargeAttr extends InstantChargeAttr {
constructor(weatherTypes: WeatherType[]) {
@@ -3268,7 +3234,6 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
)
)
- user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn})
user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn})
// Queue up an attack on the given slot.
globalScene.arena.positionalTagManager.addTag({
@@ -3312,7 +3277,6 @@ export class WishAttr extends MoveEffectAttr {
/**
* Attribute that cancels the associated move's effects when set to be combined with the user's ally's
* subsequent move this turn. Used for Grass Pledge, Water Pledge, and Fire Pledge.
- * @extends OverrideMoveEffectAttr
*/
export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
constructor() {
@@ -3367,7 +3331,6 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
/**
* Set of optional parameters that may be applied to stat stage changing effects
- * @extends MoveEffectAttrOptions
* @see {@linkcode StatStageChangeAttr}
*/
interface StatStageChangeAttrOptions extends MoveEffectAttrOptions {
@@ -3384,9 +3347,6 @@ interface StatStageChangeAttrOptions extends MoveEffectAttrOptions {
* @param stages How many stages to change the stat(s) by, [-6, 6]
* @param selfTarget `true` if the move is self-targetting
* @param options {@linkcode StatStageChangeAttrOptions} Container for any optional parameters for this attribute.
- *
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class StatStageChangeAttr extends MoveEffectAttr {
public stats: BattleStat[];
@@ -3817,8 +3777,6 @@ export class ResetStatsAttr extends MoveEffectAttr {
/**
* Attribute used for status moves, specifically Heart, Guard, and Power Swap,
* that swaps the user's and target's corresponding stat stages.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class SwapStatStagesAttr extends MoveEffectAttr {
/** The stat stages to be swapped between the user and the target */
@@ -4106,8 +4064,6 @@ export class WeightPowerAttr extends VariablePowerAttr {
/**
* Attribute used for Electro Ball move.
- * @extends VariablePowerAttr
- * @see {@linkcode apply}
**/
export class ElectroBallPowerAttr extends VariablePowerAttr {
/**
@@ -4141,8 +4097,6 @@ export class ElectroBallPowerAttr extends VariablePowerAttr {
/**
* Attribute used for Gyro Ball move.
- * @extends VariablePowerAttr
- * @see {@linkcode apply}
**/
export class GyroBallPowerAttr extends VariablePowerAttr {
/**
@@ -4398,11 +4352,11 @@ const countPositiveStatStages = (pokemon: Pokemon): number => {
export class PositiveStatStagePowerAttr extends VariablePowerAttr {
/**
- * @param {Pokemon} user The pokemon that is being used to calculate the amount of positive stats
- * @param {Pokemon} target N/A
- * @param {Move} move N/A
- * @param {any[]} args The argument for VariablePowerAttr, accumulates and sets the amount of power multiplied by stats
- * @returns {boolean} Returns true if attribute is applied
+ * @param user The pokemon that is being used to calculate the amount of positive stats
+ * @param target N/A
+ * @param move N/A
+ * @param args The argument for VariablePowerAttr, accumulates and sets the amount of power multiplied by stats
+ * @returns Returns true if attribute is applied
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const positiveStatStages: number = countPositiveStatStages(user);
@@ -4422,10 +4376,10 @@ export class PunishmentPowerAttr extends VariablePowerAttr {
private PUNISHMENT_MAX_BASE_POWER = 200;
/**
- * @param {Pokemon} user N/A
- * @param {Pokemon} target The pokemon that the move is being used against, as well as calculating the stats for the min/max base power
- * @param {Move} move N/A
- * @param {any[]} args The value that is being changed due to VariablePowerAttr
+ * @param user N/A
+ * @param target The pokemon that the move is being used against, as well as calculating the stats for the min/max base power
+ * @param move N/A
+ * @param args The value that is being changed due to VariablePowerAttr
* @returns Returns true if attribute is applied
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -4541,8 +4495,6 @@ const hasStockpileStacksCondition: MoveConditionFunc = (user) => {
/**
* Attribute used for multi-hit moves that increase power in increments of the
* move's base power for each hit, namely Triple Kick and Triple Axel.
- * @extends VariablePowerAttr
- * @see {@linkcode apply}
*/
export class MultiHitPowerIncrementAttr extends VariablePowerAttr {
/** The max number of base power increments allowed for this move */
@@ -4579,8 +4531,6 @@ export class MultiHitPowerIncrementAttr extends VariablePowerAttr {
* Attribute used for moves that double in power if the given move immediately
* preceded the move applying the attribute, namely Fusion Flare and
* Fusion Bolt.
- * @extends VariablePowerAttr
- * @see {@linkcode apply}
*/
export class LastMoveDoublePowerAttr extends VariablePowerAttr {
/** The move that must precede the current move */
@@ -4683,7 +4633,6 @@ export class CombinedPledgeStabBoostAttr extends MoveAttr {
/**
* Variable Power attribute for {@link https://bulbapedia.bulbagarden.net/wiki/Round_(move) | Round}.
* Doubles power if another Pokemon has previously selected Round this turn.
- * @extends VariablePowerAttr
*/
export class RoundPowerAttr extends VariablePowerAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder]): boolean {
@@ -4701,8 +4650,6 @@ export class RoundPowerAttr extends VariablePowerAttr {
* Attribute for the "combo" effect of {@link https://bulbapedia.bulbagarden.net/wiki/Round_(move) | Round}.
* Preempts the next move in the turn order with the first instance of any Pokemon
* using Round. Also marks the Pokemon using the cued Round to double the move's power.
- * @extends MoveEffectAttr
- * @see {@linkcode RoundPowerAttr}
*/
export class CueNextRoundAttr extends MoveEffectAttr {
constructor() {
@@ -4904,8 +4851,6 @@ export class StormAccuracyAttr extends VariableAccuracyAttr {
/**
* Attribute used for moves which never miss
* against Pokemon with the {@linkcode BattlerTagType.MINIMIZED}
- * @extends VariableAccuracyAttr
- * @see {@linkcode apply}
*/
export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr {
/**
@@ -4978,9 +4923,8 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr {
* Attribute used for tera moves that change category based on the user's Atk and SpAtk stats
* Note: Currently, `getEffectiveStat` does not ignore all abilities that affect stats except those
* with the attribute of `StatMultiplierAbAttr`
- * TODO: Remove the `.partial()` tag from Tera Blast and Tera Starstorm when the above issue is resolved
- * @extends VariableMoveCategoryAttr
*/
+// TODO: Remove the `.partial()` tag from Tera Blast and Tera Starstorm when the above issue is resolved
export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const category = (args[0] as NumberHolder);
@@ -4997,7 +4941,6 @@ export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
/**
* Increases the power of Tera Blast if the user is Terastallized into Stellar type
- * @extends VariablePowerAttr
*/
export class TeraBlastPowerAttr extends VariablePowerAttr {
/**
@@ -5024,8 +4967,6 @@ export class TeraBlastPowerAttr extends VariablePowerAttr {
/**
* Change the move category to status when used on the ally
- * @extends VariableMoveCategoryAttr
- * @see {@linkcode apply}
*/
export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr {
/**
@@ -5257,8 +5198,6 @@ export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
/**
* Changes the move's type to match the current terrain.
* Has no effect if the user is not grounded.
- * @extends VariableMoveTypeAttr
- * @see {@linkcode apply}
*/
export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
/**
@@ -5306,7 +5245,6 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
/**
* Changes type based on the user's IVs
- * @extends VariableMoveTypeAttr
*/
export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -5334,7 +5272,6 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
/**
* Changes the type of Tera Blast to match the user's tera type
- * @extends VariableMoveTypeAttr
*/
export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
/**
@@ -5361,7 +5298,6 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
/**
* Attribute used for Tera Starstorm that changes the move type to Stellar
- * @extends VariableMoveTypeAttr
*/
export class TeraStarstormTypeAttr extends VariableMoveTypeAttr {
/**
@@ -5407,7 +5343,6 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr {
/**
* Changes the type of a Pledge move based on the Pledge move combined with it.
- * @extends VariableMoveTypeAttr
*/
export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -5545,10 +5480,10 @@ export class SheerColdAccuracyAttr extends OneHitKOAccuracyAttr {
/**
* Changes the normal One Hit KO Accuracy Attr to implement the Gen VII changes,
* where if the user is Ice-Type, it has more accuracy.
- * @param {Pokemon} user Pokemon that is using the move; checks the Pokemon's level.
- * @param {Pokemon} target Pokemon that is receiving the move; checks the Pokemon's level.
- * @param {Move} move N/A
- * @param {any[]} args Uses the accuracy argument, allowing to change it from either 0 if it doesn't pass
+ * @param user Pokemon that is using the move; checks the Pokemon's level.
+ * @param target Pokemon that is receiving the move; checks the Pokemon's level.
+ * @param move N/A
+ * @param args Uses the accuracy argument, allowing to change it from either 0 if it doesn't pass
* the first if/else, or 30/20 depending on the type of the user Pokemon.
* @returns Returns true if move is successful, false if misses.
*/
@@ -5663,7 +5598,6 @@ export class FrenzyAttr extends MoveEffectAttr {
/**
* Attribute that grants {@link https://bulbapedia.bulbagarden.net/wiki/Semi-invulnerable_turn | semi-invulnerability} to the user during
* the associated move's charging phase. Should only be used for {@linkcode ChargingMove | ChargingMoves} as a `chargeAttr`.
- * @extends MoveEffectAttr
*/
export class SemiInvulnerableAttr extends MoveEffectAttr {
/** The type of {@linkcode SemiInvulnerableTag} to grant to the user */
@@ -5789,7 +5723,6 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
/**
* Adds a {@link https://bulbapedia.bulbagarden.net/wiki/Seeding | Seeding} effect to the target
* as seen with Leech Seed and Sappy Seed.
- * @extends AddBattlerTagAttr
*/
export class LeechSeedAttr extends AddBattlerTagAttr {
constructor() {
@@ -5799,7 +5732,6 @@ export class LeechSeedAttr extends AddBattlerTagAttr {
/**
* Adds the appropriate battler tag for Smack Down and Thousand arrows
- * @extends AddBattlerTagAttr
*/
export class FallDownAttr extends AddBattlerTagAttr {
constructor() {
@@ -5824,7 +5756,6 @@ export class FallDownAttr extends AddBattlerTagAttr {
/**
* Adds the appropriate battler tag for Gulp Missile when Surf or Dive is used.
- * @extends MoveEffectAttr
*/
export class GulpMissileTagAttr extends MoveEffectAttr {
constructor() {
@@ -5864,7 +5795,6 @@ export class GulpMissileTagAttr extends MoveEffectAttr {
/**
* Attribute to implement Jaw Lock's linked trapping effect between the user and target
- * @extends AddBattlerTagAttr
*/
export class JawLockAttr extends AddBattlerTagAttr {
constructor() {
@@ -6010,7 +5940,6 @@ export class ProtectAttr extends AddBattlerTagAttr {
/**
* Attribute to remove all Substitutes from the field.
- * @extends MoveEffectAttr
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Tidy_Up_(move) | Tidy Up}
* @see {@linkcode SubstituteTag}
*/
@@ -6042,7 +5971,6 @@ export class RemoveAllSubstitutesAttr extends MoveEffectAttr {
* Attribute used when a move can deal damage to {@linkcode BattlerTagType}
* Moves that always hit but do not deal double damage: Thunder, Fissure, Sky Uppercut,
* Smack Down, Hurricane, Thousand Arrows
- * @extends MoveAttr
*/
export class HitsTagAttr extends MoveAttr {
/** The {@linkcode BattlerTagType} this move hits */
@@ -6159,8 +6087,6 @@ export class AddArenaTrapTagAttr extends AddArenaTagAttr {
/**
* Attribute used for Stone Axe and Ceaseless Edge.
* Applies the given ArenaTrapTag when move is used.
- * @extends AddArenaTagAttr
- * @see {@linkcode apply}
*/
// TODO: This has exactly 1 line of code difference from the base attribute wrt. effect chances...
export class AddArenaTrapTagHitAttr extends AddArenaTagAttr {
@@ -6213,15 +6139,15 @@ export class RemoveScreensAttr extends RemoveArenaTagsAttr {
}
/**
- * Swaps arena effects between the player and enemy side
+ * Attribute to swap all valid {@linkcode ArenaTag}s between the player and enemy side of the field.
+ * Ones affecting both sides are unaffected.
*/
export class SwapArenaTagsAttr extends MoveEffectAttr {
- public SwapTags: ArenaTagType[];
+ private readonly validTags: ArenaTagType[];
-
- constructor(SwapTags: ArenaTagType[]) {
+ constructor(validTags: ArenaTagType[]) {
super(true);
- this.SwapTags = SwapTags;
+ this.validTags = validTags;
}
apply(user:Pokemon, target:Pokemon, move:Move, args: any[]): boolean {
@@ -6229,24 +6155,18 @@ export class SwapArenaTagsAttr extends MoveEffectAttr {
return false;
}
- const tagPlayerTemp = globalScene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.PLAYER);
- const tagEnemyTemp = globalScene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.ENEMY);
+ const tagPlayerTemp = globalScene.arena.findTagsOnSide((t => this.validTags.includes(t.tagType)), ArenaTagSide.PLAYER);
+ const tagEnemyTemp = globalScene.arena.findTagsOnSide((t => this.validTags.includes(t.tagType)), ArenaTagSide.ENEMY);
-
- if (tagPlayerTemp) {
- for (const swapTagsType of tagPlayerTemp) {
- globalScene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.PLAYER, true);
- globalScene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.ENEMY, true); // TODO: is the bang correct?
- }
+ for (const playerTag of tagPlayerTemp) {
+ globalScene.arena.removeTagOnSide(playerTag.tagType, ArenaTagSide.PLAYER, true);
+ globalScene.arena.addTag(playerTag.tagType, playerTag.turnCount, playerTag.sourceMove, playerTag.sourceId!, ArenaTagSide.ENEMY, true); // TODO: is the bang correct?
}
- if (tagEnemyTemp) {
- for (const swapTagsType of tagEnemyTemp) {
- globalScene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.ENEMY, true);
- globalScene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.PLAYER, true); // TODO: is the bang correct?
- }
+ for (const enemyTag of tagEnemyTemp) {
+ globalScene.arena.removeTagOnSide(enemyTag.tagType, ArenaTagSide.ENEMY, true);
+ globalScene.arena.addTag(enemyTag.tagType, enemyTag.turnCount, enemyTag.sourceMove, enemyTag.sourceId!, ArenaTagSide.PLAYER, true); // TODO: is the bang correct?
}
-
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:swapArenaTags", { pokemonName: getPokemonNameWithAffix(user) }));
return true;
}
@@ -6280,8 +6200,6 @@ export class AddPledgeEffectAttr extends AddArenaTagAttr {
/**
* Attribute used for Revival Blessing.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class RevivalBlessingAttr extends MoveEffectAttr {
constructor() {
@@ -6346,7 +6264,6 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
}
}
-
export class ForceSwitchOutAttr extends MoveEffectAttr {
constructor(
private selfSwitch: boolean = false,
@@ -6705,7 +6622,7 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
/**
* Retrieves a type from the current terrain
* @param terrainType {@linkcode TerrainType}
- * @returns {@linkcode Type}
+ * @returns
*/
private getTypeForTerrain(terrainType: TerrainType): PokemonType {
switch (terrainType) {
@@ -6726,7 +6643,7 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
/**
* Retrieves a type from the current biome
* @param biomeType {@linkcode BiomeId}
- * @returns {@linkcode Type}
+ * @returns
*/
private getTypeForBiome(biomeType: BiomeId): PokemonType {
switch (biomeType) {
@@ -6789,12 +6706,15 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
}
}
+/**
+ * Attribute to override the target's current types to the given type.
+ * Used by {@linkcode MoveId.SOAK} and {@linkcode MoveId.MAGIC_POWDER}.
+ */
export class ChangeTypeAttr extends MoveEffectAttr {
private type: PokemonType;
constructor(type: PokemonType) {
super(false);
-
this.type = type;
}
@@ -6802,7 +6722,7 @@ export class ChangeTypeAttr extends MoveEffectAttr {
target.summonData.types = [ this.type ];
target.updateInfo();
- globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(target), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`) }));
+ globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(target), typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.type])}`) }));
return true;
}
@@ -6856,8 +6776,6 @@ export class FirstMoveTypeAttr extends MoveEffectAttr {
/**
* Attribute used to call a move.
* Used by other move attributes: {@linkcode RandomMoveAttr}, {@linkcode RandomMovesetMoveAttr}, {@linkcode CopyMoveAttr}
- * @see {@linkcode apply} for move call
- * @extends OverrideMoveEffectAttr
*/
class CallMoveAttr extends OverrideMoveEffectAttr {
protected invalidMoves: ReadonlySet;
@@ -6889,8 +6807,6 @@ class CallMoveAttr extends OverrideMoveEffectAttr {
/**
* Attribute used to call a random move.
* Used for {@linkcode MoveId.METRONOME}
- * @see {@linkcode apply} for move selection and move call
- * @extends CallMoveAttr to call a selected move
*/
export class RandomMoveAttr extends CallMoveAttr {
constructor(invalidMoves: ReadonlySet) {
@@ -6938,8 +6854,6 @@ export class RandomMoveAttr extends CallMoveAttr {
* Fails if the user has no callable moves.
*
* Invalid moves are indicated by what is passed in to invalidMoves: {@linkcode invalidAssistMoves} or {@linkcode invalidSleepTalkMoves}
- * @extends RandomMoveAttr to use the callMove function on a moveId
- * @see {@linkcode getCondition} for move selection
*/
export class RandomMovesetMoveAttr extends CallMoveAttr {
private includeParty: boolean;
@@ -7125,8 +7039,6 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr {
/**
* Attribute used to copy a previously-used move.
* Used for {@linkcode MoveId.COPYCAT} and {@linkcode MoveId.MIRROR_MOVE}
- * @see {@linkcode apply} for move selection and move call
- * @extends CallMoveAttr to call a selected move
*/
export class CopyMoveAttr extends CallMoveAttr {
private mirrorMove: boolean;
@@ -7462,11 +7374,11 @@ export class SketchAttr extends MoveEffectAttr {
}
/**
* User copies the opponent's last used move, if possible
- * @param {Pokemon} user Pokemon that used the move and will replace Sketch with the copied move
- * @param {Pokemon} target Pokemon that the user wants to copy a move from
- * @param {Move} move Move being used
- * @param {any[]} args Unused
- * @returns {boolean} true if the function succeeds, otherwise false
+ * @param user Pokemon that used the move and will replace Sketch with the copied move
+ * @param target Pokemon that the user wants to copy a move from
+ * @param move Move being used
+ * @param args Unused
+ * @returns true if the function succeeds, otherwise false
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -7629,10 +7541,6 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr {
/**
* Attribute used for moves that suppress abilities like {@linkcode MoveId.GASTRO_ACID}.
* A suppressed ability cannot be activated.
- *
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
- * @see {@linkcode getCondition}
*/
export class SuppressAbilitiesAttr extends MoveEffectAttr {
/** Sets ability suppression for the target pokemon and displays a message. */
@@ -7658,8 +7566,7 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr {
/**
* Applies the effects of {@linkcode SuppressAbilitiesAttr} if the target has already moved this turn.
- * @extends MoveEffectAttr
- * @see {@linkcode MoveId.CORE_ENFORCER} (the move which uses this effect)
+ * @see {@linkcode MoveId.CORE_ENFORCER}
*/
export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr {
/**
@@ -7708,8 +7615,6 @@ export class TransformAttr extends MoveEffectAttr {
/**
* Attribute used for status moves, namely Speed Swap,
* that swaps the user's and target's corresponding stats.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class SwapStatAttr extends MoveEffectAttr {
/** The stat to be swapped between the user and the target */
@@ -7750,7 +7655,6 @@ export class SwapStatAttr extends MoveEffectAttr {
/**
* Attribute used to switch the user's own stats.
* Used by Power Shift.
- * @extends MoveEffectAttr
*/
export class ShiftStatAttr extends MoveEffectAttr {
private statToSwitch: EffectiveStat;
@@ -7765,7 +7669,7 @@ export class ShiftStatAttr extends MoveEffectAttr {
/**
* Switches the user's stats based on the {@linkcode statToSwitch} and {@linkcode statToSwitchWith} attributes.
- * @param {Pokemon} user the {@linkcode Pokemon} that used the move
+ * @param user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @param args n/a
@@ -7793,7 +7697,7 @@ export class ShiftStatAttr extends MoveEffectAttr {
/**
* Encourages the user to use the move if the stat to switch with is greater than the stat to switch.
- * @param {Pokemon} user the {@linkcode Pokemon} that used the move
+ * @param user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @returns number of points to add to the user's benefit score
@@ -7807,8 +7711,6 @@ export class ShiftStatAttr extends MoveEffectAttr {
* Attribute used for status moves, namely Power Split and Guard Split,
* that take the average of a user's and target's corresponding
* stats and assign that average back to each corresponding stat.
- * @extends MoveEffectAttr
- * @see {@linkcode apply}
*/
export class AverageStatsAttr extends MoveEffectAttr {
/** The stats to be averaged individually between the user and the target */
@@ -7861,11 +7763,7 @@ export class MoneyAttr extends MoveEffectAttr {
}
}
-/**
- * Applies {@linkcode BattlerTagType.DESTINY_BOND} to the user.
- *
- * @extends MoveEffectAttr
- */
+/** Applies {@linkcode BattlerTagType.DESTINY_BOND} to the user */
export class DestinyBondAttr extends MoveEffectAttr {
constructor() {
super(true, { trigger: MoveEffectTrigger.PRE_APPLY });
@@ -7876,7 +7774,7 @@ export class DestinyBondAttr extends MoveEffectAttr {
* @param user {@linkcode Pokemon} that is having the tag applied to.
* @param target {@linkcode Pokemon} N/A
* @param move {@linkcode Move} {@linkcode Move.DESTINY_BOND}
- * @param {any[]} args N/A
+ * @param args N/A
* @returns true
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -7886,10 +7784,7 @@ export class DestinyBondAttr extends MoveEffectAttr {
}
}
-/**
- * Attribute to apply a battler tag to the target if they have had their stats boosted this turn.
- * @extends AddBattlerTagAttr
- */
+/** Attribute to apply a battler tag to the target if they have had their stats boosted this turn */
export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr {
constructor(tag: BattlerTagType) {
super(tag, false, false, 2, 5);
@@ -7899,7 +7794,7 @@ export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr {
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
- * @param {any[]} args N/A
+ * @param args N/A
* @returns true
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -7912,7 +7807,6 @@ export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr {
/**
* Attribute to apply a status effect to the target if they have had their stats boosted this turn.
- * @extends MoveEffectAttr
*/
export class StatusIfBoostedAttr extends MoveEffectAttr {
public effect: StatusEffect;
@@ -7926,7 +7820,7 @@ export class StatusIfBoostedAttr extends MoveEffectAttr {
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} N/A
- * @param {any[]} args N/A
+ * @param args N/A
* @returns true
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@@ -8008,7 +7902,6 @@ export class AfterYouAttr extends MoveEffectAttr {
/**
* Move effect to force the target to move last, ignoring priority.
* If applied to multiple targets, they move in speed order after all other moves.
- * @extends MoveEffectAttr
*/
export class ForceLastAttr extends MoveEffectAttr {
/**
@@ -8070,9 +7963,12 @@ const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScen
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
const cancelled = new BooleanHolder(false);
- globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled}));
+ // temporary workaround to prevent displaying the message during enemy command phase
+ // TODO: either move this, or make the move condition func have a `simulated` param
+ const simulated = globalScene.phaseManager.getCurrentPhase()?.is('EnemyCommandPhase');
+ globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled, simulated}));
// Queue a message if an ability prevented usage of the move
- if (cancelled.value) {
+ if (!simulated && cancelled.value) {
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
}
return !cancelled.value;
@@ -8094,6 +7990,9 @@ const failIfGhostTypeCondition: MoveConditionFunc = (user: Pokemon, target: Poke
const failIfNoTargetHeldItemsCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.getHeldItems().filter(i => i.isTransferable)?.length > 0;
const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => {
+ if (isNullOrUndefined(target)) { // Fix bug when used against targets that have both fainted
+ return "";
+ }
const heldItems = target.getHeldItems().filter(i => i.isTransferable);
if (heldItems.length === 0) {
return "";
@@ -8181,11 +8080,11 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
/**
* User changes its type to a random type that resists the target's last used move
- * @param {Pokemon} user Pokemon that used the move and will change types
- * @param {Pokemon} target Opposing pokemon that recently used a move
- * @param {Move} move Move being used
- * @param {any[]} args Unused
- * @returns {boolean} true if the function succeeds
+ * @param user Pokemon that used the move and will change types
+ * @param target Opposing pokemon that recently used a move
+ * @param move Move being used
+ * @param args Unused
+ * @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
@@ -8246,9 +8145,6 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
* Drops the target's immunity to types it is immune to
* and makes its evasiveness be ignored during accuracy
* checks. Used by: {@linkcode MoveId.ODOR_SLEUTH | Odor Sleuth}, {@linkcode MoveId.MIRACLE_EYE | Miracle Eye} and {@linkcode MoveId.FORESIGHT | Foresight}
- *
- * @extends AddBattlerTagAttr
- * @see {@linkcode apply}
*/
export class ExposedMoveAttr extends AddBattlerTagAttr {
constructor(tagType: BattlerTagType) {
@@ -8835,7 +8731,9 @@ export function initMoves() {
.attr(AddArenaTagAttr, ArenaTagType.REFLECT, 5, true)
.target(MoveTarget.USER_SIDE),
new SelfStatusMove(MoveId.FOCUS_ENERGY, PokemonType.NORMAL, -1, 30, -1, 0, 1)
- .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, true),
+ .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, true)
+ // TODO: Remove once dragon cheer & focus energy are merged into 1 tag
+ .condition((_user, target) => !target.getTag(BattlerTagType.DRAGON_CHEER)),
new AttackMove(MoveId.BIDE, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, 1, 1)
.target(MoveTarget.USER)
.unimplemented(),
@@ -9362,7 +9260,9 @@ export function initMoves() {
.attr(AddBattlerTagAttr, BattlerTagType.HELPING_HAND)
.ignoresSubstitute()
.target(MoveTarget.NEAR_ALLY)
- .condition(failIfSingleBattle),
+ .condition(failIfSingleBattle)
+ // should stack multiplicatively if used multiple times in 1 turn
+ .edgeCase(),
new StatusMove(MoveId.TRICK, PokemonType.PSYCHIC, 100, 10, -1, 0, 3)
.unimplemented(),
new StatusMove(MoveId.ROLE_PLAY, PokemonType.PSYCHIC, -1, 10, -1, 0, 3)
@@ -11532,6 +11432,8 @@ export function initMoves() {
.attr(OpponentHighHpPowerAttr, 100),
new StatusMove(MoveId.DRAGON_CHEER, PokemonType.DRAGON, -1, 15, -1, 0, 9)
.attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true)
+ // TODO: Remove once dragon cheer & focus energy are merged into 1 tag
+ .condition((_user, target) => !target.getTag(BattlerTagType.CRIT_BOOST))
.target(MoveTarget.NEAR_ALLY),
new AttackMove(MoveId.ALLURING_VOICE, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9)
.attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED)
diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts
index cdb8d628be1..23daf0a971b 100644
--- a/src/data/moves/pokemon-move.ts
+++ b/src/data/moves/pokemon-move.ts
@@ -50,10 +50,9 @@ export class PokemonMove {
const move = this.getMove();
// TODO: Add Sky Drop's 1 turn stall
const usability = new BooleanHolder(
- !move.name.endsWith(" (N)") &&
- (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) &&
- // TODO: Review if the `MoveId.NONE` check is even necessary anymore
- !(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
+ !move.name.endsWith(" (N)")
+ && (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) // TODO: Review if the `MoveId.NONE` check is even necessary anymore
+ && !(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
);
if (pokemon.isPlayer()) {
applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability);
diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts
index 66364bfc485..98b4d06723a 100644
--- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts
+++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts
@@ -111,7 +111,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
const trainerSpriteKey = trainerConfig.getSpriteKey();
encounter.enemyPartyConfigs.push({
levelAdditiveModifier: 1,
- trainerConfig: trainerConfig,
+ trainerConfig,
});
encounter.spriteConfigs = [
diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts
index bf9c49e8c5b..8b28065dad1 100644
--- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts
+++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts
@@ -527,7 +527,8 @@ function doBerrySpritePile(isEat = false) {
const encounter = globalScene.currentBattle.mysteryEncounter!;
animationOrder.forEach((berry, i) => {
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
- let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
+ let sprite: Phaser.GameObjects.Sprite;
+ let tintSprite: Phaser.GameObjects.Sprite;
const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex);
if (sprites) {
sprite = sprites[0];
diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts
index 7d4fb0ef98f..0c47850f2b7 100644
--- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts
+++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts
@@ -93,8 +93,8 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
// Store pokemon and price
encounter.misc = {
- pokemon: pokemon,
- price: price,
+ pokemon,
+ price,
};
// If player meets the combo OR requirements for option 2, populate the token
diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts
index 196ca873f4e..a4f2b00b04b 100644
--- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts
+++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts
@@ -71,7 +71,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- level: level,
+ level,
species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
@@ -105,8 +105,8 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
hasShadow: true,
},
{
- spriteKey: spriteKey,
- fileRoot: fileRoot,
+ spriteKey,
+ fileRoot,
hasShadow: true,
tint: 0.25,
x: -5,
@@ -237,7 +237,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config = globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
- queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
+ queueEncounterMessage(`${namespace}:option.2.bossEnraged`);
globalScene.phaseManager.unshiftNew(
"StatStageChangePhase",
pokemon.getBattlerIndex(),
@@ -320,9 +320,9 @@ function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
if (prioritizedPokemon) {
const heldBerriesOfType = globalScene.findModifier(
m =>
- m instanceof BerryModifier &&
- m.pokemonId === prioritizedPokemon.id &&
- (m as BerryModifier).berryType === berryType,
+ m instanceof BerryModifier
+ && m.pokemonId === prioritizedPokemon.id
+ && (m as BerryModifier).berryType === berryType,
true,
) as BerryModifier;
diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts
index 6750051c3c4..5462c0eb336 100644
--- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts
+++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts
@@ -46,8 +46,8 @@ import {
} from "#mystery-encounters/mystery-encounter-requirements";
import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#utils/common";
import i18next from "i18next";
@@ -213,7 +213,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
female: true,
});
- let beedrillKeys: { spriteKey: string; fileRoot: string }, butterfreeKeys: { spriteKey: string; fileRoot: string };
+ let beedrillKeys: { spriteKey: string; fileRoot: string };
+ let butterfreeKeys: { spriteKey: string; fileRoot: string };
if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
beedrillKeys = getSpriteKeysFromSpecies(SpeciesId.BEEDRILL, false);
butterfreeKeys = getSpriteKeysFromSpecies(SpeciesId.BUTTERFREE, false);
@@ -247,7 +248,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
yShadow: -4,
},
{
- spriteKey: spriteKey,
+ spriteKey,
fileRoot: "trainer",
hasShadow: true,
x: 4,
@@ -440,11 +441,11 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
// Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter(item => {
return (
- (item instanceof BypassSpeedChanceModifier ||
- item instanceof ContactHeldItemTransferChanceModifier ||
- (item instanceof AttackTypeBoosterModifier &&
- (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)) &&
- item.isTransferable
+ (item instanceof BypassSpeedChanceModifier
+ || item instanceof ContactHeldItemTransferChanceModifier
+ || (item instanceof AttackTypeBoosterModifier
+ && (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG))
+ && item.isTransferable
);
});
@@ -469,10 +470,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
// If pokemon has valid item, it can be selected
const hasValidItem = pokemon.getHeldItems().some(item => {
return (
- item instanceof BypassSpeedChanceModifier ||
- item instanceof ContactHeldItemTransferChanceModifier ||
- (item instanceof AttackTypeBoosterModifier &&
- (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)
+ item instanceof BypassSpeedChanceModifier
+ || item instanceof ContactHeldItemTransferChanceModifier
+ || (item instanceof AttackTypeBoosterModifier
+ && (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)
);
});
if (!hasValidItem) {
diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts
index 09e59c7e391..42907455e22 100644
--- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts
+++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts
@@ -45,7 +45,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import { trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template";
-import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectConfig } from "#ui/handlers/abstract-option-select-ui-handler";
import { randSeedInt, randSeedShuffle } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next";
@@ -163,7 +163,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
// Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(SpeciesId.BLACEPHALON),
customPokemonData: new CustomPokemonData({
- ability: ability,
+ ability,
types: [firstType, secondType],
}),
isBoss: true,
diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts
index e2c330a1106..598f9d496a2 100644
--- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts
+++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts
@@ -37,7 +37,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
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/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next";
@@ -168,7 +168,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- species: species,
+ species,
dataSource: oricorioData,
isBoss: true,
// Gets +1 to all stats except SPD on battle start
diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts
index 8cd4c8bee66..79cccd91b26 100644
--- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts
+++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts
@@ -33,7 +33,7 @@ import {
MoneyRequirement,
} from "#mystery-encounters/mystery-encounter-requirements";
import i18next from "#plugins/i18n";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { randSeedItem } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts
index 67a7cad3466..30c4026fcad 100644
--- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts
+++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts
@@ -18,7 +18,7 @@ import {
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/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import i18next from "i18next";
/** i18n namespace for the encounter */
@@ -249,6 +249,6 @@ function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correct
setEncounterExp([pokemon.id], 100);
}
encounter.misc = {
- correctMove: correctMove,
+ correctMove,
};
}
diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts
index f7dc89b44fd..a46bac013cc 100644
--- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts
+++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts
@@ -63,7 +63,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- level: level,
+ level,
species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
@@ -120,8 +120,8 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
disableAnimation: true,
},
{
- spriteKey: spriteKey,
- fileRoot: fileRoot,
+ spriteKey,
+ fileRoot,
hasShadow: true,
tint: 0.25,
x: -5,
diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts
index a8dafdaa848..7dbbe24fb69 100644
--- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts
+++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts
@@ -42,7 +42,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
import { PartySizeRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import { PokemonData } from "#system/pokemon-data";
import { MusicPreference } from "#system/settings";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common";
import { getEnumKeys } from "#utils/enums";
import { getRandomLocaleEntry } from "#utils/i18n";
@@ -195,10 +195,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
: ""
}`;
const line2 =
- i18next.t("pokemonInfoContainer:nature") +
- " " +
- getNatureName(tradePokemon.getNature()) +
- (formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : "");
+ i18next.t("pokemonInfoContainer:nature")
+ + " "
+ + getNatureName(tradePokemon.getNature())
+ + (formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : "");
showEncounterText(`${line1}\n${line2}`, 0, 0, false);
},
};
@@ -292,16 +292,14 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
// Extra HA roll at base 1/64 odds (boosted by events and charms)
const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
- if (tradePokemon.species.abilityHidden) {
- if (tradePokemon.abilityIndex < hiddenIndex) {
- const hiddenAbilityChance = new NumberHolder(64);
- globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
+ if (tradePokemon.species.abilityHidden && tradePokemon.abilityIndex < hiddenIndex) {
+ const hiddenAbilityChance = new NumberHolder(64);
+ globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
- const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
+ const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
- if (hasHiddenAbility) {
- tradePokemon.abilityIndex = hiddenIndex;
- }
+ if (hasHiddenAbility) {
+ tradePokemon.abilityIndex = hiddenIndex;
}
}
diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts
index 6f15f150d8b..7db55010bef 100644
--- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts
+++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts
@@ -50,7 +50,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
encounter.enemyPartyConfigs.push({
trainerConfig: normalConfig,
- female: female,
+ female,
});
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
@@ -81,7 +81,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
encounter.enemyPartyConfigs.push({
trainerConfig: hardConfig,
levelAdditiveModifier: 1,
- female: female,
+ female,
});
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
@@ -101,7 +101,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
encounter.enemyPartyConfigs.push({
trainerConfig: brutalConfig,
levelAdditiveModifier: 1.5,
- female: female,
+ female,
});
encounter.spriteConfigs = [
diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts
index 1bc2404dc27..88d00f05ca8 100644
--- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts
+++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts
@@ -163,8 +163,12 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
queueEncounterMessage(`${namespace}:option.1.great`);
leaveEncounterWithoutBattle();
} else if (
- roll >=
- RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT
+ roll
+ >= RAND_LENGTH
+ - COMMON_REWARDS_PERCENT
+ - ULTRA_REWARDS_PERCENT
+ - ROGUE_REWARDS_PERCENT
+ - MASTER_REWARDS_PERCENT
) {
// Choose 1 MASTER tier item (5%)
setEncounterRewards({
diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts
index bf232c616d5..fa65164ecfd 100644
--- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts
+++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts
@@ -104,7 +104,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = {
chosenPokemon: pokemon,
- modifiers: modifiers,
+ modifiers,
};
};
@@ -187,7 +187,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = {
chosenPokemon: pokemon,
- modifiers: modifiers,
+ modifiers,
};
};
diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts
index 58ab3f2ec2d..b280f827b5c 100644
--- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts
+++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts
@@ -165,7 +165,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- level: level,
+ level,
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
@@ -221,7 +221,7 @@ async function doBiomeTransitionDialogueAndBattleInit() {
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- level: level,
+ level,
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts
index 99f6e671052..292c866c0ee 100644
--- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts
+++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts
@@ -92,11 +92,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
.getEventEncounters()
.filter(
s =>
- !getPokemonSpecies(s.species).legendary &&
- !getPokemonSpecies(s.species).subLegendary &&
- !getPokemonSpecies(s.species).mythical &&
- !NON_LEGEND_PARADOX_POKEMON.includes(s.species) &&
- !NON_LEGEND_ULTRA_BEASTS.includes(s.species),
+ !getPokemonSpecies(s.species).legendary
+ && !getPokemonSpecies(s.species).subLegendary
+ && !getPokemonSpecies(s.species).mythical
+ && !NON_LEGEND_PARADOX_POKEMON.includes(s.species)
+ && !NON_LEGEND_ULTRA_BEASTS.includes(s.species),
);
let pokemon: PlayerPokemon;
@@ -109,16 +109,16 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
* Mons rolled from the event encounter pool get 3 extra shiny rolls
*/
if (
- r === 0 ||
- ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) &&
- validEventEncounters.length === 0)
+ r === 0
+ || ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
+ && validEventEncounters.length === 0)
) {
// If you roll 1%, give shiny Magikarp with random variant
species = getPokemonSpecies(SpeciesId.MAGIKARP);
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
} else if (
- validEventEncounters.length > 0 &&
- (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
+ validEventEncounters.length > 0
+ && (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
) {
tries = 0;
do {
@@ -162,8 +162,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
encounter.spriteConfigs.push({
- spriteKey: spriteKey,
- fileRoot: fileRoot,
+ spriteKey,
+ fileRoot,
hasShadow: true,
repeat: true,
isPokemon: true,
@@ -185,8 +185,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString());
encounter.misc = {
- price: price,
- pokemon: pokemon,
+ price,
+ pokemon,
};
pokemon.calculateStats();
diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts
index 71fe961f053..fbdc1c8f714 100644
--- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts
+++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts
@@ -212,9 +212,9 @@ function endTrainerBattleAndShowDialogue(): Promise {
// Only trigger form change when Eiscue is in Noice form
// Hardcoded Eiscue for now in case it is fused with another pokemon
if (
- pokemon.species.speciesId === SpeciesId.EISCUE &&
- pokemon.hasAbility(AbilityId.ICE_FACE) &&
- pokemon.formIndex === 1
+ pokemon.species.speciesId === SpeciesId.EISCUE
+ && pokemon.hasAbility(AbilityId.ICE_FACE)
+ && pokemon.formIndex === 1
) {
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger);
}
diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts
index b8173aa5fed..796840c431f 100644
--- a/src/data/mystery-encounters/encounters/training-session-encounter.ts
+++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts
@@ -27,7 +27,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import { PokemonData } from "#system/pokemon-data";
import type { HeldModifierConfig } from "#types/held-modifier-config";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { isNullOrUndefined, randSeedShuffle } from "#utils/common";
import { getEnumValues } from "#utils/enums";
import i18next from "i18next";
@@ -113,7 +113,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
let ivIndexes: any[] = [];
playerPokemon.ivs.forEach((iv, index) => {
if (iv < 31) {
- ivIndexes.push({ iv: iv, index: index });
+ ivIndexes.push({ iv, index });
}
});
@@ -188,7 +188,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
// Return the options for nature selection
return getEnumValues(Nature).map((nature: Nature) => {
const option: OptionSelectItem = {
- label: getNatureName(nature, true, true, true, globalScene.uiTheme),
+ label: getNatureName(nature, true, true, true),
handler: () => {
// Pokemon and second option selected
encounter.setDialogueToken("nature", getNatureName(nature));
@@ -324,9 +324,9 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
// Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals)
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
if (
- !isNullOrUndefined(rootFusionSpecies) &&
- speciesStarterCosts.hasOwnProperty(rootFusionSpecies) &&
- !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr
+ !isNullOrUndefined(rootFusionSpecies)
+ && speciesStarterCosts.hasOwnProperty(rootFusionSpecies)
+ && !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr
) {
globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |=
playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
@@ -396,7 +396,7 @@ function getEnemyConfig(playerPokemon: PlayerPokemon, segments: number, modifier
formIndex: playerPokemon.formIndex,
level: playerPokemon.level,
dataSource: data,
- modifierConfigs: modifierConfigs,
+ modifierConfigs,
},
],
};
diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts
index af576ffd8be..ed588ea2884 100644
--- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts
+++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts
@@ -249,7 +249,7 @@ async function tryApplyDigRewardItems() {
await showEncounterText(
i18next.t("battle:rewardGainCount", {
modifierName: leftovers.name,
- count: 2,
+ count: 1,
}),
null,
undefined,
diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts
index 6aeff852de7..7bbc4a57757 100644
--- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts
+++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts
@@ -71,7 +71,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const randomEggMove: MoveId = eggMoves[eggMoveIndex];
encounter.misc = {
eggMove: randomEggMove,
- pokemon: pokemon,
+ pokemon,
};
if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove));
@@ -91,7 +91,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
- level: level,
+ level,
species: pokemon.species,
dataSource: new PokemonData(pokemon),
isBoss: false,
@@ -114,8 +114,8 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
encounter.spriteConfigs = [
{
- spriteKey: spriteKey,
- fileRoot: fileRoot,
+ spriteKey,
+ fileRoot,
hasShadow: true,
x: -5,
repeat: true,
diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts
index 790bdf0dbef..1fcbd2961d1 100644
--- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts
+++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts
@@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene";
import { allSpecies, modifierTypes } from "#data/data-lists";
import { getLevelTotalExp } from "#data/exp";
import type { PokemonSpecies } from "#data/pokemon-species";
+import { AbilityId } from "#enums/ability-id";
import { Challenges } from "#enums/challenges";
import { ModifierTier } from "#enums/modifier-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@@ -10,8 +11,9 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Nature } from "#enums/nature";
import { PartyMemberStrength } from "#enums/party-member-strength";
import { PlayerGender } from "#enums/player-gender";
-import { PokemonType } from "#enums/pokemon-type";
+import { MAX_POKEMON_TYPE, PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
+import { StatusEffect } from "#enums/status-effect";
import { TrainerType } from "#enums/trainer-type";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import type { PokemonHeldItemModifier } from "#modifiers/modifier";
@@ -219,6 +221,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
await showEncounterText(`${namespace}:option.1.dreamComplete`);
await doNewTeamPostProcess(transformations);
+ globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
setEncounterRewards({
guaranteedModifierTypeFuncs: [
modifierTypes.MEMORY_MUSHROOM,
@@ -230,7 +233,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
],
fillRemaining: false,
});
- leaveEncounterWithoutBattle(true);
+ leaveEncounterWithoutBattle(false);
})
.build(),
)
@@ -282,7 +285,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
species: transformation.newSpecies,
isBoss: newPokemon.getSpeciesForm().getBaseStatTotal() > NON_LEGENDARY_BST_THRESHOLD,
level: previousPokemon.level,
- dataSource: dataSource,
+ dataSource,
modifierConfigs: newPokemonHeldItemConfigs,
};
@@ -296,7 +299,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
].clone();
trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG));
const enemyPartyConfig: EnemyPartyConfig = {
- trainerConfig: trainerConfig,
+ trainerConfig,
pokemonConfigs: enemyPokemonConfigs,
female: genderIndex === PlayerGender.FEMALE,
};
@@ -431,6 +434,8 @@ function getTeamTransformations(): PokemonTransformation[] {
newAbilityIndex,
undefined,
);
+
+ transformation.newPokemon.teraType = randSeedInt(MAX_POKEMON_TYPE);
}
return pokemonTransformations;
@@ -440,6 +445,8 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
let atLeastOneNewStarter = false;
for (const transformation of transformations) {
const previousPokemon = transformation.previousPokemon;
+ const oldHpRatio = previousPokemon.getHpRatio(true);
+ const oldStatus = previousPokemon.status;
const newPokemon = transformation.newPokemon;
const speciesRootForm = newPokemon.species.getRootSpeciesId();
@@ -462,6 +469,19 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
}
newPokemon.calculateStats();
+ if (oldHpRatio > 0) {
+ newPokemon.hp = Math.ceil(oldHpRatio * newPokemon.getMaxHp());
+ // Assume that the `status` instance can always safely be transferred to the new pokemon
+ // This is the case (as of version 1.10.4)
+ // Safeguard against COMATOSE here
+ if (!newPokemon.hasAbility(AbilityId.COMATOSE, false, true)) {
+ newPokemon.status = oldStatus;
+ }
+ } else {
+ newPokemon.hp = 0;
+ newPokemon.doSetStatus(StatusEffect.FAINT);
+ }
+
await newPokemon.updateInfo();
}
@@ -521,12 +541,12 @@ async function postProcessTransformedPokemon(
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
if (
- !forBattle &&
- (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())
+ !forBattle
+ && (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())
) {
if (
- newPokemon.getSpeciesForm().abilityHidden &&
- newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1
+ newPokemon.getSpeciesForm().abilityHidden
+ && newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1
) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
}
@@ -624,10 +644,10 @@ function getTransformedSpecies(
const bstInRange = speciesBst >= bstMin && speciesBst <= bstCap;
// Checks that a Pokemon has not already been added in the +600 or 570-600 slots;
const validBst =
- (!hasPokemonBstBetween570And600 ||
- speciesBst < NON_LEGENDARY_BST_THRESHOLD ||
- speciesBst > SUPER_LEGENDARY_BST_THRESHOLD) &&
- (!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD);
+ (!hasPokemonBstBetween570And600
+ || speciesBst < NON_LEGENDARY_BST_THRESHOLD
+ || speciesBst > SUPER_LEGENDARY_BST_THRESHOLD)
+ && (!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD);
return bstInRange && validBst && !EXCLUDED_TRANSFORMATION_SPECIES.includes(s.speciesId);
});
@@ -772,9 +792,9 @@ async function addEggMoveToNewPokemonMoveset(
// For pokemon that the player owns (including ones just caught), unlock the egg move
if (
- !forBattle &&
- !isNullOrUndefined(randomEggMoveIndex) &&
- !!globalScene.gameData.dexData[speciesRootForm].caughtAttr
+ !forBattle
+ && !isNullOrUndefined(randomEggMoveIndex)
+ && !!globalScene.gameData.dexData[speciesRootForm].caughtAttr
) {
await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
}
diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts
index 6ab2f8dae00..fc7bb15d343 100644
--- a/src/data/mystery-encounters/mystery-encounter-option.ts
+++ b/src/data/mystery-encounters/mystery-encounter-option.ts
@@ -76,9 +76,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
*/
hasRequirements(): boolean {
return (
- this.requirements.length > 0 ||
- this.primaryPokemonRequirements.length > 0 ||
- this.secondaryPokemonRequirements.length > 0
+ this.requirements.length > 0
+ || this.primaryPokemonRequirements.length > 0
+ || this.secondaryPokemonRequirements.length > 0
);
}
@@ -87,9 +87,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
*/
meetsRequirements(): boolean {
return (
- !this.requirements.some(requirement => !requirement.meetsRequirement()) &&
- this.meetsSupportingRequirementAndSupportingPokemonSelected() &&
- this.meetsPrimaryRequirementAndPrimaryPokemonSelected()
+ !this.requirements.some(requirement => !requirement.meetsRequirement())
+ && this.meetsSupportingRequirementAndSupportingPokemonSelected()
+ && this.meetsPrimaryRequirementAndPrimaryPokemonSelected()
);
}
@@ -209,7 +209,7 @@ export class MysteryEncounterOptionBuilder implements Partial> {
- return Object.assign(this, { hasDexProgress: hasDexProgress });
+ return Object.assign(this, { hasDexProgress });
}
/**
@@ -220,7 +220,7 @@ export class MysteryEncounterOptionBuilder implements Partial> {
if (requirement instanceof EncounterPokemonRequirement) {
- Error("Incorrectly added pokemon requirement as scene requirement.");
+ new Error("Incorrectly added pokemon requirement as scene requirement.");
}
this.requirements.push(requirement);
@@ -240,7 +240,7 @@ export class MysteryEncounterOptionBuilder implements Partial> {
- return Object.assign(this, { onPreOptionPhase: onPreOptionPhase });
+ return Object.assign(this, { onPreOptionPhase });
}
/**
@@ -248,13 +248,13 @@ export class MysteryEncounterOptionBuilder implements Partial> {
- return Object.assign(this, { onOptionPhase: onOptionPhase });
+ return Object.assign(this, { onOptionPhase });
}
withPostOptionPhase(
onPostOptionPhase: OptionPhaseCallback,
): this & Required> {
- return Object.assign(this, { onPostOptionPhase: onPostOptionPhase });
+ return Object.assign(this, { onPostOptionPhase });
}
/**
@@ -265,7 +265,7 @@ export class MysteryEncounterOptionBuilder implements Partial> {
if (requirement instanceof EncounterSceneRequirement) {
- Error("Incorrectly added scene requirement as pokemon requirement.");
+ new Error("Incorrectly added scene requirement as pokemon requirement.");
}
this.primaryPokemonRequirements.push(requirement);
@@ -315,7 +315,7 @@ export class MysteryEncounterOptionBuilder implements Partial> {
if (requirement instanceof EncounterSceneRequirement) {
- Error("Incorrectly added scene requirement as pokemon requirement.");
+ new Error("Incorrectly added scene requirement as pokemon requirement.");
}
this.secondaryPokemonRequirements.push(requirement);
diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts
index d71964db4b8..f20d513419e 100644
--- a/src/data/mystery-encounters/mystery-encounter-requirements.ts
+++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts
@@ -222,8 +222,8 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
const waveIndex = globalScene.currentBattle.waveIndex;
if (
- (waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) ||
- (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)
+ (waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex)
+ || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)
) {
return false;
}
@@ -276,9 +276,9 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
override meetsRequirement(): boolean {
const timeOfDay = globalScene.arena?.getTimeOfDay();
return !(
- !isNullOrUndefined(timeOfDay) &&
- this.requiredTimeOfDay?.length > 0 &&
- !this.requiredTimeOfDay.includes(timeOfDay)
+ !isNullOrUndefined(timeOfDay)
+ && this.requiredTimeOfDay?.length > 0
+ && !this.requiredTimeOfDay.includes(timeOfDay)
);
}
@@ -298,9 +298,9 @@ export class WeatherRequirement extends EncounterSceneRequirement {
override meetsRequirement(): boolean {
const currentWeather = globalScene.arena.weather?.weatherType;
return !(
- !isNullOrUndefined(currentWeather) &&
- this.requiredWeather?.length > 0 &&
- !this.requiredWeather.includes(currentWeather!)
+ !isNullOrUndefined(currentWeather)
+ && this.requiredWeather?.length > 0
+ && !this.requiredWeather.includes(currentWeather!)
);
}
@@ -336,8 +336,8 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
? globalScene.getPokemonAllowedInBattle().length
: globalScene.getPlayerParty().length;
if (
- (partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) ||
- (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)
+ (partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize)
+ || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)
) {
return false;
}
@@ -572,15 +572,15 @@ export class MoveRequirement extends EncounterPokemonRequirement {
// get the Pokemon with at least one move in the required moves list
return partyPokemon.filter(
pokemon =>
- (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
- pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
+ (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
+ && pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
);
}
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves
return partyPokemon.filter(
pokemon =>
- (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
- !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
+ (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
+ && !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
);
}
@@ -678,15 +678,15 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
if (!this.invertQuery) {
return partyPokemon.filter(
pokemon =>
- (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
- this.requiredAbilities.some(ability => pokemon.hasAbility(ability, false)),
+ (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
+ && this.requiredAbilities.some(ability => pokemon.hasAbility(ability, false)),
);
}
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed abilities
return partyPokemon.filter(
pokemon =>
- (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
- this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0,
+ (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
+ && this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0,
);
}
@@ -728,9 +728,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status
return (
- isNullOrUndefined(pokemon.status) ||
- isNullOrUndefined(pokemon.status.effect) ||
- pokemon.status.effect === statusEffect
+ isNullOrUndefined(pokemon.status)
+ || isNullOrUndefined(pokemon.status.effect)
+ || pokemon.status.effect === statusEffect
);
}
return pokemon.status?.effect === statusEffect;
@@ -743,9 +743,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status
return (
- isNullOrUndefined(pokemon.status) ||
- isNullOrUndefined(pokemon.status.effect) ||
- pokemon.status.effect === statusEffect
+ isNullOrUndefined(pokemon.status)
+ || isNullOrUndefined(pokemon.status.effect)
+ || pokemon.status.effect === statusEffect
);
}
return pokemon.status?.effect === statusEffect;
@@ -796,9 +796,8 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
filterByForm(pokemon, formChangeItem) {
return (
- pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
- // Get all form changes for this species with an item trigger, including any compound triggers
- pokemonFormChanges[pokemon.species.speciesId]
+ pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) // Get all form changes for this species with an item trigger, including any compound triggers
+ && pokemonFormChanges[pokemon.species.speciesId]
.filter(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger))
// Returns true if any form changes match this item
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
@@ -870,8 +869,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
pokemon =>
pokemon.getHeldItems().filter(it => {
return (
- !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
- (!this.requireTransferable || it.isTransferable)
+ !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
+ && (!this.requireTransferable || it.isTransferable)
);
}).length > 0,
);
@@ -880,8 +879,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter(it => {
return (
- this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
- (!this.requireTransferable || it.isTransferable)
+ this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
+ && (!this.requireTransferable || it.isTransferable)
);
});
if (requiredItems && requiredItems.length > 0) {
@@ -924,9 +923,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
this.requiredHeldItemTypes.some(heldItemType => {
return pokemon.getHeldItems().some(it => {
return (
- it instanceof AttackTypeBoosterModifier &&
- (it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
- (!this.requireTransferable || it.isTransferable)
+ it instanceof AttackTypeBoosterModifier
+ && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
+ && (!this.requireTransferable || it.isTransferable)
);
});
}),
@@ -939,9 +938,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
pokemon.getHeldItems().filter(it => {
return !this.requiredHeldItemTypes.some(
heldItemType =>
- it instanceof AttackTypeBoosterModifier &&
- (it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
- (!this.requireTransferable || it.isTransferable),
+ it instanceof AttackTypeBoosterModifier
+ && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
+ && (!this.requireTransferable || it.isTransferable),
);
}).length > 0,
);
@@ -952,10 +951,10 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
return (
this.requiredHeldItemTypes.some(
heldItemType =>
- it instanceof AttackTypeBoosterModifier &&
- (it.type as AttackTypeBoosterModifierType).moveType === heldItemType,
- ) &&
- (!this.requireTransferable || it.isTransferable)
+ it instanceof AttackTypeBoosterModifier
+ && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType,
+ )
+ && (!this.requireTransferable || it.isTransferable)
);
});
if (requiredItems && requiredItems.length > 0) {
@@ -1021,8 +1020,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
override meetsRequirement(): boolean {
// Party Pokemon inside required friendship range
if (
- !isNullOrUndefined(this.requiredFriendshipRange) &&
- this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]
+ !isNullOrUndefined(this.requiredFriendshipRange)
+ && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]
) {
const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon);
@@ -1037,8 +1036,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
if (!this.invertQuery) {
return partyPokemon.filter(
pokemon =>
- pokemon.friendship >= this.requiredFriendshipRange[0] &&
- pokemon.friendship <= this.requiredFriendshipRange[1],
+ pokemon.friendship >= this.requiredFriendshipRange[0]
+ && pokemon.friendship <= this.requiredFriendshipRange[1],
);
}
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed requiredFriendshipRanges
diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts
index 580fdc2ca38..273e14248e6 100644
--- a/src/data/mystery-encounters/mystery-encounter.ts
+++ b/src/data/mystery-encounters/mystery-encounter.ts
@@ -576,10 +576,9 @@ export class MysteryEncounterBuilder implements Partial {
*/
/**
- * @static Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType
- * NOTE: if new functions are added to {@linkcode MysteryEncounter} class
+ * Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType
* @param encounterType
- * @returns this
+ * @returns a new instance of MysteryEncounterBuilder with encounterType set
*/
static withEncounterType(
encounterType: MysteryEncounterType,
@@ -658,7 +657,7 @@ export class MysteryEncounterBuilder implements Partial {
withIntroSpriteConfigs(
spriteConfigs: MysteryEncounterSpriteConfig[],
): this & Pick {
- return Object.assign(this, { spriteConfigs: spriteConfigs });
+ return Object.assign(this, { spriteConfigs });
}
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this {
@@ -703,7 +702,7 @@ export class MysteryEncounterBuilder implements Partial {
* @returns
*/
withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick {
- return Object.assign(this, { encounterTier: encounterTier });
+ return Object.assign(this, { encounterTier });
}
/**
@@ -753,7 +752,7 @@ export class MysteryEncounterBuilder implements Partial {
withContinuousEncounter(
continuousEncounter: boolean,
): this & Required> {
- return Object.assign(this, { continuousEncounter: continuousEncounter });
+ return Object.assign(this, { continuousEncounter });
}
/**
@@ -807,7 +806,7 @@ export class MysteryEncounterBuilder implements Partial {
withMaxAllowedEncounters(
maxAllowedEncounters: number,
): this & Required> {
- return Object.assign(this, { maxAllowedEncounters: maxAllowedEncounters });
+ return Object.assign(this, { maxAllowedEncounters });
}
/**
@@ -821,7 +820,7 @@ export class MysteryEncounterBuilder implements Partial {
requirement: EncounterSceneRequirement,
): this & Required> {
if (requirement instanceof EncounterPokemonRequirement) {
- Error("Incorrectly added pokemon requirement as scene requirement.");
+ new Error("Incorrectly added pokemon requirement as scene requirement.");
}
this.requirements.push(requirement);
return this;
@@ -864,7 +863,7 @@ export class MysteryEncounterBuilder implements Partial {
requirement: EncounterPokemonRequirement,
): this & Required> {
if (requirement instanceof EncounterSceneRequirement) {
- Error("Incorrectly added scene requirement as pokemon requirement.");
+ new Error("Incorrectly added scene requirement as pokemon requirement.");
}
this.primaryPokemonRequirements.push(requirement);
@@ -917,7 +916,7 @@ export class MysteryEncounterBuilder implements Partial {
excludePrimaryFromSecondaryRequirements = false,
): this & Required> {
if (requirement instanceof EncounterSceneRequirement) {
- Error("Incorrectly added scene requirement as pokemon requirement.");
+ new Error("Incorrectly added scene requirement as pokemon requirement.");
}
this.secondaryPokemonRequirements.push(requirement);
@@ -939,7 +938,7 @@ export class MysteryEncounterBuilder implements Partial {
* @returns
*/
withRewards(doEncounterRewards: () => boolean): this & Required> {
- return Object.assign(this, { doEncounterRewards: doEncounterRewards });
+ return Object.assign(this, { doEncounterRewards });
}
/**
@@ -953,7 +952,7 @@ export class MysteryEncounterBuilder implements Partial {
* @returns
*/
withExp(doEncounterExp: () => boolean): this & Required> {
- return Object.assign(this, { doEncounterExp: doEncounterExp });
+ return Object.assign(this, { doEncounterExp });
}
/**
@@ -974,7 +973,7 @@ export class MysteryEncounterBuilder implements Partial {
* @returns
*/
withOnVisualsStart(onVisualsStart: () => boolean): this & Required> {
- return Object.assign(this, { onVisualsStart: onVisualsStart });
+ return Object.assign(this, { onVisualsStart });
}
/**
@@ -984,7 +983,7 @@ export class MysteryEncounterBuilder implements Partial {
* @returns
*/
withCatchAllowed(catchAllowed: boolean): this & Required> {
- return Object.assign(this, { catchAllowed: catchAllowed });
+ return Object.assign(this, { catchAllowed });
}
/**
@@ -1004,7 +1003,7 @@ export class MysteryEncounterBuilder implements Partial {
hideBattleIntroMessage: boolean,
): this & Required> {
return Object.assign(this, {
- hideBattleIntroMessage: hideBattleIntroMessage,
+ hideBattleIntroMessage,
});
}
@@ -1015,7 +1014,7 @@ export class MysteryEncounterBuilder implements Partial {
withAutoHideIntroVisuals(
autoHideIntroVisuals: boolean,
): this & Required> {
- return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals });
+ return Object.assign(this, { autoHideIntroVisuals });
}
/**
@@ -1027,7 +1026,7 @@ export class MysteryEncounterBuilder implements Partial {
enterIntroVisualsFromRight: boolean,
): this & Required> {
return Object.assign(this, {
- enterIntroVisualsFromRight: enterIntroVisualsFromRight,
+ enterIntroVisualsFromRight,
});
}
diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts
index 54179ee2604..1a9b008f9e9 100644
--- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts
+++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts
@@ -1,6 +1,5 @@
import { globalScene } from "#app/global-scene";
import type { TextStyle } from "#enums/text-style";
-import { UiTheme } from "#enums/ui-theme";
import { getTextWithColors } from "#ui/text";
import { isNullOrUndefined } from "#utils/common";
import i18next from "i18next";
@@ -16,14 +15,12 @@ export function getEncounterText(keyOrString?: string, primaryStyle?: TextStyle)
return null;
}
- const uiTheme = globalScene.uiTheme ?? UiTheme.DEFAULT;
-
let textString: string | null = getTextWithDialogueTokens(keyOrString);
// Can only color the text if a Primary Style is defined
// primaryStyle is applied to all text that does not have its own specified style
if (primaryStyle && textString) {
- textString = getTextWithColors(textString, primaryStyle, uiTheme, true);
+ textString = getTextWithColors(textString, primaryStyle, true);
}
return textString;
diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts
index 5c976cbc8cd..0903624d903 100644
--- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts
+++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts
@@ -46,9 +46,9 @@ import type { PokemonData } from "#system/pokemon-data";
import type { TrainerConfig } from "#trainers/trainer-config";
import { trainerConfigs } from "#trainers/trainer-config";
import type { HeldModifierConfig } from "#types/held-modifier-config";
-import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler";
-import { PartyUiMode } from "#ui/party-ui-handler";
+import type { OptionSelectConfig, OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { PartyOption, PokemonSelectFilter } from "#ui/handlers/party-ui-handler";
+import { PartyUiMode } from "#ui/handlers/party-ui-handler";
import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next";
@@ -193,7 +193,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
// This can be amplified or counteracted by setting levelAdditiveModifier in config
// levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
// Leaving null/undefined will disable level scaling
- const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0;
+ const mult = partyConfig.levelAdditiveModifier ?? 0;
const additive = Math.max(Math.round((globalScene.currentBattle.waveIndex / 10) * mult), 0);
battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
@@ -297,9 +297,9 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
// Set Boss
if (config.isBoss) {
- let segments = !isNullOrUndefined(config.bossSegments)
- ? config.bossSegments!
- : globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, level, enemySpecies, true);
+ let segments =
+ config.bossSegments
+ ?? globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, level, enemySpecies, true);
if (!isNullOrUndefined(config.bossSegmentModifier)) {
segments += config.bossSegmentModifier;
}
@@ -545,83 +545,74 @@ export function selectPokemonForOption(
UiMode.PARTY,
PartyUiMode.SELECT,
-1,
- (slotIndex: number, _option: PartyOption) => {
- if (slotIndex < globalScene.getPlayerParty().length) {
- globalScene.ui.setMode(modeToSetOnExit).then(() => {
- const pokemon = globalScene.getPlayerParty()[slotIndex];
- const secondaryOptions = onPokemonSelected(pokemon);
- if (!secondaryOptions) {
- globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
- "selectedPokemon",
- pokemon.getNameToRender(),
- );
- resolve(true);
- return;
- }
-
- // There is a second option to choose after selecting the Pokemon
- globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
- const displayOptions = () => {
- // Always appends a cancel option to bottom of options
- const fullOptions = secondaryOptions
- .map(option => {
- // Update handler to resolve promise
- const onSelect = option.handler;
- option.handler = () => {
- onSelect();
- globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
- "selectedPokemon",
- pokemon.getNameToRender(),
- );
- resolve(true);
- return true;
- };
- return option;
- })
- .concat({
- label: i18next.t("menu:cancel"),
- handler: () => {
- globalScene.ui.clearText();
- globalScene.ui.setMode(modeToSetOnExit);
- resolve(false);
- return true;
- },
- onHover: () => {
- showEncounterText(i18next.t("mysteryEncounterMessages:cancelOption"), 0, 0, false);
- },
- });
-
- const config: OptionSelectConfig = {
- options: fullOptions,
- maxOptions: 7,
- yOffset: 0,
- supportHover: true,
- };
-
- // Do hover over the starting selection option
- if (fullOptions[0].onHover) {
- fullOptions[0].onHover();
- }
- globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true);
- };
-
- const textPromptKey =
- globalScene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt;
- if (!textPromptKey) {
- displayOptions();
- } else {
- showEncounterText(textPromptKey).then(() => displayOptions());
- }
- });
- });
- } else {
- globalScene.ui.setMode(modeToSetOnExit).then(() => {
- if (onPokemonNotSelected) {
- onPokemonNotSelected();
- }
- resolve(false);
- });
+ async (slotIndex: number, _option: PartyOption) => {
+ await globalScene.ui.setMode(modeToSetOnExit);
+ if (slotIndex >= globalScene.getPlayerParty().length) {
+ onPokemonNotSelected?.();
+ resolve(false);
+ return;
}
+
+ const pokemon = globalScene.getPlayerParty()[slotIndex];
+ const secondaryOptions = onPokemonSelected(pokemon);
+ if (!secondaryOptions) {
+ globalScene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
+ resolve(true);
+ return;
+ }
+
+ // There is a second option to choose after selecting the Pokemon
+ await globalScene.ui.setMode(UiMode.MESSAGE);
+ // TODO: fix this
+ const displayOptions = () => {
+ // Always appends a cancel option to bottom of options
+ const fullOptions = secondaryOptions
+ .map(option => {
+ // Update handler to resolve promise
+ const onSelect = option.handler;
+ option.handler = () => {
+ onSelect();
+ globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
+ "selectedPokemon",
+ pokemon.getNameToRender(),
+ );
+ resolve(true);
+ return true;
+ };
+ return option;
+ })
+ .concat({
+ label: i18next.t("menu:cancel"),
+ handler: () => {
+ globalScene.ui.clearText();
+ globalScene.ui.setMode(modeToSetOnExit);
+ resolve(false);
+ return true;
+ },
+ onHover: () => {
+ showEncounterText(i18next.t("mysteryEncounterMessages:cancelOption"), 0, 0, false);
+ },
+ });
+
+ const config: OptionSelectConfig = {
+ options: fullOptions,
+ maxOptions: 7,
+ yOffset: 0,
+ supportHover: true,
+ };
+
+ // Do hover over the starting selection option
+ if (fullOptions[0]?.onHover) {
+ fullOptions[0].onHover();
+ }
+ globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true);
+ };
+
+ const textPromptKey = globalScene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt;
+ if (textPromptKey) {
+ await showEncounterText(textPromptKey);
+ }
+ displayOptions();
},
selectablePokemonFilter,
);
@@ -651,24 +642,16 @@ export function selectOptionThenPokemon(
return new Promise(resolve => {
const modeToSetOnExit = globalScene.ui.getMode();
- const displayOptions = (config: OptionSelectConfig) => {
- globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
- if (!optionSelectPromptKey) {
- // Do hover over the starting selection option
- if (fullOptions[0].onHover) {
- fullOptions[0].onHover();
- }
- globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
- } else {
- showEncounterText(optionSelectPromptKey).then(() => {
- // Do hover over the starting selection option
- if (fullOptions[0].onHover) {
- fullOptions[0].onHover();
- }
- globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
- });
- }
- });
+ const displayOptions = async (config: OptionSelectConfig) => {
+ await globalScene.ui.setMode(UiMode.MESSAGE);
+ if (optionSelectPromptKey) {
+ showEncounterText(optionSelectPromptKey);
+ }
+ // Do hover over the starting selection option
+ if (fullOptions[0]?.onHover) {
+ fullOptions[0].onHover();
+ }
+ globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
};
const selectPokemonAfterOption = (selectedOptionIndex: number) => {
@@ -683,7 +666,7 @@ export function selectOptionThenPokemon(
globalScene.ui.setMode(modeToSetOnExit).then(() => {
const result: PokemonAndOptionSelected = {
selectedPokemonIndex: slotIndex,
- selectedOptionIndex: selectedOptionIndex,
+ selectedOptionIndex,
};
resolve(result);
});
@@ -965,10 +948,10 @@ export function transitionMysteryEncounterIntroVisuals(hide = true, destroy = tr
export function handleMysteryEncounterBattleStartEffects() {
const encounter = globalScene.currentBattle.mysteryEncounter;
if (
- globalScene.currentBattle.isBattleMysteryEncounter() &&
- encounter &&
- encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE &&
- !encounter.startOfBattleEffectsComplete
+ globalScene.currentBattle.isBattleMysteryEncounter()
+ && encounter
+ && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE
+ && !encounter.startOfBattleEffectsComplete
) {
const effects = encounter.startOfBattleEffects;
effects.forEach(effect => {
@@ -986,7 +969,7 @@ export function handleMysteryEncounterBattleStartEffects() {
/**
* Can queue extra phases or logic during {@linkcode TurnInitPhase}
* Should mostly just be used for injecting custom phases into the battle system on turn start
- * @return boolean - if true, will skip the remainder of the {@linkcode TurnInitPhase}
+ * @returns boolean - if true, will skip the remainder of the {@linkcode TurnInitPhase}
*/
export function handleMysteryEncounterTurnStartEffects(): boolean {
const encounter = globalScene.currentBattle.mysteryEncounter;
@@ -1003,7 +986,7 @@ export function handleMysteryEncounterTurnStartEffects(): boolean {
* @param level the level of the mon, which differs between MEs
* @param isBoss whether the mon should be a Boss
* @param rerollHidden whether the mon should get an extra roll for Hidden Ability
- * @returns {@linkcode EnemyPokemon} for the requested encounter
+ * @returns for the requested encounter
*/
export function getRandomEncounterSpecies(level: number, isBoss = false, rerollHidden = false): EnemyPokemon {
let bossSpecies: PokemonSpecies;
@@ -1093,7 +1076,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
.filter(b => {
return !Array.isArray(b) || !randSeedInt(b[1]);
})
- .map(b => (!Array.isArray(b) ? b : b[0]));
+ .map(b => (Array.isArray(b) ? b[0] : b));
}, i * 100);
if (biomes! && biomes.length > 0) {
const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b));
@@ -1107,12 +1090,10 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
}
} else if (biomeLinks.hasOwnProperty(currentBiome)) {
currentBiome = biomeLinks[currentBiome] as BiomeId;
+ } else if (i % 50 === 0) {
+ currentBiome = BiomeId.END;
} else {
- if (!(i % 50)) {
- currentBiome = BiomeId.END;
- } else {
- currentBiome = globalScene.generateRandomBiome(i);
- }
+ currentBiome = globalScene.generateRandomBiome(i);
}
currentArena = globalScene.newArena(currentBiome);
diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts
index 93ef59d6ee7..0d07300d00d 100644
--- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts
+++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts
@@ -31,9 +31,9 @@ import {
showEncounterText,
} from "#mystery-encounters/encounter-dialogue-utils";
import { achvs } from "#system/achv";
-import type { PartyOption } from "#ui/party-ui-handler";
-import { PartyUiMode } from "#ui/party-ui-handler";
-import { SummaryUiMode } from "#ui/summary-ui-handler";
+import type { PartyOption } from "#ui/handlers/party-ui-handler";
+import { PartyUiMode } from "#ui/handlers/party-ui-handler";
+import { SummaryUiMode } from "#ui/handlers/summary-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, isNullOrUndefined, randSeedInt } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
@@ -265,11 +265,11 @@ export function getRandomSpeciesByStarterCost(
.filter(s => {
const pokemonSpecies = getPokemonSpecies(s[0]);
return (
- pokemonSpecies &&
- (!excludedSpecies || !excludedSpecies.includes(s[0])) &&
- (allowSubLegendary || !pokemonSpecies.subLegendary) &&
- (allowLegendary || !pokemonSpecies.legendary) &&
- (allowMythical || !pokemonSpecies.mythical)
+ pokemonSpecies
+ && (!excludedSpecies || !excludedSpecies.includes(s[0]))
+ && (allowSubLegendary || !pokemonSpecies.subLegendary)
+ && (allowLegendary || !pokemonSpecies.legendary)
+ && (allowMythical || !pokemonSpecies.mythical)
);
})
.map(s => [getPokemonSpecies(s[0]), s[1]]);
@@ -409,10 +409,10 @@ export async function applyModifierTypeToPlayerPokemon(
const modifier = modType.newModifier(pokemon);
const existing = globalScene.findModifier(
(m): m is PokemonHeldItemModifier =>
- m instanceof PokemonHeldItemModifier &&
- m.type.id === modType.id &&
- m.pokemonId === pokemon.id &&
- m.matchType(modifier),
+ m instanceof PokemonHeldItemModifier
+ && m.type.id === modType.id
+ && m.pokemonId === pokemon.id
+ && m.matchType(modifier),
) as PokemonHeldItemModifier | undefined;
// At max stacks
@@ -650,8 +650,8 @@ export async function catchPokemon(
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (
- speciesForm.abilityHidden &&
- (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
+ speciesForm.abilityHidden
+ && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
}
@@ -988,8 +988,8 @@ export async function addPokemonDataToDexAndValidateAchievements(pokemon: Player
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (
- speciesForm.abilityHidden &&
- (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
+ speciesForm.abilityHidden
+ && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
}
diff --git a/src/data/nature.ts b/src/data/nature.ts
index 65e401cd288..116732d60b3 100644
--- a/src/data/nature.ts
+++ b/src/data/nature.ts
@@ -1,7 +1,6 @@
import { Nature } from "#enums/nature";
import { EFFECTIVE_STATS, getShortenedStatKey, Stat } from "#enums/stat";
import { TextStyle } from "#enums/text-style";
-import { UiTheme } from "#enums/ui-theme";
import { getBBCodeFrag } from "#ui/text";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next";
@@ -11,7 +10,6 @@ export function getNatureName(
includeStatEffects = false,
forStarterSelect = false,
ignoreBBCode = false,
- uiTheme: UiTheme = UiTheme.DEFAULT,
): string {
let ret = toCamelCase(Nature[nature]);
//Translating nature
@@ -31,7 +29,7 @@ export function getNatureName(
}
const textStyle = forStarterSelect ? TextStyle.SUMMARY_ALT : TextStyle.WINDOW;
const getTextFrag = !ignoreBBCode
- ? (text: string, style: TextStyle) => getBBCodeFrag(text, style, uiTheme)
+ ? (text: string, style: TextStyle) => getBBCodeFrag(text, style)
: (text: string, _style: TextStyle) => text;
if (increasedStat && decreasedStat) {
ret = `${getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(`, textStyle)}${getTextFrag(`+${i18next.t(getShortenedStatKey(increasedStat))}`, TextStyle.SUMMARY_PINK)}${getTextFrag("/", textStyle)}${getTextFrag(`-${i18next.t(getShortenedStatKey(decreasedStat))}`, TextStyle.SUMMARY_BLUE)}${getTextFrag(")", textStyle)}`;
diff --git a/src/data/phase-priority-queue.ts b/src/data/phase-priority-queue.ts
index 88361b0f4fa..2c83348cc7b 100644
--- a/src/data/phase-priority-queue.ts
+++ b/src/data/phase-priority-queue.ts
@@ -44,6 +44,34 @@ export abstract class PhasePriorityQueue {
public clear(): void {
this.queue.splice(0, this.queue.length);
}
+
+ /**
+ * Attempt to remove one or more Phases from the current queue.
+ * @param phaseFilter - The function to select phases for removal
+ * @param removeCount - The maximum number of phases to remove, or `all` to remove all matching phases;
+ * default `1`
+ * @returns The number of successfully removed phases
+ * @todo Remove this eventually once the patchwork bug this is used for is fixed
+ */
+ public tryRemovePhase(phaseFilter: (phase: Phase) => boolean, removeCount: number | "all" = 1): number {
+ if (removeCount === "all") {
+ removeCount = this.queue.length;
+ } else if (removeCount < 1) {
+ return 0;
+ }
+ let numRemoved = 0;
+
+ do {
+ const phaseIndex = this.queue.findIndex(phaseFilter);
+ if (phaseIndex === -1) {
+ break;
+ }
+ this.queue.splice(phaseIndex, 1);
+ numRemoved++;
+ } while (numRemoved < removeCount && this.queue.length > 0);
+
+ return numRemoved;
+ }
}
/**
@@ -58,8 +86,8 @@ export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
if (phaseA.getPriority() === phaseB.getPriority()) {
return (
- (phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) *
- (isTrickRoom() ? -1 : 1)
+ (phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD))
+ * (isTrickRoom() ? -1 : 1)
);
}
diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts
index 5a50713c5a9..7eb8ed263d5 100644
--- a/src/data/pokemon-forms.ts
+++ b/src/data/pokemon-forms.ts
@@ -58,7 +58,7 @@ export class SpeciesFormChange {
return false;
}
- if (!pokemon.species.forms.length) {
+ if (pokemon.species.forms.length === 0) {
return false;
}
diff --git a/src/data/pokemon-forms/form-change-triggers.ts b/src/data/pokemon-forms/form-change-triggers.ts
index 7c042b27058..f51b090878f 100644
--- a/src/data/pokemon-forms/form-change-triggers.ts
+++ b/src/data/pokemon-forms/form-change-triggers.ts
@@ -82,10 +82,10 @@ export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
// Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier
const m = r as PokemonFormChangeItemModifier;
return (
- "formChangeItem" in m &&
- m.pokemonId === pokemon.id &&
- m.formChangeItem === this.item &&
- m.active === this.active
+ "formChangeItem" in m
+ && m.pokemonId === pokemon.id
+ && m.formChangeItem === this.item
+ && m.active === this.active
);
});
}
@@ -155,7 +155,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
}
canChange(pokemon: Pokemon): boolean {
- return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
+ return pokemon.moveset.filter(m => m.moveId === this.move).length > 0 === this.known;
}
}
@@ -182,7 +182,7 @@ export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigg
description = i18next.t("pokemonEvolutions:forms.postMove");
canChange(pokemon: Pokemon): boolean {
return (
- pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
+ pokemon.summonData && pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length > 0 === this.used
);
}
}
@@ -211,9 +211,10 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
canChange(pokemon: Pokemon): boolean {
return (
- this.formKey ===
- pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
- .formKey
+ this.formKey
+ === pokemon.species.forms[
+ globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)
+ ].formKey
);
}
}
@@ -259,10 +260,10 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
return (
- !isAbilitySuppressed &&
- !isWeatherSuppressed &&
- pokemon.hasAbility(this.ability) &&
- this.weathers.includes(currentWeather)
+ !isAbilitySuppressed
+ && !isWeatherSuppressed
+ && pokemon.hasAbility(this.ability)
+ && this.weathers.includes(currentWeather)
);
}
}
@@ -288,7 +289,7 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
/**
* Checks if the Pokemon has the required ability and the weather is one that will revert
* the Pokemon to its original form or the weather or ability is suppressed
- * @param {Pokemon} pokemon the pokemon that is trying to do the form change
+ * @param pokemon the pokemon that is trying to do the form change
* @returns `true` if the Pokemon will revert to its original form, `false` otherwise
*/
canChange(pokemon: Pokemon): boolean {
diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts
index 064ad57cfb3..2d76c2c0400 100644
--- a/src/data/pokemon-species.ts
+++ b/src/data/pokemon-species.ts
@@ -26,8 +26,8 @@ import { loadPokemonVariantAssets } from "#sprites/pokemon-sprite";
import { hasExpSprite } from "#sprites/sprite-utils";
import type { Variant, VariantSet } from "#sprites/variant";
import { populateVariantColorCache, variantColorCache, variantData } from "#sprites/variant";
-import type { StarterMoveset } from "#system/game-data";
import type { Localizable } from "#types/locales";
+import type { StarterMoveset } from "#types/save-data";
import { isNullOrUndefined, randSeedFloat, randSeedGauss, randSeedInt } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { toCamelCase, toPascalCase } from "#utils/strings";
@@ -202,8 +202,8 @@ export abstract class PokemonSpeciesForm {
}
let starterSpeciesId = this.speciesId;
while (
- !(starterSpeciesId in starterPassiveAbilities) ||
- !(formIndex in starterPassiveAbilities[starterSpeciesId])
+ !(starterSpeciesId in starterPassiveAbilities)
+ || !(formIndex in starterPassiveAbilities[starterSpeciesId])
) {
if (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) {
starterSpeciesId = pokemonPrevolutions[starterSpeciesId];
@@ -221,8 +221,8 @@ export abstract class PokemonSpeciesForm {
getLevelMoves(): LevelMoves {
if (
- pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId) &&
- pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
+ pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId)
+ && pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
) {
return pokemonSpeciesFormLevelMoves[this.speciesId][this.formIndex].slice(0);
}
@@ -302,9 +302,9 @@ export abstract class PokemonSpeciesForm {
const formSpriteKey = this.getFormSpriteKey(formIndex);
const showGenderDiffs =
- this.genderDiffs &&
- female &&
- ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
+ this.genderDiffs
+ && female
+ && ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
return `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`;
}
@@ -425,7 +425,7 @@ export abstract class PokemonSpeciesForm {
}
let ret = speciesId.toString();
const forms = getPokemonSpecies(speciesId).forms;
- if (forms.length) {
+ if (forms.length > 0) {
if (formIndex !== undefined && formIndex >= forms.length) {
console.warn(
`Attempted accessing form with index ${formIndex} of species ${getPokemonSpecies(speciesId).getName()} with only ${forms.length || 0} forms`,
@@ -487,8 +487,8 @@ export abstract class PokemonSpeciesForm {
}
}
if (
- pokemonFormLevelMoves.hasOwnProperty(this.speciesId) &&
- pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
+ pokemonFormLevelMoves.hasOwnProperty(this.speciesId)
+ && pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
) {
if (!pokemonFormLevelMoves[this.speciesId][this.formIndex].find(lm => lm[0] <= 5 && lm[1] === moveId)) {
return false;
@@ -593,14 +593,14 @@ export abstract class PokemonSpeciesForm {
});
}
- cry(soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound {
+ cry(soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound | null {
const cryKey = this.getCryKey(this.formIndex);
let cry: AnySound | null = globalScene.sound.get(cryKey) as AnySound;
if (cry?.pendingRemove) {
cry = null;
}
cry = globalScene.playSound(cry ?? cryKey, soundConfig);
- if (ignorePlay) {
+ if (cry && ignorePlay) {
cry.stop();
}
return cry;
@@ -758,7 +758,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
}
getName(formIndex?: number): string {
- if (formIndex !== undefined && this.forms.length) {
+ if (formIndex !== undefined && this.forms.length > 0) {
const form = this.forms[formIndex];
let key: string | null;
switch (form.formKey) {
@@ -795,7 +795,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
return Gender.GENDERLESS;
}
- if (randSeedFloat() <= this.malePercent) {
+ if (randSeedFloat() * 100 <= this.malePercent) {
return Gender.MALE;
}
return Gender.FEMALE;
@@ -846,9 +846,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
? i18next.t(`battlePokemonForm:${toCamelCase(formKey)}`, { pokemonName: this.name })
: i18next.t(`pokemonForm:battleForm.${toCamelCase(formKey)}`);
} else if (
- region === Region.NORMAL ||
- (this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0) ||
- this.speciesId === SpeciesId.PALDEA_TAUROS
+ region === Region.NORMAL
+ || (this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0)
+ || this.speciesId === SpeciesId.PALDEA_TAUROS
) {
// More special cases can be added here
const i18key = `pokemonForm:${speciesName}${formText}`;
@@ -919,7 +919,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
* The calculation with evolution delay is a weighted average of the easeIn and easeOut functions where preferredMinLevel is the denominator.
* This also means a lower value of x will lead to a higher evolution chance.
* @param strength {@linkcode PartyMemberStrength} The strength of the party member in question
- * @returns {@linkcode number} The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance.
+ * @returns The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance.
*/
private getStrengthLevelDiff(strength: PartyMemberStrength): number {
switch (Math.min(strength, PartyMemberStrength.STRONGER)) {
@@ -947,7 +947,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
): SpeciesId {
const prevolutionLevels = this.getPrevolutionLevels();
- if (prevolutionLevels.length) {
+ if (prevolutionLevels.length > 0) {
for (let pl = prevolutionLevels.length - 1; pl >= 0; pl--) {
const prevolutionLevel = prevolutionLevels[pl];
if (level < prevolutionLevel[1]) {
@@ -958,11 +958,11 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
if (
// If evolutions shouldn't happen, add more cases here :)
- !allowEvolving ||
- !pokemonEvolutions.hasOwnProperty(this.speciesId) ||
- (globalScene.currentBattle?.waveIndex === 20 &&
- globalScene.gameMode.isClassic &&
- globalScene.currentBattle.trainer)
+ !allowEvolving
+ || !pokemonEvolutions.hasOwnProperty(this.speciesId)
+ || (globalScene.currentBattle?.waveIndex === 20
+ && globalScene.gameMode.isClassic
+ && globalScene.currentBattle.trainer)
) {
return this.speciesId;
}
@@ -988,41 +988,39 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
if (!forTrainer && isRegionalEvolution) {
evolutionChance = 0;
- } else {
- if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
- if (strength === PartyMemberStrength.STRONGER) {
- evolutionChance = 1;
- } else {
- const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve
- const minChance: number = 0.875 - 0.125 * strength;
-
- evolutionChance = Math.min(
- minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance),
- 1,
- );
- }
+ } else if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
+ if (strength === PartyMemberStrength.STRONGER) {
+ evolutionChance = 1;
} else {
- const preferredMinLevel = Math.max(ev.level - 1 + ev.wildDelay! * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct?
- let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1);
-
- if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
- const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(
- ev => ev.speciesId === this.speciesId,
- )!.level; // TODO: is the bang correct?
- if (prevolutionLevel > 1) {
- evolutionLevel = prevolutionLevel;
- }
- }
+ const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve
+ const minChance: number = 0.875 - 0.125 * strength;
evolutionChance = Math.min(
- 0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) +
- 0.35 *
- easeOutFunc(
- Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5),
- ),
+ minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance),
1,
);
}
+ } else {
+ const preferredMinLevel = Math.max(ev.level - 1 + ev.wildDelay! * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct?
+ let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1);
+
+ if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
+ const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(
+ ev => ev.speciesId === this.speciesId,
+ )!.level; // TODO: is the bang correct?
+ if (prevolutionLevel > 1) {
+ evolutionLevel = prevolutionLevel;
+ }
+ }
+
+ evolutionChance = Math.min(
+ 0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel)
+ + 0.35
+ * easeOutFunc(
+ Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5),
+ ),
+ 1,
+ );
}
//TODO: Adjust templates and delays so we don't have to hardcode it
@@ -1097,9 +1095,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
for (const p of allEvolvingPokemon) {
for (const e of pokemonEvolutions[p]) {
if (
- e.speciesId === this.speciesId &&
- (!this.forms.length || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey) &&
- prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p))
+ e.speciesId === this.speciesId
+ && (this.forms.length === 0 || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey)
+ && prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p))
) {
const speciesId = Number.parseInt(p) as SpeciesId;
const level = e.level;
@@ -1135,9 +1133,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
prevolutionLevels[l][0],
Math.min(
Math.max(
- evolution?.level! +
- Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) -
- 1,
+ evolution?.level!
+ + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5)
+ - 1,
2,
evolution?.level!,
),
@@ -1146,15 +1144,13 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
]); // TODO: are those bangs correct?
}
const lastPrevolutionLevel = ret[prevolutionLevels.length - 1][1];
- const evolution = pokemonEvolutions[prevolutionLevels[prevolutionLevels.length - 1][0]].find(
- e => e.speciesId === this.speciesId,
- );
+ const evolution = pokemonEvolutions[prevolutionLevels.at(-1)![0]].find(e => e.speciesId === this.speciesId);
ret.push([
this.speciesId,
Math.min(
Math.max(
- lastPrevolutionLevel +
- Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
+ lastPrevolutionLevel
+ + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
lastPrevolutionLevel + 1,
evolution?.level!,
),
@@ -1176,16 +1172,16 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
const mythical = this.mythical;
return species => {
return (
- (subLegendary ||
- legendary ||
- mythical ||
- (pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution &&
- pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution)) &&
- species.subLegendary === subLegendary &&
- species.legendary === legendary &&
- species.mythical === mythical &&
- (this.isTrainerForbidden() || !species.isTrainerForbidden()) &&
- species.speciesId !== SpeciesId.DITTO
+ (subLegendary
+ || legendary
+ || mythical
+ || (pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution
+ && pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution))
+ && species.subLegendary === subLegendary
+ && species.legendary === legendary
+ && species.mythical === mythical
+ && (this.isTrainerForbidden() || !species.isTrainerForbidden())
+ && species.speciesId !== SpeciesId.DITTO
);
};
}
@@ -1202,19 +1198,19 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
}
getFormSpriteKey(formIndex?: number) {
- if (this.forms.length && formIndex !== undefined && formIndex >= this.forms.length) {
+ if (this.forms.length > 0 && formIndex !== undefined && formIndex >= this.forms.length) {
console.warn(
`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`,
);
formIndex = Math.min(formIndex, this.forms.length - 1);
}
- return this.forms?.length ? this.forms[formIndex || 0].getFormSpriteKey() : "";
+ return this.forms?.length > 0 ? this.forms[formIndex || 0].getFormSpriteKey() : "";
}
/**
- * Generates a {@linkcode bigint} corresponding to the maximum unlocks possible for this species,
+ * Generates a {@linkcode BigInt} corresponding to the maximum unlocks possible for this species,
* taking into account if the species has a male/female gender, and which variants are implemented.
- * @returns {@linkcode bigint} Maximum unlocks, can be compared with {@linkcode DexEntry.caughtAttr}.
+ * @returns The maximum unlocks for the species as a `BigInt`; can be compared with {@linkcode DexEntry.caughtAttr}.
*/
getFullUnlocksData(): bigint {
let caughtAttr = 0n;
diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts
index 0bd6af0bb04..87ffbbab4cd 100644
--- a/src/data/pokemon/pokemon-data.ts
+++ b/src/data/pokemon/pokemon-data.ts
@@ -11,6 +11,7 @@ import type { MoveId } from "#enums/move-id";
import type { Nature } from "#enums/nature";
import type { PokemonType } from "#enums/pokemon-type";
import type { SpeciesId } from "#enums/species-id";
+import { StatusEffect } from "#enums/status-effect";
import type { AttackMoveResult } from "#types/attack-move-result";
import type { IllusionData } from "#types/illusion-data";
import type { TurnMove } from "#types/turn-move";
@@ -326,6 +327,14 @@ export class PokemonTurnData {
public switchedInThisTurn = false;
public failedRunAway = false;
public joinedRound = false;
+ /** Tracker for a pending status effect
+ *
+ * @remarks
+ * Set whenever {@linkcode Pokemon#trySetStatus} succeeds in order to prevent subsequent status effects
+ * from being applied. Necessary because the status is not actually set until the {@linkcode ObtainStatusEffectPhase} runs,
+ * which may not happen before another status effect is attempted to be applied.
+ */
+ public pendingStatus: StatusEffect = StatusEffect.NONE;
/**
* The amount of times this Pokemon has acted again and used a move in the current turn.
* Used to make sure multi-hits occur properly when the user is
diff --git a/src/data/positional-tags/positional-tag.ts b/src/data/positional-tags/positional-tag.ts
index 77ca6f0e9eb..a877b45b045 100644
--- a/src/data/positional-tags/positional-tag.ts
+++ b/src/data/positional-tags/positional-tag.ts
@@ -126,7 +126,9 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
// Silently disappear if either source or target are missing or happen to be the same pokemon
// (i.e. targeting oneself)
// We also need to check for fainted targets as they don't technically leave the field until _after_ the turn ends
- return !!source && !!target && source !== target && !target.isFainted();
+ // TODO: Figure out a way to store the target's offensive stat if they faint to allow pending attacks to persist
+ // TODO: Remove the `?.scene` checks once battle anims are cleaned up - needed to avoid catch+release crash
+ return !!source?.scene && !!target?.scene && source !== target && !target.isFainted();
}
}
diff --git a/src/data/splash-messages.ts b/src/data/splash-messages.ts
index 55ba185bfb2..21e7e5d05e6 100644
--- a/src/data/splash-messages.ts
+++ b/src/data/splash-messages.ts
@@ -53,8 +53,8 @@ const SEASONAL_WEIGHT_MULTIPLIER = 15;
//#region Common Messages
const commonSplashMessages = [
- ...Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
- ...Array(POKEMON_NAMES_WEIGHT_MULTIPLIER).fill("underratedPokemon"),
+ ...new Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
+ ...new Array(POKEMON_NAMES_WEIGHT_MULTIPLIER).fill("underratedPokemon"),
"joinTheDiscord",
"infiniteLevels",
"everythingIsStackable",
@@ -333,7 +333,7 @@ export function getSplashMessages(): string[] {
if (now >= startDate && now <= endDate) {
console.log(`Adding ${messages.length} ${name} splash messages (weight: x${SEASONAL_WEIGHT_MULTIPLIER})`);
for (const message of messages) {
- const weightedMessage = Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
+ const weightedMessage = new Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
splashMessages.push(...weightedMessage);
}
}
diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts
index 211e265ccf6..34cd2d87617 100644
--- a/src/data/status-effect.ts
+++ b/src/data/status-effect.ts
@@ -59,12 +59,12 @@ export function getStatusEffectObtainText(
if (!sourceText) {
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain` as ParseKeys;
- return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
+ return i18next.t(i18nKey, { pokemonNameWithAffix });
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtainSource` as ParseKeys;
return i18next.t(i18nKey, {
- pokemonNameWithAffix: pokemonNameWithAffix,
- sourceText: sourceText,
+ pokemonNameWithAffix,
+ sourceText,
});
}
@@ -73,7 +73,7 @@ export function getStatusEffectActivationText(statusEffect: StatusEffect, pokemo
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.activation` as ParseKeys;
- return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
+ return i18next.t(i18nKey, { pokemonNameWithAffix });
}
export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
@@ -81,7 +81,7 @@ export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNa
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.overlap` as ParseKeys;
- return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
+ return i18next.t(i18nKey, { pokemonNameWithAffix });
}
export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
@@ -89,7 +89,7 @@ export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameW
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.heal` as ParseKeys;
- return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
+ return i18next.t(i18nKey, { pokemonNameWithAffix });
}
export function getStatusEffectDescriptor(statusEffect: StatusEffect): string {
diff --git a/src/data/terrain.ts b/src/data/terrain.ts
index 7906450d0ea..139230605bf 100644
--- a/src/data/terrain.ts
+++ b/src/data/terrain.ts
@@ -65,14 +65,10 @@ export class Terrain {
// Psychic terrain will only cancel a move if it:
return (
// ... is neither spread nor field-targeted,
- !isFieldTargeted(move) &&
- !isSpreadMove(move) &&
- // .. has positive final priority,
- move.getPriority(user) > 0 &&
- // ...and is targeting at least 1 grounded opponent
- user
- .getOpponents(true)
- .some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
+ !isFieldTargeted(move)
+ && !isSpreadMove(move) // .. has positive final priority,
+ && move.getPriority(user) > 0 // ...and is targeting at least 1 grounded opponent
+ && user.getOpponents(true).some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
);
}
diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts
index f0042e0af91..9d891444829 100644
--- a/src/data/trainers/trainer-config.ts
+++ b/src/data/trainers/trainer-config.ts
@@ -140,8 +140,8 @@ export class TrainerConfig {
this.victoryBgm = "victory_trainer";
this.partyTemplates = [trainerPartyTemplates.TWO_AVG];
this.speciesFilter = species =>
- (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) &&
- !species.isTrainerForbidden();
+ (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical))
+ && !species.isTrainerForbidden();
}
getKey(): string {
@@ -204,7 +204,7 @@ export class TrainerConfig {
/**
* Returns the derived trainer type for a given trainer type.
* @param trainerTypeToDeriveFrom - The trainer type to derive from. (If null, the this.trainerType property will be used.)
- * @returns {TrainerType} - The derived trainer type.
+ * @returns - The derived trainer type.
*/
getDerivedType(trainerTypeToDeriveFrom: TrainerType | null = null): TrainerType {
let trainerType = trainerTypeToDeriveFrom ? trainerTypeToDeriveFrom : this.trainerType;
@@ -274,9 +274,9 @@ export class TrainerConfig {
/**
* Sets the configuration for trainers with genders, including the female name and encounter background music (BGM).
- * @param {string} [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned.
- * @param {TrainerType | string} [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string.
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @param [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned.
+ * @param [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string.
+ * @returns The updated TrainerConfig instance.
*/
setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig {
// If the female name is 'Ivy' (the rival), assign a localized name.
@@ -314,7 +314,7 @@ export class TrainerConfig {
* Sets the configuration for trainers with double battles, including the name of the double trainer and the encounter BGM.
* @param nameDouble The name of the double trainer (e.g., "Ace Duo" for Trainer Class Doubles or "red_blue_double" for NAMED trainer doubles).
* @param doubleEncounterBgm The encounter BGM for the double trainer, which can be a TrainerType or a string.
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
setHasDouble(nameDouble: string, doubleEncounterBgm?: TrainerType | string): TrainerConfig {
this.hasDouble = true;
@@ -331,7 +331,7 @@ export class TrainerConfig {
/**
* Sets the trainer type for double battles.
* @param trainerTypeDouble The TrainerType of the partner in a double battle.
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
setDoubleTrainerType(trainerTypeDouble: TrainerType): TrainerConfig {
this.trainerTypeDouble = trainerTypeDouble;
@@ -356,7 +356,7 @@ export class TrainerConfig {
/**
* Sets the title for double trainers
* @param titleDouble The key for the title in the i18n file. (e.g., "champion_double").
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
setDoubleTitle(titleDouble: string): TrainerConfig {
// First check if i18n is initialized
@@ -523,9 +523,9 @@ export class TrainerConfig {
* Initializes the trainer configuration for an evil team admin.
* @param title The title of the evil team admin.
* @param poolName The evil team the admin belongs to.
- * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader.
+ * @param signatureSpecies The signature species for the evil team leader.
* @param specialtyType The specialty Type of the admin, if they have one
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
initForEvilTeamAdmin(
title: string,
@@ -566,7 +566,7 @@ export class TrainerConfig {
/**
* Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter.
* @param _isMale Whether the stat trainer is Male or Female (for localization of the title).
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
initForStatTrainer(_isMale = false): TrainerConfig {
if (!getIsInitialized()) {
@@ -590,10 +590,10 @@ export class TrainerConfig {
/**
* Initializes the trainer configuration for an evil team leader. Temporarily hardcoding evil leader teams though.
- * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader.
- * @param {PokemonType} specialtyType The specialty type for the evil team Leader.
+ * @param signatureSpecies The signature species for the evil team leader.
+ * @param specialtyType The specialty type for the evil team Leader.
* @param boolean Whether or not this is the rematch fight
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
initForEvilTeamLeader(
title: string,
@@ -631,12 +631,12 @@ export class TrainerConfig {
/**
* Initializes the trainer configuration for a Gym Leader.
- * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Gym Leader. Added to party in reverse order.
+ * @param signatureSpecies The signature species for the Gym Leader. Added to party in reverse order.
* @param isMale Whether the Gym Leader is Male or Not (for localization of the title).
- * @param {PokemonType} specialtyType The specialty type for the Gym Leader.
+ * @param specialtyType The specialty type for the Gym Leader.
* @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false.
* @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size.
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
initForGymLeader(
signatureSpecies: (SpeciesId | SpeciesId[])[],
@@ -748,9 +748,9 @@ export class TrainerConfig {
/**
* Initializes the trainer configuration for a Champion.
- * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Champion.
+ * @param signatureSpecies The signature species for the Champion.
* @param isMale Whether the Champion is Male or Female (for localization of the title).
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
initForChampion(isMale: boolean): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
@@ -785,7 +785,7 @@ export class TrainerConfig {
/**
* Sets a localized name for the trainer. This should only be used for trainers that dont use a "initFor" function and are considered "named" trainers
* @param name - The name of the trainer.
- * @returns {TrainerConfig} The updated TrainerConfig instance.
+ * @returns The updated TrainerConfig instance.
*/
setLocalizedName(name: string): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
@@ -798,9 +798,9 @@ export class TrainerConfig {
/**
* Retrieves the title for the trainer based on the provided trainer slot and variant.
- * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
- * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title.
- * @returns {string} - The title of the trainer.
+ * @param trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
+ * @param variant - The variant of the trainer to determine the specific title.
+ * @returns - The title of the trainer.
*/
getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string {
const ret = this.name;
@@ -816,8 +816,8 @@ export class TrainerConfig {
if (this.nameFemale) {
// Check if the variant is either female or this is for the partner in a double battle
if (
- variant === TrainerVariant.FEMALE ||
- (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)
+ variant === TrainerVariant.FEMALE
+ || (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)
) {
return this.nameFemale;
}
@@ -1088,8 +1088,8 @@ export const trainerConfigs: TrainerConfigs = {
s =>
[s.ability1, s.ability2, s.abilityHidden].some(
a =>
- !!a &&
- [
+ !!a
+ && [
AbilityId.WHITE_SMOKE,
AbilityId.GLUTTONY,
AbilityId.HONEY_GATHER,
@@ -1102,8 +1102,8 @@ export const trainerConfigs: TrainerConfigs = {
AbilityId.SUPERSWEET_SYRUP,
AbilityId.HOSPITALITY,
].includes(a),
- ) ||
- s
+ )
+ || s
.getLevelMoves()
.some(plm =>
[MoveId.SOFT_BOILED, MoveId.SPORE, MoveId.MILK_DRINK, MoveId.OVERHEAT, MoveId.TEATIME].includes(plm[1]),
@@ -1566,8 +1566,8 @@ export const trainerConfigs: TrainerConfigs = {
s =>
[s.ability1, s.ability2, s.abilityHidden].some(
a =>
- !!a &&
- [
+ !!a
+ && [
AbilityId.DRIZZLE,
AbilityId.SWIFT_SWIM,
AbilityId.HYDRATION,
@@ -4642,9 +4642,9 @@ export const trainerConfigs: TrainerConfigs = {
2,
getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) =>
- !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- species.baseTotal >= 450,
+ !pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && species.baseTotal >= 450,
),
),
[TrainerType.RIVAL_3]: new TrainerConfig(++t)
@@ -4717,9 +4717,9 @@ export const trainerConfigs: TrainerConfigs = {
2,
getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) =>
- !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- species.baseTotal >= 450,
+ !pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && species.baseTotal >= 450,
),
)
.setSpeciesFilter(species => species.baseTotal >= 540),
@@ -4798,9 +4798,9 @@ export const trainerConfigs: TrainerConfigs = {
2,
getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) =>
- !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- species.baseTotal >= 450,
+ !pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && species.baseTotal >= 450,
),
)
.setSpeciesFilter(species => species.baseTotal >= 540)
@@ -4880,9 +4880,9 @@ export const trainerConfigs: TrainerConfigs = {
2,
getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) =>
- !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- species.baseTotal >= 450,
+ !pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && species.baseTotal >= 450,
),
)
.setSpeciesFilter(species => species.baseTotal >= 540)
@@ -4976,9 +4976,9 @@ export const trainerConfigs: TrainerConfigs = {
2,
getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) =>
- !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- species.baseTotal >= 450,
+ !pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && species.baseTotal >= 450,
),
)
.setSpeciesFilter(species => species.baseTotal >= 540)
diff --git a/src/data/type.ts b/src/data/type.ts
index c9bf346fb85..111acf7e4f7 100644
--- a/src/data/type.ts
+++ b/src/data/type.ts
@@ -286,7 +286,7 @@ export function getTypeDamageMultiplierColor(
case 0.5:
return "#FE8E00";
case 1:
- return undefined;
+ return;
case 2:
return "#4AA500";
case 4:
@@ -306,7 +306,7 @@ export function getTypeDamageMultiplierColor(
case 0.5:
return "#0093FF";
case 1:
- return undefined;
+ return;
case 2:
return "#FE8E00";
case 4:
diff --git a/src/data/weather.ts b/src/data/weather.ts
index 59be56826a4..84a5e1ba4f8 100644
--- a/src/data/weather.ts
+++ b/src/data/weather.ts
@@ -386,5 +386,5 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
}
}
- return weatherPool.length ? weatherPool[0].weatherType : WeatherType.NONE;
+ return weatherPool.length > 0 ? weatherPool[0].weatherType : WeatherType.NONE;
}
diff --git a/src/enums/ability-attr.ts b/src/enums/ability-attr.ts
index a3b9511ad02..64b955e963d 100644
--- a/src/enums/ability-attr.ts
+++ b/src/enums/ability-attr.ts
@@ -10,4 +10,4 @@ export const AbilityAttr = Object.freeze({
ABILITY_HIDDEN: 4,
});
-export type AbilityAttr = ObjectValues;
\ No newline at end of file
+export type AbilityAttr = ObjectValues;
diff --git a/src/enums/ai-type.ts b/src/enums/ai-type.ts
index 13931172a4a..5438a026c2f 100644
--- a/src/enums/ai-type.ts
+++ b/src/enums/ai-type.ts
@@ -1,5 +1,5 @@
export enum AiType {
RANDOM,
SMART_RANDOM,
- SMART
+ SMART,
}
diff --git a/src/enums/arena-tag-side.ts b/src/enums/arena-tag-side.ts
index 3e326ce158a..5f25a74ab36 100644
--- a/src/enums/arena-tag-side.ts
+++ b/src/enums/arena-tag-side.ts
@@ -1,5 +1,5 @@
export enum ArenaTagSide {
BOTH,
PLAYER,
- ENEMY
+ ENEMY,
}
diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts
index 7f9364395c0..b021fa9c805 100644
--- a/src/enums/arena-tag-type.ts
+++ b/src/enums/arena-tag-type.ts
@@ -1,15 +1,12 @@
-import type { ArenaTagTypeMap } from "#data/arena-tag";
-import type { NonSerializableArenaTagType, SerializableArenaTagType } from "#types/arena-tags";
-
/**
* Enum representing all different types of {@linkcode ArenaTag}s.
* @privateRemarks
* ⚠️ When modifying the fields in this enum, ensure that:
- * - The entry is added to / removed from {@linkcode ArenaTagTypeMap}
+ * - The entry is added to / removed from {@linkcode ArenaTagTypeMap}
* - The tag is added to / removed from {@linkcode NonSerializableArenaTagType} or {@linkcode SerializableArenaTagType}
-*/
+ */
export enum ArenaTagType {
- NONE = "NONE",
+ NONE = "NONE",
MUD_SPORT = "MUD_SPORT",
WATER_SPORT = "WATER_SPORT",
SPIKES = "SPIKES",
@@ -36,5 +33,5 @@ export enum ArenaTagType {
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
FAIRY_LOCK = "FAIRY_LOCK",
- NEUTRALIZING_GAS = "NEUTRALIZING_GAS"
+ NEUTRALIZING_GAS = "NEUTRALIZING_GAS",
}
diff --git a/src/enums/battle-spec.ts b/src/enums/battle-spec.ts
index 00bc7f92fea..24d7d211d83 100644
--- a/src/enums/battle-spec.ts
+++ b/src/enums/battle-spec.ts
@@ -1,4 +1,4 @@
export enum BattleSpec {
- DEFAULT,
- FINAL_BOSS
+ DEFAULT,
+ FINAL_BOSS,
}
diff --git a/src/enums/battle-style.ts b/src/enums/battle-style.ts
index 2fba10f1bf9..d9545515497 100644
--- a/src/enums/battle-style.ts
+++ b/src/enums/battle-style.ts
@@ -3,5 +3,5 @@ export enum BattleStyle {
/** Display option to switch active pokemon at battle start. */
SWITCH,
/** Hide option to switch active pokemon at battle start. */
- SET
+ SET,
}
diff --git a/src/enums/battle-type.ts b/src/enums/battle-type.ts
index 81cf89ef488..225f795f123 100644
--- a/src/enums/battle-type.ts
+++ b/src/enums/battle-type.ts
@@ -2,5 +2,5 @@ export enum BattleType {
WILD,
TRAINER,
CLEAR,
- MYSTERY_ENCOUNTER
+ MYSTERY_ENCOUNTER,
}
diff --git a/src/enums/battler-index.ts b/src/enums/battler-index.ts
index 97c44f51c5c..1d7635c89dc 100644
--- a/src/enums/battler-index.ts
+++ b/src/enums/battler-index.ts
@@ -7,5 +7,5 @@ export enum BattlerIndex {
PLAYER,
PLAYER_2,
ENEMY,
- ENEMY_2
+ ENEMY_2,
}
diff --git a/src/enums/berry-type.ts b/src/enums/berry-type.ts
index 97c69148146..3844ee61b9b 100644
--- a/src/enums/berry-type.ts
+++ b/src/enums/berry-type.ts
@@ -9,5 +9,5 @@ export enum BerryType {
SALAC,
LANSAT,
STARF,
- LEPPA
+ LEPPA,
}
diff --git a/src/enums/biome-id.ts b/src/enums/biome-id.ts
index 08a0d742738..8b086cb1cd5 100644
--- a/src/enums/biome-id.ts
+++ b/src/enums/biome-id.ts
@@ -34,5 +34,5 @@ export enum BiomeId {
SNOWY_FOREST,
ISLAND = 40,
LABORATORY,
- END = 50
+ END = 50,
}
diff --git a/src/enums/buttons.ts b/src/enums/buttons.ts
index f828b280d45..3b725b99a17 100644
--- a/src/enums/buttons.ts
+++ b/src/enums/buttons.ts
@@ -1,19 +1,19 @@
export enum Button {
- UP,
- DOWN,
- LEFT,
- RIGHT,
- SUBMIT,
- ACTION,
- CANCEL,
- MENU,
- STATS,
- CYCLE_SHINY,
- CYCLE_FORM,
- CYCLE_GENDER,
- CYCLE_ABILITY,
- CYCLE_NATURE,
- CYCLE_TERA,
- SPEED_UP,
- SLOW_DOWN,
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ SUBMIT,
+ ACTION,
+ CANCEL,
+ MENU,
+ STATS,
+ CYCLE_SHINY,
+ CYCLE_FORM,
+ CYCLE_GENDER,
+ CYCLE_ABILITY,
+ CYCLE_NATURE,
+ CYCLE_TERA,
+ SPEED_UP,
+ SLOW_DOWN,
}
diff --git a/src/enums/challenges.ts b/src/enums/challenges.ts
index 8d4f4c7a22a..3d1484cb416 100644
--- a/src/enums/challenges.ts
+++ b/src/enums/challenges.ts
@@ -1,12 +1,12 @@
export enum Challenges {
- SINGLE_GENERATION,
- SINGLE_TYPE,
- LOWER_MAX_STARTER_COST,
- LOWER_STARTER_POINTS,
- FRESH_START,
- INVERSE_BATTLE,
- FLIP_STAT,
- LIMITED_CATCH,
- LIMITED_SUPPORT,
- HARDCORE,
+ SINGLE_GENERATION,
+ SINGLE_TYPE,
+ LOWER_MAX_STARTER_COST,
+ LOWER_STARTER_POINTS,
+ FRESH_START,
+ INVERSE_BATTLE,
+ FLIP_STAT,
+ LIMITED_CATCH,
+ LIMITED_SUPPORT,
+ HARDCORE,
}
diff --git a/src/enums/color.ts b/src/enums/color.ts
index 99c2d14cb63..ec16a10d5e9 100644
--- a/src/enums/color.ts
+++ b/src/enums/color.ts
@@ -1,83 +1,83 @@
export enum Color {
- WHITE = "#ffffff",
- OFF_WHITE = "#f8f8f8",
- LIGHT_GREY = "#a0a0a0",
- GREY = "#484848",
- DARK_GREY = "#404040",
- PINK = "#f89890",
- RED = "#e13d3d",
- RED2 = "#e70808",
- REDORANGE = "#d64b00",
- ORANGE = "#f8b050",
- LIGHT_YELLOW = "#e8e8a8",
- YELLOW = "#ccbe00",
- DARK_YELLOW = "#a68e17",
- GREEN = "#78c850",
- BLUE = "#40c8f8",
- COMMON = "#ffffff",
- GREAT = "#3890f8",
- ULTRA = "#f8d038",
- ROGUE = "#d52929",
- MASTER = "#e020c0",
- LUXURY = "#e64a18"
+ WHITE = "#ffffff",
+ OFF_WHITE = "#f8f8f8",
+ LIGHT_GREY = "#a0a0a0",
+ GREY = "#484848",
+ DARK_GREY = "#404040",
+ PINK = "#f89890",
+ RED = "#e13d3d",
+ RED2 = "#e70808",
+ REDORANGE = "#d64b00",
+ ORANGE = "#f8b050",
+ LIGHT_YELLOW = "#e8e8a8",
+ YELLOW = "#ccbe00",
+ DARK_YELLOW = "#a68e17",
+ GREEN = "#78c850",
+ BLUE = "#40c8f8",
+ COMMON = "#ffffff",
+ GREAT = "#3890f8",
+ ULTRA = "#f8d038",
+ ROGUE = "#d52929",
+ MASTER = "#e020c0",
+ LUXURY = "#e64a18",
}
export enum TypeColor {
- NORMAL = "#ADA594",
- FIGHTING = "#A55239",
- FLYING = "#9CADF7",
- POISON = "#9141CB",
- GROUND = "#AE7A3B",
- ROCK = "#BDA55A",
- BUG = "#ADBD21",
- GHOST = "#6363B5",
- STEEL = "#81A6BE",
- FIRE = "#F75231",
- WATER = "#399CFF",
- GRASS = "#7BCE52",
- ELECTRIC = "#FFC631",
- PSYCHIC = "#EF4179",
- ICE = "#5ACEE7",
- DRAGON = "#7B63E7",
- DARK = "#735A4A",
- FAIRY = "#EF70EF",
+ NORMAL = "#ADA594",
+ FIGHTING = "#A55239",
+ FLYING = "#9CADF7",
+ POISON = "#9141CB",
+ GROUND = "#AE7A3B",
+ ROCK = "#BDA55A",
+ BUG = "#ADBD21",
+ GHOST = "#6363B5",
+ STEEL = "#81A6BE",
+ FIRE = "#F75231",
+ WATER = "#399CFF",
+ GRASS = "#7BCE52",
+ ELECTRIC = "#FFC631",
+ PSYCHIC = "#EF4179",
+ ICE = "#5ACEE7",
+ DRAGON = "#7B63E7",
+ DARK = "#735A4A",
+ FAIRY = "#EF70EF",
}
export enum TypeShadow {
- NORMAL = "#574F4A",
- FIGHTING = "#4E637C",
- FLYING = "#4E637C",
- POISON = "#352166",
- GROUND = "#572D1E",
- ROCK = "#5F442D",
- BUG = "#5F5010",
- GHOST = "#323D5B",
- STEEL = "#415C5F",
- FIRE = "#7C1818",
- WATER = "#1C4E80",
- GRASS = "#4F6729",
- ELECTRIC = "#804618",
- PSYCHIC = "#782155",
- ICE = "#2D5C74",
- DRAGON = "#313874",
- DARK = "#392725",
- FAIRY = "#663878",
+ NORMAL = "#574F4A",
+ FIGHTING = "#4E637C",
+ FLYING = "#4E637C",
+ POISON = "#352166",
+ GROUND = "#572D1E",
+ ROCK = "#5F442D",
+ BUG = "#5F5010",
+ GHOST = "#323D5B",
+ STEEL = "#415C5F",
+ FIRE = "#7C1818",
+ WATER = "#1C4E80",
+ GRASS = "#4F6729",
+ ELECTRIC = "#804618",
+ PSYCHIC = "#782155",
+ ICE = "#2D5C74",
+ DRAGON = "#313874",
+ DARK = "#392725",
+ FAIRY = "#663878",
}
export enum ShadowColor {
- GREY = "#636363",
- PURPLE = "#6b5a73",
- LIGHT_GREY = "#d0d0c8",
- BROWN = "#69402a",
- PINK = "#fca2a2",
- BRIGHT_RED = "#f83018",
- RED = "#984038",
- MAROON = "#632929",
- GREEN = "#306850",
- BLUE = "#006090",
- LIGHT_YELLOW = "#ded6b5",
- YELLOW = "#ebd773",
- DARK_YELLOW = "#a0a060",
- ORANGE = "#c07800",
- LIGHT_ORANGE = "#ffbd73",
+ GREY = "#636363",
+ PURPLE = "#6b5a73",
+ LIGHT_GREY = "#d0d0c8",
+ BROWN = "#69402a",
+ PINK = "#fca2a2",
+ BRIGHT_RED = "#f83018",
+ RED = "#984038",
+ MAROON = "#632929",
+ GREEN = "#306850",
+ BLUE = "#006090",
+ LIGHT_YELLOW = "#ded6b5",
+ YELLOW = "#ebd773",
+ DARK_YELLOW = "#a0a060",
+ ORANGE = "#c07800",
+ LIGHT_ORANGE = "#ffbd73",
}
diff --git a/src/enums/command.ts b/src/enums/command.ts
index 4cd626bb066..a1ac4501a0b 100644
--- a/src/enums/command.ts
+++ b/src/enums/command.ts
@@ -3,5 +3,5 @@ export enum Command {
BALL,
POKEMON,
RUN,
- TERA
+ TERA,
}
diff --git a/src/enums/devices.ts b/src/enums/devices.ts
index b085dfbada3..fafe929655e 100644
--- a/src/enums/devices.ts
+++ b/src/enums/devices.ts
@@ -1,4 +1,4 @@
export enum Device {
- GAMEPAD,
- KEYBOARD,
+ GAMEPAD,
+ KEYBOARD,
}
diff --git a/src/enums/drop-down-column.ts b/src/enums/drop-down-column.ts
index b413d1f0bf4..0adf0327e4c 100644
--- a/src/enums/drop-down-column.ts
+++ b/src/enums/drop-down-column.ts
@@ -5,5 +5,5 @@ export enum DropDownColumn {
CAUGHT,
UNLOCKS,
MISC,
- SORT
+ SORT,
}
diff --git a/src/enums/dynamic-phase-type.ts b/src/enums/dynamic-phase-type.ts
index b9ea6bf197d..3146b136dac 100644
--- a/src/enums/dynamic-phase-type.ts
+++ b/src/enums/dynamic-phase-type.ts
@@ -3,5 +3,5 @@
*/
// TODO: We currently assume these are in order
export enum DynamicPhaseType {
- POST_SUMMON
+ POST_SUMMON,
}
diff --git a/src/enums/ease-type.ts b/src/enums/ease-type.ts
index fbe06fd536d..638a21dbb96 100644
--- a/src/enums/ease-type.ts
+++ b/src/enums/ease-type.ts
@@ -1,15 +1,15 @@
export enum EaseType {
- NONE,
- LINEAR = "Linear",
- QUADRATIC = "Quad",
- CUBIC = "Cubic",
- QUARTIC = "Quart",
- QUINTIC = "Quint",
- SINUSOIDAL = "Sine",
- EXPONENTIAL = "Expo",
- CIRCULAR = "Circ",
- ELASTIC = "Elastic",
- BACK = "Back",
- BOUNCE = "Bounce",
- STEPPED = "Stepped",
+ NONE,
+ LINEAR = "Linear",
+ QUADRATIC = "Quad",
+ CUBIC = "Cubic",
+ QUARTIC = "Quart",
+ QUINTIC = "Quint",
+ SINUSOIDAL = "Sine",
+ EXPONENTIAL = "Expo",
+ CIRCULAR = "Circ",
+ ELASTIC = "Elastic",
+ BACK = "Back",
+ BOUNCE = "Bounce",
+ STEPPED = "Stepped",
}
diff --git a/src/enums/egg-source-types.ts b/src/enums/egg-source-types.ts
index a670d86704b..8956e8c9795 100644
--- a/src/enums/egg-source-types.ts
+++ b/src/enums/egg-source-types.ts
@@ -1,7 +1,7 @@
export enum EggSourceType {
- GACHA_MOVE,
- GACHA_LEGENDARY,
- GACHA_SHINY,
- SAME_SPECIES_EGG,
- EVENT
+ GACHA_MOVE,
+ GACHA_LEGENDARY,
+ GACHA_SHINY,
+ SAME_SPECIES_EGG,
+ EVENT,
}
diff --git a/src/enums/egg-type.ts b/src/enums/egg-type.ts
index 901e60b3c76..8244bba4ad1 100644
--- a/src/enums/egg-type.ts
+++ b/src/enums/egg-type.ts
@@ -2,5 +2,5 @@ export enum EggTier {
COMMON,
RARE,
EPIC,
- LEGENDARY
+ LEGENDARY,
}
diff --git a/src/enums/encounter-anims.ts b/src/enums/encounter-anims.ts
index bd1461473c9..55c64e2a90e 100644
--- a/src/enums/encounter-anims.ts
+++ b/src/enums/encounter-anims.ts
@@ -7,5 +7,5 @@ export enum EncounterAnim {
MAGMA_BG,
MAGMA_SPOUT,
SMOKESCREEN,
- DANCE
+ DANCE,
}
diff --git a/src/enums/exp-gains-speed.ts b/src/enums/exp-gains-speed.ts
index f5f36a1c78d..b98345552ae 100644
--- a/src/enums/exp-gains-speed.ts
+++ b/src/enums/exp-gains-speed.ts
@@ -7,5 +7,5 @@ export enum ExpGainsSpeed {
/** Faster speed. */
FASTER,
/** Skip gaining exp animation. */
- SKIP
+ SKIP,
}
diff --git a/src/enums/exp-notification.ts b/src/enums/exp-notification.ts
index a64d8591e76..491578ec49a 100644
--- a/src/enums/exp-notification.ts
+++ b/src/enums/exp-notification.ts
@@ -5,5 +5,5 @@ export enum ExpNotification {
/** Display smaller flyout showing level gained on gaining a new level. */
ONLY_LEVEL_UP,
/** Do not show any flyouts for EXP gains or levelups. */
- SKIP
+ SKIP,
}
diff --git a/src/enums/field-position.ts b/src/enums/field-position.ts
index 5b7f9c6c570..5ca5ee9ac66 100644
--- a/src/enums/field-position.ts
+++ b/src/enums/field-position.ts
@@ -1,5 +1,5 @@
export enum FieldPosition {
CENTER,
LEFT,
- RIGHT
+ RIGHT,
}
diff --git a/src/enums/fixed-boss-waves.ts b/src/enums/fixed-boss-waves.ts
index 623d9035472..b1725d14215 100644
--- a/src/enums/fixed-boss-waves.ts
+++ b/src/enums/fixed-boss-waves.ts
@@ -18,5 +18,5 @@ export enum ClassicFixedBossWaves {
ELITE_FOUR_3 = 186,
ELITE_FOUR_4 = 188,
CHAMPION = 190,
- RIVAL_6 = 195
+ RIVAL_6 = 195,
}
diff --git a/src/enums/form-change-item.ts b/src/enums/form-change-item.ts
index 15620eafd0a..77818ca3488 100644
--- a/src/enums/form-change-item.ts
+++ b/src/enums/form-change-item.ts
@@ -96,8 +96,8 @@ export enum FormChangeItem {
DRACO_PLATE,
DREAD_PLATE,
PIXIE_PLATE,
- BLANK_PLATE,// TODO: Find a potential use for this
- LEGEND_PLATE,// TODO: Find a potential use for this
+ BLANK_PLATE, // TODO: Find a potential use for this
+ LEGEND_PLATE, // TODO: Find a potential use for this
FIGHTING_MEMORY,
FLYING_MEMORY,
POISON_MEMORY,
@@ -115,5 +115,5 @@ export enum FormChangeItem {
DRAGON_MEMORY,
DARK_MEMORY,
FAIRY_MEMORY,
- NORMAL_MEMORY
+ NORMAL_MEMORY,
}
diff --git a/src/enums/gacha-types.ts b/src/enums/gacha-types.ts
index 08f147b27b1..b5b60f35d21 100644
--- a/src/enums/gacha-types.ts
+++ b/src/enums/gacha-types.ts
@@ -1,9 +1,9 @@
import type { ObjectValues } from "#types/type-helpers";
export const GachaType = Object.freeze({
- MOVE: 0,
- LEGENDARY: 1,
- SHINY: 2
+ MOVE: 0,
+ LEGENDARY: 1,
+ SHINY: 2,
});
export type GachaType = ObjectValues;
diff --git a/src/enums/game-data-type.ts b/src/enums/game-data-type.ts
index d672253794a..d671c7666f3 100644
--- a/src/enums/game-data-type.ts
+++ b/src/enums/game-data-type.ts
@@ -7,5 +7,5 @@ export enum GameDataType {
SETTINGS,
TUTORIALS,
SEEN_DIALOGUES,
- RUN_HISTORY
+ RUN_HISTORY,
}
diff --git a/src/enums/game-modes.ts b/src/enums/game-modes.ts
index 837b634621c..061f622cae2 100644
--- a/src/enums/game-modes.ts
+++ b/src/enums/game-modes.ts
@@ -3,5 +3,5 @@ export enum GameModes {
ENDLESS,
SPLICED_ENDLESS,
DAILY,
- CHALLENGE
+ CHALLENGE,
}
diff --git a/src/enums/hit-result.ts b/src/enums/hit-result.ts
index 3e62587dd6c..9df8ca6121a 100644
--- a/src/enums/hit-result.ts
+++ b/src/enums/hit-result.ts
@@ -11,5 +11,5 @@ export enum HitResult {
INDIRECT,
IMMUNE,
CONFUSION,
- INDIRECT_KO
+ INDIRECT_KO,
}
diff --git a/src/enums/learn-move-situation.ts b/src/enums/learn-move-situation.ts
index 9b329d0f3de..e12aef5a11b 100644
--- a/src/enums/learn-move-situation.ts
+++ b/src/enums/learn-move-situation.ts
@@ -3,6 +3,6 @@ export enum LearnMoveSituation {
LEVEL_UP,
RELEARN,
EVOLUTION,
- EVOLUTION_FUSED,// If fusionSpecies has Evolved
- EVOLUTION_FUSED_BASE
+ EVOLUTION_FUSED, // If fusionSpecies has Evolved
+ EVOLUTION_FUSED_BASE,
}
diff --git a/src/enums/learn-move-type.ts b/src/enums/learn-move-type.ts
index 442639c1bc7..3162d8d7e73 100644
--- a/src/enums/learn-move-type.ts
+++ b/src/enums/learn-move-type.ts
@@ -4,5 +4,5 @@ export enum LearnMoveType {
/** For learning a move via Memory Mushroom */
MEMORY,
/** For learning a move via TM */
- TM
+ TM,
}
diff --git a/src/enums/modifier-pool-type.ts b/src/enums/modifier-pool-type.ts
index 0d2b92ba80d..ca3ed3a11c7 100644
--- a/src/enums/modifier-pool-type.ts
+++ b/src/enums/modifier-pool-type.ts
@@ -3,5 +3,5 @@ export enum ModifierPoolType {
WILD,
TRAINER,
ENEMY_BUFF,
- DAILY_STARTER
+ DAILY_STARTER,
}
diff --git a/src/enums/money-format.ts b/src/enums/money-format.ts
index 643247194a7..228e3f9d9f0 100644
--- a/src/enums/money-format.ts
+++ b/src/enums/money-format.ts
@@ -1,4 +1,4 @@
export enum MoneyFormat {
NORMAL,
- ABBREVIATED
+ ABBREVIATED,
}
diff --git a/src/enums/move-anims-common.ts b/src/enums/move-anims-common.ts
index f21e4c8be4a..96498f6d294 100644
--- a/src/enums/move-anims-common.ts
+++ b/src/enums/move-anims-common.ts
@@ -1,20 +1,20 @@
export enum AnimFrameTarget {
USER,
TARGET,
- GRAPHIC
+ GRAPHIC,
}
export enum AnimFocus {
TARGET = 1,
USER,
USER_TARGET,
- SCREEN
+ SCREEN,
}
export enum AnimBlendType {
NORMAL,
ADD,
- SUBTRACT
+ SUBTRACT,
}
export enum ChargeAnim {
@@ -38,7 +38,7 @@ export enum ChargeAnim {
SOLAR_BLADE_CHARGING,
BEAK_BLAST_CHARGING,
METEOR_BEAM_CHARGING,
- ELECTRO_SHOT_CHARGING
+ ELECTRO_SHOT_CHARGING,
}
export enum CommonAnim {
@@ -91,5 +91,5 @@ export enum CommonAnim {
ELECTRIC_TERRAIN,
GRASSY_TERRAIN,
PSYCHIC_TERRAIN,
- LOCK_ON = 2120
+ LOCK_ON = 2120,
}
diff --git a/src/enums/move-category.ts b/src/enums/move-category.ts
index 0408655e6db..f0a171b2fea 100644
--- a/src/enums/move-category.ts
+++ b/src/enums/move-category.ts
@@ -1,5 +1,5 @@
export enum MoveCategory {
PHYSICAL,
SPECIAL,
- STATUS
+ STATUS,
}
diff --git a/src/enums/move-effect-trigger.ts b/src/enums/move-effect-trigger.ts
index d22953c3690..a33a6766bef 100644
--- a/src/enums/move-effect-trigger.ts
+++ b/src/enums/move-effect-trigger.ts
@@ -2,5 +2,5 @@ export enum MoveEffectTrigger {
PRE_APPLY,
POST_APPLY,
/** Triggers one time after all target effects have applied */
- POST_TARGET
+ POST_TARGET,
}
diff --git a/src/enums/move-flags.ts b/src/enums/move-flags.ts
index 6cdc1e5f8cc..acd73f897e7 100644
--- a/src/enums/move-flags.ts
+++ b/src/enums/move-flags.ts
@@ -29,7 +29,7 @@ export enum MoveFlags {
SLICING_MOVE = 1 << 8,
/**
* Indicates a move should be affected by {@linkcode AbilityId.RECKLESS}
- * @see {@linkcode Move.recklessMove()}
+ * @see {@linkcode Move.recklessMove}
*/
RECKLESS_MOVE = 1 << 9,
/** Indicates a move should be affected by {@linkcode AbilityId.BULLETPROOF} */
@@ -50,5 +50,5 @@ export enum MoveFlags {
/** Indicates a move is able to be redirected to allies in a double battle if the attacker faints */
REDIRECT_COUNTER = 1 << 18,
/** Indicates a move is able to be reflected by {@linkcode AbilityId.MAGIC_BOUNCE} and {@linkcode MoveId.MAGIC_COAT} */
- REFLECTABLE = 1 << 19
+ REFLECTABLE = 1 << 19,
}
diff --git a/src/enums/move-result.ts b/src/enums/move-result.ts
index d402f5b1aed..c213b6378ca 100644
--- a/src/enums/move-result.ts
+++ b/src/enums/move-result.ts
@@ -3,5 +3,5 @@ export enum MoveResult {
SUCCESS,
FAIL,
MISS,
- OTHER
+ OTHER,
}
diff --git a/src/enums/move-source-type.ts b/src/enums/move-source-type.ts
index d9afb07e7f7..745d4d058ba 100644
--- a/src/enums/move-source-type.ts
+++ b/src/enums/move-source-type.ts
@@ -2,11 +2,11 @@
* Used for challenge types that modify movesets, these denote the various sources of moves for pokemon.
*/
export enum MoveSourceType {
- LEVEL_UP,// Currently unimplemented for move access
- RELEARNER,// Relearner moves currently unimplemented
+ LEVEL_UP, // Currently unimplemented for move access
+ RELEARNER, // Relearner moves currently unimplemented
COMMON_TM,
GREAT_TM,
ULTRA_TM,
COMMON_EGG,
- RARE_EGG
+ RARE_EGG,
}
diff --git a/src/enums/move-target.ts b/src/enums/move-target.ts
index 615628cf4e8..91ea9e8cac9 100644
--- a/src/enums/move-target.ts
+++ b/src/enums/move-target.ts
@@ -25,5 +25,5 @@ export enum MoveTarget {
ENEMY_SIDE,
BOTH_SIDES,
PARTY,
- CURSE
+ CURSE,
}
diff --git a/src/enums/move-use-mode.ts b/src/enums/move-use-mode.ts
index 13ea5248853..b08977195f0 100644
--- a/src/enums/move-use-mode.ts
+++ b/src/enums/move-use-mode.ts
@@ -1,6 +1,3 @@
-import type { PostDancingMoveAbAttr } from "#abilities/ability";
-import type { DelayedAttackAttr } from "#app/@types/move-types";
-import type { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
import type { ObjectValues } from "#types/type-helpers";
/**
@@ -15,9 +12,9 @@ import type { ObjectValues } from "#types/type-helpers";
*/
export const MoveUseMode = {
/**
- * This move was used normally (i.e. clicking on the button) or called via Instruct.
- * It deducts PP from the user's moveset (failing if out of PP), and interacts normally with other moves and abilities.
- */
+ * This move was used normally (i.e. clicking on the button) or called via Instruct.
+ * It deducts PP from the user's moveset (failing if out of PP), and interacts normally with other moves and abilities.
+ */
NORMAL: 1,
/**
@@ -71,7 +68,7 @@ export const MoveUseMode = {
* @todo Consider other means of implementing FS/DD than this - we currently only use it
* to prevent pushing to move history and avoid re-delaying the attack portion
*/
- DELAYED_ATTACK: 6
+ DELAYED_ATTACK: 6,
} as const;
export type MoveUseMode = ObjectValues;
@@ -96,7 +93,7 @@ export type MoveUseMode = ObjectValues;
* | {@linkcode MoveUseMode.DELAYED_ATTACK} | `true` |
*/
export function isVirtual(useMode: MoveUseMode): boolean {
- return useMode >= MoveUseMode.INDIRECT
+ return useMode >= MoveUseMode.INDIRECT;
}
/**
@@ -161,4 +158,4 @@ export function isIgnorePP(useMode: MoveUseMode): boolean {
*/
export function isReflected(useMode: MoveUseMode): boolean {
return useMode === MoveUseMode.REFLECTED;
-}
\ No newline at end of file
+}
diff --git a/src/enums/multi-hit-type.ts b/src/enums/multi-hit-type.ts
index 27e8214112e..1f737df6ca9 100644
--- a/src/enums/multi-hit-type.ts
+++ b/src/enums/multi-hit-type.ts
@@ -3,5 +3,5 @@ export enum MultiHitType {
_2_TO_5,
_3,
_10,
- BEAT_UP
+ BEAT_UP,
}
diff --git a/src/enums/mystery-encounter-mode.ts b/src/enums/mystery-encounter-mode.ts
index f1e98ca5b18..c8f58354b6c 100644
--- a/src/enums/mystery-encounter-mode.ts
+++ b/src/enums/mystery-encounter-mode.ts
@@ -8,5 +8,5 @@ export enum MysteryEncounterMode {
/** Enables special boss music during encounter */
BOSS_BATTLE,
/** If there is no battle in the {@linkcode MysteryEncounter} or option selected */
- NO_BATTLE
+ NO_BATTLE,
}
diff --git a/src/enums/mystery-encounter-option-mode.ts b/src/enums/mystery-encounter-option-mode.ts
index a994c30581b..4c41f1f06ca 100644
--- a/src/enums/mystery-encounter-option-mode.ts
+++ b/src/enums/mystery-encounter-option-mode.ts
@@ -6,5 +6,5 @@ export enum MysteryEncounterOptionMode {
/** Default style on requirements not met, special style on requirements met */
DEFAULT_OR_SPECIAL,
/** Disabled on requirements not met, special style on requirements met */
- DISABLED_OR_SPECIAL
+ DISABLED_OR_SPECIAL,
}
diff --git a/src/enums/mystery-encounter-tier.ts b/src/enums/mystery-encounter-tier.ts
index 484acc7aba9..aa9e7290381 100644
--- a/src/enums/mystery-encounter-tier.ts
+++ b/src/enums/mystery-encounter-tier.ts
@@ -7,5 +7,5 @@ export enum MysteryEncounterTier {
GREAT = 40,
ULTRA = 19,
ROGUE = 3,
- MASTER = 0 // Not currently used
+ MASTER = 0, // Not currently used
}
diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts
index b973652b113..2286b15f5c3 100644
--- a/src/enums/mystery-encounter-type.ts
+++ b/src/enums/mystery-encounter-type.ts
@@ -29,5 +29,5 @@ export enum MysteryEncounterType {
FUN_AND_GAMES,
UNCOMMON_BREED,
GLOBAL_TRADE_SYSTEM,
- THE_EXPERT_POKEMON_BREEDER
+ THE_EXPERT_POKEMON_BREEDER,
}
diff --git a/src/enums/party-member-strength.ts b/src/enums/party-member-strength.ts
index 793796a65b4..24e78e4d3d6 100644
--- a/src/enums/party-member-strength.ts
+++ b/src/enums/party-member-strength.ts
@@ -4,5 +4,5 @@ export enum PartyMemberStrength {
WEAK,
AVERAGE,
STRONG,
- STRONGER
+ STRONGER,
}
diff --git a/src/enums/passive.ts b/src/enums/passive.ts
index b9c2dacd463..88ff0d960c7 100644
--- a/src/enums/passive.ts
+++ b/src/enums/passive.ts
@@ -3,5 +3,5 @@
*/
export enum Passive {
UNLOCKED = 1,
- ENABLED = 2
+ ENABLED = 2,
}
diff --git a/src/enums/player-gender.ts b/src/enums/player-gender.ts
index b6cd550b1f2..72f9c556f5e 100644
--- a/src/enums/player-gender.ts
+++ b/src/enums/player-gender.ts
@@ -4,5 +4,5 @@
export enum PlayerGender {
UNSET,
MALE,
- FEMALE
+ FEMALE,
}
diff --git a/src/enums/pokemon-anim-type.ts b/src/enums/pokemon-anim-type.ts
index b153fb2e652..daee1d1ae31 100644
--- a/src/enums/pokemon-anim-type.ts
+++ b/src/enums/pokemon-anim-type.ts
@@ -22,5 +22,5 @@ export enum PokemonAnimType {
* Dondozo "spits out" Tatsugiri, moving Tatsugiri back to its original
* field position.
*/
- COMMANDER_REMOVE
+ COMMANDER_REMOVE,
}
diff --git a/src/enums/pokemon-type.ts b/src/enums/pokemon-type.ts
index eca02bae275..bfa27c03b8a 100644
--- a/src/enums/pokemon-type.ts
+++ b/src/enums/pokemon-type.ts
@@ -18,5 +18,8 @@ export enum PokemonType {
DRAGON,
DARK,
FAIRY,
- STELLAR
+ STELLAR,
}
+
+/** The largest legal value for a {@linkcode PokemonType} (includes Stellar) */
+export const MAX_POKEMON_TYPE = PokemonType.STELLAR;
diff --git a/src/enums/shop-cursor-target.ts b/src/enums/shop-cursor-target.ts
index 11f524399b2..2d788f50883 100644
--- a/src/enums/shop-cursor-target.ts
+++ b/src/enums/shop-cursor-target.ts
@@ -9,5 +9,5 @@ export enum ShopCursorTarget {
/** Cursor points to Shop row */
SHOP,
/** Cursor points to Check Team row */
- CHECK_TEAM
+ CHECK_TEAM,
}
diff --git a/src/enums/stat.ts b/src/enums/stat.ts
index 72ccc065850..36e9891f23c 100644
--- a/src/enums/stat.ts
+++ b/src/enums/stat.ts
@@ -15,28 +15,28 @@ export enum Stat {
/** Accuracy */
ACC,
/** Evasiveness */
- EVA
+ EVA,
}
/** A constant array comprised of the {@linkcode Stat} values that make up {@linkcode PermanentStat}. */
-export const PERMANENT_STATS = [ Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const;
+export const PERMANENT_STATS = [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD] as const;
/** Type used to describe the core, permanent stats of a Pokemon. */
-export type PermanentStat = typeof PERMANENT_STATS[number];
+export type PermanentStat = (typeof PERMANENT_STATS)[number];
/** A constant array comprised of the {@linkcode Stat} values that make up {@linkcode EFfectiveStat}. */
-export const EFFECTIVE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const;
+export const EFFECTIVE_STATS = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD] as const;
/** Type used to describe the intersection of core stats and stats that have stages in battle. */
-export type EffectiveStat = typeof EFFECTIVE_STATS[number];
+export type EffectiveStat = (typeof EFFECTIVE_STATS)[number];
/** A constant array comprised of {@linkcode Stat} the values that make up {@linkcode BattleStat}. */
-export const BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC, Stat.EVA ] as const;
+export const BATTLE_STATS = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC, Stat.EVA] as const;
/** Type used to describe the stats that have stages which can be incremented and decremented in battle. */
-export type BattleStat = typeof BATTLE_STATS[number];
+export type BattleStat = (typeof BATTLE_STATS)[number];
/** A constant array comprised of {@linkcode Stat} the values that make up {@linkcode TempBattleStat}. */
-export const TEMP_BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC ] as const;
+export const TEMP_BATTLE_STATS = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC] as const;
/** Type used to describe the stats that have X item (`TEMP_STAT_STAGE_BOOSTER`) equivalents. */
-export type TempBattleStat = typeof TEMP_BATTLE_STATS[number];
+export type TempBattleStat = (typeof TEMP_BATTLE_STATS)[number];
/**
* Provides the translation key corresponding to the amount of stat stages and whether those stat stages
diff --git a/src/enums/tera-ai-mode.ts b/src/enums/tera-ai-mode.ts
index 35d4e4f3420..a31ad9fd810 100644
--- a/src/enums/tera-ai-mode.ts
+++ b/src/enums/tera-ai-mode.ts
@@ -1,5 +1,5 @@
export enum TeraAIMode {
NO_TERA,
INSTANT_TERA,
- SMART_TERA
+ SMART_TERA,
}
diff --git a/src/enums/text-style.ts b/src/enums/text-style.ts
index 964a985cdd6..1c6f4e8ca1b 100644
--- a/src/enums/text-style.ts
+++ b/src/enums/text-style.ts
@@ -54,6 +54,6 @@ export const TextStyle = Object.freeze({
PERFECT_IV: 53,
ME_OPTION_DEFAULT: 54, // Default style for choices in ME
ME_OPTION_SPECIAL: 55, // Style for choices with special requirements in ME
- SHADOW_TEXT: 56 // to obscure unavailable options
-})
-export type TextStyle = typeof TextStyle[keyof typeof TextStyle];
\ No newline at end of file
+ SHADOW_TEXT: 56, // to obscure unavailable options
+});
+export type TextStyle = (typeof TextStyle)[keyof typeof TextStyle];
diff --git a/src/enums/time-of-day.ts b/src/enums/time-of-day.ts
index 3fd05308cc6..a5a15f39ef8 100644
--- a/src/enums/time-of-day.ts
+++ b/src/enums/time-of-day.ts
@@ -3,5 +3,5 @@ export enum TimeOfDay {
DAWN,
DAY,
DUSK,
- NIGHT
+ NIGHT,
}
diff --git a/src/enums/trainer-pool-tier.ts b/src/enums/trainer-pool-tier.ts
index da6355d021b..08f37062d85 100644
--- a/src/enums/trainer-pool-tier.ts
+++ b/src/enums/trainer-pool-tier.ts
@@ -3,5 +3,5 @@ export enum TrainerPoolTier {
UNCOMMON,
RARE,
SUPER_RARE,
- ULTRA_RARE
+ ULTRA_RARE,
}
diff --git a/src/enums/trainer-slot.ts b/src/enums/trainer-slot.ts
index 2dfa468f74c..8e801e03d0f 100644
--- a/src/enums/trainer-slot.ts
+++ b/src/enums/trainer-slot.ts
@@ -1,5 +1,5 @@
export enum TrainerSlot {
NONE,
TRAINER,
- TRAINER_PARTNER
+ TRAINER_PARTNER,
}
diff --git a/src/enums/trainer-type.ts b/src/enums/trainer-type.ts
index e22dc5d81c7..9e8705e9886 100644
--- a/src/enums/trainer-type.ts
+++ b/src/enums/trainer-type.ts
@@ -255,5 +255,5 @@ export enum TrainerType {
RIVAL_3,
RIVAL_4,
RIVAL_5,
- RIVAL_6
+ RIVAL_6,
}
diff --git a/src/enums/trainer-variant.ts b/src/enums/trainer-variant.ts
index cd8d71cc1b9..074079458a6 100644
--- a/src/enums/trainer-variant.ts
+++ b/src/enums/trainer-variant.ts
@@ -1,5 +1,5 @@
export enum TrainerVariant {
DEFAULT,
FEMALE,
- DOUBLE
+ DOUBLE,
}
diff --git a/src/enums/ui-theme.ts b/src/enums/ui-theme.ts
index 50b5c4f65a3..0592db13b6e 100644
--- a/src/enums/ui-theme.ts
+++ b/src/enums/ui-theme.ts
@@ -1,4 +1,4 @@
export enum UiTheme {
DEFAULT,
- LEGACY
+ LEGACY,
}
diff --git a/src/enums/unlockables.ts b/src/enums/unlockables.ts
index 77b39a17e90..7ff21285870 100644
--- a/src/enums/unlockables.ts
+++ b/src/enums/unlockables.ts
@@ -1,7 +1,6 @@
-
export enum Unlockables {
ENDLESS_MODE,
MINI_BLACK_HOLE,
SPLICED_ENDLESS_MODE,
- EVIOLITE
+ EVIOLITE,
}
diff --git a/src/enums/variant-tier.ts b/src/enums/variant-tier.ts
index 279846d5f60..67c04eda3da 100644
--- a/src/enums/variant-tier.ts
+++ b/src/enums/variant-tier.ts
@@ -1,5 +1,5 @@
export enum VariantTier {
- STANDARD,
- RARE,
- EPIC
+ STANDARD,
+ RARE,
+ EPIC,
}
diff --git a/src/events/arena.ts b/src/events/arena.ts
index 5415b8eb026..cf287de3176 100644
--- a/src/events/arena.ts
+++ b/src/events/arena.ts
@@ -16,10 +16,7 @@ export enum ArenaEventType {
TAG_REMOVED = "onTagRemoved",
}
-/**
- * Base container class for all {@linkcode ArenaEventType} events
- * @extends Event
- */
+/** Base container class for all {@linkcode ArenaEventType} events */
export class ArenaEvent extends Event {
/** The total duration of the {@linkcode ArenaEventType} */
public duration: number;
@@ -29,10 +26,7 @@ export class ArenaEvent extends Event {
this.duration = duration;
}
}
-/**
- * Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events
- * @extends ArenaEvent
- */
+/** Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events */
export class WeatherChangedEvent extends ArenaEvent {
/** The {@linkcode WeatherType} being overridden */
public oldWeatherType: WeatherType;
@@ -45,10 +39,7 @@ export class WeatherChangedEvent extends ArenaEvent {
this.newWeatherType = newWeatherType;
}
}
-/**
- * Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events
- * @extends ArenaEvent
- */
+/** Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events */
export class TerrainChangedEvent extends ArenaEvent {
/** The {@linkcode TerrainType} being overridden */
public oldTerrainType: TerrainType;
@@ -62,10 +53,7 @@ export class TerrainChangedEvent extends ArenaEvent {
}
}
-/**
- * Container class for {@linkcode ArenaEventType.TAG_ADDED} events
- * @extends ArenaEvent
- */
+/** Container class for {@linkcode ArenaEventType.TAG_ADDED} events */
export class TagAddedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being added */
public arenaTagType: ArenaTagType;
@@ -91,10 +79,7 @@ export class TagAddedEvent extends ArenaEvent {
this.arenaTagMaxLayers = arenaTagMaxLayers!; // TODO: is this bang correct?
}
}
-/**
- * Container class for {@linkcode ArenaEventType.TAG_REMOVED} events
- * @extends ArenaEvent
- */
+/** Container class for {@linkcode ArenaEventType.TAG_REMOVED} events */
export class TagRemovedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being removed */
public arenaTagType: ArenaTagType;
diff --git a/src/events/battle-scene.ts b/src/events/battle-scene.ts
index 29aee1053cd..fdee812f54e 100644
--- a/src/events/battle-scene.ts
+++ b/src/events/battle-scene.ts
@@ -43,10 +43,7 @@ export enum BattleSceneEventType {
NEW_ARENA = "onNewArena",
}
-/**
- * Container class for {@linkcode BattleSceneEventType.CANDY_UPGRADE_NOTIFICATION_CHANGED} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.CANDY_UPGRADE_NOTIFICATION_CHANGED} events */
export class CandyUpgradeNotificationChangedEvent extends Event {
/** The new value the setting was changed to */
public newValue: number;
@@ -57,10 +54,7 @@ export class CandyUpgradeNotificationChangedEvent extends Event {
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.MOVE_USED} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.MOVE_USED} events */
export class MoveUsedEvent extends Event {
/** The ID of the {@linkcode Pokemon} that used the {@linkcode Move} */
public pokemonId: number;
@@ -76,10 +70,7 @@ export class MoveUsedEvent extends Event {
this.ppUsed = ppUsed;
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.BERRY_USED} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.BERRY_USED} events */
export class BerryUsedEvent extends Event {
/** The {@linkcode BerryModifier} being used */
public berryModifier: BerryModifier;
@@ -90,28 +81,19 @@ export class BerryUsedEvent extends Event {
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.ENCOUNTER_PHASE} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.ENCOUNTER_PHASE} events */
export class EncounterPhaseEvent extends Event {
constructor() {
super(BattleSceneEventType.ENCOUNTER_PHASE);
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.TURN_INIT} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.TURN_INIT} events */
export class TurnInitEvent extends Event {
constructor() {
super(BattleSceneEventType.TURN_INIT);
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.TURN_END} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.TURN_END} events */
export class TurnEndEvent extends Event {
/** The amount of turns in the current battle */
public turnCount: number;
@@ -121,10 +103,7 @@ export class TurnEndEvent extends Event {
this.turnCount = turnCount;
}
}
-/**
- * Container class for {@linkcode BattleSceneEventType.NEW_ARENA} events
- * @extends Event
- */
+/** Container class for {@linkcode BattleSceneEventType.NEW_ARENA} events */
export class NewArenaEvent extends Event {
constructor() {
super(BattleSceneEventType.NEW_ARENA);
diff --git a/src/events/egg.ts b/src/events/egg.ts
index a0c26c82883..30a005d192f 100644
--- a/src/events/egg.ts
+++ b/src/events/egg.ts
@@ -8,7 +8,6 @@ export enum EggEventType {
/**
* Container class for {@linkcode EggEventType.EGG_COUNT_CHANGED} events
- * @extends Event
*/
export class EggCountChangedEvent extends Event {
/** The updated egg count. */
diff --git a/src/field/arena.ts b/src/field/arena.ts
index 7d7cf8ecce4..2b84b1f2d2b 100644
--- a/src/field/arena.ts
+++ b/src/field/arena.ts
@@ -116,9 +116,11 @@ export class Arena {
return overrideSpecies;
}
const isBossSpecies =
- !!globalScene.getEncounterBossSegments(waveIndex, level) &&
- !!this.pokemonPool[BiomePoolTier.BOSS].length &&
- (this.biomeType !== BiomeId.END || globalScene.gameMode.isClassic || globalScene.gameMode.isWaveFinal(waveIndex));
+ !!globalScene.getEncounterBossSegments(waveIndex, level)
+ && this.pokemonPool[BiomePoolTier.BOSS].length > 0
+ && (this.biomeType !== BiomeId.END
+ || globalScene.gameMode.isClassic
+ || globalScene.gameMode.isWaveFinal(waveIndex));
const randVal = isBossSpecies ? 64 : 512;
// luck influences encounter rarity
let luckModifier = 0;
@@ -144,14 +146,14 @@ export class Arena {
? BiomePoolTier.BOSS_SUPER_RARE
: BiomePoolTier.BOSS_ULTRA_RARE;
console.log(BiomePoolTier[tier]);
- while (!this.pokemonPool[tier]?.length) {
+ while (this.pokemonPool[tier]?.length === 0) {
console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
tier--;
}
const tierPool = this.pokemonPool[tier];
let ret: PokemonSpecies;
let regen = false;
- if (!tierPool.length) {
+ if (tierPool.length === 0) {
ret = globalScene.randomSpecies(waveIndex, level);
} else {
// TODO: should this use `randSeedItem`?
@@ -203,8 +205,8 @@ export class Arena {
randomTrainerType(waveIndex: number, isBoss = false): TrainerType {
const isTrainerBoss =
- !!this.trainerPool[BiomePoolTier.BOSS].length &&
- (globalScene.gameMode.isTrainerBoss(waveIndex, this.biomeType, globalScene.offsetGym) || isBoss);
+ this.trainerPool[BiomePoolTier.BOSS].length > 0
+ && (globalScene.gameMode.isTrainerBoss(waveIndex, this.biomeType, globalScene.offsetGym) || isBoss);
console.log(isBoss, this.trainerPool);
const tierValue = randSeedInt(!isTrainerBoss ? 512 : 64);
let tier = !isTrainerBoss
@@ -225,12 +227,12 @@ export class Arena {
? BiomePoolTier.BOSS_SUPER_RARE
: BiomePoolTier.BOSS_ULTRA_RARE;
console.log(BiomePoolTier[tier]);
- while (tier && !this.trainerPool[tier].length) {
+ while (tier && this.trainerPool[tier].length === 0) {
console.log(`Downgraded trainer rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
tier--;
}
const tierPool = this.trainerPool[tier] || [];
- return !tierPool.length ? TrainerType.BREEDER : tierPool[randSeedInt(tierPool.length)];
+ return tierPool.length === 0 ? TrainerType.BREEDER : tierPool[randSeedInt(tierPool.length)];
}
getSpeciesFormIndex(species: PokemonSpecies): number {
@@ -322,15 +324,14 @@ export class Arena {
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
if (
- this.weather?.isImmutable() &&
- ![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather)
+ this.weather?.isImmutable()
+ && ![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather)
) {
globalScene.phaseManager.unshiftNew(
"CommonAnimPhase",
undefined,
undefined,
CommonAnim.SUNNY + (oldWeatherType - 1),
- true,
);
globalScene.phaseManager.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!);
return false;
@@ -349,13 +350,7 @@ export class Arena {
); // TODO: is this bang correct?
if (this.weather) {
- globalScene.phaseManager.unshiftNew(
- "CommonAnimPhase",
- undefined,
- undefined,
- CommonAnim.SUNNY + (weather - 1),
- true,
- );
+ globalScene.phaseManager.unshiftNew("CommonAnimPhase", undefined, undefined, CommonAnim.SUNNY + (weather - 1));
globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
} else {
globalScene.phaseManager.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
@@ -737,14 +732,12 @@ export class Arena {
* Attempt to get a tag from the Arena via {@linkcode getTagOnSide} that applies to both sides
* @param tagType - The {@linkcode ArenaTagType} to retrieve
* @returns The existing {@linkcode ArenaTag}, or `undefined` if not present.
- * @overload
*/
getTag(tagType: ArenaTagType): ArenaTag | undefined;
/**
* Attempt to get a tag from the Arena via {@linkcode getTagOnSide} that applies to both sides
* @param tagType - The constructor of the {@linkcode ArenaTag} to retrieve
* @returns The existing {@linkcode ArenaTag}, or `undefined` if not present.
- * @overload
*/
getTag(tagType: Constructor | AbstractConstructor): T | undefined;
getTag(tagType: ArenaTagType | Constructor | AbstractConstructor): ArenaTag | undefined {
@@ -865,7 +858,7 @@ export class Arena {
}
removeAllTags(): void {
- while (this.tags.length) {
+ while (this.tags.length > 0) {
this.tags[0].onRemove(this);
this.eventTarget.dispatchEvent(
new TagRemovedEvent(this.tags[0].tagType, this.tags[0].side, this.tags[0].turnCount),
diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts
index d3776f349e6..cf0a0f30529 100644
--- a/src/field/mystery-encounter-intro.ts
+++ b/src/field/mystery-encounter-intro.ts
@@ -87,6 +87,7 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
variant: Variant;
}[];
+ // TODO: Refactor
constructor(encounter: MysteryEncounter) {
super(globalScene, -72, 76);
this.encounter = encounter;
@@ -193,17 +194,15 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
sprite.setPosition(sprite.x, sprite.y + y);
tintSprite.setPosition(tintSprite.x, tintSprite.y + y);
}
- } else {
// Single sprite
- if (this.spriteConfigs.length === 1) {
- sprite.x = origin;
- tintSprite.x = origin;
- } else {
- // Do standard sprite spacing (not including offset sprites)
- sprite.x = minX + (n + 0.5) * spacingValue + origin;
- tintSprite.x = minX + (n + 0.5) * spacingValue + origin;
- n++;
- }
+ } else if (this.spriteConfigs.length === 1) {
+ sprite.x = origin;
+ tintSprite.x = origin;
+ } else {
+ // Do standard sprite spacing (not including offset sprites)
+ sprite.x = minX + (n + 0.5) * spacingValue + origin;
+ tintSprite.x = minX + (n + 0.5) * spacingValue + origin;
+ n++;
}
if (!isNullOrUndefined(pokemonShinySparkle)) {
@@ -457,7 +456,7 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: sprite,
alpha: alpha || 1,
- duration: duration,
+ duration,
ease: ease || "Linear",
});
} else {
@@ -490,7 +489,7 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: sprite,
alpha: 0,
- duration: duration,
+ duration,
ease: ease || "Linear",
onComplete: () => {
sprite.setVisible(false);
diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts
index f29e8b204e8..84ed3de6166 100644
--- a/src/field/pokemon.ts
+++ b/src/field/pokemon.ts
@@ -137,18 +137,18 @@ import { loadMoveAnimations } from "#sprites/pokemon-asset-loader";
import type { Variant } from "#sprites/variant";
import { populateVariantColors, variantColorCache, variantData } from "#sprites/variant";
import { achvs } from "#system/achv";
-import type { StarterDataEntry, StarterMoveset } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
import { RibbonData } from "#system/ribbons/ribbon-data";
import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods";
import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#types/ability-types";
import type { DamageCalculationResult, DamageResult } from "#types/damage-result";
import type { IllusionData } from "#types/illusion-data";
+import type { StarterDataEntry, StarterMoveset } from "#types/save-data";
import type { TurnMove } from "#types/turn-move";
import { BattleInfo } from "#ui/battle-info";
import { EnemyBattleInfo } from "#ui/enemy-battle-info";
-import type { PartyOption } from "#ui/party-ui-handler";
-import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import type { PartyOption } from "#ui/handlers/party-ui-handler";
+import { PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
import { PlayerBattleInfo } from "#ui/player-battle-info";
import { applyChallenges } from "#utils/challenge-utils";
import {
@@ -315,7 +315,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
super(globalScene, x, y);
if (!species.isObtainable() && this.isPlayer()) {
- throw `Cannot create a player Pokemon for species "${species.getName(formIndex)}"`;
+ throw new Error(`Cannot create a player Pokemon for species "${species.getName(formIndex)}"`);
}
this.species = species;
@@ -454,7 +454,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
getNameToRender(useIllusion = true) {
const illusion = this.summonData.illusion;
const name = useIllusion ? (illusion?.name ?? this.name) : this.name;
- const nickname: string | undefined = useIllusion ? illusion?.nickname : this.nickname;
+ const nickname: string | undefined = useIllusion ? (illusion?.nickname ?? this.nickname) : this.nickname;
try {
if (nickname) {
return decodeURIComponent(escape(atob(nickname))); // TODO: Remove `atob` and `escape`... eventually...
@@ -893,7 +893,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
getFormKey(): string {
- if (!this.species.forms.length || this.species.forms.length <= this.formIndex) {
+ if (this.species.forms.length === 0 || this.species.forms.length <= this.formIndex) {
return "";
}
return this.species.forms[this.formIndex].formKey;
@@ -903,7 +903,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (!this.fusionSpecies) {
return null;
}
- if (!this.fusionSpecies.forms.length || this.fusionSpecies.forms.length <= this.fusionFormIndex) {
+ if (this.fusionSpecies.forms.length === 0 || this.fusionSpecies.forms.length <= this.fusionFormIndex) {
return "";
}
return this.fusionSpecies.forms[this.fusionFormIndex].formKey;
@@ -1079,17 +1079,13 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return !(
// Neither pokemon can be already transformed
(
- this.isTransformed() ||
- target.isTransformed() ||
- // Neither pokemon can be behind an illusion
- target.summonData.illusion ||
- this.summonData.illusion ||
- // The target cannot be behind a substitute
- target.getTag(BattlerTagType.SUBSTITUTE) ||
- // Transforming to/from fusion pokemon causes various problems (crashes, etc.)
- // TODO: Consider lifting restriction once bug is fixed
- this.isFusion() ||
- target.isFusion()
+ this.isTransformed()
+ || target.isTransformed() // Neither pokemon can be behind an illusion
+ || target.summonData.illusion
+ || this.summonData.illusion // The target cannot be behind a substitute
+ || target.getTag(BattlerTagType.SUBSTITUTE) // Transforming to/from fusion pokemon causes various problems (crashes, etc.) // TODO: Consider lifting restriction once bug is fixed
+ || this.isFusion()
+ || target.isFusion()
)
);
}
@@ -1109,7 +1105,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (!ignoreOverride && this.summonData.fusionSpeciesForm) {
return this.summonData.fusionSpeciesForm;
}
- if (!fusionSpecies?.forms?.length || fusionFormIndex >= fusionSpecies?.forms.length) {
+ if (fusionSpecies?.forms?.length === 0 || fusionFormIndex >= fusionSpecies?.forms.length) {
return fusionSpecies;
}
return fusionSpecies?.forms[fusionFormIndex];
@@ -1126,12 +1122,12 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
getSpriteScale(): number {
const formKey = this.getFormKey();
if (
- this.isMax() === true ||
- formKey === "segin-starmobile" ||
- formKey === "schedar-starmobile" ||
- formKey === "navi-starmobile" ||
- formKey === "ruchbah-starmobile" ||
- formKey === "caph-starmobile"
+ this.isMax() === true
+ || formKey === "segin-starmobile"
+ || formKey === "schedar-starmobile"
+ || formKey === "navi-starmobile"
+ || formKey === "ruchbah-starmobile"
+ || formKey === "caph-starmobile"
) {
// G-Max and starmobiles have flat 1.5x scale
return 1.5;
@@ -1252,7 +1248,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
// During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus"
const currentPhase = globalScene.phaseManager.getCurrentPhase();
- return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
+ return !(currentPhase.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
}
/** If this Pokemon has a Substitute on the field, removes its sprite from the field. */
@@ -1291,10 +1287,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
targets.push(subTag.sprite);
}
globalScene.tweens.add({
- targets: targets,
+ targets,
x: (_target, _key, value: number) => value + relX,
y: (_target, _key, value: number) => value + relY,
- duration: duration,
+ duration,
ease: "Sine.easeOut",
onComplete: () => resolve(),
});
@@ -1492,8 +1488,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
let ret =
- statVal.value *
- this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems);
+ statVal.value
+ * this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems);
switch (stat) {
case Stat.ATK:
@@ -1768,7 +1764,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns Whether this Pokemon is currently fused with another species.
*/
isFusion(useIllusion = false): boolean {
- return useIllusion ? !!this.summonData.illusion?.fusionSpecies : !!this.fusionSpecies;
+ return !!(useIllusion ? (this.summonData.illusion?.fusionSpecies ?? this.fusionSpecies) : this.fusionSpecies);
}
/**
@@ -1802,8 +1798,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
return (
- (this.species.speciesId === species && this.getFormKey() === formKey) ||
- (this.fusionSpecies?.speciesId === species && this.getFusionFormKey() === formKey)
+ (this.species.speciesId === species && this.getFormKey() === formKey)
+ || (this.fusionSpecies?.speciesId === species && this.getFusionFormKey() === formKey)
);
}
@@ -1900,12 +1896,12 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
}
- if (!types.length || !includeTeraType) {
+ if (types.length === 0 || !includeTeraType) {
if (
- !ignoreOverride &&
- this.summonData.types &&
- this.summonData.types.length > 0 &&
- (!this.summonData.illusion || !useIllusion)
+ !ignoreOverride
+ && this.summonData.types
+ && this.summonData.types.length > 0
+ && (!this.summonData.illusion || !useIllusion)
) {
this.summonData.types.forEach(t => types.push(t));
} else {
@@ -1926,15 +1922,15 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (fusionSpeciesForm) {
// Check if the fusion Pokemon also has permanent changes from ME when determining the fusion types
const fusionType1 =
- this.fusionCustomPokemonData?.types &&
- this.fusionCustomPokemonData.types.length > 0 &&
- this.fusionCustomPokemonData.types[0] !== PokemonType.UNKNOWN
+ this.fusionCustomPokemonData?.types
+ && this.fusionCustomPokemonData.types.length > 0
+ && this.fusionCustomPokemonData.types[0] !== PokemonType.UNKNOWN
? this.fusionCustomPokemonData.types[0]
: fusionSpeciesForm.type1;
const fusionType2 =
- this.fusionCustomPokemonData?.types &&
- this.fusionCustomPokemonData.types.length > 1 &&
- this.fusionCustomPokemonData.types[1] !== PokemonType.UNKNOWN
+ this.fusionCustomPokemonData?.types
+ && this.fusionCustomPokemonData.types.length > 1
+ && this.fusionCustomPokemonData.types[1] !== PokemonType.UNKNOWN
? this.fusionCustomPokemonData.types[1]
: fusionSpeciesForm.type2;
@@ -1948,18 +1944,18 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (secondType === PokemonType.UNKNOWN && isNullOrUndefined(fusionType2)) {
// If second pokemon was monotype and shared its primary type
secondType =
- customTypes &&
- this.customPokemonData.types.length > 1 &&
- this.customPokemonData.types[1] !== PokemonType.UNKNOWN
+ customTypes
+ && this.customPokemonData.types.length > 1
+ && this.customPokemonData.types[1] !== PokemonType.UNKNOWN
? this.customPokemonData.types[1]
: (speciesForm.type2 ?? PokemonType.UNKNOWN);
}
} else {
// If not a fusion, just get the second type from the species, checking for permanent changes from ME
secondType =
- customTypes &&
- this.customPokemonData.types.length > 1 &&
- this.customPokemonData.types[1] !== PokemonType.UNKNOWN
+ customTypes
+ && this.customPokemonData.types.length > 1
+ && this.customPokemonData.types[1] !== PokemonType.UNKNOWN
? this.customPokemonData.types[1]
: (speciesForm.type2 ?? PokemonType.UNKNOWN);
}
@@ -1971,7 +1967,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
// become UNKNOWN if no types are present
- if (!types.length) {
+ if (types.length === 0) {
types.push(PokemonType.UNKNOWN);
}
@@ -2121,16 +2117,16 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
public hasPassive(): boolean {
// returns override if valid for current case
if (
- (Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer()) ||
- (Overrides.ENEMY_HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isEnemy())
+ (Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer())
+ || (Overrides.ENEMY_HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isEnemy())
) {
return false;
}
if (
- ((Overrides.PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) &&
- this.isPlayer()) ||
- ((Overrides.ENEMY_PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.ENEMY_HAS_PASSIVE_ABILITY_OVERRIDE) &&
- this.isEnemy())
+ ((Overrides.PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE)
+ && this.isPlayer())
+ || ((Overrides.ENEMY_PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.ENEMY_HAS_PASSIVE_ABILITY_OVERRIDE)
+ && this.isEnemy())
) {
return true;
}
@@ -2139,10 +2135,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
const { currentBattle, gameMode } = globalScene;
const waveIndex = currentBattle?.waveIndex;
if (
- this.isEnemy() &&
- (currentBattle?.battleSpec === BattleSpec.FINAL_BOSS ||
- gameMode.isEndlessMinorBoss(waveIndex) ||
- gameMode.isEndlessMajorBoss(waveIndex))
+ this.isEnemy()
+ && (currentBattle?.battleSpec === BattleSpec.FINAL_BOSS
+ || gameMode.isEndlessMinorBoss(waveIndex)
+ || gameMode.isEndlessMajorBoss(waveIndex))
) {
return false;
}
@@ -2181,9 +2177,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
// (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized)
// If the ability itself is neutralizing gas, don't suppress it (handled through arena tag)
const unsuppressable =
- !ability.isSuppressable ||
- thisAbilitySuppressing ||
- (hasSuppressingAbility && !suppressAbilitiesTag.shouldApplyToSelf());
+ !ability.isSuppressable
+ || thisAbilitySuppressing
+ || (hasSuppressingAbility && !suppressAbilitiesTag.shouldApplyToSelf());
if (!unsuppressable) {
return false;
}
@@ -2221,8 +2217,16 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType);
}
- public getAbilityPriorities(): [number, number] {
- return [this.getAbility().postSummonPriority, this.getPassiveAbility().postSummonPriority];
+ /**
+ * Return the ability priorities of the pokemon's ability and, if enabled, its passive ability
+ * @returns A tuple containing the ability priorities of the pokemon
+ */
+ public getAbilityPriorities(): [number] | [activePriority: number, passivePriority: number] {
+ const abilityPriority = this.getAbility().postSummonPriority;
+ if (this.hasPassive()) {
+ return [abilityPriority, this.getPassiveAbility().postSummonPriority];
+ }
+ return [abilityPriority];
}
/**
@@ -2276,11 +2280,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
public isGrounded(): boolean {
return (
- !!this.getTag(GroundedTag) ||
- (!this.isOfType(PokemonType.FLYING, true, true) &&
- !this.hasAbility(AbilityId.LEVITATE) &&
- !this.getTag(BattlerTagType.FLOATING) &&
- !this.getTag(SemiInvulnerableTag))
+ !!this.getTag(GroundedTag)
+ || (!this.isOfType(PokemonType.FLYING, true, true)
+ && !this.hasAbility(AbilityId.LEVITATE)
+ && !this.getTag(BattlerTagType.FLOATING)
+ && !this.getTag(SemiInvulnerableTag))
);
}
@@ -2346,9 +2350,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
// then bypass the check for ion deluge and electrify
if (
- this.isTerastallized &&
- (move.id === MoveId.TERA_BLAST ||
- (move.id === MoveId.TERA_STARSTORM && moveTypeHolder.value === PokemonType.STELLAR))
+ this.isTerastallized
+ && (move.id === MoveId.TERA_BLAST
+ || (move.id === MoveId.TERA_STARSTORM && moveTypeHolder.value === PokemonType.STELLAR))
) {
return moveTypeHolder.value as PokemonType;
}
@@ -2505,17 +2509,13 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
defenderType,
});
}
- if (ignoreImmunity.value) {
- if (multiplier.value === 0) {
- return 1;
- }
+ if (ignoreImmunity.value && multiplier.value === 0) {
+ return 1;
}
const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[];
- if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType))) {
- if (multiplier.value === 0) {
- return 1;
- }
+ if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType)) && multiplier.value === 0) {
+ return 1;
}
}
return multiplier.value;
@@ -2526,11 +2526,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, typeMultiplierAgainstFlying);
// Handle strong winds lowering effectiveness of types super effective against pure flying
if (
- !ignoreStrongWinds &&
- arena.weather?.weatherType === WeatherType.STRONG_WINDS &&
- !arena.weather.isEffectSuppressed() &&
- this.isOfType(PokemonType.FLYING) &&
- typeMultiplierAgainstFlying.value === 2
+ !ignoreStrongWinds
+ && arena.weather?.weatherType === WeatherType.STRONG_WINDS
+ && !arena.weather.isEffectSuppressed()
+ && this.isOfType(PokemonType.FLYING)
+ && typeMultiplierAgainstFlying.value === 2
) {
multiplier /= 2;
if (!simulated) {
@@ -2550,8 +2550,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
const enemyTypes = opponent.getTypes(true, false, false, true);
/** Is this Pokemon faster than the opponent? */
const outspeed =
- (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false)) >=
- opponent.getEffectiveStat(Stat.SPD, this);
+ (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false))
+ >= opponent.getEffectiveStat(Stat.SPD, this);
/**
* Based on how effectively this Pokemon defends against the opponent's types.
@@ -2647,11 +2647,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Gets all level up moves in a given range for a particular pokemon.
- * @param {number} startingLevel Don't include moves below this level
- * @param {boolean} includeEvolutionMoves Whether to include evolution moves
- * @param {boolean} simulateEvolutionChain Whether to include moves from prior evolutions
- * @param {boolean} includeRelearnerMoves Whether to include moves that would require a relearner. Note the move relearner inherently allows evolution moves
- * @returns {LevelMoves} A list of moves and the levels they can be learned at
+ * @param startingLevel Don't include moves below this level
+ * @param includeEvolutionMoves Whether to include evolution moves
+ * @param simulateEvolutionChain Whether to include moves from prior evolutions
+ * @param includeRelearnerMoves Whether to include moves that would require a relearner. Note the move relearner inherently allows evolution moves
+ * @returns A list of moves and the levels they can be learned at
*/
getLevelMoves(
startingLevel?: number,
@@ -2671,9 +2671,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
.getLevelMoves()
.filter(
lm =>
- (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) ||
- (includeRelearnerMoves && lm[0] === RELEARN_MOVE) ||
- lm[0] > 0,
+ (includeEvolutionMoves && lm[0] === EVOLVE_MOVE)
+ || (includeRelearnerMoves && lm[0] === RELEARN_MOVE)
+ || lm[0] > 0,
);
} else {
if (simulateEvolutionChain) {
@@ -2692,8 +2692,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
levelMoves.push(
...speciesLevelMoves.filter(
lm =>
- (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) ||
- ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1])),
+ (includeEvolutionMoves && lm[0] === EVOLVE_MOVE)
+ || ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1])),
),
);
}
@@ -2703,9 +2703,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
.getLevelMoves()
.filter(
lm =>
- (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) ||
- (includeRelearnerMoves && lm[0] === RELEARN_MOVE) ||
- lm[0] > 0,
+ (includeEvolutionMoves && lm[0] === EVOLVE_MOVE)
+ || (includeRelearnerMoves && lm[0] === RELEARN_MOVE)
+ || lm[0] > 0,
);
}
if (this.fusionSpecies && learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE) {
@@ -2733,9 +2733,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
levelMoves.push(
...speciesLevelMoves.filter(
lm =>
- (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) ||
- ((!e || lm[0] > 1) &&
- (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1])),
+ (includeEvolutionMoves && lm[0] === EVOLVE_MOVE)
+ || ((!e || lm[0] > 1)
+ && (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1])),
),
);
}
@@ -2746,9 +2746,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
.getLevelMoves()
.filter(
lm =>
- (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) ||
- (includeRelearnerMoves && lm[0] === RELEARN_MOVE) ||
- lm[0] > 0,
+ (includeEvolutionMoves && lm[0] === EVOLVE_MOVE)
+ || (includeRelearnerMoves && lm[0] === RELEARN_MOVE)
+ || lm[0] > 0,
),
);
}
@@ -2918,8 +2918,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
// Checks if there is no variant data for both the index or index with form
if (
- !this.shiny ||
- (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId))
+ !this.shiny
+ || (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId))
) {
return 0;
}
@@ -2954,10 +2954,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE);
- if (applyModifiersToOverride) {
- if (!this.hasTrainer()) {
- globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, haThreshold);
- }
+ if (applyModifiersToOverride && !this.hasTrainer()) {
+ globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, haThreshold);
}
if (randSeedInt(65536) < haThreshold.value) {
@@ -2980,14 +2978,14 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
? this.species.getCompatibleFusionSpeciesFilter()
: (species: PokemonSpecies) => {
return (
- pokemonEvolutions.hasOwnProperty(species.speciesId) &&
- !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
- !species.subLegendary &&
- !species.legendary &&
- !species.mythical &&
- !species.isTrainerForbidden() &&
- species.speciesId !== this.species.speciesId &&
- species.speciesId !== SpeciesId.DITTO
+ pokemonEvolutions.hasOwnProperty(species.speciesId)
+ && !pokemonPrevolutions.hasOwnProperty(species.speciesId)
+ && !species.subLegendary
+ && !species.legendary
+ && !species.mythical
+ && !species.isTrainerForbidden()
+ && species.speciesId !== this.species.speciesId
+ && species.speciesId !== SpeciesId.DITTO
);
};
@@ -3000,8 +2998,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
this.fusionSpecies =
- fusionOverride ??
- globalScene.randomSpecies(globalScene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
+ fusionOverride
+ ?? globalScene.randomSpecies(globalScene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
this.fusionAbilityIndex =
this.fusionSpecies.abilityHidden && hasHiddenAbility
? 2
@@ -3051,25 +3049,27 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
public generateAndPopulateMoveset(): void {
this.moveset = [];
let movePool: [MoveId, number][] = [];
- const allLevelMoves = this.getLevelMoves(1, true, true);
+ const allLevelMoves = this.getLevelMoves(1, true, true, this.hasTrainer());
if (!allLevelMoves) {
console.warn("Error encountered trying to generate moveset for:", this.species.name);
return;
}
- for (let m = 0; m < allLevelMoves.length; m++) {
- const levelMove = allLevelMoves[m];
+ for (const levelMove of allLevelMoves) {
if (this.level < levelMove[0]) {
break;
}
- let weight = levelMove[0];
+ let weight = levelMove[0] + 20;
// Evolution Moves
- if (weight === EVOLVE_MOVE) {
- weight = 50;
+ if (levelMove[0] === EVOLVE_MOVE) {
+ weight = 70;
}
// Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight. Trainers use actual relearn moves.
- if ((weight === 1 && allMoves[levelMove[1]].power >= 80) || (weight === RELEARN_MOVE && this.hasTrainer())) {
- weight = 40;
+ if (
+ (levelMove[0] === 1 && allMoves[levelMove[1]].power >= 80)
+ || (levelMove[0] === RELEARN_MOVE && this.hasTrainer())
+ ) {
+ weight = 60;
}
if (!movePool.some(m => m[0] === levelMove[1]) && !allMoves[levelMove[1]].name.endsWith(" (N)")) {
movePool.push([levelMove[1], weight]);
@@ -3084,10 +3084,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
for (const p of tmSpecies[tm]) {
if (Array.isArray(p)) {
if (
- p[0] === this.species.speciesId ||
- (this.fusionSpecies &&
- p[0] === this.fusionSpecies.speciesId &&
- p.slice(1).indexOf(this.species.forms[this.formIndex]) > -1)
+ p[0] === this.species.speciesId
+ || (this.fusionSpecies
+ && p[0] === this.fusionSpecies.speciesId
+ && p.slice(1).indexOf(this.species.forms[this.formIndex]) > -1)
) {
compatible = true;
break;
@@ -3099,11 +3099,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (compatible && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
if (tmPoolTiers[moveId] === ModifierTier.COMMON && this.level >= 15) {
- movePool.push([moveId, 4]);
+ movePool.push([moveId, 24]);
} else if (tmPoolTiers[moveId] === ModifierTier.GREAT && this.level >= 30) {
- movePool.push([moveId, 8]);
+ movePool.push([moveId, 28]);
} else if (tmPoolTiers[moveId] === ModifierTier.ULTRA && this.level >= 50) {
- movePool.push([moveId, 14]);
+ movePool.push([moveId, 34]);
}
}
}
@@ -3113,35 +3113,35 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
for (let i = 0; i < 3; i++) {
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
- movePool.push([moveId, 40]);
+ movePool.push([moveId, 60]);
}
}
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
// No rare egg moves before e4
if (
- this.level >= 170 &&
- !movePool.some(m => m[0] === moveId) &&
- !allMoves[moveId].name.endsWith(" (N)") &&
- !this.isBoss()
+ this.level >= 170
+ && !movePool.some(m => m[0] === moveId)
+ && !allMoves[moveId].name.endsWith(" (N)")
+ && !this.isBoss()
) {
- movePool.push([moveId, 30]);
+ movePool.push([moveId, 50]);
}
if (this.fusionSpecies) {
for (let i = 0; i < 3; i++) {
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][i];
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
- movePool.push([moveId, 40]);
+ movePool.push([moveId, 60]);
}
}
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
// No rare egg moves before e4
if (
- this.level >= 170 &&
- !movePool.some(m => m[0] === moveId) &&
- !allMoves[moveId].name.endsWith(" (N)") &&
- !this.isBoss()
+ this.level >= 170
+ && !movePool.some(m => m[0] === moveId)
+ && !allMoves[moveId].name.endsWith(" (N)")
+ && !this.isBoss()
) {
- movePool.push([moveId, 30]);
+ movePool.push([moveId, 50]);
}
}
}
@@ -3181,8 +3181,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
);
movePool = movePool.map(m => [
m[0],
- m[1] *
- (allMoves[m[0]].category === MoveCategory.STATUS
+ m[1]
+ * (allMoves[m[0]].category === MoveCategory.STATUS
? 1
: Math.max(Math.min(allMoves[m[0]].calculateEffectivePower() / maxPower, 1), 0.5)),
]);
@@ -3209,12 +3209,58 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
Math.ceil(Math.pow(m[1], weightMultiplier) * 100),
]);
+ const STAB_BLACKLIST: ReadonlySet = new Set([
+ MoveId.BEAT_UP,
+ MoveId.BELCH,
+ MoveId.BIDE,
+ MoveId.COMEUPPANCE,
+ MoveId.COUNTER,
+ MoveId.DOOM_DESIRE,
+ MoveId.DRAGON_RAGE,
+ MoveId.DREAM_EATER,
+ MoveId.ENDEAVOR,
+ MoveId.EXPLOSION,
+ MoveId.FAKE_OUT,
+ MoveId.FIRST_IMPRESSION,
+ MoveId.FISSURE,
+ MoveId.FLING,
+ MoveId.FOCUS_PUNCH,
+ MoveId.FUTURE_SIGHT,
+ MoveId.GUILLOTINE,
+ MoveId.HOLD_BACK,
+ MoveId.HORN_DRILL,
+ MoveId.LAST_RESORT,
+ MoveId.METAL_BURST,
+ MoveId.MIRROR_COAT,
+ MoveId.MISTY_EXPLOSION,
+ MoveId.NATURAL_GIFT,
+ MoveId.NATURES_MADNESS,
+ MoveId.NIGHT_SHADE,
+ MoveId.PSYWAVE,
+ MoveId.RUINATION,
+ MoveId.SELF_DESTRUCT,
+ MoveId.SHEER_COLD,
+ MoveId.SHELL_TRAP,
+ MoveId.SKY_DROP,
+ MoveId.SNORE,
+ MoveId.SONIC_BOOM,
+ MoveId.SPIT_UP,
+ MoveId.STEEL_BEAM,
+ MoveId.STEEL_ROLLER,
+ MoveId.SUPER_FANG,
+ MoveId.SYNCHRONOISE,
+ MoveId.UPPER_HAND,
+ ]);
+
// All Pokemon force a STAB move first
const stabMovePool = baseWeights.filter(
- m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type),
+ m =>
+ allMoves[m[0]].category !== MoveCategory.STATUS
+ && this.isOfType(allMoves[m[0]].type)
+ && !STAB_BLACKLIST.has(m[0]),
);
- if (stabMovePool.length) {
+ if (stabMovePool.length > 0) {
const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0);
let rand = randSeedInt(totalWeight);
let index = 0;
@@ -3222,6 +3268,20 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
rand -= stabMovePool[index++][1];
}
this.moveset.push(new PokemonMove(stabMovePool[index][0]));
+ } else {
+ // If there are no damaging STAB moves, just force a random damaging move
+ const attackMovePool = baseWeights.filter(
+ m => allMoves[m[0]].category !== MoveCategory.STATUS && !STAB_BLACKLIST.has(m[0]),
+ );
+ if (attackMovePool.length > 0) {
+ const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0);
+ let rand = randSeedInt(totalWeight);
+ let index = 0;
+ while (rand > attackMovePool[index][1]) {
+ rand -= attackMovePool[index++][1];
+ }
+ this.moveset.push(new PokemonMove(attackMovePool[index][0], 0, 0));
+ }
}
while (baseWeights.length > this.moveset.length && this.moveset.length < 4) {
@@ -3234,8 +3294,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
m =>
!this.moveset.some(
mo =>
- m[0] === mo.moveId ||
- (allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
+ m[0] === mo.moveId
+ || (allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
),
)
.map(m => {
@@ -3248,9 +3308,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
ret = Math.ceil(Math.sqrt(m[1]));
} else if (allMoves[m[0]].category !== MoveCategory.STATUS) {
ret = Math.ceil(
- (m[1] /
- Math.max(Math.pow(4, this.moveset.filter(mo => (mo.getMove().power ?? 0) > 1).length) / 8, 0.5)) *
- (this.isOfType(allMoves[m[0]].type) ? 20 : 1),
+ (m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo.getMove().power ?? 0) > 1).length) / 8, 0.5))
+ * (this.isOfType(allMoves[m[0]].type) && !STAB_BLACKLIST.has(m[0]) ? 20 : 1),
);
} else {
ret = m[1];
@@ -3263,8 +3322,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
m =>
!this.moveset.some(
mo =>
- m[0] === mo.moveId ||
- (allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
+ m[0] === mo.moveId
+ || (allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
),
);
}
@@ -3279,9 +3338,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
// Trigger FormChange, except for enemy Pokemon during Mystery Encounters, to avoid crashes
if (
- this.isPlayer() ||
- !globalScene.currentBattle?.isBattleMysteryEncounter() ||
- !globalScene.currentBattle?.mysteryEncounter
+ this.isPlayer()
+ || !globalScene.currentBattle?.isBattleMysteryEncounter()
+ || !globalScene.currentBattle?.mysteryEncounter
) {
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger);
}
@@ -3437,7 +3496,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param isCritical determines whether a critical hit has occurred or not (`false` by default)
* @param simulated determines whether effects are applied without altering game state (`true` by default)
* @param ignoreHeldItems determines whether this Pokemon's held items should be ignored during the stat calculation, default `false`
- * @return the stat stage multiplier to be used for effective stat calculation
+ * @returns the stat stage multiplier to be used for effective stat calculation
*/
getStatStageMultiplier(
stat: EffectiveStat,
@@ -3493,8 +3552,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* This method considers various factors such as the user's accuracy level, the target's evasion level,
* abilities, and modifiers to compute the final accuracy multiplier.
*
- * @param target {@linkcode Pokemon} - The target Pokémon against which the move is used.
- * @param sourceMove {@linkcode Move} - The move being used by the user.
+ * @param target - The target Pokémon against which the move is used.
+ * @param sourceMove - The move being used by the user.
* @returns The calculated accuracy multiplier.
*/
getAccuracyMultiplier(target: Pokemon, sourceMove: Move): number {
@@ -3687,9 +3746,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (
- source.isTerastallized &&
- source.getTeraType() === PokemonType.STELLAR &&
- (!source.stellarTypesBoosted.includes(moveType) || source.hasSpecies(SpeciesId.TERAPAGOS))
+ source.isTerastallized
+ && source.getTeraType() === PokemonType.STELLAR
+ && (!source.stellarTypesBoosted.includes(moveType) || source.hasSpecies(SpeciesId.TERAPAGOS))
) {
stabMultiplier.value += matchesSourceType ? 0.5 : 0.2;
}
@@ -3858,10 +3917,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/** Halves damage if the attacker is using a physical attack while burned */
let burnMultiplier = 1;
if (
- isPhysical &&
- source.status &&
- source.status.effect === StatusEffect.BURN &&
- !move.hasAttr("BypassBurnDamageReductionAttr")
+ isPhysical
+ && source.status
+ && source.status.effect === StatusEffect.BURN
+ && !move.hasAttr("BypassBurnDamageReductionAttr")
) {
const burnDamageReductionCancelled = new BooleanHolder(false);
if (!ignoreSourceAbility) {
@@ -3909,26 +3968,26 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/** Halves damage if this Pokemon is grounded in Misty Terrain against a Dragon-type attack */
const mistyTerrainMultiplier =
- globalScene.arena.terrain?.terrainType === TerrainType.MISTY &&
- this.isGrounded() &&
- moveType === PokemonType.DRAGON
+ globalScene.arena.terrain?.terrainType === TerrainType.MISTY
+ && this.isGrounded()
+ && moveType === PokemonType.DRAGON
? 0.5
: 1;
damage.value = toDmgValue(
- baseDamage *
- targetMultiplier *
- multiStrikeEnhancementMultiplier.value *
- arenaAttackTypeMultiplier.value *
- glaiveRushMultiplier.value *
- criticalMultiplier.value *
- randomMultiplier *
- stabMultiplier *
- typeMultiplier *
- burnMultiplier *
- screenMultiplier.value *
- hitsTagMultiplier.value *
- mistyTerrainMultiplier,
+ baseDamage
+ * targetMultiplier
+ * multiStrikeEnhancementMultiplier.value
+ * arenaAttackTypeMultiplier.value
+ * glaiveRushMultiplier.value
+ * criticalMultiplier.value
+ * randomMultiplier
+ * stabMultiplier
+ * typeMultiplier
+ * burnMultiplier
+ * screenMultiplier.value
+ * hitsTagMultiplier.value
+ * mistyTerrainMultiplier,
);
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
@@ -4008,7 +4067,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
*/
getCriticalHitResult(source: Pokemon, move: Move): boolean {
if (move.hasAttr("FixedDamageAttr")) {
- // fixed damage moves (Dragon Rage, etc.) will nevet crit
+ // fixed damage moves (Dragon Rage, etc.) will never crit
return false;
}
@@ -4103,7 +4162,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
isCritical = false,
ignoreSegments = false,
ignoreFaintPhase = false,
- source = undefined,
+ source,
}: {
result?: DamageResult;
isCritical?: boolean;
@@ -4172,8 +4231,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
SpeciesFormKey.PRIMAL,
] as string[];
return (
- megaForms.includes(this.getFormKey()) ||
- (!!this.getFusionFormKey() && megaForms.includes(this.getFusionFormKey()!))
+ megaForms.includes(this.getFormKey())
+ || (!!this.getFusionFormKey() && megaForms.includes(this.getFusionFormKey()!))
);
}
@@ -4232,18 +4291,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
- /**@overload */
getTag(tagType: BattlerTagType.GRUDGE): GrudgeTag | undefined;
-
- /** @overload */
getTag(tagType: BattlerTagType.SUBSTITUTE): SubstituteTag | undefined;
-
- /** @overload */
getTag(tagType: BattlerTagType): BattlerTag | undefined;
-
- /** @overload */
getTag(tagType: Constructor): T | undefined;
-
getTag(tagType: BattlerTagType | Constructor): BattlerTag | undefined {
return typeof tagType === "function"
? this.summonData.tags.find(t => t instanceof tagType)
@@ -4288,8 +4339,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
tags
.filter(
t =>
- lapseType === BattlerTagLapseType.FAINT ||
- (t.lapseTypes.some(lType => lType === lapseType) && !t.lapse(this, lapseType)),
+ lapseType === BattlerTagLapseType.FAINT
+ || (t.lapseTypes.some(lType => lType === lapseType) && !t.lapse(this, lapseType)),
)
.forEach(t => {
t.onRemove(this);
@@ -4352,10 +4403,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
for (const tag of source.summonData.tags) {
if (
- !tag.isBatonPassable ||
- (tag.tagType === BattlerTagType.TELEKINESIS &&
- this.species.speciesId === SpeciesId.GENGAR &&
- this.getFormKey() === "mega")
+ !tag.isBatonPassable
+ || (tag.tagType === BattlerTagType.TELEKINESIS
+ && this.species.speciesId === SpeciesId.GENGAR
+ && this.getFormKey() === "mega")
) {
continue;
}
@@ -4389,7 +4440,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param user - The move user
* @param target - The target of the move
*
- * @returns {boolean} `true` if the move is disabled for this Pokemon due to the player's target selection
+ * @returns `true` if the move is disabled for this Pokemon due to the player's target selection
*
* @see {@linkcode MoveRestrictionBattlerTag}
*/
@@ -4470,9 +4521,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
getLastNonVirtualMove(ignoreStruggle = false, ignoreFollowUp = true): TurnMove | undefined {
return this.getLastXMoves(-1).find(
m =>
- m.move !== MoveId.NONE &&
- (!ignoreStruggle || m.move !== MoveId.STRUGGLE) &&
- (!isVirtual(m.useMode) || (!ignoreFollowUp && m.useMode === MoveUseMode.FOLLOW_UP)),
+ m.move !== MoveId.NONE
+ && (!ignoreStruggle || m.move !== MoveId.STRUGGLE)
+ && (!isVirtual(m.useMode) || (!ignoreFollowUp && m.useMode === MoveUseMode.FOLLOW_UP)),
);
}
@@ -4514,28 +4565,36 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
});
}
- cry(soundConfig?: Phaser.Types.Sound.SoundConfig, sceneOverride?: BattleScene): AnySound {
+ cry(soundConfig?: Phaser.Types.Sound.SoundConfig, sceneOverride?: BattleScene): AnySound | null {
const scene = sceneOverride ?? globalScene; // TODO: is `sceneOverride` needed?
const cry = this.getSpeciesForm(undefined, true).cry(soundConfig);
+ if (!cry) {
+ return cry;
+ }
let duration = cry.totalDuration * 1000;
if (this.fusionSpecies && this.getSpeciesForm(undefined, true) !== this.getFusionSpeciesForm(undefined, true)) {
- let fusionCry = this.getFusionSpeciesForm(undefined, true).cry(soundConfig, true);
+ const fusionCry = this.getFusionSpeciesForm(undefined, true).cry(soundConfig, true);
+ if (!fusionCry) {
+ return cry;
+ }
duration = Math.min(duration, fusionCry.totalDuration * 1000);
fusionCry.destroy();
scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => {
try {
SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2)));
- fusionCry = this.getFusionSpeciesForm(undefined, true).cry({
+ const fusionCryInner = this.getFusionSpeciesForm(undefined, true).cry({
seek: Math.max(fusionCry.totalDuration * 0.4, 0),
...soundConfig,
});
- SoundFade.fadeIn(
- scene,
- fusionCry,
- fixedInt(Math.ceil(duration * 0.2)),
- scene.masterVolume * scene.fieldVolume,
- 0,
- );
+ if (fusionCryInner) {
+ SoundFade.fadeIn(
+ scene,
+ fusionCryInner,
+ fixedInt(Math.ceil(duration * 0.2)),
+ scene.masterVolume * scene.fieldVolume,
+ 0,
+ );
+ }
} catch (err) {
console.error(err);
}
@@ -4563,14 +4622,14 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
crySoundConfig.rate = 0.7;
}
}
- const cry = globalScene.playSound(key, crySoundConfig) as AnySound;
+ const cry = globalScene.playSound(key, crySoundConfig);
if (!cry || globalScene.fieldVolume === 0) {
callback();
return;
}
const sprite = this.getSprite();
const tintSprite = this.getTintSprite();
- const delay = Math.max(globalScene.sound.get(key).totalDuration * 50, 25);
+ const delay = Math.max(cry.totalDuration * 50, 25);
let frameProgress = 0;
let frameThreshold: number;
@@ -4623,20 +4682,20 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
const key = this.species.getCryKey(this.formIndex);
let i = 0;
let rate = 0.85;
- const cry = globalScene.playSound(key, { rate: rate }) as AnySound;
+ const cry = globalScene.playSound(key, { rate });
const sprite = this.getSprite();
const tintSprite = this.getTintSprite();
- let duration = cry.totalDuration * 1000;
const fusionCryKey = this.fusionSpecies!.getCryKey(this.fusionFormIndex);
let fusionCry = globalScene.playSound(fusionCryKey, {
- rate: rate,
- }) as AnySound;
+ rate,
+ });
if (!cry || !fusionCry || globalScene.fieldVolume === 0) {
callback();
return;
}
fusionCry.stop();
+ let duration = cry.totalDuration * 1000;
duration = Math.min(duration, fusionCry.totalDuration * 1000);
fusionCry.destroy();
const delay = Math.max(duration * 0.05, 25);
@@ -4679,16 +4738,20 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (i === transitionIndex && fusionCryKey) {
SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2)));
fusionCry = globalScene.playSound(fusionCryKey, {
- seek: Math.max(fusionCry.totalDuration * 0.4, 0),
- rate: rate,
+ // TODO: This bang is correct as this callback can only be called once, but
+ // this whole block with conditionally reassigning fusionCry needs a second lock.
+ seek: Math.max(fusionCry!.totalDuration * 0.4, 0),
+ rate,
});
- SoundFade.fadeIn(
- globalScene,
- fusionCry,
- fixedInt(Math.ceil((duration / rate) * 0.2)),
- globalScene.masterVolume * globalScene.fieldVolume,
- 0,
- );
+ if (fusionCry) {
+ SoundFade.fadeIn(
+ globalScene,
+ fusionCry,
+ fixedInt(Math.ceil((duration / rate) * 0.2)),
+ globalScene.masterVolume * globalScene.fieldVolume,
+ 0,
+ );
+ }
}
rate *= 0.99;
if (cry && !cry.pendingRemove) {
@@ -4727,8 +4790,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
isOppositeGender(pokemon: Pokemon): boolean {
return (
- this.gender !== Gender.GENDERLESS &&
- pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE)
+ this.gender !== Gender.GENDERLESS
+ && pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE)
);
}
@@ -4790,7 +4853,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (effect !== StatusEffect.FAINT) {
// Status-overriding moves (i.e. Rest) fail if their respective status already exists;
// all other moves fail if the target already has _any_ status
- if (overrideStatus ? this.status?.effect === effect : this.status) {
+ if (overrideStatus ? this.status?.effect === effect : this.status || this.turnData.pendingStatus) {
this.queueStatusImmuneMessage(quiet, overrideStatus ? "overlap" : "other"); // having different status displays generic fail message
return false;
}
@@ -4843,8 +4906,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
case StatusEffect.FREEZE: {
const weatherType = globalScene.arena.getWeatherType();
isImmune =
- this.isOfType(PokemonType.ICE) ||
- (!ignoreField && (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN));
+ this.isOfType(PokemonType.ICE)
+ || (!ignoreField && (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN));
break;
}
case StatusEffect.BURN:
@@ -4934,7 +4997,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
*/
if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) {
const currentPhase = globalScene.phaseManager.getCurrentPhase();
- if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
+ if (currentPhase.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
this.turnData.hitCount = 1;
this.turnData.hitsLeft = 1;
}
@@ -4942,6 +5005,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (overrideStatus) {
this.resetStatus(false);
+ } else {
+ this.turnData.pendingStatus = effect;
}
globalScene.phaseManager.unshiftNew(
@@ -4961,6 +5026,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Set this Pokemon's {@linkcode status | non-volatile status condition} to the specified effect.
* @param effect - The {@linkcode StatusEffect} to set
* @remarks
+ * Clears this pokemon's `pendingStatus` in its {@linkcode Pokemon.turnData | turnData}.
+ *
* ⚠️ This method does **not** check for feasibility; that is the responsibility of the caller.
*/
doSetStatus(effect: Exclude): void;
@@ -4969,6 +5036,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param effect - {@linkcode StatusEffect.SLEEP}
* @param sleepTurnsRemaining - The number of turns to inflict sleep for; defaults to a random number between 2 and 4
* @remarks
+ * Clears this pokemon's `pendingStatus` in its {@linkcode Pokemon#turnData}.
+ *
* ⚠️ This method does **not** check for feasibility; that is the responsibility of the caller.
*/
doSetStatus(effect: StatusEffect.SLEEP, sleepTurnsRemaining?: number): void;
@@ -4978,6 +5047,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param sleepTurnsRemaining - The number of turns to inflict sleep for; defaults to a random number between 2 and 4
* and is unused for all non-sleep Statuses
* @remarks
+ * Clears this pokemon's `pendingStatus` in its {@linkcode Pokemon#turnData}.
+ *
* ⚠️ This method does **not** check for feasibility; that is the responsibility of the caller.
*/
doSetStatus(effect: StatusEffect, sleepTurnsRemaining?: number): void;
@@ -4987,6 +5058,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param sleepTurnsRemaining - The number of turns to inflict sleep for; defaults to a random number between 2 and 4
* and is unused for all non-sleep Statuses
* @remarks
+ * Clears this pokemon's `pendingStatus` in its {@linkcode Pokemon#turnData}.
+ *
* ⚠️ This method does **not** check for feasibility; that is the responsibility of the caller.
* @todo Make this and all related fields private and change tests to use a field-based helper or similar
*/
@@ -4994,6 +5067,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
effect: StatusEffect,
sleepTurnsRemaining = effect !== StatusEffect.SLEEP ? 0 : this.randBattleSeedIntRange(2, 4),
): void {
+ // Reset any pending status
+ this.turnData.pendingStatus = StatusEffect.NONE;
switch (effect) {
case StatusEffect.POISON:
case StatusEffect.TOXIC:
@@ -5065,15 +5140,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
const lastStatus = this.status?.effect;
this.status = null;
this.setFrameRate(10);
- if (lastStatus === StatusEffect.SLEEP) {
- if (this.getTag(BattlerTagType.NIGHTMARE)) {
- this.lapseTag(BattlerTagType.NIGHTMARE);
- }
+ if (lastStatus === StatusEffect.SLEEP && this.getTag(BattlerTagType.NIGHTMARE)) {
+ this.lapseTag(BattlerTagType.NIGHTMARE);
}
- if (confusion) {
- if (this.getTag(BattlerTagType.CONFUSED)) {
- this.lapseTag(BattlerTagType.CONFUSED);
- }
+ if (confusion && this.getTag(BattlerTagType.CONFUSED)) {
+ this.lapseTag(BattlerTagType.CONFUSED);
}
if (reloadAssets) {
this.loadAssets(false).then(() => this.playAnim());
@@ -5115,9 +5186,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
// If this Pokemon has Commander and Dondozo as an active ally, hide this Pokemon's sprite.
if (
- this.hasAbilityWithAttr("CommanderAbAttr") &&
- globalScene.currentBattle.double &&
- this.getAlly()?.species.speciesId === SpeciesId.DONDOZO
+ this.hasAbilityWithAttr("CommanderAbAttr")
+ && globalScene.currentBattle.double
+ && this.getAlly()?.species.speciesId === SpeciesId.DONDOZO
) {
this.setVisible(false);
}
@@ -5156,7 +5227,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Reset a {@linkcode Pokemon}'s {@linkcode PokemonWaveData | waveData}.
* Should be called upon starting a new wave in addition to whenever an arena transition occurs.
- * @see {@linkcode resetBattleAndWaveData()}
+ * @see {@linkcode resetBattleAndWaveData}
*/
resetWaveData(): void {
this.waveData = new PokemonWaveData();
@@ -5207,7 +5278,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: tintSprite,
alpha: alpha || 1,
- duration: duration,
+ duration,
ease: ease || "Linear",
});
} else {
@@ -5222,7 +5293,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: tintSprite,
alpha: 0,
- duration: duration,
+ duration,
ease: ease || "Linear",
onComplete: () => {
tintSprite?.setVisible(false);
@@ -5548,7 +5619,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
keys = Array.from(paletteColors.keys()).sort((a: number, b: number) =>
paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1,
);
- } while (mappedColors.size);
+ } while (mappedColors.size > 0);
return keys.map(c => Object.values(rgbaFromArgb(c)));
});
@@ -5557,8 +5628,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
spriteColors.forEach((sc: number[], i: number) => {
paletteDeltas.push([]);
- for (let p = 0; p < palette.length; p++) {
- paletteDeltas[i].push(deltaRgb(sc, palette[p]));
+ for (const p of palette) {
+ paletteDeltas[i].push(deltaRgb(sc, p));
}
});
@@ -5591,7 +5662,6 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Generates a random number using the current battle's seed, or the global seed if `globalScene.currentBattle` is falsy
- *
* This calls either {@linkcode BattleScene.randBattleSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle-scene.ts`
* which calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts`
* which calls {@linkcode randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`,
@@ -5723,8 +5793,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
getPersistentTreasureCount(): number {
return (
- this.getHeldItems().filter(m => m.is("DamageMoneyRewardModifier")).length +
- globalScene.findModifiers(m => m.is("MoneyMultiplierModifier") || m.is("ExtraModifierModifier")).length
+ this.getHeldItems().filter(m => m.is("DamageMoneyRewardModifier")).length
+ + globalScene.findModifiers(m => m.is("MoneyMultiplierModifier") || m.is("ExtraModifierModifier")).length
);
}
}
@@ -5764,9 +5834,8 @@ export class PlayerPokemon extends Pokemon {
if (!dataSource) {
if (
- globalScene.gameMode.isDaily ||
- // Keldeo is excluded due to crashes involving its signature move and the associated form change
- (Overrides.STARTER_SPECIES_OVERRIDE && Overrides.STARTER_SPECIES_OVERRIDE !== SpeciesId.KELDEO)
+ globalScene.gameMode.isDaily // Keldeo is excluded due to crashes involving its signature move and the associated form change
+ || (Overrides.STARTER_SPECIES_OVERRIDE && Overrides.STARTER_SPECIES_OVERRIDE !== SpeciesId.KELDEO)
) {
this.generateAndPopulateMoveset();
} else {
@@ -5816,8 +5885,8 @@ export class PlayerPokemon extends Pokemon {
if (Array.isArray(p)) {
const [pkm, form] = p;
if (
- (pkm === this.species.speciesId || (this.fusionSpecies && pkm === this.fusionSpecies.speciesId)) &&
- form === this.getFormKey()
+ (pkm === this.species.speciesId || (this.fusionSpecies && pkm === this.fusionSpecies.speciesId))
+ && form === this.getFormKey()
) {
compatible = true;
break;
@@ -6327,9 +6396,9 @@ export class EnemyPokemon extends Pokemon {
const speciesId = this.species.speciesId;
if (
- speciesId in Overrides.ENEMY_FORM_OVERRIDES &&
- !isNullOrUndefined(Overrides.ENEMY_FORM_OVERRIDES[speciesId]) &&
- this.species.forms[Overrides.ENEMY_FORM_OVERRIDES[speciesId]]
+ speciesId in Overrides.ENEMY_FORM_OVERRIDES
+ && !isNullOrUndefined(Overrides.ENEMY_FORM_OVERRIDES[speciesId])
+ && this.species.forms[Overrides.ENEMY_FORM_OVERRIDES[speciesId]]
) {
this.formIndex = Overrides.ENEMY_FORM_OVERRIDES[speciesId];
} else if (globalScene.gameMode.isDaily && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
@@ -6399,8 +6468,8 @@ export class EnemyPokemon extends Pokemon {
}
this.bossSegments =
- bossSegments ??
- globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, this.level, this.species, true);
+ bossSegments
+ ?? globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, this.level, this.species, true);
this.bossSegmentIndex = this.bossSegments - 1;
}
@@ -6465,7 +6534,7 @@ export class EnemyPokemon extends Pokemon {
// Filter out any moves this Pokemon cannot use
let movePool = this.getMoveset().filter(m => m.isUsable(this));
// If no moves are left, use Struggle. Otherwise, continue with move selection
- if (movePool.length) {
+ if (movePool.length > 0) {
// If there's only 1 move in the move pool, use it.
if (movePool.length === 1) {
return {
@@ -6518,14 +6587,14 @@ export class EnemyPokemon extends Pokemon {
const isCritical = move.hasAttr("CritOnlyAttr") || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
return (
- move.category !== MoveCategory.STATUS &&
- moveTargets.some(p => {
+ move.category !== MoveCategory.STATUS
+ && moveTargets.some(p => {
const doesNotFail =
- move.applyConditions(this, p, move) ||
- [MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id);
+ move.applyConditions(this, p, move)
+ || [MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id);
return (
- doesNotFail &&
- p.getAttackDamage({
+ doesNotFail
+ && p.getAttackDamage({
source: this,
move,
ignoreAbility: !p.waveData.abilityRevealed,
@@ -6533,6 +6602,7 @@ export class EnemyPokemon extends Pokemon {
ignoreAllyAbility: !p.getAlly()?.waveData.abilityRevealed,
ignoreSourceAllyAbility: false,
isCritical,
+ simulated: true,
}).damage >= p.hp
);
})
@@ -6569,8 +6639,9 @@ export class EnemyPokemon extends Pokemon {
* If the target is an ally, the target benefit score is multiplied by -1.
*/
let targetScore =
- move.getUserBenefitScore(this, target, move) +
- move.getTargetBenefitScore(this, target, move) * (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1);
+ move.getUserBenefitScore(this, target, move)
+ + move.getTargetBenefitScore(this, target, move)
+ * (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1);
if (Number.isNaN(targetScore)) {
console.error(`Move ${move.name} returned score of NaN`);
targetScore = 0;
@@ -6580,8 +6651,8 @@ export class EnemyPokemon extends Pokemon {
* target score to -20
*/
if (
- (move.name.endsWith(" (N)") || !move.applyConditions(this, target, move)) &&
- ![MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id)
+ (move.name.endsWith(" (N)") || !move.applyConditions(this, target, move))
+ && ![MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id)
) {
targetScore = -20;
} else if (move.is("AttackMove")) {
@@ -6641,14 +6712,14 @@ export class EnemyPokemon extends Pokemon {
} else if (this.aiType === AiType.SMART) {
// The chance to advance to the next best move increases when the compared moves' scores are closer to each other.
while (
- r < sortedMovePool.length - 1 &&
- moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])] >=
- 0 &&
- globalScene.randBattleSeedInt(100) <
- Math.round(
- (moveScores[movePool.indexOf(sortedMovePool[r + 1])] /
- moveScores[movePool.indexOf(sortedMovePool[r])]) *
- 50,
+ r < sortedMovePool.length - 1
+ && moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])]
+ >= 0
+ && globalScene.randBattleSeedInt(100)
+ < Math.round(
+ (moveScores[movePool.indexOf(sortedMovePool[r + 1])]
+ / moveScores[movePool.indexOf(sortedMovePool[r])])
+ * 50,
)
) {
r++;
@@ -6708,7 +6779,7 @@ export class EnemyPokemon extends Pokemon {
return scoreA < scoreB ? 1 : scoreA > scoreB ? -1 : 0;
});
- if (!sortedBenefitScores.length) {
+ if (sortedBenefitScores.length === 0) {
// Set target to BattlerIndex.ATTACKER when using a counter move
// This is the same as when the player does so
if (move.hasAttr("CounterDamageAttr")) {
@@ -6719,7 +6790,7 @@ export class EnemyPokemon extends Pokemon {
}
let targetWeights = sortedBenefitScores.map(s => s[1]);
- const lowestWeight = targetWeights[targetWeights.length - 1];
+ const lowestWeight = targetWeights.at(-1) ?? 0;
// If the lowest target weight (i.e. benefit score) is negative, add abs(lowestWeight) to all target weights
if (lowestWeight < 1) {
@@ -6809,9 +6880,9 @@ export class EnemyPokemon extends Pokemon {
const hpRemainder = this.hp - roundedHpThreshold;
let segmentsBypassed = 0;
while (
- segmentsBypassed < this.bossSegmentIndex &&
- this.canBypassBossSegments(segmentsBypassed + 1) &&
- damage - hpRemainder >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))
+ segmentsBypassed < this.bossSegmentIndex
+ && this.canBypassBossSegments(segmentsBypassed + 1)
+ && damage - hpRemainder >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))
) {
segmentsBypassed++;
//console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1)));
@@ -6849,10 +6920,12 @@ export class EnemyPokemon extends Pokemon {
}
canBypassBossSegments(segmentCount = 1): boolean {
- if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
- if (!this.formIndex && this.bossSegmentIndex - segmentCount < 1) {
- return false;
- }
+ if (
+ globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS
+ && !this.formIndex
+ && this.bossSegmentIndex - segmentCount < 1
+ ) {
+ return false;
}
return true;
@@ -6871,7 +6944,7 @@ export class EnemyPokemon extends Pokemon {
const leftoverStats = EFFECTIVE_STATS.filter((s: EffectiveStat) => this.getStatStage(s) < 6);
const statWeights = leftoverStats.map((s: EffectiveStat) => this.getStat(s, false));
- let boostedStat: EffectiveStat;
+ let boostedStat: EffectiveStat | undefined;
const statThresholds: number[] = [];
let totalWeight = 0;
@@ -6889,6 +6962,11 @@ export class EnemyPokemon extends Pokemon {
}
}
+ if (boostedStat === undefined) {
+ this.bossSegmentIndex--;
+ return;
+ }
+
let stages = 1;
// increase the boost if the boss has at least 3 segments and we passed last shield
@@ -6904,7 +6982,7 @@ export class EnemyPokemon extends Pokemon {
"StatStageChangePhase",
this.getBattlerIndex(),
true,
- [boostedStat!],
+ [boostedStat],
stages,
true,
true,
diff --git a/src/field/trainer.ts b/src/field/trainer.ts
index acbc8031dbc..f5b2e5dad99 100644
--- a/src/field/trainer.ts
+++ b/src/field/trainer.ts
@@ -17,7 +17,7 @@ import { getIsInitialized, initI18n } from "#plugins/i18n";
import type { TrainerConfig } from "#trainers/trainer-config";
import { trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, type TrainerPartyTemplate } from "#trainers/trainer-party-template";
-import { randSeedInt, randSeedItem, randSeedWeightedItem } from "#utils/common";
+import { randSeedInt, randSeedItem } from "#utils/common";
import { getRandomLocaleEntry } from "#utils/i18n";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { toCamelCase } from "#utils/strings";
@@ -54,16 +54,14 @@ export class Trainer extends Phaser.GameObjects.Container {
) {
super(globalScene, -72, 80);
this.config =
- trainerConfigOverride ??
- (trainerConfigs.hasOwnProperty(trainerType)
+ trainerConfigOverride
+ ?? (trainerConfigs.hasOwnProperty(trainerType)
? trainerConfigs[trainerType]
: trainerConfigs[TrainerType.ACE_TRAINER]);
this.variant = variant;
this.partyTemplateIndex = Math.min(
- partyTemplateIndex !== undefined
- ? partyTemplateIndex
- : randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
+ partyTemplateIndex !== undefined ? partyTemplateIndex : randSeedItem(this.config.partyTemplates.map((_, i) => i)),
this.config.partyTemplates.length - 1,
);
// TODO: Rework this and add actual error handling for missing names
@@ -235,8 +233,8 @@ export class Trainer extends Phaser.GameObjects.Container {
getEncounterBgm(): string {
return !this.variant
? this.config.encounterBgm
- : (this.variant === TrainerVariant.DOUBLE ? this.config.doubleEncounterBgm : this.config.femaleEncounterBgm) ||
- this.config.encounterBgm;
+ : (this.variant === TrainerVariant.DOUBLE ? this.config.doubleEncounterBgm : this.config.femaleEncounterBgm)
+ || this.config.encounterBgm;
}
getEncounterMessages(): string[] {
@@ -250,17 +248,15 @@ export class Trainer extends Phaser.GameObjects.Container {
getVictoryMessages(): string[] {
return !this.variant
? this.config.victoryMessages
- : (this.variant === TrainerVariant.DOUBLE
- ? this.config.doubleVictoryMessages
- : this.config.femaleVictoryMessages) || this.config.victoryMessages;
+ : (this.variant === TrainerVariant.DOUBLE ? this.config.doubleVictoryMessages : this.config.femaleVictoryMessages)
+ || this.config.victoryMessages;
}
getDefeatMessages(): string[] {
return !this.variant
? this.config.defeatMessages
- : (this.variant === TrainerVariant.DOUBLE
- ? this.config.doubleDefeatMessages
- : this.config.femaleDefeatMessages) || this.config.defeatMessages;
+ : (this.variant === TrainerVariant.DOUBLE ? this.config.doubleDefeatMessages : this.config.femaleDefeatMessages)
+ || this.config.defeatMessages;
}
getPartyTemplate(): TrainerPartyTemplate {
@@ -399,16 +395,14 @@ export class Trainer extends Phaser.GameObjects.Container {
} else {
newSpeciesPool = speciesPoolFiltered;
}
- } else {
// If the index is odd, use the species pool for the partner trainer (that way he only uses his own pokemon in battle)
// Since the only currently allowed double battle with named trainers is Tate & Liza, we need to make sure that Solrock is the first pokemon in the party for Tate and Lunatone for Liza
- if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.TATE]) {
- newSpeciesPool = [SpeciesId.SOLROCK];
- } else if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.LIZA]) {
- newSpeciesPool = [SpeciesId.LUNATONE];
- } else {
- newSpeciesPool = speciesPoolPartnerFiltered;
- }
+ } else if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.TATE]) {
+ newSpeciesPool = [SpeciesId.SOLROCK];
+ } else if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.LIZA]) {
+ newSpeciesPool = [SpeciesId.LUNATONE];
+ } else {
+ newSpeciesPool = speciesPoolPartnerFiltered;
}
// Fallback for when the species pool is empty
if (newSpeciesPool.length === 0) {
@@ -447,9 +441,9 @@ export class Trainer extends Phaser.GameObjects.Container {
},
this.config.hasStaticParty
? this.config.getDerivedType() + ((index + 1) << 8)
- : globalScene.currentBattle.waveIndex +
- (this.config.getDerivedType() << 10) +
- (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8),
+ : globalScene.currentBattle.waveIndex
+ + (this.config.getDerivedType() << 10)
+ + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8),
);
return ret!; // TODO: is this bang correct?
@@ -473,7 +467,7 @@ export class Trainer extends Phaser.GameObjects.Container {
? TrainerPoolTier.SUPER_RARE
: TrainerPoolTier.ULTRA_RARE;
console.log(TrainerPoolTier[tier]);
- while (!this.config.speciesPools.hasOwnProperty(tier) || !this.config.speciesPools[tier].length) {
+ while (!this.config.speciesPools.hasOwnProperty(tier) || this.config.speciesPools[tier].length === 0) {
console.log(
`Downgraded trainer Pokemon rarity tier from ${TrainerPoolTier[tier]} to ${TrainerPoolTier[tier - 1]}`,
);
@@ -497,8 +491,8 @@ export class Trainer extends Phaser.GameObjects.Container {
} else if (template.isBalanced(battle.enemyParty.length)) {
const partyMemberTypes = battle.enemyParty.flatMap(p => p.getTypes(true));
if (
- partyMemberTypes.indexOf(ret.type1) > -1 ||
- (ret.type2 !== null && partyMemberTypes.indexOf(ret.type2) > -1)
+ partyMemberTypes.indexOf(ret.type1) > -1
+ || (ret.type2 !== null && partyMemberTypes.indexOf(ret.type2) > -1)
) {
retry = true;
}
@@ -754,7 +748,7 @@ export class Trainer extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: tintSprite,
alpha: alpha || 1,
- duration: duration,
+ duration,
ease: ease || "Linear",
});
} else {
@@ -770,7 +764,7 @@ export class Trainer extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: tintSprite,
alpha: 0,
- duration: duration,
+ duration,
ease: ease || "Linear",
onComplete: () => {
tintSprite.setVisible(false);
@@ -790,10 +784,12 @@ export class Trainer extends Phaser.GameObjects.Container {
* @returns boolean Whether the EnemyPokemon should Terastalize this turn
*/
shouldTera(pokemon: EnemyPokemon): boolean {
- if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA) {
- if (!pokemon.isTerastallized && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)) {
- return true;
- }
+ if (
+ this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA
+ && !pokemon.isTerastallized
+ && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)
+ ) {
+ return true;
}
return false;
}
diff --git a/src/game-mode.ts b/src/game-mode.ts
index 513a2baaf95..9ea3adf59d3 100644
--- a/src/game-mode.ts
+++ b/src/game-mode.ts
@@ -225,8 +225,8 @@ export class GameMode implements GameModeConfig {
return waveIndex > 10 && waveIndex < 50 && !(waveIndex % 10);
default:
return (
- waveIndex % 30 === (offsetGym ? 0 : 20) &&
- (biomeType !== BiomeId.END || this.isClassic || this.isWaveFinal(waveIndex))
+ waveIndex % 30 === (offsetGym ? 0 : 20)
+ && (biomeType !== BiomeId.END || this.isClassic || this.isWaveFinal(waveIndex))
);
}
}
@@ -241,10 +241,10 @@ export class GameMode implements GameModeConfig {
const allFinalBossSpecies = allSpecies.filter(
s =>
- (s.subLegendary || s.legendary || s.mythical) &&
- s.baseTotal >= 600 &&
- s.speciesId !== SpeciesId.ETERNATUS &&
- s.speciesId !== SpeciesId.ARCEUS,
+ (s.subLegendary || s.legendary || s.mythical)
+ && s.baseTotal >= 600
+ && s.speciesId !== SpeciesId.ETERNATUS
+ && s.speciesId !== SpeciesId.ARCEUS,
);
return randSeedItem(allFinalBossSpecies);
}
@@ -315,21 +315,21 @@ export class GameMode implements GameModeConfig {
/**
* Checks whether there is a fixed battle on this gamemode on a given wave.
- * @param {number} waveIndex The wave to check.
- * @returns {boolean} If this game mode has a fixed battle on this wave
+ * @param waveIndex The wave to check.
+ * @returns If this game mode has a fixed battle on this wave
*/
isFixedBattle(waveIndex: number): boolean {
const dummyConfig = new FixedBattleConfig();
return (
- this.battleConfig.hasOwnProperty(waveIndex) ||
- applyChallenges(ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig)
+ this.battleConfig.hasOwnProperty(waveIndex)
+ || applyChallenges(ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig)
);
}
/**
* Returns the config for the fixed battle for a particular wave.
- * @param {number} waveIndex The wave to check.
- * @returns {boolean} The fixed battle for this wave.
+ * @param waveIndex The wave to check.
+ * @returns The fixed battle for this wave.
*/
getFixedBattle(waveIndex: number): FixedBattleConfig {
const challengeConfig = new FixedBattleConfig();
diff --git a/src/init/init.ts b/src/init/init.ts
index ba9738e2be8..b717664b654 100644
--- a/src/init/init.ts
+++ b/src/init/init.ts
@@ -1,6 +1,5 @@
import { initAbilities } from "#abilities/ability";
import { initBiomes } from "#balance/biomes";
-import { initEggMoves } from "#balance/egg-moves";
import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions";
import { initSpecies } from "#balance/pokemon-species";
import { initChallenges } from "#data/challenge";
@@ -12,7 +11,7 @@ import { initMoves } from "#moves/move";
import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters";
import { initAchievements } from "#system/achv";
import { initVouchers } from "#system/voucher";
-import { initStatsKeys } from "#ui/game-stats-ui-handler";
+import { initStatsKeys } from "#ui/handlers/game-stats-ui-handler";
/** Initialize the game. */
export function initializeGame() {
@@ -24,7 +23,6 @@ export function initializeGame() {
initPokemonPrevolutions();
initPokemonStarters();
initBiomes();
- initEggMoves();
initPokemonForms();
initTrainerTypeDialogue();
initSpecies();
diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts
index 03d2278f26c..0207297fd58 100644
--- a/src/inputs-controller.ts
+++ b/src/inputs-controller.ts
@@ -406,9 +406,9 @@ export class InputsController {
}
this.lastSource = "gamepad";
if (
- !this.selectedDevice[Device.GAMEPAD] ||
- (globalScene.ui.getMode() !== UiMode.GAMEPAD_BINDING &&
- this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase())
+ !this.selectedDevice[Device.GAMEPAD]
+ || (globalScene.ui.getMode() !== UiMode.GAMEPAD_BINDING
+ && this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase())
) {
this.setChosenGamepad(pad.id);
}
@@ -585,7 +585,7 @@ export class InputsController {
resetConfigs(): void {
this.configs = new Map();
- if (this.getGamepadsName()?.length) {
+ if (this.getGamepadsName()?.length > 0) {
this.setupGamepad(this.selectedDevice[Device.GAMEPAD]);
}
this.setupKeyboard();
diff --git a/src/loading-scene.ts b/src/loading-scene.ts
index 0a42fc3fa7d..a4cbaf9ae64 100644
--- a/src/loading-scene.ts
+++ b/src/loading-scene.ts
@@ -60,9 +60,7 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("pbinfo_enemy_type", "ui");
this.loadAtlas("pbinfo_enemy_type1", "ui");
this.loadAtlas("pbinfo_enemy_type2", "ui");
- this.loadAtlas("pbinfo_stat", "ui");
this.loadAtlas("pbinfo_stat_numbers", "ui");
- this.loadImage("overlay_lv", "ui");
this.loadAtlas("numbers", "ui");
this.loadAtlas("numbers_red", "ui");
this.loadAtlas("overlay_hp", "ui");
@@ -125,7 +123,6 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("party_slot_main", "ui");
this.loadAtlas("party_slot_main_short", "ui");
this.loadAtlas("party_slot", "ui");
- this.loadImage("party_slot_overlay_lv", "ui");
this.loadImage("party_slot_hp_bar", "ui");
this.loadAtlas("party_slot_hp_overlay", "ui");
this.loadAtlas("party_pb", "ui");
@@ -134,23 +131,16 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("party_transfer", "ui");
this.loadImage("summary_bg", "ui");
- this.loadImage("summary_overlay_shiny", "ui");
this.loadImage("summary_profile", "ui");
this.loadImage("summary_profile_prompt_z", "ui"); // The pixel Z button prompt
this.loadImage("summary_profile_prompt_a", "ui"); // The pixel A button prompt
- this.loadImage("summary_profile_ability", "ui"); // Pixel text 'ABILITY'
- this.loadImage("summary_profile_passive", "ui"); // Pixel text 'PASSIVE'
this.loadImage("summary_status", "ui");
this.loadImage("summary_stats", "ui");
this.loadImage("summary_stats_overlay_exp", "ui");
this.loadImage("summary_moves", "ui");
this.loadImage("summary_moves_effect", "ui");
this.loadImage("summary_moves_overlay_row", "ui");
- this.loadImage("summary_moves_overlay_pp", "ui");
this.loadAtlas("summary_moves_cursor", "ui");
- for (let t = 1; t <= 3; t++) {
- this.loadImage(`summary_tabs_${t}`, "ui");
- }
this.loadImage("scroll_bar", "ui");
this.loadImage("scroll_bar_handle", "ui");
@@ -164,6 +154,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("select_gen_cursor", "ui");
this.loadImage("select_gen_cursor_highlight", "ui");
+ this.loadImage("language_icon", "ui");
this.loadImage("saving_icon", "ui");
this.loadImage("discord", "ui");
this.loadImage("google", "ui");
@@ -230,23 +221,119 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("pb", "");
this.loadAtlas("items", "");
- this.loadAtlas("types", "");
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_
- const lang = i18next.resolvedLanguage;
- if (lang !== "en") {
- if (hasAllLocalizedSprites(lang)) {
- this.loadAtlas(`statuses_${lang}`, "");
- this.loadAtlas(`types_${lang}`, "");
- } else {
- // Fallback to English
- this.loadAtlas("statuses", "");
- this.loadAtlas("types", "");
- }
- } else {
- this.loadAtlas("statuses", "");
- this.loadAtlas("types", "");
+ const lang = i18next.resolvedLanguage ?? "en";
+ const keySuffix = lang !== "en" && hasAllLocalizedSprites(lang) ? `_${lang}` : "";
+
+ this.loadAtlas(`statuses${keySuffix}`, "");
+ this.loadAtlas(`types${keySuffix}`, "");
+ for (let t = 1; t <= 3; t++) {
+ this.loadImage(
+ `summary_tabs_${t}${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_tabs_${t}${keySuffix}.png`,
+ );
}
+ this.loadImage(
+ `summary_dexnb_label${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_dexnb_label${keySuffix}.png`,
+ ); // Pixel text 'No'
+ this.loadImage(
+ `summary_dexnb_label_overlay_shiny${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_dexnb_label_overlay_shiny${keySuffix}.png`,
+ ); // Pixel text 'No' shiny
+ this.loadImage(
+ `summary_profile_profile_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_profile_profile_title${keySuffix}.png`,
+ ); // Pixel text 'PROFILE'
+ this.loadImage(
+ `summary_profile_ability${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_profile_ability${keySuffix}.png`,
+ ); // Pixel text 'ABILITY'
+ this.loadImage(
+ `summary_profile_passive${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_profile_passive${keySuffix}.png`,
+ ); // Pixel text 'PASSIVE'
+ this.loadImage(
+ `summary_profile_memo_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_profile_memo_title${keySuffix}.png`,
+ ); // Pixel text 'TRAINER MEMO'
+ this.loadImage(
+ `summary_stats_item_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_stats_item_title${keySuffix}.png`,
+ ); // Pixel text 'ITEM'
+ this.loadImage(
+ `summary_stats_stats_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_stats_stats_title${keySuffix}.png`,
+ ); // Pixel text 'STATS'
+ this.loadImage(
+ `summary_stats_exp_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_stats_exp_title${keySuffix}.png`,
+ ); // Pixel text 'EXP.'
+ this.loadImage(
+ `summary_stats_expbar_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_stats_expbar_title${keySuffix}.png`,
+ ); // Pixel mini text 'EXP'
+ this.loadImage(
+ `summary_moves_moves_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_moves_moves_title${keySuffix}.png`,
+ ); // Pixel text 'MOVES'
+ this.loadImage(
+ `summary_moves_descriptions_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_moves_descriptions_title${keySuffix}.png`,
+ ); // Pixel text 'DESCRIPTIONS'
+ this.loadImage(
+ `summary_moves_overlay_pp${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_moves_overlay_pp${keySuffix}.png`,
+ ); // Pixel text 'PP'
+ this.loadImage(
+ `summary_moves_effect_title${keySuffix}`,
+ "ui",
+ `text_images/${lang}/summary/summary_moves_effect_title${keySuffix}.png`,
+ ); // Pixel text 'EFFECT'
+
+ this.loadAtlas(`pbinfo_stat${keySuffix}`, "ui", `text_images/${lang}/battle_ui/pbinfo_stat${keySuffix}`); // Pixel text for in-battle stats info tab
+ this.loadImage(`overlay_lv${keySuffix}`, "ui", `text_images/${lang}/battle_ui/overlay_lv${keySuffix}.png`); // Pixel text in-battle 'Lv.'
+ this.loadImage(
+ `overlay_hp_label${keySuffix}`,
+ "ui",
+ `text_images/${lang}/battle_ui/overlay_hp_label${keySuffix}.png`,
+ ); // Pixel text in-battle 'HP'
+ this.loadImage(
+ `overlay_hp_label_boss${keySuffix}`,
+ "ui",
+ `text_images/${lang}/battle_ui/overlay_hp_label_boss${keySuffix}.png`,
+ ); // Pixel text in-battle 'BOSS'
+ this.loadImage(
+ `overlay_exp_label${keySuffix}`,
+ "ui",
+ `text_images/${lang}/battle_ui/overlay_exp_label${keySuffix}.png`,
+ ); // Pixel text in-battle 'EXP'
+ this.loadImage(
+ `party_slot_overlay_lv${keySuffix}`,
+ "ui",
+ `text_images/${lang}/party_ui/party_slot_overlay_lv${keySuffix}.png`,
+ ); // Pixel text party 'Lv.'
+ this.loadImage(
+ `party_slot_overlay_hp${keySuffix}`,
+ "ui",
+ `text_images/${lang}/party_ui/party_slot_overlay_hp${keySuffix}.png`,
+ ); // Pixel text party 'HP'
+
if (timedEventManager.activeEventHasBanner()) {
const availableLangs = timedEventManager.getEventBannerLangs();
if (lang && availableLangs.includes(lang)) {
@@ -256,7 +343,6 @@ export class LoadingScene extends SceneBase {
}
}
- this.loadAtlas("statuses", "");
this.loadAtlas("categories", "");
this.loadAtlas("egg", "egg");
diff --git a/src/main.ts b/src/main.ts
index 7e4943bdca5..fe510eafb64 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,6 +10,10 @@ import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin";
import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
+if (import.meta.env.DEV) {
+ document.title += " (Beta)";
+}
+
// Catch global errors and display them in an alert so users can report the issue.
window.onerror = (_message, _source, _lineno, _colno, error) => {
console.error(error);
@@ -101,7 +105,7 @@ const startGame = async () => {
antialias: false,
pipeline: [InvertPostFX] as unknown as Phaser.Types.Core.PipelineConfig,
scene: [LoadingScene, BattleScene],
- version: version,
+ version,
});
game.sound.pauseOnBlur = false;
if (manifest) {
diff --git a/src/messages.ts b/src/messages.ts
index 177b4cc9b05..c9673345110 100644
--- a/src/messages.ts
+++ b/src/messages.ts
@@ -6,8 +6,8 @@ import i18next from "i18next";
/**
* Retrieves the Pokemon's name, potentially with an affix indicating its role (wild or foe) in the current battle context, translated
* @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance
- * @param {boolean} useIllusion - Whether we want the name of the illusion or not. Default value : true
- * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage"
+ * @param useIllusion - Whether we want the name of the illusion or not. Default value : true
+ * @returns ex: "Wild Gengar", "Ectoplasma sauvage"
*/
export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusion = true): string {
if (!pokemon) {
diff --git a/src/modifier/init-modifier-pools.ts b/src/modifier/init-modifier-pools.ts
index 3e180846183..ba12920407d 100644
--- a/src/modifier/init-modifier-pools.ts
+++ b/src/modifier/init-modifier-pools.ts
@@ -97,12 +97,12 @@ function initCommonModifierPool() {
const thresholdPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
- p
+ p.hp
+ && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA)
+ && p
.getMoveset()
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
- .length,
+ .length > 0,
).length,
3,
);
@@ -116,12 +116,12 @@ function initCommonModifierPool() {
const thresholdPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
- p
+ p.hp
+ && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA)
+ && p
.getMoveset()
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
- .length,
+ .length > 0,
).length,
3,
);
@@ -152,9 +152,9 @@ function initGreatModifierPool() {
const statusEffectPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !!p.status &&
- !p.getHeldItems().some(i => {
+ p.hp
+ && !!p.status
+ && !p.getHeldItems().some(i => {
if (i instanceof TurnStatusEffectModifier) {
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
}
@@ -218,9 +218,9 @@ function initGreatModifierPool() {
const statusEffectPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !!p.status &&
- !p.getHeldItems().some(i => {
+ p.hp
+ && !!p.status
+ && !p.getHeldItems().some(i => {
if (i instanceof TurnStatusEffectModifier) {
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
}
@@ -230,9 +230,9 @@ function initGreatModifierPool() {
3,
);
const thresholdPartyMemberCount = Math.floor(
- (Math.min(party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length, 3) +
- statusEffectPartyMemberCount) /
- 2,
+ (Math.min(party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length, 3)
+ + statusEffectPartyMemberCount)
+ / 2,
);
return thresholdPartyMemberCount;
},
@@ -244,12 +244,12 @@ function initGreatModifierPool() {
const thresholdPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
- p
+ p.hp
+ && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA)
+ && p
.getMoveset()
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
- .length,
+ .length > 0,
).length,
3,
);
@@ -263,12 +263,12 @@ function initGreatModifierPool() {
const thresholdPartyMemberCount = Math.min(
party.filter(
p =>
- p.hp &&
- !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
- p
+ p.hp
+ && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA)
+ && p
.getMoveset()
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
- .length,
+ .length > 0,
).length,
3,
);
@@ -369,9 +369,9 @@ function initUltraModifierPool() {
return party.some(p => {
// Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd
if (
- !p.isMax() &&
- (p.getSpeciesForm(true).speciesId in pokemonEvolutions ||
- (p.isFusion() && p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))
+ !p.isMax()
+ && (p.getSpeciesForm(true).speciesId in pokemonEvolutions
+ || (p.isFusion() && p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))
) {
// Check if Pokemon is already holding an Eviolite
return !p.getHeldItems().some(i => i.type.id === "EVIOLITE");
@@ -391,9 +391,9 @@ function initUltraModifierPool() {
// If a party member doesn't already have a Leek and is one of the relevant species, Leek can appear
return party.some(
p =>
- !p.getHeldItems().some(i => i instanceof SpeciesCritBoosterModifier) &&
- (checkedSpecies.includes(p.getSpeciesForm(true).speciesId) ||
- (p.isFusion() && checkedSpecies.includes(p.getFusionSpeciesForm(true).speciesId))),
+ !p.getHeldItems().some(i => i instanceof SpeciesCritBoosterModifier)
+ && (checkedSpecies.includes(p.getSpeciesForm(true).speciesId)
+ || (p.isFusion() && checkedSpecies.includes(p.getFusionSpeciesForm(true).speciesId))),
)
? 12
: 0;
@@ -633,9 +633,9 @@ function initMasterModifierPool() {
new WeightedModifierType(
modifierTypes.DNA_SPLICERS,
(party: Pokemon[]) =>
- !(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) &&
- !globalScene.gameMode.isSplicedOnly &&
- party.filter(p => !p.fusionSpecies).length > 1
+ !(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted())
+ && !globalScene.gameMode.isSplicedOnly
+ && party.filter(p => !p.fusionSpecies).length > 1
? 24
: 0,
24,
@@ -643,8 +643,9 @@ function initMasterModifierPool() {
new WeightedModifierType(
modifierTypes.MINI_BLACK_HOLE,
() =>
- globalScene.gameMode.isDaily ||
- (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))
+ globalScene.gameMode.isDaily
+ || (!globalScene.gameMode.isFreshStartChallenge()
+ && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))
? 1
: 0,
1,
@@ -842,9 +843,9 @@ function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedModifier
function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc {
return () => {
const lures = globalScene.getModifiers(DoubleBattleChanceBoosterModifier);
- return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) &&
- (lures.length === 0 ||
- lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0)
+ return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199)
+ && (lures.length === 0
+ || lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0)
? weight
: 0;
};
diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts
index 8b77900cb62..155ada1c18d 100644
--- a/src/modifier/modifier-type.ts
+++ b/src/modifier/modifier-type.ts
@@ -115,8 +115,8 @@ import {
import type { PokemonMove } from "#moves/pokemon-move";
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#system/voucher";
import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#types/modifier-types";
-import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler";
-import { PartyUiHandler } from "#ui/party-ui-handler";
+import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/handlers/party-ui-handler";
+import { PartyUiHandler } from "#ui/handlers/party-ui-handler";
import { getModifierTierTextTint } from "#ui/text";
import { applyChallenges } from "#utils/challenge-utils";
import {
@@ -150,7 +150,7 @@ export class ModifierType {
/**
* Checks if the modifier type is of a specific type
* @param modifierType - The type to check against
- * @return Whether the modifier type is of the specified type
+ * @returns Whether the modifier type is of the specified type
*/
public is(modifierType: K): this is ModifierTypeInstanceMap[K] {
const targetType = ModifierTypeConstructorMap[modifierType];
@@ -485,8 +485,8 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
super(
localeKey,
iconImage,
- newModifierFunc ||
- ((_type, args) =>
+ newModifierFunc
+ || ((_type, args) =>
new PokemonHpRestoreModifier(
this,
(args[0] as PlayerPokemon).id,
@@ -495,11 +495,12 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
this.healStatus,
false,
)),
- selectFilter ||
- ((pokemon: PlayerPokemon) => {
+ selectFilter
+ || ((pokemon: PlayerPokemon) => {
if (
- !pokemon.hp ||
- (pokemon.isFullHp() && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))))
+ !pokemon.hp
+ || (pokemon.isFullHp()
+ && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))))
) {
return PartyUiHandler.NoEffectMessage;
}
@@ -640,7 +641,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
iconImage,
(_type, args) => new PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
(pokemon: PlayerPokemon) => {
- if (!pokemon.getMoveset().filter(m => m.ppUsed).length) {
+ if (pokemon.getMoveset().filter(m => m.ppUsed).length === 0) {
return PartyUiHandler.NoEffectMessage;
}
return null;
@@ -732,7 +733,7 @@ export class RememberMoveModifierType extends PokemonModifierType {
iconImage,
(type, args) => new RememberMoveModifier(type, (args[0] as PlayerPokemon).id, args[1] as number),
(pokemon: PlayerPokemon) => {
- if (!pokemon.getLearnableLevelMoves().length) {
+ if (pokemon.getLearnableLevelMoves().length === 0) {
return PartyUiHandler.NoEffectMessage;
}
return null;
@@ -873,11 +874,7 @@ export class AttackTypeBoosterModifierType
export type SpeciesStatBoosterItem = keyof typeof SpeciesStatBoosterModifierTypeGenerator.items;
-/**
- * Modifier type for {@linkcode SpeciesStatBoosterModifier}
- * @extends PokemonHeldItemModifierType
- * @implements GeneratedPersistentModifierType
- */
+/** Modifier type for {@linkcode SpeciesStatBoosterModifier} */
export class SpeciesStatBoosterModifierType
extends PokemonHeldItemModifierType
implements GeneratedPersistentModifierType
@@ -1144,8 +1141,8 @@ export class TmModifierType extends PokemonModifierType {
(_type, args) => new TmModifier(this, (args[0] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => {
if (
- pokemon.compatibleTms.indexOf(moveId) === -1 ||
- pokemon.getMoveset().filter(m => m.moveId === moveId).length
+ pokemon.compatibleTms.indexOf(moveId) === -1
+ || pokemon.getMoveset().filter(m => m.moveId === moveId).length > 0
) {
return PartyUiHandler.NoEffectMessage;
}
@@ -1184,20 +1181,21 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
(_type, args) => new EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => {
if (
- pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) &&
- pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem))
- .length &&
- pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
+ pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId)
+ && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem))
+ .length > 0
+ && pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
) {
return null;
}
if (
- pokemon.isFusion() &&
- pokemon.fusionSpecies &&
- pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) &&
- pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.validate(pokemon, true, this.evolutionItem))
- .length &&
- pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
+ pokemon.isFusion()
+ && pokemon.fusionSpecies
+ && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)
+ && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e =>
+ e.validate(pokemon, true, this.evolutionItem),
+ ).length > 0
+ && pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
) {
return null;
}
@@ -1236,9 +1234,8 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G
(pokemon: PlayerPokemon) => {
// Make sure the Pokemon has alternate forms
if (
- pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
- // Get all form changes for this species with an item trigger, including any compound triggers
- pokemonFormChanges[pokemon.species.speciesId]
+ pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) // Get all form changes for this species with an item trigger, including any compound triggers
+ && pokemonFormChanges[pokemon.species.speciesId]
.filter(
fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger) && fc.preFormKey === pokemon.getFormKey(),
)
@@ -1306,7 +1303,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
.filter(m => m.is("AttackMove"))
.map(m => m.type),
);
- if (!attackMoveTypes.length) {
+ if (attackMoveTypes.length === 0) {
return null;
}
@@ -1395,7 +1392,6 @@ class TempStatStageBoosterModifierTypeGenerator extends ModifierTypeGenerator {
* Modifier type generator for {@linkcode SpeciesStatBoosterModifierType}, which
* encapsulates the logic for weighting the most useful held item from
* the current list of {@linkcode items}.
- * @extends ModifierTypeGenerator
*/
class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
/** Object comprised of the currently available species-based stat boosting held items */
@@ -1473,8 +1469,8 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
.getHeldItems()
.some(
m =>
- m instanceof SpeciesStatBoosterModifier &&
- (m as SpeciesStatBoosterModifier).contains(checkedSpecies[0], checkedStats[0]),
+ m instanceof SpeciesStatBoosterModifier
+ && (m as SpeciesStatBoosterModifier).contains(checkedSpecies[0], checkedStats[0]),
);
if (!hasItem) {
@@ -1531,7 +1527,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
.filter(tm => tmPoolTiers[tm] === tier)
.filter(tm => !allMoves[tm].name.endsWith(" (N)"))
.filter((tm, i, array) => array.indexOf(tm) === i);
- if (!tierUniqueCompatibleTms.length) {
+ if (tierUniqueCompatibleTms.length === 0) {
return null;
}
// TODO: should this use `randSeedItem`?
@@ -1552,12 +1548,12 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
party
.filter(
p =>
- pokemonEvolutions.hasOwnProperty(p.species.speciesId) &&
- (!p.pauseEvolutions ||
- p.species.speciesId === SpeciesId.SLOWPOKE ||
- p.species.speciesId === SpeciesId.EEVEE ||
- p.species.speciesId === SpeciesId.KIRLIA ||
- p.species.speciesId === SpeciesId.SNORUNT),
+ pokemonEvolutions.hasOwnProperty(p.species.speciesId)
+ && (!p.pauseEvolutions
+ || p.species.speciesId === SpeciesId.SLOWPOKE
+ || p.species.speciesId === SpeciesId.EEVEE
+ || p.species.speciesId === SpeciesId.KIRLIA
+ || p.species.speciesId === SpeciesId.SNORUNT),
)
.flatMap(p => {
const evolutions = pokemonEvolutions[p.species.speciesId];
@@ -1566,14 +1562,14 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
party
.filter(
p =>
- p.isFusion() &&
- p.fusionSpecies &&
- pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) &&
- (!p.pauseEvolutions ||
- p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE ||
- p.fusionSpecies.speciesId === SpeciesId.EEVEE ||
- p.fusionSpecies.speciesId === SpeciesId.KIRLIA ||
- p.fusionSpecies.speciesId === SpeciesId.SNORUNT),
+ p.isFusion()
+ && p.fusionSpecies
+ && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)
+ && (!p.pauseEvolutions
+ || p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE
+ || p.fusionSpecies.speciesId === SpeciesId.EEVEE
+ || p.fusionSpecies.speciesId === SpeciesId.KIRLIA
+ || p.fusionSpecies.speciesId === SpeciesId.SNORUNT),
)
.flatMap(p => {
const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId];
@@ -1584,7 +1580,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
.flatMap(e => e.evoItem)
.filter(i => !!i && i > 50 === rare);
- if (!evolutionItemPool.length) {
+ if (evolutionItemPool.length === 0) {
return null;
}
@@ -1610,34 +1606,34 @@ export class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
let formChangeItemTriggers = formChanges
.filter(
fc =>
- ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 &&
- fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) ||
- globalScene.getModifiers(MegaEvolutionAccessModifier).length) &&
- ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 &&
- fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) ||
- globalScene.getModifiers(GigantamaxAccessModifier).length) &&
- (!fc.conditions.length ||
- fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p))
- .length) &&
- fc.preFormKey === p.getFormKey(),
+ ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1
+ && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1)
+ || globalScene.getModifiers(MegaEvolutionAccessModifier).length > 0)
+ && ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1
+ && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1)
+ || globalScene.getModifiers(GigantamaxAccessModifier).length > 0)
+ && (fc.conditions.length === 0
+ || fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p))
+ .length > 0)
+ && fc.preFormKey === p.getFormKey(),
)
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
.filter(
t =>
- t?.active &&
- !globalScene.findModifier(
+ t?.active
+ && !globalScene.findModifier(
m =>
- m instanceof PokemonFormChangeItemModifier &&
- m.pokemonId === p.id &&
- m.formChangeItem === t.item,
+ m instanceof PokemonFormChangeItemModifier
+ && m.pokemonId === p.id
+ && m.formChangeItem === t.item,
),
);
if (p.species.speciesId === SpeciesId.NECROZMA) {
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
- let foundULTRA_Z = false,
- foundN_LUNA = false,
- foundN_SOLAR = false;
+ let foundULTRA_Z = false;
+ let foundN_LUNA = false;
+ let foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, _i) => {
console.log("Checking ", fc.item);
switch (fc.item) {
@@ -1670,7 +1666,7 @@ export class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
.filter(i => (i && i < 100) === isRareFormChangeItem);
// convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party.
- if (!formChangeItemPool.length) {
+ if (formChangeItemPool.length === 0) {
return null;
}
@@ -1960,7 +1956,7 @@ const modifierTypeInitObj = Object.freeze({
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) {
return new TerastallizeModifierType(pregenArgs[0] as PokemonType);
}
- if (!globalScene.getModifiers(TerastallizeAccessModifier).length) {
+ if (globalScene.getModifiers(TerastallizeAccessModifier).length === 0) {
return null;
}
const teraTypes: PokemonType[] = [];
@@ -2399,10 +2395,10 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
? weightedModifierType.modifierType.generateType(party)
: weightedModifierType.modifierType;
const weight =
- !existingModifiers.length ||
- itemModifierType instanceof PokemonHeldItemModifierType ||
- itemModifierType instanceof FormChangeItemModifierType ||
- existingModifiers.find(m => m.stackCount < m.getMaxStackCount(true))
+ existingModifiers.length === 0
+ || itemModifierType instanceof PokemonHeldItemModifierType
+ || itemModifierType instanceof FormChangeItemModifierType
+ || existingModifiers.find(m => m.stackCount < m.getMaxStackCount(true))
? weightedModifierType.weight instanceof Function
? // biome-ignore lint/complexity/noBannedTypes: TODO: refactor to not use Function type
(weightedModifierType.weight as Function)(party, rerollCount)
@@ -2524,16 +2520,16 @@ export function getPlayerModifierTypeOptions(
} else {
// Guaranteed mod options first
if (
- customModifierSettings?.guaranteedModifierTypeOptions &&
- customModifierSettings.guaranteedModifierTypeOptions.length > 0
+ customModifierSettings?.guaranteedModifierTypeOptions
+ && customModifierSettings.guaranteedModifierTypeOptions.length > 0
) {
options.push(...customModifierSettings.guaranteedModifierTypeOptions!);
}
// Guaranteed mod functions second
if (
- customModifierSettings.guaranteedModifierTypeFuncs &&
- customModifierSettings.guaranteedModifierTypeFuncs.length > 0
+ customModifierSettings.guaranteedModifierTypeFuncs
+ && customModifierSettings.guaranteedModifierTypeFuncs.length > 0
) {
customModifierSettings.guaranteedModifierTypeFuncs!.forEach((mod, _i) => {
const modifierId = Object.keys(modifierTypeInitObj).find(k => modifierTypeInitObj[k] === mod) as string;
@@ -2595,11 +2591,11 @@ function getModifierTypeOptionWithRetry(
applyChallenges(ChallengeType.WAVE_REWARD, candidate, candidateValidity);
let r = 0;
while (
- (existingOptions.length &&
- ++r < retryCount &&
- existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group)
- .length) ||
- !candidateValidity.value
+ (existingOptions.length > 0
+ && ++r < retryCount
+ && existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group)
+ .length > 0)
+ || !candidateValidity.value
) {
candidate = getNewModifierTypeOption(
party,
@@ -2703,9 +2699,9 @@ export function getEnemyBuffModifierForWave(
let r = 0;
let matchingModifier: PersistentModifier | undefined;
while (
- ++r < retryCount &&
- (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) &&
- matchingModifier.getMaxStackCount() < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)
+ ++r < retryCount
+ && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id))
+ && matchingModifier.getMaxStackCount() < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)
) {
candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier);
}
@@ -2832,7 +2828,7 @@ function getNewModifierTypeOption(
}
tier += upgradeCount;
- while (tier && (!pool.hasOwnProperty(tier) || !pool[tier].length)) {
+ while (tier && (!pool.hasOwnProperty(tier) || pool[tier].length === 0)) {
tier--;
if (upgradeCount) {
upgradeCount--;
@@ -2843,7 +2839,7 @@ function getNewModifierTypeOption(
if (tier < ModifierTier.MASTER && allowLuckUpgrades) {
const partyLuckValue = getPartyLuckValue(party);
const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4));
- while (pool.hasOwnProperty(tier + upgradeCount + 1) && pool[tier + upgradeCount + 1].length) {
+ while (pool.hasOwnProperty(tier + upgradeCount + 1) && pool[tier + upgradeCount + 1].length > 0) {
if (randSeedInt(upgradeOdds) < 4) {
upgradeCount++;
} else {
@@ -2858,7 +2854,7 @@ function getNewModifierTypeOption(
}
const tierThresholds = Object.keys(thresholds[tier]);
- const totalWeight = Number.parseInt(tierThresholds[tierThresholds.length - 1]);
+ const totalWeight = Number.parseInt(tierThresholds.at(-1)!);
const value = randSeedInt(totalWeight);
let index: number | undefined;
for (const t of tierThresholds) {
diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts
index 7d0478628b4..1f470e592c2 100644
--- a/src/modifier/modifier.ts
+++ b/src/modifier/modifier.ts
@@ -73,8 +73,8 @@ export class ModifierBar extends Phaser.GameObjects.Container {
/**
* Method to update content displayed in {@linkcode ModifierBar}
- * @param {PersistentModifier[]} modifiers - The list of modifiers to be displayed in the {@linkcode ModifierBar}
- * @param {boolean} hideHeldItems - If set to "true", only modifiers not assigned to a Pokémon are displayed
+ * @param modifiers - The list of modifiers to be displayed in the {@linkcode ModifierBar}
+ * @param hideHeldItems - If set to "true", only modifiers not assigned to a Pokémon are displayed
*/
updateModifiers(modifiers: PersistentModifier[], hideHeldItems = false) {
this.removeAll(true);
@@ -345,9 +345,6 @@ export class AddVoucherModifier extends ConsumableModifier {
* modifier will be removed. If a modifier of the same type is to be added, it
* will reset {@linkcode battleCount} back to {@linkcode maxBattles} of the
* existing modifier instead of adding that modifier directly.
- * @extends PersistentModifier
- * @abstract
- * @see {@linkcode add}
*/
export abstract class LapsingPersistentModifier extends PersistentModifier {
/** The maximum amount of battles the modifier will exist for */
@@ -458,8 +455,6 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
/**
* Modifier used for passive items, specifically lures, that
* temporarily increases the chance of a double battle.
- * @extends LapsingPersistentModifier
- * @see {@linkcode apply}
*/
export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier {
public declare type: DoubleBattleChanceBoosterModifierType;
@@ -495,8 +490,6 @@ export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier
* Modifier used for party-wide items, specifically the X items, that
* temporarily increases the stat stage multiplier of the corresponding
* {@linkcode TempBattleStat}.
- * @extends LapsingPersistentModifier
- * @see {@linkcode apply}
*/
export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
/** The stat whose stat stage multiplier will be temporarily increased */
@@ -562,8 +555,6 @@ export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
/**
* Modifier used for party-wide items, namely Dire Hit, that
* temporarily increments the critical-hit stage
- * @extends LapsingPersistentModifier
- * @see {@linkcode apply}
*/
export class TempCritBoosterModifier extends LapsingPersistentModifier {
clone() {
@@ -818,8 +809,6 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi
/**
* Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that
* increase the value of a given {@linkcode PermanentStat}.
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class BaseStatModifier extends PokemonHeldItemModifier {
protected stat: PermanentStat;
@@ -1126,8 +1115,6 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items that Applies {@linkcode Stat} boost(s)
* using a multiplier.
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class StatBoosterModifier extends PokemonHeldItemModifier {
/** The stats that the held item boosts */
@@ -1194,8 +1181,6 @@ export class StatBoosterModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items, specifically Eviolite, that apply
* {@linkcode Stat} boost(s) using a multiplier if the holder can evolve.
- * @extends StatBoosterModifier
- * @see {@linkcode apply}
*/
export class EvolutionStatBoosterModifier extends StatBoosterModifier {
matchType(modifier: Modifier): boolean {
@@ -1216,13 +1201,16 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier {
/**
* Boosts the incoming stat value by a {@linkcode EvolutionStatBoosterModifier.multiplier} if the holder
- * can evolve. Note that, if the holder is a fusion, they will receive
+ * can evolve
+ *
+ * @remarks
+ * Note that, if the holder is a fusion, they will receive
* only half of the boost if either of the fused members are fully
* evolved. However, if they are both unevolved, the full boost
* will apply.
- * @param pokemon {@linkcode Pokemon} that holds the item
- * @param _stat {@linkcode Stat} The {@linkcode Stat} to be boosted
- * @param statValue{@linkcode NumberHolder} that holds the resulting value of the stat
+ * @param pokemon - The `Pokemon` holding the item
+ * @param _stat - The `Stat` to be boosted
+ * @param statValue - Holds the resulting value of the stat
* @returns `true` if the stat boost applies successfully, false otherwise
* @see shouldApply
*/
@@ -1246,8 +1234,6 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier {
/**
* Modifier used for held items that Applies {@linkcode Stat} boost(s) using a
* multiplier if the holder is of a specific {@linkcode SpeciesId}.
- * @extends StatBoosterModifier
- * @see {@linkcode apply}
*/
export class SpeciesStatBoosterModifier extends StatBoosterModifier {
/** The species that the held item's stat boost(s) apply to */
@@ -1302,9 +1288,9 @@ export class SpeciesStatBoosterModifier extends StatBoosterModifier {
*/
override shouldApply(pokemon: Pokemon, stat: Stat, statValue: NumberHolder): boolean {
return (
- super.shouldApply(pokemon, stat, statValue) &&
- (this.species.includes(pokemon.getSpeciesForm(true).speciesId) ||
- (pokemon.isFusion() && this.species.includes(pokemon.getFusionSpeciesForm(true).speciesId)))
+ super.shouldApply(pokemon, stat, statValue)
+ && (this.species.includes(pokemon.getSpeciesForm(true).speciesId)
+ || (pokemon.isFusion() && this.species.includes(pokemon.getFusionSpeciesForm(true).speciesId)))
);
}
@@ -1321,8 +1307,6 @@ export class SpeciesStatBoosterModifier extends StatBoosterModifier {
/**
* Modifier used for held items that apply critical-hit stage boost(s).
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class CritBoosterModifier extends PokemonHeldItemModifier {
/** The amount of stages by which the held item increases the current critical-hit stage value */
@@ -1369,8 +1353,6 @@ export class CritBoosterModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items that apply critical-hit stage boost(s)
* if the holder is of a specific {@linkcode SpeciesId}.
- * @extends CritBoosterModifier
- * @see {@linkcode shouldApply}
*/
export class SpeciesCritBoosterModifier extends CritBoosterModifier {
/** The species that the held item's critical-hit stage boost applies to */
@@ -1415,9 +1397,9 @@ export class SpeciesCritBoosterModifier extends CritBoosterModifier {
*/
override shouldApply(pokemon: Pokemon, critStage: NumberHolder): boolean {
return (
- super.shouldApply(pokemon, critStage) &&
- (this.species.includes(pokemon.getSpeciesForm(true).speciesId) ||
- (pokemon.isFusion() && this.species.includes(pokemon.getFusionSpeciesForm(true).speciesId)))
+ super.shouldApply(pokemon, critStage)
+ && (this.species.includes(pokemon.getSpeciesForm(true).speciesId)
+ || (pokemon.isFusion() && this.species.includes(pokemon.getFusionSpeciesForm(true).speciesId)))
);
}
}
@@ -1440,8 +1422,8 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
if (modifier instanceof AttackTypeBoosterModifier) {
const attackTypeBoosterModifier = modifier as AttackTypeBoosterModifier;
return (
- attackTypeBoosterModifier.moveType === this.moveType &&
- attackTypeBoosterModifier.boostMultiplier === this.boostMultiplier
+ attackTypeBoosterModifier.moveType === this.moveType
+ && attackTypeBoosterModifier.boostMultiplier === this.boostMultiplier
);
}
@@ -1471,10 +1453,10 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
*/
override shouldApply(pokemon?: Pokemon, moveType?: PokemonType, movePower?: NumberHolder): boolean {
return (
- super.shouldApply(pokemon, moveType, movePower) &&
- typeof moveType === "number" &&
- movePower instanceof NumberHolder &&
- this.moveType === moveType
+ super.shouldApply(pokemon, moveType, movePower)
+ && typeof moveType === "number"
+ && movePower instanceof NumberHolder
+ && this.moveType === moveType
);
}
@@ -1694,8 +1676,6 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a
* set {@linkcode StatusEffect} at the end of a turn.
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class TurnStatusEffectModifier extends PokemonHeldItemModifier {
/** The status effect to be applied by the held item */
@@ -1721,7 +1701,7 @@ export class TurnStatusEffectModifier extends PokemonHeldItemModifier {
* would be the only item able to {@linkcode apply} successfully.
* @override
* @param modifier {@linkcode Modifier} being type tested
- * @return `true` if {@linkcode modifier} is an instance of
+ * @returns `true` if {@linkcode modifier} is an instance of
* TurnStatusEffectModifier, false otherwise
*/
matchType(modifier: Modifier): boolean {
@@ -1966,8 +1946,6 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items, namely White Herb, that restore adverse stat
* stages in battle.
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier {
matchType(modifier: Modifier) {
@@ -2013,8 +1991,6 @@ export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier {
/**
* Modifier used for held items, namely Mystical Rock, that extend the
* duration of weather and terrain effects.
- * @extends PokemonHeldItemModifier
- * @see {@linkcode apply}
*/
export class FieldEffectModifier extends PokemonHeldItemModifier {
/**
@@ -2090,8 +2066,8 @@ export class TerastallizeModifier extends ConsumablePokemonModifier {
*/
override shouldApply(playerPokemon?: PlayerPokemon): boolean {
return (
- super.shouldApply(playerPokemon) &&
- [playerPokemon?.species.speciesId, playerPokemon?.fusionSpecies?.speciesId].filter(
+ super.shouldApply(playerPokemon)
+ && [playerPokemon?.species.speciesId, playerPokemon?.fusionSpecies?.speciesId].filter(
s => s === SpeciesId.TERAPAGOS || s === SpeciesId.OGERPON || s === SpeciesId.SHEDINJA,
).length === 0
);
@@ -2138,8 +2114,8 @@ export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
*/
override shouldApply(playerPokemon?: PlayerPokemon, multiplier?: number): boolean {
return (
- super.shouldApply(playerPokemon) &&
- (this.fainted || (!isNullOrUndefined(multiplier) && typeof multiplier === "number"))
+ super.shouldApply(playerPokemon)
+ && (this.fainted || (!isNullOrUndefined(multiplier) && typeof multiplier === "number"))
);
}
@@ -2159,8 +2135,11 @@ export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
pokemon.resetStatus(true, true, false, false);
}
pokemon.hp = Math.min(
- pokemon.hp +
- Math.max(Math.ceil(Math.max(Math.floor(this.restorePercent * 0.01 * pokemon.getMaxHp()), restorePoints)), 1),
+ pokemon.hp
+ + Math.max(
+ Math.ceil(Math.max(Math.floor(this.restorePercent * 0.01 * pokemon.getMaxHp()), restorePoints)),
+ 1,
+ ),
pokemon.getMaxHp(),
);
return true;
@@ -3210,7 +3189,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
override apply(pokemon: Pokemon, target?: Pokemon, ..._args: unknown[]): boolean {
const opponents = this.getTargets(pokemon, target);
- if (!opponents.length) {
+ if (opponents.length === 0) {
return false;
}
@@ -3228,7 +3207,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
) as PokemonHeldItemModifier[];
for (let i = 0; i < transferredItemCount; i++) {
- if (!itemModifiers.length) {
+ if (itemModifiers.length === 0) {
break;
}
const randItemIndex = pokemon.randBattleSeedInt(itemModifiers.length);
@@ -3243,7 +3222,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
globalScene.phaseManager.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
}
- return !!transferredModifierTypes.length;
+ return transferredModifierTypes.length > 0;
}
abstract getTransferredItemCount(): number;
@@ -3397,8 +3376,6 @@ export class ExtraModifierModifier extends PersistentModifier {
/**
* Modifier used for timed boosts to the player's shop item rewards.
- * @extends LapsingPersistentModifier
- * @see {@linkcode apply}
*/
export class TempExtraModifierModifier extends LapsingPersistentModifier {
/**
diff --git a/src/phase-manager.ts b/src/phase-manager.ts
index aa01a0ffc10..4bb7e0a4b37 100644
--- a/src/phase-manager.ts
+++ b/src/phase-manager.ts
@@ -1,3 +1,4 @@
+import { PHASE_START_COLOR } from "#app/constants/colors";
import { globalScene } from "#app/global-scene";
import type { Phase } from "#app/phase";
import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#data/phase-priority-queue";
@@ -102,10 +103,13 @@ import { WeatherEffectPhase } from "#phases/weather-effect-phase";
import type { PhaseMap, PhaseString } from "#types/phase-types";
import { type Constructor, coerceArray } from "#utils/common";
-/*
+/**
+ * @module
* Manager for phases used by battle scene.
*
- * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.*
+ * @remarks
+ * **This file must not be imported or used directly.**
+ * The manager is exclusively used by the Battle Scene and is NOT intended for external use.
*/
/**
@@ -236,7 +240,7 @@ export class PhaseManager {
/** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */
private dynamicPhaseTypes: Constructor[];
- private currentPhase: Phase | null = null;
+ private currentPhase: Phase;
private standbyPhase: Phase | null = null;
constructor() {
@@ -260,7 +264,9 @@ export class PhaseManager {
}
/* Phase Functions */
- getCurrentPhase(): Phase | null {
+
+ /** @returns The currently running {@linkcode Phase}. */
+ getCurrentPhase(): Phase {
return this.currentPhase;
}
@@ -355,44 +361,43 @@ export class PhaseManager {
if (this.phaseQueuePrependSpliceIndex > -1) {
this.clearPhaseQueueSplice();
}
- if (this.phaseQueuePrepend.length) {
- while (this.phaseQueuePrepend.length) {
- const poppedPhase = this.phaseQueuePrepend.pop();
- if (poppedPhase) {
- this.phaseQueue.unshift(poppedPhase);
- }
+ this.phaseQueue.unshift(...this.phaseQueuePrepend);
+ this.phaseQueuePrepend.splice(0);
+
+ const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
+ // Check if there are any conditional phases queued
+ for (const [condition, phase] of this.conditionalQueue) {
+ // Evaluate the condition associated with the phase
+ if (condition()) {
+ // If the condition is met, add the phase to the phase queue
+ this.pushPhase(phase);
+ } else {
+ // If the condition is not met, re-add the phase back to the end of the conditional queue
+ unactivatedConditionalPhases.push([condition, phase]);
}
}
- if (!this.phaseQueue.length) {
+
+ this.conditionalQueue = unactivatedConditionalPhases;
+
+ // If no phases are left, unshift phases to start a new turn.
+ if (this.phaseQueue.length === 0) {
this.populatePhaseQueue();
// Clear the conditionalQueue if there are no phases left in the phaseQueue
this.conditionalQueue = [];
}
- this.currentPhase = this.phaseQueue.shift() ?? null;
+ // Bang is justified as `populatePhaseQueue` ensures we always have _something_ in the queue at all times
+ this.currentPhase = this.phaseQueue.shift()!;
- const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
- // Check if there are any conditional phases queued
- while (this.conditionalQueue?.length) {
- // Retrieve the first conditional phase from the queue
- const conditionalPhase = this.conditionalQueue.shift();
- // Evaluate the condition associated with the phase
- if (conditionalPhase?.[0]()) {
- // If the condition is met, add the phase to the phase queue
- this.pushPhase(conditionalPhase[1]);
- } else if (conditionalPhase) {
- // If the condition is not met, re-add the phase back to the front of the conditional queue
- unactivatedConditionalPhases.push(conditionalPhase);
- } else {
- console.warn("condition phase is undefined/null!", conditionalPhase);
- }
- }
- this.conditionalQueue.push(...unactivatedConditionalPhases);
+ this.startCurrentPhase();
+ }
- if (this.currentPhase) {
- console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
- this.currentPhase.start();
- }
+ /**
+ * Helper method to start and log the current phase.
+ */
+ private startCurrentPhase(): void {
+ console.log(`%cStart Phase ${this.currentPhase.phaseName}`, `color:${PHASE_START_COLOR};`);
+ this.currentPhase.start();
}
overridePhase(phase: Phase): boolean {
@@ -402,8 +407,7 @@ export class PhaseManager {
this.standbyPhase = this.currentPhase;
this.currentPhase = phase;
- console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
- phase.start();
+ this.startCurrentPhase();
return true;
}
@@ -520,6 +524,25 @@ export class PhaseManager {
this.dynamicPhaseQueues[type].push(phase);
}
+ /**
+ * Attempt to remove one or more Phases from the given DynamicPhaseQueue, removing the equivalent amount of {@linkcode ActivatePriorityQueuePhase}s from the queue.
+ * @param type - The {@linkcode DynamicPhaseType} to check
+ * @param phaseFilter - The function to select phases for removal
+ * @param removeCount - The maximum number of phases to remove, or `all` to remove all matching phases;
+ * default `1`
+ * @todo Remove this eventually once the patchwork bug this is used for is fixed
+ */
+ public tryRemoveDynamicPhase(
+ type: DynamicPhaseType,
+ phaseFilter: (phase: Phase) => boolean,
+ removeCount: number | "all" = 1,
+ ): void {
+ const numRemoved = this.dynamicPhaseQueues[type].tryRemovePhase(phaseFilter, removeCount);
+ for (let x = 0; x < numRemoved; x++) {
+ this.tryRemovePhase(p => p.is("ActivatePriorityQueuePhase"));
+ }
+ }
+
/**
* Unshifts the top phase from the corresponding dynamic queue onto {@linkcode phaseQueue}
* @param type {@linkcode DynamicPhaseType} The type of dynamic phase to start
@@ -600,7 +623,7 @@ export class PhaseManager {
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
*/
private populatePhaseQueue(): void {
- if (this.nextCommandPhaseQueue.length) {
+ if (this.nextCommandPhaseQueue.length > 0) {
this.phaseQueue.push(...this.nextCommandPhaseQueue);
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
}
diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts
index 699caa2af21..81f85850e88 100644
--- a/src/phases/attempt-capture-phase.ts
+++ b/src/phases/attempt-capture-phase.ts
@@ -21,9 +21,9 @@ import type { EnemyPokemon } from "#field/pokemon";
import { PokemonHeldItemModifier } from "#modifiers/modifier";
import { PokemonPhase } from "#phases/pokemon-phase";
import { achvs } from "#system/achv";
-import type { PartyOption } from "#ui/party-ui-handler";
-import { PartyUiMode } from "#ui/party-ui-handler";
-import { SummaryUiMode } from "#ui/summary-ui-handler";
+import type { PartyOption } from "#ui/handlers/party-ui-handler";
+import { PartyUiMode } from "#ui/handlers/party-ui-handler";
+import { SummaryUiMode } from "#ui/handlers/summary-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder } from "#utils/common";
import i18next from "i18next";
@@ -136,10 +136,10 @@ export class AttemptCapturePhase extends PokemonPhase {
} else if (shakeCount++ < (isCritical ? 1 : 3)) {
// Shake check (skip check for critical or guaranteed captures, but still play the sound)
if (
- pokeballMultiplier === -1 ||
- isCritical ||
- modifiedCatchRate >= 255 ||
- pokemon.randBattleSeedInt(65536) < shakeProbability
+ pokeballMultiplier === -1
+ || isCritical
+ || modifiedCatchRate >= 255
+ || pokemon.randBattleSeedInt(65536) < shakeProbability
) {
globalScene.playSound("se/pb_move");
} else {
@@ -231,8 +231,9 @@ export class AttemptCapturePhase extends PokemonPhase {
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (
- speciesForm.abilityHidden &&
- (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
+ speciesForm.abilityHidden
+ && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex)
+ === speciesForm.getAbilityCount() - 1
) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
}
diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts
index 2dbb74c4a85..8a798d67554 100644
--- a/src/phases/battle-end-phase.ts
+++ b/src/phases/battle-end-phase.ts
@@ -38,8 +38,8 @@ export class BattleEndPhase extends BattlePhase {
globalScene.gameData.gameStats.battles++;
if (
- globalScene.gameMode.isEndless &&
- globalScene.currentBattle.waveIndex + 1 > globalScene.gameData.gameStats.highestEndlessWave
+ globalScene.gameMode.isEndless
+ && globalScene.currentBattle.waveIndex + 1 > globalScene.gameData.gameStats.highestEndlessWave
) {
globalScene.gameData.gameStats.highestEndlessWave = globalScene.currentBattle.waveIndex + 1;
}
diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts
index f4e8ee56c55..504bb6eb4bd 100644
--- a/src/phases/check-switch-phase.ts
+++ b/src/phases/check-switch-phase.ts
@@ -39,19 +39,19 @@ export class CheckSwitchPhase extends BattlePhase {
// ...if there are no other allowed Pokemon in the player's party to switch with
if (
- !globalScene
+ globalScene
.getPlayerParty()
.slice(1)
- .filter(p => p.isActive()).length
+ .filter(p => p.isActive()).length === 0
) {
return super.end();
}
// ...or if any player Pokemon has an effect that prevents the checked Pokemon from switching
if (
- pokemon.getTag(BattlerTagType.FRENZY) ||
- pokemon.isTrapped() ||
- globalScene.getPlayerField().some(p => p.getTag(BattlerTagType.COMMANDED))
+ pokemon.getTag(BattlerTagType.FRENZY)
+ || pokemon.isTrapped()
+ || globalScene.getPlayerField().some(p => p.getTag(BattlerTagType.COMMANDED))
) {
return super.end();
}
diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts
index 6ca0679af4d..b9867e22522 100644
--- a/src/phases/command-phase.ts
+++ b/src/phases/command-phase.ts
@@ -62,8 +62,8 @@ export class CommandPhase extends FieldPhase {
return;
}
if (
- (turn === 1 && (!commandCursorMemory || cursorResetEvent)) ||
- commandUiHandler.getCursor() === Command.POKEMON
+ (turn === 1 && (!commandCursorMemory || cursorResetEvent))
+ || commandUiHandler.getCursor() === Command.POKEMON
) {
commandUiHandler.setCursor(Command.FIGHT);
}
@@ -98,8 +98,8 @@ export class CommandPhase extends FieldPhase {
private checkCommander(): void {
// If the Pokemon has applied Commander's effects to its ally, skip this command
if (
- globalScene.currentBattle?.double &&
- this.getPokemon().getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === this.getPokemon()
+ globalScene.currentBattle?.double
+ && this.getPokemon().getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === this.getPokemon()
) {
globalScene.currentBattle.turnCommands[this.fieldIndex] = {
command: Command.FIGHT,
@@ -125,9 +125,9 @@ export class CommandPhase extends FieldPhase {
for (const queuedMove of moveQueue) {
const movesetQueuedMove = moveset.find(m => m.moveId === queuedMove.move);
if (
- queuedMove.move !== MoveId.NONE &&
- !isVirtual(queuedMove.useMode) &&
- !movesetQueuedMove?.isUsable(playerPokemon, isIgnorePP(queuedMove.useMode))
+ queuedMove.move !== MoveId.NONE
+ && !isVirtual(queuedMove.useMode)
+ && !movesetQueuedMove?.isUsable(playerPokemon, isIgnorePP(queuedMove.useMode))
) {
entriesToDelete++;
} else {
@@ -194,8 +194,8 @@ export class CommandPhase extends FieldPhase {
}
if (
- globalScene.currentBattle.isBattleMysteryEncounter() &&
- globalScene.currentBattle.mysteryEncounter?.skipToFightInput
+ globalScene.currentBattle.isBattleMysteryEncounter()
+ && globalScene.currentBattle.mysteryEncounter?.skipToFightInput
) {
globalScene.ui.clearText();
globalScene.ui.setMode(UiMode.FIGHT, this.fieldIndex);
@@ -233,7 +233,7 @@ export class CommandPhase extends FieldPhase {
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
globalScene.ui.showText(
- i18next.t(cannotSelectKey, { moveName: moveName }),
+ i18next.t(cannotSelectKey, { moveName }),
null,
() => {
globalScene.ui.clearText();
@@ -335,9 +335,9 @@ export class CommandPhase extends FieldPhase {
if (turnCommand.move && (moveTargets.targets.length <= 1 || moveTargets.multiple)) {
turnCommand.move.targets = moveTargets.targets;
} else if (
- turnCommand.move &&
- playerPokemon.getTag(BattlerTagType.CHARGING) &&
- playerPokemon.getMoveQueue().length >= 1
+ turnCommand.move
+ && playerPokemon.getTag(BattlerTagType.CHARGING)
+ && playerPokemon.getMoveQueue().length > 0
) {
turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets;
} else {
@@ -402,17 +402,17 @@ export class CommandPhase extends FieldPhase {
if (biomeType === BiomeId.END && battleType === BattleType.WILD) {
if (
- (isClassic && !isClassicFinalBoss && someUncaughtSpeciesOnField) ||
- (isFullFreshStart && !isClassicFinalBoss) ||
- (isEndless && !isEndlessMinorBoss)
+ (isClassic && !isClassicFinalBoss && someUncaughtSpeciesOnField)
+ || (isFullFreshStart && !isClassicFinalBoss)
+ || (isEndless && !isEndlessMinorBoss)
) {
// Uncatchable paradox mons in classic and endless
this.queueShowText("battle:noPokeballForce");
} else if (
- (isClassic && isClassicFinalBoss && missingMultipleStarters) ||
- (isFullFreshStart && isClassicFinalBoss) ||
- (isEndless && isEndlessMinorBoss) ||
- isDaily
+ (isClassic && isClassicFinalBoss && missingMultipleStarters)
+ || (isFullFreshStart && isClassicFinalBoss)
+ || (isEndless && isEndlessMinorBoss)
+ || isDaily
) {
// Uncatchable final boss in classic, endless and daily
this.queueShowText("battle:noPokeballForceFinalBoss");
@@ -456,18 +456,17 @@ export class CommandPhase extends FieldPhase {
const numBallTypes = 5;
if (cursor < numBallTypes) {
- const targetPokemon = globalScene.getEnemyPokemon();
+ const targetPokemon = globalScene.getEnemyPokemon(false);
if (
- targetPokemon?.isBoss() &&
- targetPokemon?.bossSegmentIndex >= 1 &&
- // TODO: Decouple this hardcoded exception for wonder guard and just check the target...
- !targetPokemon?.hasAbility(AbilityId.WONDER_GUARD, false, true)
+ targetPokemon?.isBoss()
+ && targetPokemon?.bossSegmentIndex >= 1 // TODO: Decouple this hardcoded exception for wonder guard and just check the target...
+ && !targetPokemon?.hasAbility(AbilityId.WONDER_GUARD, false, true)
) {
// When facing the final boss, it must be weakened unless a Master Ball is used AND no challenges are active.
// The message is customized for the final boss.
if (
- isFinalBoss &&
- (cursor < PokeballType.MASTER_BALL || (cursor === PokeballType.MASTER_BALL && isChallengeActive))
+ isFinalBoss
+ && (cursor < PokeballType.MASTER_BALL || (cursor === PokeballType.MASTER_BALL && isChallengeActive))
) {
this.queueShowText("battle:noPokeballForceFinalBossCatchable");
return false;
@@ -481,7 +480,7 @@ export class CommandPhase extends FieldPhase {
globalScene.currentBattle.turnCommands[this.fieldIndex] = {
command: Command.BALL,
- cursor: cursor,
+ cursor,
};
globalScene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets;
if (this.fieldIndex) {
@@ -593,8 +592,8 @@ export class CommandPhase extends FieldPhase {
return false;
}
if (
- currentBattle.battleType === BattleType.TRAINER ||
- currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
+ currentBattle.battleType === BattleType.TRAINER
+ || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) {
this.queueShowText("battle:noEscapeTrainer");
return false;
@@ -641,9 +640,8 @@ export class CommandPhase extends FieldPhase {
* @returns Whether the command was successful
*/
handleCommand(command: Command.FIGHT | Command.TERA, cursor: number, useMode?: MoveUseMode, move?: TurnMove): boolean;
- handleCommand(command: Command.BALL, cursor: number): boolean;
handleCommand(command: Command.POKEMON, cursor: number, useBaton: boolean): boolean;
- handleCommand(command: Command.RUN, cursor: number): boolean;
+ handleCommand(command: Command.BALL | Command.RUN, cursor: number): boolean;
handleCommand(command: Command, cursor: number, useMode?: boolean | MoveUseMode, move?: TurnMove): boolean;
public handleCommand(
diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts
index 78da1dcfa2f..c5359fac0f3 100644
--- a/src/phases/common-anim-phase.ts
+++ b/src/phases/common-anim-phase.ts
@@ -10,19 +10,12 @@ export class CommonAnimPhase extends PokemonPhase {
public readonly phaseName: "CommonAnimPhase" | "PokemonHealPhase" | "WeatherEffectPhase" = "CommonAnimPhase";
private anim: CommonAnim | null;
private targetIndex?: BattlerIndex;
- private playOnEmptyField: boolean;
- constructor(
- battlerIndex?: BattlerIndex,
- targetIndex?: BattlerIndex,
- anim: CommonAnim | null = null,
- playOnEmptyField = false,
- ) {
+ constructor(battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex, anim: CommonAnim | null = null) {
super(battlerIndex);
this.anim = anim;
this.targetIndex = targetIndex;
- this.playOnEmptyField = playOnEmptyField;
}
setAnimation(anim: CommonAnim) {
diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts
index e9d28e0fe2a..bfd66bd02e4 100644
--- a/src/phases/egg-hatch-phase.ts
+++ b/src/phases/egg-hatch-phase.ts
@@ -9,9 +9,9 @@ import { doShinySparkleAnim } from "#field/anims";
import type { PlayerPokemon } from "#field/pokemon";
import type { EggLapsePhase } from "#phases/egg-lapse-phase";
import { achvs } from "#system/achv";
-import { EggCounterContainer } from "#ui/egg-counter-container";
-import type { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler";
-import { PokemonInfoContainer } from "#ui/pokemon-info-container";
+import { EggCounterContainer } from "#ui/containers/egg-counter-container";
+import { PokemonInfoContainer } from "#ui/containers/pokemon-info-container";
+import type { EggHatchSceneUiHandler } from "#ui/handlers/egg-hatch-scene-ui-handler";
import { fixedInt, getFrameMs, randInt } from "#utils/common";
import i18next from "i18next";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
@@ -32,7 +32,7 @@ export class EggHatchPhase extends Phase {
private eggCounterContainer: EggCounterContainer;
/** The scene handler for egg hatching */
- private eggHatchHandler: EggHatchSceneHandler;
+ private eggHatchHandler: EggHatchSceneUiHandler;
/** The phaser gameobject container that holds everything */
private eggHatchContainer: Phaser.GameObjects.Container;
/** The phaser image that is the background */
@@ -64,7 +64,7 @@ export class EggHatchPhase extends Phase {
private canSkip: boolean;
private skipped: boolean;
/** The sound effect being played when the egg is hatched */
- private evolutionBgm: AnySound;
+ private evolutionBgm: AnySound | null;
private eggLapsePhase: EggLapsePhase;
constructor(hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: number) {
@@ -92,7 +92,7 @@ export class EggHatchPhase extends Phase {
globalScene.fadeOutBgm(undefined, false);
- this.eggHatchHandler = globalScene.ui.getHandler() as EggHatchSceneHandler;
+ this.eggHatchHandler = globalScene.ui.getHandler() as EggHatchSceneUiHandler;
this.eggHatchContainer = this.eggHatchHandler.eggHatchContainer;
@@ -230,6 +230,7 @@ export class EggHatchPhase extends Phase {
} else {
globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true));
}
+ this.pokemon?.destroy();
super.end();
}
diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts
index c236c5c3abc..d771c8599b4 100644
--- a/src/phases/egg-summary-phase.ts
+++ b/src/phases/egg-summary-phase.ts
@@ -39,6 +39,10 @@ export class EggSummaryPhase extends Phase {
}
end() {
+ this.eggHatchData.forEach(data => {
+ data.pokemon?.destroy();
+ });
+ this.eggHatchData = [];
globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true));
globalScene.ui.setModeForceTransition(UiMode.MESSAGE).then(() => {
super.end();
diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts
index b870f7f6e7a..0918ced65e5 100644
--- a/src/phases/encounter-phase.ts
+++ b/src/phases/encounter-phase.ts
@@ -111,10 +111,10 @@ export class EncounterPhase extends BattlePhase {
let enemySpecies = globalScene.randomSpecies(battle.waveIndex, level, true);
// If player has golden bug net, rolls 10% chance to replace non-boss wave wild species from the golden bug net bug pool
if (
- globalScene.findModifier(m => m instanceof BoostBugSpawnModifier) &&
- !globalScene.gameMode.isBoss(battle.waveIndex) &&
- globalScene.arena.biomeType !== BiomeId.END &&
- randSeedInt(10) === 0
+ globalScene.findModifier(m => m instanceof BoostBugSpawnModifier)
+ && !globalScene.gameMode.isBoss(battle.waveIndex)
+ && globalScene.arena.biomeType !== BiomeId.END
+ && randSeedInt(10) === 0
) {
enemySpecies = getGoldenBugNetSpecies(level);
}
@@ -146,15 +146,15 @@ export class EncounterPhase extends BattlePhase {
globalScene.gameData.setPokemonSeen(
enemyPokemon,
true,
- battle.battleType === BattleType.TRAINER ||
- battle?.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE,
+ battle.battleType === BattleType.TRAINER
+ || battle?.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE,
);
}
if (enemyPokemon.species.speciesId === SpeciesId.ETERNATUS) {
if (
- globalScene.gameMode.isClassic &&
- (battle.battleSpec === BattleSpec.FINAL_BOSS || globalScene.gameMode.isWaveFinal(battle.waveIndex))
+ globalScene.gameMode.isClassic
+ && (battle.battleSpec === BattleSpec.FINAL_BOSS || globalScene.gameMode.isWaveFinal(battle.waveIndex))
) {
if (battle.battleSpec !== BattleSpec.FINAL_BOSS) {
enemyPokemon.formIndex = 1;
@@ -320,8 +320,8 @@ export class EncounterPhase extends BattlePhase {
const { battleType, waveIndex } = globalScene.currentBattle;
if (
- globalScene.isMysteryEncounterValidForWave(battleType, waveIndex) &&
- !globalScene.currentBattle.isBattleMysteryEncounter()
+ globalScene.isMysteryEncounterValidForWave(battleType, waveIndex)
+ && !globalScene.currentBattle.isBattleMysteryEncounter()
) {
// Increment ME spawn chance if an ME could have spawned but did not
// Only do this AFTER session has been saved to avoid duplicating increments
@@ -442,28 +442,27 @@ export class EncounterPhase extends BattlePhase {
}
};
- const encounterMessages = globalScene.currentBattle.trainer?.getEncounterMessages();
+ const encounterMessages = trainer?.getEncounterMessages() ?? [];
- if (!encounterMessages?.length) {
+ if (encounterMessages.length === 0) {
doSummon();
} else {
- let message: string;
+ let message = "";
globalScene.executeWithSeedOffset(
() => (message = randSeedItem(encounterMessages)),
globalScene.currentBattle.waveIndex,
);
- message = message!; // tell TS compiler it's defined now
const showDialogueAndSummon = () => {
globalScene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => {
globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doSummon()));
});
};
- if (globalScene.currentBattle.trainer?.config.hasCharSprite && !globalScene.ui.shouldSkipDialogue(message)) {
+ if (trainer?.config.hasCharSprite && !globalScene.ui.shouldSkipDialogue(message)) {
globalScene
.showFieldOverlay(500)
.then(() =>
globalScene.charSprite
- .showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0]))
+ .showCharacter(trainer.getKey()!, getCharVariantFromDialogue(encounterMessages[0]))
.then(() => showDialogueAndSummon()),
); // TODO: is this bang correct?
} else {
@@ -549,9 +548,9 @@ export class EncounterPhase extends BattlePhase {
}
/** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */
if (
- enemyPokemon.species.speciesId === SpeciesId.ETERNATUS &&
- (globalScene.gameMode.isBattleClassicFinalBoss(globalScene.currentBattle.waveIndex) ||
- globalScene.gameMode.isEndlessMajorBoss(globalScene.currentBattle.waveIndex))
+ enemyPokemon.species.speciesId === SpeciesId.ETERNATUS
+ && (globalScene.gameMode.isBattleClassicFinalBoss(globalScene.currentBattle.waveIndex)
+ || globalScene.gameMode.isEndlessMajorBoss(globalScene.currentBattle.waveIndex))
) {
const enemyMBH = globalScene.findModifier(
m => m instanceof TurnHeldItemTransferModifier,
@@ -571,7 +570,7 @@ export class EncounterPhase extends BattlePhase {
globalScene.phaseManager.create("PostSummonPhase", p.getBattlerIndex()),
() => {
// if there is not a player party, we can't continue
- if (!globalScene.getPlayerParty().length) {
+ if (globalScene.getPlayerParty().length === 0) {
return false;
}
// how many player pokemon are on the field ?
@@ -617,8 +616,8 @@ export class EncounterPhase extends BattlePhase {
}
if (
- globalScene.currentBattle.battleType !== BattleType.TRAINER &&
- (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily)
+ globalScene.currentBattle.battleType !== BattleType.TRAINER
+ && (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily)
) {
const minPartySize = globalScene.currentBattle.double ? 2 : 1;
if (availablePartyMembers.length > minPartySize) {
@@ -651,7 +650,7 @@ export class EncounterPhase extends BattlePhase {
const ordinalUsed =
!i18next.exists(localizationKey, { fallbackLng: [] }) || i18next.resolvedLanguage === "en"
? i18next.t("battleSpecDialogue:key", {
- count: count,
+ count,
ordinal: true,
})
: "";
@@ -660,7 +659,7 @@ export class EncounterPhase extends BattlePhase {
const genderStr = PlayerGender[genderIndex].toLowerCase();
const encounterDialogue = i18next.t(localizationKey, {
context: genderStr,
- cycleCount: cycleCount,
+ cycleCount,
});
if (!globalScene.gameData.getSeenDialogues()[localizationKey]) {
globalScene.gameData.saveSeenDialogue(localizationKey);
diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts
index 7e4dff37b0f..1214ce67937 100644
--- a/src/phases/enemy-command-phase.ts
+++ b/src/phases/enemy-command-phase.ts
@@ -38,9 +38,9 @@ export class EnemyCommandPhase extends FieldPhase {
const trainer = battle.trainer;
if (
- battle.double &&
- enemyPokemon.hasAbility(AbilityId.COMMANDER) &&
- enemyPokemon.getAlly()?.getTag(BattlerTagType.COMMANDED)
+ battle.double
+ && enemyPokemon.hasAbility(AbilityId.COMMANDER)
+ && enemyPokemon.getAlly()?.getTag(BattlerTagType.COMMANDED)
) {
this.skipTurn = true;
}
@@ -54,13 +54,13 @@ export class EnemyCommandPhase extends FieldPhase {
* member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers),
* the enemy will switch to that Pokemon.
*/
- if (trainer && !enemyPokemon.getMoveQueue().length) {
+ if (trainer && enemyPokemon.getMoveQueue().length === 0) {
const opponents = enemyPokemon.getOpponents();
if (!enemyPokemon.isTrapped()) {
const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true);
- if (partyMemberScores.length) {
+ if (partyMemberScores.length > 0) {
const matchupScores = opponents.map(opp => enemyPokemon.getMatchupScore(opp));
const matchupScore = matchupScores.reduce((total, score) => (total += score), 0) / matchupScores.length;
diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts
index ad3db97d520..5943ed730ff 100644
--- a/src/phases/evolution-phase.ts
+++ b/src/phases/evolution-phase.ts
@@ -10,7 +10,7 @@ import { LearnMoveSituation } from "#enums/learn-move-situation";
import { UiMode } from "#enums/ui-mode";
import { cos, sin } from "#field/anims";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
-import type { EvolutionSceneHandler } from "#ui/evolution-scene-handler";
+import type { EvolutionSceneUiHandler } from "#ui/handlers/evolution-scene-ui-handler";
import { fixedInt, getFrameMs, randInt } from "#utils/common";
import i18next from "i18next";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
@@ -28,9 +28,10 @@ export class EvolutionPhase extends Phase {
private evolution: SpeciesFormEvolution | null;
private fusionSpeciesEvolved: boolean; // Whether the evolution is of the fused species
- private evolutionBgm: AnySound;
- private evolutionHandler: EvolutionSceneHandler;
+ private evolutionBgm: AnySound | null;
+ private evolutionHandler: EvolutionSceneUiHandler;
+ /** Container for all assets used by the scene. When the scene is cleared, the children within this are destroyed. */
protected evolutionContainer: Phaser.GameObjects.Container;
protected evolutionBaseBg: Phaser.GameObjects.Image;
protected evolutionBg: Phaser.GameObjects.Video;
@@ -78,7 +79,7 @@ export class EvolutionPhase extends Phase {
*
*/
private setupEvolutionAssets(): void {
- this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneHandler;
+ this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneUiHandler;
this.evolutionContainer = this.evolutionHandler.evolutionContainer;
this.evolutionBaseBg = globalScene.add.image(0, 0, "default_bg").setOrigin(0);
@@ -297,7 +298,9 @@ export class EvolutionPhase extends Phase {
this.evolutionBg.setVisible(false);
},
});
- SoundFade.fadeOut(globalScene, this.evolutionBgm, 100);
+ if (this.evolutionBgm) {
+ SoundFade.fadeOut(globalScene, this.evolutionBgm, 100);
+ }
}
/**
@@ -377,7 +380,9 @@ export class EvolutionPhase extends Phase {
* Fadeout evolution music, play the cry, show the evolution completed text, and end the phase
*/
private onEvolutionComplete(evolvedPokemon: Pokemon) {
- SoundFade.fadeOut(globalScene, this.evolutionBgm, 100);
+ if (this.evolutionBgm) {
+ SoundFade.fadeOut(globalScene, this.evolutionBgm, 100);
+ }
globalScene.time.delayedCall(250, () => {
this.pokemon.cry();
globalScene.time.delayedCall(1250, () => {
@@ -522,6 +527,7 @@ export class EvolutionPhase extends Phase {
return;
}
if (i === lastCycle) {
+ this.pokemonTintSprite.setVisible(false).setActive(false);
this.pokemonEvoTintSprite.setScale(1);
}
},
diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts
index 2d953043866..349dfcfa8e5 100644
--- a/src/phases/faint-phase.ts
+++ b/src/phases/faint-phase.ts
@@ -89,13 +89,13 @@ export class FaintPhase extends PokemonPhase {
if (pokemon.isPlayer()) {
globalScene.arena.playerFaints += 1;
globalScene.currentBattle.playerFaintsHistory.push({
- pokemon: pokemon,
+ pokemon,
turn: globalScene.currentBattle.turn,
});
} else {
globalScene.currentBattle.enemyFaints += 1;
globalScene.currentBattle.enemyFaintsHistory.push({
- pokemon: pokemon,
+ pokemon,
turn: globalScene.currentBattle.turn,
});
}
@@ -112,10 +112,10 @@ export class FaintPhase extends PokemonPhase {
pokemon.resetTera();
// TODO: this can be simplified by just checking whether lastAttack is defined
- if (pokemon.turnData.attacksReceived?.length) {
+ if (pokemon.turnData.attacksReceived?.length > 0) {
const lastAttack = pokemon.turnData.attacksReceived[0];
applyAbAttrs("PostFaintAbAttr", {
- pokemon: pokemon,
+ pokemon,
// TODO: We should refactor lastAttack's sourceId to forbid null and just use undefined
attacker: globalScene.getPokemonById(lastAttack.sourceId) ?? undefined,
// TODO: improve the way that we provide the move that knocked out the pokemon...
@@ -131,14 +131,14 @@ export class FaintPhase extends PokemonPhase {
for (const p of alivePlayField) {
applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon });
}
- if (pokemon.turnData.attacksReceived?.length) {
+ if (pokemon.turnData.attacksReceived?.length > 0) {
const defeatSource = this.source;
if (defeatSource?.isOnField()) {
applyAbAttrs("PostVictoryAbAttr", { pokemon: defeatSource });
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr");
- if (pvattrs.length) {
+ if (pvattrs.length > 0) {
for (const pvattr of pvattrs) {
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
}
@@ -151,13 +151,13 @@ export class FaintPhase extends PokemonPhase {
const legalPlayerPokemon = globalScene.getPokemonAllowedInBattle();
/** The total number of legal player Pokemon that aren't currently on the field */
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
- if (!legalPlayerPokemon.length) {
+ if (legalPlayerPokemon.length === 0) {
/** If the player doesn't have any legal Pokemon, end the game */
globalScene.phaseManager.unshiftNew("GameOverPhase");
} else if (
- globalScene.currentBattle.double &&
- legalPlayerPokemon.length === 1 &&
- legalPlayerPartyPokemon.length === 0
+ globalScene.currentBattle.double
+ && legalPlayerPokemon.length === 1
+ && legalPlayerPartyPokemon.length === 0
) {
/**
* If the player has exactly one Pokemon in total at this point in a double battle, and that Pokemon
@@ -174,10 +174,11 @@ export class FaintPhase extends PokemonPhase {
} else {
globalScene.phaseManager.unshiftNew("VictoryPhase", this.battlerIndex);
if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) {
- const hasReservePartyMember = !!globalScene
- .getEnemyParty()
- .filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot)
- .length;
+ const hasReservePartyMember =
+ globalScene
+ .getEnemyParty()
+ .filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot)
+ .length > 0;
if (hasReservePartyMember) {
globalScene.phaseManager.pushNew("SwitchSummonPhase", SwitchType.SWITCH, this.fieldIndex, -1, false, false);
}
diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts
index 7b7f5a6dac6..7521adee6c9 100644
--- a/src/phases/form-change-phase.ts
+++ b/src/phases/form-change-phase.ts
@@ -8,7 +8,7 @@ import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { EvolutionPhase } from "#phases/evolution-phase";
import { achvs } from "#system/achv";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
+import type { PartyUiHandler } from "#ui/handlers/party-ui-handler";
import { fixedInt } from "#utils/common";
export class FormChangePhase extends EvolutionPhase {
@@ -76,8 +76,8 @@ export class FormChangePhase extends EvolutionPhase {
globalScene.validateAchv(achvs.MEGA_EVOLVE);
playEvolutionFanfare = true;
} else if (
- this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 ||
- this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1
+ this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1
+ || this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1
) {
globalScene.validateAchv(achvs.GIGANTAMAX);
playEvolutionFanfare = true;
diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts
index 89162e591fc..dd29b97d590 100644
--- a/src/phases/game-over-phase.ts
+++ b/src/phases/game-over-phase.ts
@@ -16,13 +16,13 @@ import type { EndCardPhase } from "#phases/end-card-phase";
import { achvs, ChallengeAchv } from "#system/achv";
import { ArenaData } from "#system/arena-data";
import { ChallengeData } from "#system/challenge-data";
-import type { SessionSaveData } from "#system/game-data";
import { ModifierData as PersistentModifierData } from "#system/modifier-data";
import { PokemonData } from "#system/pokemon-data";
import { RibbonData, type RibbonFlag } from "#system/ribbons/ribbon-data";
import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods";
import { TrainerData } from "#system/trainer-data";
import { trainerConfigs } from "#trainers/trainer-config";
+import type { SessionSaveData } from "#types/save-data";
import { checkSpeciesValidForChallenge, isNuzlockeChallenge } from "#utils/challenge-utils";
import { isLocal, isLocalServerConnected } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
@@ -52,9 +52,9 @@ export class GameOverPhase extends BattlePhase {
// Handle Mystery Encounter special Game Over cases
// Situations such as when player lost a battle, but it isn't treated as full Game Over
if (
- !this.isVictory &&
- globalScene.currentBattle.mysteryEncounter?.onGameOver &&
- !globalScene.currentBattle.mysteryEncounter.onGameOver()
+ !this.isVictory
+ && globalScene.currentBattle.mysteryEncounter?.onGameOver
+ && !globalScene.currentBattle.mysteryEncounter.onGameOver()
) {
// Do not end the game
return this.end();
@@ -90,8 +90,8 @@ export class GameOverPhase extends BattlePhase {
globalScene.phaseManager.pushNew("SummonPhase", 1);
}
if (
- globalScene.currentBattle.waveIndex > 1 &&
- globalScene.currentBattle.battleType !== BattleType.TRAINER
+ globalScene.currentBattle.waveIndex > 1
+ && globalScene.currentBattle.battleType !== BattleType.TRAINER
) {
globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double);
if (globalScene.currentBattle.double && availablePartyMembers > 1) {
@@ -204,7 +204,7 @@ export class GameOverPhase extends BattlePhase {
}
this.getRunHistoryEntry().then(runHistoryEntry => {
globalScene.gameData.saveRunHistory(runHistoryEntry, this.isVictory);
- globalScene.phaseManager.pushNew("PostGameOverPhase", endCardPhase);
+ globalScene.phaseManager.pushNew("PostGameOverPhase", globalScene.sessionSlotId, endCardPhase);
this.end();
});
};
@@ -262,7 +262,7 @@ export class GameOverPhase extends BattlePhase {
.newclear({
slot: globalScene.sessionSlotId,
isVictory: this.isVictory,
- clientSessionId: clientSessionId,
+ clientSessionId,
})
.then(success => doGameOver(!globalScene.gameMode.isDaily || !!success))
.catch(_err => {
@@ -290,8 +290,8 @@ export class GameOverPhase extends BattlePhase {
globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.ENDLESS_MODE);
}
if (
- globalScene.getPlayerParty().filter(p => p.fusionSpecies).length &&
- !globalScene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]
+ globalScene.getPlayerParty().filter(p => p.fusionSpecies).length > 0
+ && !globalScene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]
) {
globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.SPLICED_ENDLESS_MODE);
}
@@ -299,8 +299,8 @@ export class GameOverPhase extends BattlePhase {
globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.MINI_BLACK_HOLE);
}
if (
- !globalScene.gameData.unlocks[Unlockables.EVIOLITE] &&
- globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)
+ !globalScene.gameData.unlocks[Unlockables.EVIOLITE]
+ && globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)
) {
globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.EVIOLITE);
}
diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts
index a714d247730..e75d7c8f6f3 100644
--- a/src/phases/learn-move-phase.ts
+++ b/src/phases/learn-move-phase.ts
@@ -10,8 +10,8 @@ import { UiMode } from "#enums/ui-mode";
import type { Pokemon } from "#field/pokemon";
import type { Move } from "#moves/move";
import { PlayerPartyMemberPokemonPhase } from "#phases/player-party-member-pokemon-phase";
-import { EvolutionSceneHandler } from "#ui/evolution-scene-handler";
-import { SummaryUiMode } from "#ui/summary-ui-handler";
+import { EvolutionSceneUiHandler } from "#ui/handlers/evolution-scene-ui-handler";
+import { SummaryUiMode } from "#ui/handlers/summary-ui-handler";
import i18next from "i18next";
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
@@ -47,7 +47,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
}
this.messageMode =
- globalScene.ui.getHandler() instanceof EvolutionSceneHandler ? UiMode.EVOLUTION_SCENE : UiMode.MESSAGE;
+ globalScene.ui.getHandler() instanceof EvolutionSceneUiHandler ? UiMode.EVOLUTION_SCENE : UiMode.MESSAGE;
globalScene.ui.setMode(this.messageMode);
// If the Pokemon has less than 4 moves, the new move is added to the largest empty moveset index
// If it has 4 moves, the phase then checks if the player wants to replace the move itself.
diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts
index ecaf9abca46..b73312115f7 100644
--- a/src/phases/move-effect-phase.ts
+++ b/src/phases/move-effect-phase.ts
@@ -92,7 +92,7 @@ export class MoveEffectPhase extends PokemonPhase {
}
this.targets = targets;
- this.hitChecks = Array(this.targets.length).fill([HitCheckResult.PENDING, 0]);
+ this.hitChecks = new Array(this.targets.length).fill([HitCheckResult.PENDING, 0]);
}
/**
@@ -309,8 +309,8 @@ export class MoveEffectPhase extends PokemonPhase {
// Play the animation if the move was successful against any of its targets or it has a POST_TARGET effect (like self destruct)
if (
- this.moveHistoryEntry.result === MoveResult.SUCCESS ||
- move.getAttrs("MoveEffectAttr").some(attr => attr.trigger === MoveEffectTrigger.POST_TARGET)
+ this.moveHistoryEntry.result === MoveResult.SUCCESS
+ || move.getAttrs("MoveEffectAttr").some(attr => attr.trigger === MoveEffectTrigger.POST_TARGET)
) {
const firstTarget = this.getFirstTarget();
new MoveAnim(
@@ -344,7 +344,7 @@ export class MoveEffectPhase extends PokemonPhase {
return;
}
- if (this.queuedPhases.length) {
+ if (this.queuedPhases.length > 0) {
globalScene.phaseManager.appendToPhase(this.queuedPhases, "MoveEndPhase");
}
const moveType = user.getMoveType(this.move, true);
@@ -400,10 +400,17 @@ export class MoveEffectPhase extends PokemonPhase {
* @param user - The {@linkcode Pokemon} using this phase's invoked move
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
* @param hitResult - The {@linkcode HitResult} of the attempted move
+ * @param damage - The amount of damage dealt to the target in the interaction
* @param wasCritical - `true` if the move was a critical hit
*/
- protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, wasCritical = false): void {
- const params = { pokemon: target, opponent: user, move: this.move, hitResult };
+ protected applyOnGetHitAbEffects(
+ user: Pokemon,
+ target: Pokemon,
+ hitResult: HitResult,
+ damage: number,
+ wasCritical = false,
+ ): void {
+ const params = { pokemon: target, opponent: user, move: this.move, hitResult, damage };
applyAbAttrs("PostDefendAbAttr", params);
if (wasCritical) {
@@ -424,9 +431,9 @@ export class MoveEffectPhase extends PokemonPhase {
}
if (
- dealsDamage &&
- !target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
- !this.move.hitsSubstitute(user, target)
+ dealsDamage
+ && !target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr")
+ && !this.move.hitsSubstitute(user, target)
) {
const flinched = new BooleanHolder(false);
globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
@@ -465,13 +472,13 @@ export class MoveEffectPhase extends PokemonPhase {
// TODO: Break up this chunky boolean to make it more palatable
return (
- ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.moveTarget) &&
- (bypassIgnoreProtect.value || !this.move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) &&
- (hasConditionalProtectApplied.value ||
- (!target.findTags(t => t instanceof DamageProtectedTag).length &&
- target.findTags(t => t instanceof ProtectedTag).some(t => target.lapseTag(t.tagType))) ||
- (this.move.category !== MoveCategory.STATUS &&
- target.findTags(t => t instanceof DamageProtectedTag).some(t => target.lapseTag(t.tagType))))
+ ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.moveTarget)
+ && (bypassIgnoreProtect.value || !this.move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target }))
+ && (hasConditionalProtectApplied.value
+ || (target.findTags(t => t instanceof DamageProtectedTag).length === 0
+ && target.findTags(t => t instanceof ProtectedTag).some(t => target.lapseTag(t.tagType)))
+ || (this.move.category !== MoveCategory.STATUS
+ && target.findTags(t => t instanceof DamageProtectedTag).some(t => target.lapseTag(t.tagType))))
);
}
@@ -512,9 +519,9 @@ export class MoveEffectPhase extends PokemonPhase {
// Commander causes moves used against the target to miss
if (
- !fieldTargeted &&
- globalScene.currentBattle.double &&
- target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target
+ !fieldTargeted
+ && globalScene.currentBattle.double
+ && target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target
) {
return [HitCheckResult.MISS, 0];
}
@@ -559,16 +566,17 @@ export class MoveEffectPhase extends PokemonPhase {
// Strikes after the first in a multi-strike move are guaranteed to hit,
// unless the move is flagged to check all hits and the user does not have Skill Link.
- if (user.turnData.hitsLeft < user.turnData.hitCount) {
- if (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr("MaxMultiHitAbAttr")) {
- return [HitCheckResult.HIT, effectiveness];
- }
+ if (
+ user.turnData.hitsLeft < user.turnData.hitCount
+ && (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr("MaxMultiHitAbAttr"))
+ ) {
+ return [HitCheckResult.HIT, effectiveness];
}
const bypassAccuracy =
- bypassAccAndInvuln ||
- target.getTag(BattlerTagType.ALWAYS_GET_HIT) ||
- (target.getTag(BattlerTagType.TELEKINESIS) && !this.move.hasAttr("OneHitKOAttr"));
+ bypassAccAndInvuln
+ || target.getTag(BattlerTagType.ALWAYS_GET_HIT)
+ || (target.getTag(BattlerTagType.TELEKINESIS) && !this.move.hasAttr("OneHitKOAttr"));
if (moveAccuracy === -1 || bypassAccuracy) {
return [HitCheckResult.HIT, effectiveness];
@@ -614,8 +622,8 @@ export class MoveEffectPhase extends PokemonPhase {
}
// TODO: Fix lock on / mind reader check.
if (
- user.getTag(BattlerTagType.IGNORE_ACCURACY) &&
- (user.getLastXMoves().find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1
+ user.getTag(BattlerTagType.IGNORE_ACCURACY)
+ && (user.getLastXMoves().find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1
) {
return true;
}
@@ -730,12 +738,12 @@ export class MoveEffectPhase extends PokemonPhase {
): void {
applyFilteredMoveAttrs(
(attr: MoveAttr) =>
- attr.is("MoveEffectAttr") &&
- attr.trigger === triggerType &&
- (isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget) &&
- (!attr.firstHitOnly || this.firstHit) &&
- (!attr.lastHitOnly || this.lastHit) &&
- (!attr.firstTargetOnly || (firstTarget ?? true)),
+ attr.is("MoveEffectAttr")
+ && attr.trigger === triggerType
+ && (isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget)
+ && (!attr.firstHitOnly || this.firstHit)
+ && (!attr.lastHitOnly || this.lastHit)
+ && (!attr.firstTargetOnly || (firstTarget ?? true)),
user,
target,
this.move,
@@ -763,12 +771,12 @@ export class MoveEffectPhase extends PokemonPhase {
this.triggerMoveEffects(MoveEffectTrigger.PRE_APPLY, user, target);
- const [hitResult, wasCritical] = this.applyMove(user, target, effectiveness);
+ const [hitResult, wasCritical, dmg] = this.applyMove(user, target, effectiveness);
// Apply effects to the user (always) and the target (if not blocked by substitute).
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, true);
if (!this.move.hitsSubstitute(user, target)) {
- this.applyOnTargetEffects(user, target, hitResult, firstTarget, wasCritical);
+ this.applyOnTargetEffects(user, target, hitResult, firstTarget, dmg, wasCritical);
}
if (this.lastHit) {
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
@@ -788,9 +796,13 @@ export class MoveEffectPhase extends PokemonPhase {
* @param user - The {@linkcode Pokemon} using this phase's invoked move
* @param target - The {@linkcode Pokemon} targeted by the move
* @param effectiveness - The effectiveness of the move against the target
- * @returns The {@linkcode HitResult} of the move against the target and a boolean indicating whether the target was crit
+ * @returns The {@linkcode HitResult} of the move against the target, a boolean indicating whether the target was crit, and the amount of damage dealt
*/
- protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] {
+ protected applyMoveDamage(
+ user: Pokemon,
+ target: Pokemon,
+ effectiveness: TypeDamageMultiplier,
+ ): [result: HitResult, critical: boolean, damage: number] {
const isCritical = target.getCriticalHitResult(user, this.move);
/*
@@ -821,7 +833,7 @@ export class MoveEffectPhase extends PokemonPhase {
const isOneHitKo = result === HitResult.ONE_HIT_KO;
if (!dmg) {
- return [result, false];
+ return [result, false, 0];
}
target.lapseTags(BattlerTagLapseType.HIT);
@@ -850,7 +862,7 @@ export class MoveEffectPhase extends PokemonPhase {
}
if (damage <= 0) {
- return [result, isCritical];
+ return [result, isCritical, damage];
}
if (user.isPlayer()) {
@@ -869,7 +881,7 @@ export class MoveEffectPhase extends PokemonPhase {
target.turnData.attacksReceived.unshift({
move: this.move.id,
result: result as DamageResult,
- damage: damage,
+ damage,
critical: isCritical,
sourceId: user.id,
sourceBattlerIndex: user.getBattlerIndex(),
@@ -879,7 +891,7 @@ export class MoveEffectPhase extends PokemonPhase {
globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage));
}
- return [result, isCritical];
+ return [result, isCritical, damage];
}
/**
@@ -932,12 +944,17 @@ export class MoveEffectPhase extends PokemonPhase {
* @param user - The {@linkcode Pokemon} using this phase's invoked move
* @param target - The {@linkcode Pokemon} struck by the move
* @param effectiveness - The effectiveness of the move against the target
+ * @returns The {@linkcode HitResult} of the move against the target, a boolean indicating whether the target was crit, and the amount of damage dealt
*/
- protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] {
+ protected applyMove(
+ user: Pokemon,
+ target: Pokemon,
+ effectiveness: TypeDamageMultiplier,
+ ): [HitResult, critical: boolean, damage: number] {
const moveCategory = user.getMoveCategory(target, this.move);
if (moveCategory === MoveCategory.STATUS) {
- return [HitResult.STATUS, false];
+ return [HitResult.STATUS, false, 0];
}
const result = this.applyMoveDamage(user, target, effectiveness);
@@ -960,6 +977,7 @@ export class MoveEffectPhase extends PokemonPhase {
* @param target - The {@linkcode Pokemon} targeted by the move
* @param hitResult - The {@linkcode HitResult} obtained from applying the move
* @param firstTarget - `true` if the target is the first Pokemon hit by the attack
+ * @param damage - The amount of damage dealt to the target in the interaction
* @param wasCritical - `true` if the move was a critical hit
*/
protected applyOnTargetEffects(
@@ -967,6 +985,7 @@ export class MoveEffectPhase extends PokemonPhase {
target: Pokemon,
hitResult: HitResult,
firstTarget: boolean,
+ damage: number,
wasCritical = false,
): void {
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
@@ -979,8 +998,8 @@ export class MoveEffectPhase extends PokemonPhase {
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
- this.applyOnGetHitAbEffects(user, target, hitResult, wasCritical);
- applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult });
+ this.applyOnGetHitAbEffects(user, target, hitResult, damage, wasCritical);
+ applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult, damage });
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
if (!user.isPlayer() && this.move.is("AttackMove")) {
diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts
index 9a8e509e302..96943065ff0 100644
--- a/src/phases/move-phase.ts
+++ b/src/phases/move-phase.ts
@@ -1,4 +1,5 @@
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
+import { MOVE_COLOR } from "#app/constants/colors";
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import Overrides from "#app/overrides";
@@ -24,6 +25,7 @@ import { applyMoveAttrs } from "#moves/apply-attrs";
import { frenzyMissFunc } from "#moves/move-utils";
import type { PokemonMove } from "#moves/pokemon-move";
import { BattlePhase } from "#phases/battle-phase";
+import type { TurnMove } from "#types/turn-move";
import { NumberHolder } from "#utils/common";
import { enumValueToKey } from "#utils/enums";
import i18next from "i18next";
@@ -41,6 +43,13 @@ export class MovePhase extends BattlePhase {
/** Whether the current move should fail and retain PP. */
protected cancelled = false;
+ /** The move history entry object that is pushed to the pokemon's move history
+ *
+ * @remarks
+ * Can be edited _after_ being pushed to the history to adjust the result, targets, etc, for this move phase.
+ */
+ protected moveHistoryEntry: TurnMove;
+
public get pokemon(): Pokemon {
return this._pokemon;
}
@@ -82,6 +91,11 @@ export class MovePhase extends BattlePhase {
this.move = move;
this.useMode = useMode;
this.forcedLast = forcedLast;
+ this.moveHistoryEntry = {
+ move: MoveId.NONE,
+ targets,
+ useMode,
+ };
}
/**
@@ -91,9 +105,9 @@ export class MovePhase extends BattlePhase {
*/
public canMove(ignoreDisableTags = false): boolean {
return (
- this.pokemon.isActive(true) &&
- this.move.isUsable(this.pokemon, isIgnorePP(this.useMode), ignoreDisableTags) &&
- this.targets.length > 0
+ this.pokemon.isActive(true)
+ && this.move.isUsable(this.pokemon, isIgnorePP(this.useMode), ignoreDisableTags)
+ && this.targets.length > 0
);
}
@@ -118,7 +132,10 @@ export class MovePhase extends BattlePhase {
public start(): void {
super.start();
- console.log(MoveId[this.move.moveId], enumValueToKey(MoveUseMode, this.useMode));
+ console.log(
+ `%cMove: ${MoveId[this.move.moveId]}\nUse Mode: ${enumValueToKey(MoveUseMode, this.useMode)}`,
+ `color:${MOVE_COLOR}`,
+ );
// Check if move is unusable (e.g. running out of PP due to a mid-turn Spite
// or the user no longer being on field), ending the phase early if not.
@@ -181,8 +198,8 @@ export class MovePhase extends BattlePhase {
const moveQueue = this.pokemon.getMoveQueue();
if (
- (targets.length === 0 && !this.move.getMove().hasAttr("AddArenaTrapTagAttr")) ||
- (moveQueue.length > 0 && moveQueue[0].move === MoveId.NONE)
+ (targets.length === 0 && !this.move.getMove().hasAttr("AddArenaTrapTagAttr"))
+ || (moveQueue.length > 0 && moveQueue[0].move === MoveId.NONE)
) {
this.showMoveText();
this.showFailedText();
@@ -204,8 +221,8 @@ export class MovePhase extends BattlePhase {
}
if (
- this.useMode === MoveUseMode.INDIRECT &&
- [StatusEffect.SLEEP, StatusEffect.FREEZE].includes(this.pokemon.status.effect)
+ this.useMode === MoveUseMode.INDIRECT
+ && [StatusEffect.SLEEP, StatusEffect.FREEZE].includes(this.pokemon.status.effect)
) {
// Dancer thaws out or wakes up a frozen/sleeping user prior to use
this.pokemon.resetStatus(false);
@@ -222,8 +239,8 @@ export class MovePhase extends BattlePhase {
switch (this.pokemon.status.effect) {
case StatusEffect.PARALYSIS:
activated =
- (this.pokemon.randBattleSeedInt(4) === 0 || Overrides.STATUS_ACTIVATION_OVERRIDE === true) &&
- Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
+ (this.pokemon.randBattleSeedInt(4) === 0 || Overrides.STATUS_ACTIVATION_OVERRIDE === true)
+ && Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
break;
case StatusEffect.SLEEP: {
applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove());
@@ -244,9 +261,9 @@ export class MovePhase extends BattlePhase {
.getMove()
.findAttr(
attr => attr.is("HealStatusEffectAttr") && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE),
- ) ||
- (!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) ||
- Overrides.STATUS_ACTIVATION_OVERRIDE === false;
+ )
+ || (!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true)
+ || Overrides.STATUS_ACTIVATION_OVERRIDE === false;
activated = !healed;
break;
@@ -397,8 +414,8 @@ export class MovePhase extends BattlePhase {
// even on failure, as will all moves blocked by terrain.
// TODO: Verify if this also applies to primal weather failures
if (
- failedDueToTerrain ||
- [MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)
+ failedDueToTerrain
+ || [MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)
) {
applyAbAttrs("PokemonTypeChangeAbAttr", {
pokemon: this.pokemon,
@@ -410,19 +427,15 @@ export class MovePhase extends BattlePhase {
if (showText) {
this.showMoveText();
}
-
- this.pokemon.pushMoveHistory({
- move: this.move.moveId,
- targets: this.targets,
- result: MoveResult.FAIL,
- useMode: this.useMode,
- });
+ const moveHistoryEntry = this.moveHistoryEntry;
+ moveHistoryEntry.result = MoveResult.FAIL;
+ this.pokemon.pushMoveHistory(moveHistoryEntry);
// Use move-specific failure messages if present before checking terrain/weather blockage
// and falling back to the classic "But it failed!".
const failureMessage =
- move.getFailedText(this.pokemon, targets[0], move) ||
- (failedDueToTerrain
+ move.getFailedText(this.pokemon, targets[0], move)
+ || (failedDueToTerrain
? getTerrainBlockMessage(targets[0], globalScene.arena.getTerrainType())
: failedDueToWeather
? getWeatherBlockMessage(globalScene.arena.getWeatherType())
@@ -528,9 +541,9 @@ export class MovePhase extends BattlePhase {
// TODO: don't hardcode this interaction.
// Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect)
if (
- redirectTag &&
- (!redirectTag.powder ||
- (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT)))
+ redirectTag
+ && (!redirectTag.powder
+ || (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT)))
) {
redirectTarget.value = p.getBattlerIndex();
redirectedByAbility = false;
@@ -591,9 +604,9 @@ export class MovePhase extends BattlePhase {
// account for metal burst and comeuppance hitting remaining targets in double battles
// counterattack will redirect to remaining ally if original attacker faints
if (
- globalScene.currentBattle.double &&
- this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER) &&
- globalScene.getField()[this.targets[0]].hp === 0
+ globalScene.currentBattle.double
+ && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)
+ && globalScene.getField()[this.targets[0]].hp === 0
) {
const opposingField = this.pokemon.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField();
this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER;
@@ -630,12 +643,9 @@ export class MovePhase extends BattlePhase {
frenzyMissFunc(this.pokemon, this.move.getMove());
}
- this.pokemon.pushMoveHistory({
- move: MoveId.NONE,
- result: MoveResult.FAIL,
- targets: this.targets,
- useMode: this.useMode,
- });
+ const moveHistoryEntry = this.moveHistoryEntry;
+ moveHistoryEntry.result = MoveResult.FAIL;
+ this.pokemon.pushMoveHistory(moveHistoryEntry);
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
@@ -649,13 +659,16 @@ export class MovePhase extends BattlePhase {
* Displays the move's usage text to the player as applicable for the move being used.
*/
public showMoveText(): void {
+ const moveId = this.move.moveId;
if (
- this.move.moveId === MoveId.NONE ||
- this.pokemon.getTag(BattlerTagType.RECHARGING) ||
- this.pokemon.getTag(BattlerTagType.INTERRUPTED)
+ moveId === MoveId.NONE
+ || this.pokemon.getTag(BattlerTagType.RECHARGING)
+ || this.pokemon.getTag(BattlerTagType.INTERRUPTED)
) {
return;
}
+ // Showing move text always adjusts the move history entry's move id
+ this.moveHistoryEntry.move = moveId;
// TODO: This should be done by the move...
globalScene.phaseManager.queueMessage(
@@ -668,7 +681,7 @@ export class MovePhase extends BattlePhase {
// Moves with pre-use messages (Magnitude, Chilly Reception, Fickle Beam, etc.) always display their messages even on failure
// TODO: This assumes single target for message funcs - is this sustainable?
- applyMoveAttrs("PreMoveMessageAttr", this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove());
+ applyMoveAttrs("PreMoveMessageAttr", this.pokemon, this.getActiveTargetPokemon()[0], this.move.getMove());
}
/**
diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts
index 9363efcb460..df670deaf26 100644
--- a/src/phases/mystery-encounter-phases.ts
+++ b/src/phases/mystery-encounter-phases.ts
@@ -77,10 +77,7 @@ export class MysteryEncounterPhase extends Phase {
if (!this.optionSelectSettings) {
// Saves the selected option in the ME save data, only if this is not a followup option select phase
// Can be used for analytics purposes to track what options are popular on certain encounters
- const encounterSaveData =
- globalScene.mysteryEncounterSaveData.encounteredEvents[
- globalScene.mysteryEncounterSaveData.encounteredEvents.length - 1
- ];
+ const encounterSaveData = globalScene.mysteryEncounterSaveData.encounteredEvents.at(-1)!;
if (encounterSaveData.type === globalScene.currentBattle.mysteryEncounter?.encounterType) {
encounterSaveData.selectedOption = index;
}
@@ -225,9 +222,9 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
tags
.filter(
t =>
- includedLapseTags.includes(t.tagType) &&
- t.lapseTypes.includes(BattlerTagLapseType.TURN_END) &&
- !t.lapse(pokemon, BattlerTagLapseType.TURN_END),
+ includedLapseTags.includes(t.tagType)
+ && t.lapseTypes.includes(BattlerTagLapseType.TURN_END)
+ && !t.lapse(pokemon, BattlerTagLapseType.TURN_END),
)
.forEach(t => {
t.onRemove(pokemon);
@@ -244,7 +241,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
const legalPlayerPokemon = globalScene.getPokemonAllowedInBattle();
// The total number of legal player Pokemon that aren't currently on the field
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
- if (!legalPlayerPokemon.length) {
+ if (legalPlayerPokemon.length === 0) {
globalScene.phaseManager.unshiftNew("GameOverPhase");
return this.end();
}
@@ -292,10 +289,7 @@ export class MysteryEncounterBattlePhase extends Phase {
this.doMysteryEncounterBattle();
}
- /**
- * Gets intro battle message for new battle
- * @private
- */
+ /** Get intro battle message for new battle */
private getBattleMessage(): string {
const enemyField = globalScene.getEnemyField();
const encounterMode = globalScene.currentBattle.mysteryEncounter!.encounterMode;
@@ -326,8 +320,7 @@ export class MysteryEncounterBattlePhase extends Phase {
}
/**
- * Queues {@linkcode SummonPhase}s for the new battle, and handles trainer animations/dialogue if it's a Trainer battle
- * @private
+ * Queue {@linkcode SummonPhase}s for the new battle and handle trainer animations/dialogue for Trainer battles
*/
private doMysteryEncounterBattle() {
const encounterMode = globalScene.currentBattle.mysteryEncounter!.encounterMode;
@@ -372,7 +365,7 @@ export class MysteryEncounterBattlePhase extends Phase {
const encounterMessages = globalScene.currentBattle.trainer?.getEncounterMessages();
- if (!encounterMessages || !encounterMessages.length) {
+ if (!encounterMessages || encounterMessages.length === 0) {
doSummon();
} else {
const trainer = globalScene.currentBattle.trainer;
@@ -404,7 +397,6 @@ export class MysteryEncounterBattlePhase extends Phase {
/**
* Initiate {@linkcode SummonPhase}s, {@linkcode ScanIvsPhase}, {@linkcode PostSummonPhase}s, etc.
- * @private
*/
private endBattleSetup() {
const enemyField = globalScene.getEnemyField();
@@ -453,10 +445,7 @@ export class MysteryEncounterBattlePhase extends Phase {
this.end();
}
- /**
- * Ease in enemy trainer
- * @private
- */
+ /** Ease in enemy trainer */
private showEnemyTrainer(): void {
// Show enemy trainer
const trainer = globalScene.currentBattle.trainer;
diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts
index 1030d5eb9d9..154e95bc970 100644
--- a/src/phases/party-heal-phase.ts
+++ b/src/phases/party-heal-phase.ts
@@ -38,13 +38,15 @@ export class PartyHealPhase extends BattlePhase {
pokemon.updateInfo(true);
}
const healSong = globalScene.playSoundWithoutBgm("heal");
- globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => {
- healSong.destroy();
- if (this.resumeBgm && bgmPlaying) {
- globalScene.playBgm();
- }
- globalScene.ui.fadeIn(500).then(() => this.end());
- });
+ if (healSong) {
+ globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => {
+ healSong.destroy();
+ if (this.resumeBgm && bgmPlaying) {
+ globalScene.playBgm();
+ }
+ globalScene.ui.fadeIn(500).then(() => this.end());
+ });
+ }
});
globalScene.arena.playerTerasUsed = 0;
}
diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts
index 9739c58d667..1a1a7e2efa3 100644
--- a/src/phases/pokemon-phase.ts
+++ b/src/phases/pokemon-phase.ts
@@ -16,8 +16,8 @@ export abstract class PokemonPhase extends FieldPhase {
super();
battlerIndex =
- battlerIndex ??
- globalScene
+ battlerIndex
+ ?? globalScene
.getField()
.find(p => p?.isActive())
?.getBattlerIndex();
diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts
index 3ac112a8a8b..a69ec2d468b 100644
--- a/src/phases/post-game-over-phase.ts
+++ b/src/phases/post-game-over-phase.ts
@@ -5,10 +5,11 @@ import type { EndCardPhase } from "#phases/end-card-phase";
export class PostGameOverPhase extends Phase {
public readonly phaseName = "PostGameOverPhase";
private endCardPhase?: EndCardPhase;
+ private slotId: number;
- constructor(endCardPhase?: EndCardPhase) {
+ constructor(slotId: number, endCardPhase?: EndCardPhase) {
super();
-
+ this.slotId = slotId;
this.endCardPhase = endCardPhase;
}
@@ -20,16 +21,14 @@ export class PostGameOverPhase extends Phase {
if (!success) {
return globalScene.reset(true);
}
- globalScene.gameData
- .tryClearSession(globalScene.sessionSlotId)
- .then((success: boolean | [boolean, boolean]) => {
- if (!success[0]) {
- return globalScene.reset(true);
- }
- globalScene.reset();
- globalScene.phaseManager.unshiftNew("TitlePhase");
- this.end();
- });
+ globalScene.gameData.tryClearSession(this.slotId).then((success: boolean | [boolean, boolean]) => {
+ if (!success[0]) {
+ return globalScene.reset(true);
+ }
+ globalScene.reset();
+ globalScene.phaseManager.unshiftNew("TitlePhase");
+ this.end();
+ });
});
};
diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts
index 913a29cded8..5de068f2ae5 100644
--- a/src/phases/post-summon-phase.ts
+++ b/src/phases/post-summon-phase.ts
@@ -20,8 +20,8 @@ export class PostSummonPhase extends PokemonPhase {
// If this is mystery encounter and has post summon phase tag, apply post summon effects
if (
- globalScene.currentBattle.isBattleMysteryEncounter() &&
- pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0
+ globalScene.currentBattle.isBattleMysteryEncounter()
+ && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0
) {
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
}
diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts
index 0235fb51da3..a9dedf4c325 100644
--- a/src/phases/revival-blessing-phase.ts
+++ b/src/phases/revival-blessing-phase.ts
@@ -3,8 +3,8 @@ import { SwitchType } from "#enums/switch-type";
import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon";
import { BattlePhase } from "#phases/battle-phase";
-import type { PartyOption } from "#ui/party-ui-handler";
-import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import type { PartyOption } from "#ui/handlers/party-ui-handler";
+import { PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
import { isNullOrUndefined, toDmgValue } from "#utils/common";
import i18next from "i18next";
@@ -43,9 +43,9 @@ export class RevivalBlessingPhase extends BattlePhase {
const allyPokemon = this.user.getAlly();
if (
- globalScene.currentBattle.double &&
- globalScene.getPlayerParty().length > 1 &&
- !isNullOrUndefined(allyPokemon)
+ globalScene.currentBattle.double
+ && globalScene.getPlayerParty().length > 1
+ && !isNullOrUndefined(allyPokemon)
) {
if (slotIndex <= 1) {
// Revived ally pokemon
diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts
index eebee28bfbb..eb84345fa6b 100644
--- a/src/phases/scan-ivs-phase.ts
+++ b/src/phases/scan-ivs-phase.ts
@@ -20,58 +20,54 @@ export class ScanIvsPhase extends PokemonPhase {
const pokemon = this.getPokemon();
- let enemyIvs: number[] = [];
- let statsContainer: Phaser.GameObjects.Sprite[] = [];
- let statsContainerLabels: Phaser.GameObjects.Sprite[] = [];
- const enemyField = globalScene.getEnemyField();
- const uiTheme = globalScene.uiTheme; // Assuming uiTheme is accessible
- for (let e = 0; e < enemyField.length; e++) {
- enemyIvs = enemyField[e].ivs;
+ for (const enemy of globalScene.getEnemyField()) {
+ const enemyIvs = enemy.ivs;
// we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists
- const currentIvs = globalScene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs;
- statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[];
- statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0);
- for (let s = 0; s < statsContainerLabels.length; s++) {
- const ivStat = Stat[statsContainerLabels[s].frame.name];
- if (enemyIvs[ivStat] > currentIvs[ivStat] && PERMANENT_STATS.indexOf(Number(ivStat)) >= 0) {
+ const currentIvs = globalScene.gameData.dexData[enemy.species.getRootSpeciesId()].ivs;
+ const statsContainer = enemy.getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[];
+ const statsContainerLabels = statsContainer.filter(m => m.name.includes("icon_stat_label"));
+ for (const statContainer of statsContainerLabels) {
+ const ivStat = Stat[statContainer.frame.name] as Stat;
+ if (enemyIvs[ivStat] > currentIvs[ivStat] && PERMANENT_STATS.includes(Number(ivStat))) {
const hexColour =
enemyIvs[ivStat] === 31
- ? getTextColor(TextStyle.PERFECT_IV, false, uiTheme)
- : getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme);
+ ? getTextColor(TextStyle.PERFECT_IV, false)
+ : getTextColor(TextStyle.SUMMARY_GREEN, false);
const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color;
- statsContainerLabels[s].setTint(hexTextColour);
+ statContainer.setTint(hexTextColour);
}
- statsContainerLabels[s].setVisible(true);
+ statContainer.setVisible(true);
}
}
- if (!globalScene.hideIvs) {
- globalScene.ui.showText(
- i18next.t("battle:ivScannerUseQuestion", {
- pokemonName: getPokemonNameWithAffix(pokemon),
- }),
- null,
- () => {
- globalScene.ui.setMode(
- UiMode.CONFIRM,
- () => {
- globalScene.ui.setMode(UiMode.MESSAGE);
- globalScene.ui.clearText();
- globalScene.ui
- .getMessageHandler()
- .promptIvs(pokemon.id, pokemon.ivs)
- .then(() => this.end());
- },
- () => {
- globalScene.ui.setMode(UiMode.MESSAGE);
- globalScene.ui.clearText();
- this.end();
- },
- );
- },
- );
- } else {
+ if (globalScene.hideIvs) {
this.end();
+ return;
}
+
+ globalScene.ui.showText(
+ i18next.t("battle:ivScannerUseQuestion", {
+ pokemonName: getPokemonNameWithAffix(pokemon),
+ }),
+ null,
+ () => {
+ globalScene.ui.setMode(
+ UiMode.CONFIRM,
+ () => {
+ globalScene.ui.setMode(UiMode.MESSAGE);
+ globalScene.ui.clearText();
+ globalScene.ui
+ .getMessageHandler()
+ .promptIvs(pokemon.id, pokemon.ivs)
+ .then(() => this.end());
+ },
+ () => {
+ globalScene.ui.setMode(UiMode.MESSAGE);
+ globalScene.ui.clearText();
+ this.end();
+ },
+ );
+ },
+ );
}
}
diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts
index d02d69fc934..3276c34306c 100644
--- a/src/phases/select-biome-phase.ts
+++ b/src/phases/select-biome-phase.ts
@@ -5,7 +5,7 @@ import { ChallengeType } from "#enums/challenge-type";
import { UiMode } from "#enums/ui-mode";
import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier";
import { BattlePhase } from "#phases/battle-phase";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, randSeedInt } from "#utils/common";
@@ -44,9 +44,9 @@ export class SelectBiomePhase extends BattlePhase {
};
if (
- (gameMode.isClassic && gameMode.isWaveFinal(nextWaveIndex + 9)) ||
- (gameMode.isDaily && gameMode.isWaveFinal(nextWaveIndex)) ||
- (gameMode.hasShortBiomes && !(nextWaveIndex % 50))
+ (gameMode.isClassic && gameMode.isWaveFinal(nextWaveIndex + 9))
+ || (gameMode.isDaily && gameMode.isWaveFinal(nextWaveIndex))
+ || (gameMode.hasShortBiomes && !(nextWaveIndex % 50))
) {
setNextBiome(BiomeId.END);
} else if (gameMode.hasRandomBiomes) {
diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts
index 05c890136ee..51adeb21af0 100644
--- a/src/phases/select-modifier-phase.ts
+++ b/src/phases/select-modifier-phase.ts
@@ -24,9 +24,9 @@ import {
TmModifierType,
} from "#modifiers/modifier-type";
import { BattlePhase } from "#phases/battle-phase";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
-import { SHOP_OPTIONS_ROW_LIMIT } from "#ui/modifier-select-ui-handler";
-import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
+import { SHOP_OPTIONS_ROW_LIMIT } from "#ui/handlers/modifier-select-ui-handler";
+import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
import { isNullOrUndefined, NumberHolder } from "#utils/common";
import i18next from "i18next";
@@ -177,7 +177,7 @@ export class SelectModifierPhase extends BattlePhase {
this.openModifierMenu(modifierType, cost, modifierSelectCallback);
}
} else {
- this.applyModifier(modifierType.newModifier()!);
+ this.applyModifier(modifierType.newModifier()!, cost);
}
return cost === -1;
}
@@ -215,11 +215,11 @@ export class SelectModifierPhase extends BattlePhase {
-1,
(fromSlotIndex: number, itemIndex: number, itemQuantity: number, toSlotIndex: number) => {
if (
- toSlotIndex !== undefined &&
- fromSlotIndex < 6 &&
- toSlotIndex < 6 &&
- fromSlotIndex !== toSlotIndex &&
- itemIndex > -1
+ toSlotIndex !== undefined
+ && fromSlotIndex < 6
+ && toSlotIndex < 6
+ && fromSlotIndex !== toSlotIndex
+ && itemIndex > -1
) {
const itemModifiers = globalScene.findModifiers(
m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === party[fromSlotIndex].id,
@@ -306,10 +306,10 @@ export class SelectModifierPhase extends BattlePhase {
-1,
(fromSlotIndex: number, spliceSlotIndex: number) => {
if (
- spliceSlotIndex !== undefined &&
- fromSlotIndex < 6 &&
- spliceSlotIndex < 6 &&
- fromSlotIndex !== spliceSlotIndex
+ spliceSlotIndex !== undefined
+ && fromSlotIndex < 6
+ && spliceSlotIndex < 6
+ && fromSlotIndex !== spliceSlotIndex
) {
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct?
@@ -380,9 +380,9 @@ export class SelectModifierPhase extends BattlePhase {
// If custom modifiers are specified, overrides default item count
if (this.customModifierSettings) {
const newItemCount =
- (this.customModifierSettings.guaranteedModifierTiers?.length ?? 0) +
- (this.customModifierSettings.guaranteedModifierTypeOptions?.length ?? 0) +
- (this.customModifierSettings.guaranteedModifierTypeFuncs?.length ?? 0);
+ (this.customModifierSettings.guaranteedModifierTiers?.length ?? 0)
+ + (this.customModifierSettings.guaranteedModifierTypeOptions?.length ?? 0)
+ + (this.customModifierSettings.guaranteedModifierTypeFuncs?.length ?? 0);
if (this.customModifierSettings.fillRemaining) {
const originalCount = modifierCountHolder.value;
modifierCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount;
diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts
index ef3fa74bd44..50c06f96a77 100644
--- a/src/phases/select-starter-phase.ts
+++ b/src/phases/select-starter-phase.ts
@@ -7,8 +7,8 @@ import { ChallengeType } from "#enums/challenge-type";
import type { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { overrideHeldItems, overrideModifiers } from "#modifiers/modifier";
-import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler";
-import type { Starter } from "#ui/starter-select-ui-handler";
+import { SaveSlotUiMode } from "#ui/handlers/save-slot-select-ui-handler";
+import type { Starter } from "#ui/handlers/starter-select-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { isNullOrUndefined } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
@@ -50,9 +50,9 @@ export class SelectStarterPhase extends Phase {
const starterProps = globalScene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
if (
- starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES &&
- !isNullOrUndefined(Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]) &&
- starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!]
+ starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES
+ && !isNullOrUndefined(Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId])
+ && starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!]
) {
starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!;
}
diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts
index 3805cb919b2..2f439419cd0 100644
--- a/src/phases/select-target-phase.ts
+++ b/src/phases/select-target-phase.ts
@@ -30,7 +30,7 @@ export class SelectTargetPhase extends PokemonPhase {
globalScene.phaseManager.queueMessage(i18next.t(errorMessage, { moveName: moveObject.name }), 0, true);
targets = [];
}
- if (targets.length < 1) {
+ if (targets.length === 0) {
globalScene.currentBattle.turnCommands[this.fieldIndex] = null;
globalScene.phaseManager.unshiftNew("CommandPhase", this.fieldIndex);
} else {
diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts
index 140ef841929..6c15342ddeb 100644
--- a/src/phases/stat-stage-change-phase.ts
+++ b/src/phases/stat-stage-change-phase.ts
@@ -22,6 +22,7 @@ export type StatStageChangeCallback = (
relativeChanges: number[],
) => void;
+// TODO: Refactor this mess of a phase
export class StatStageChangePhase extends PokemonPhase {
public readonly phaseName = "StatStageChangePhase";
private stats: BattleStat[];
@@ -62,13 +63,12 @@ export class StatStageChangePhase extends PokemonPhase {
start() {
// Check if multiple stats are being changed at the same time, then run SSCPhase for each of them
if (this.stats.length > 1) {
- for (let i = 0; i < this.stats.length; i++) {
- const stat = [this.stats[i]];
+ for (const stat of this.stats) {
globalScene.phaseManager.unshiftNew(
"StatStageChangePhase",
this.battlerIndex,
this.selfTarget,
- stat,
+ [stat],
this.stages,
this.showMessage,
this.ignoreAbilities,
@@ -100,20 +100,18 @@ export class StatStageChangePhase extends PokemonPhase {
}
});
}
+ } else if (!this.comingFromStickyWeb) {
+ opponentPokemon = globalScene.getPlayerField()[globalScene.currentBattle.lastPlayerInvolved];
} else {
- if (!this.comingFromStickyWeb) {
- opponentPokemon = globalScene.getPlayerField()[globalScene.currentBattle.lastPlayerInvolved];
- } else {
- const stickyTagID = globalScene.arena.findTagsOnSide(
- (t: ArenaTag) => t.tagType === ArenaTagType.STICKY_WEB,
- ArenaTagSide.ENEMY,
- )[0].sourceId;
- globalScene.getPlayerField().forEach(e => {
- if (e.id === stickyTagID) {
- opponentPokemon = e;
- }
- });
- }
+ const stickyTagID = globalScene.arena.findTagsOnSide(
+ (t: ArenaTag) => t.tagType === ArenaTagType.STICKY_WEB,
+ ArenaTagSide.ENEMY,
+ )[0].sourceId;
+ globalScene.getPlayerField().forEach(e => {
+ if (e.id === stickyTagID) {
+ opponentPokemon = e;
+ }
+ });
}
if (!pokemon.isActive(true)) {
@@ -161,11 +159,9 @@ export class StatStageChangePhase extends PokemonPhase {
/** Potential stat reflection due to Mirror Armor, does not apply to Octolock end of turn effect */
if (
- opponentPokemon !== undefined &&
- // TODO: investigate whether this is stoping mirror armor from applying to non-octolock
- // reasons for stat drops if the user has the Octolock tag
- !pokemon.findTag(t => t instanceof OctolockTag) &&
- !this.comingFromMirrorArmorUser
+ opponentPokemon !== undefined // TODO: investigate whether this is stoping mirror armor from applying to non-octolock // reasons for stat drops if the user has the Octolock tag
+ && !pokemon.findTag(t => t instanceof OctolockTag)
+ && !this.comingFromMirrorArmorUser
) {
applyAbAttrs("ReflectStatStageChangeAbAttr", {
pokemon,
@@ -249,7 +245,7 @@ export class StatStageChangePhase extends PokemonPhase {
handleTutorial(Tutorial.Stat_Change).then(() => super.end());
};
- if (relLevels.filter(l => l).length && globalScene.moveAnimations) {
+ if (relLevels.filter(l => l).length > 0 && globalScene.moveAnimations) {
pokemon.enableMask();
const pokemonMaskSprite = pokemon.maskSprite;
@@ -309,13 +305,13 @@ export class StatStageChangePhase extends PokemonPhase {
while (
(existingPhase = globalScene.phaseManager.findPhase(
p =>
- p.is("StatStageChangePhase") &&
- p.battlerIndex === this.battlerIndex &&
- p.stats.length === 1 &&
- p.stats[0] === this.stats[0] &&
- p.selfTarget === this.selfTarget &&
- p.showMessage === this.showMessage &&
- p.ignoreAbilities === this.ignoreAbilities,
+ p.is("StatStageChangePhase")
+ && p.battlerIndex === this.battlerIndex
+ && p.stats.length === 1
+ && p.stats[0] === this.stats[0]
+ && p.selfTarget === this.selfTarget
+ && p.showMessage === this.showMessage
+ && p.ignoreAbilities === this.ignoreAbilities,
) as StatStageChangePhase)
) {
this.stages += existingPhase.stages;
@@ -328,13 +324,13 @@ export class StatStageChangePhase extends PokemonPhase {
while (
(existingPhase = globalScene.phaseManager.findPhase(
p =>
- p.is("StatStageChangePhase") &&
- p.battlerIndex === this.battlerIndex &&
- p.selfTarget === this.selfTarget &&
- accEva.some(s => p.stats.includes(s)) === isAccEva &&
- p.stages === this.stages &&
- p.showMessage === this.showMessage &&
- p.ignoreAbilities === this.ignoreAbilities,
+ p.is("StatStageChangePhase")
+ && p.battlerIndex === this.battlerIndex
+ && p.selfTarget === this.selfTarget
+ && accEva.some(s => p.stats.includes(s)) === isAccEva
+ && p.stages === this.stages
+ && p.showMessage === this.showMessage
+ && p.ignoreAbilities === this.ignoreAbilities,
) as StatStageChangePhase)
) {
this.stats.push(...existingPhase.stats);
@@ -369,7 +365,8 @@ export class StatStageChangePhase extends PokemonPhase {
.map(s => i18next.t(getStatKey(s)))
.join(
", ",
- )}${relStageStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${i18next.t(getStatKey(relStageStats[relStageStats.length - 1]))}`;
+ // Bang is justified as we explicitly check for the existence of 2+ args
+ )}${relStageStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${i18next.t(getStatKey(relStageStats.at(-1)!))}`;
messages.push(
i18next.t(getStatStageChangeDescriptionKey(Math.abs(Number.parseInt(rl)), stages >= 1), {
pokemonNameWithAffix: getPokemonNameWithAffix(this.getPokemon()),
diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts
index e4c8aa9af7a..dda70f46ec9 100644
--- a/src/phases/summon-phase.ts
+++ b/src/phases/summon-phase.ts
@@ -97,8 +97,8 @@ export class SummonPhase extends PartyMemberPokemonPhase {
});
globalScene.time.delayedCall(750, () => this.summon());
} else if (
- globalScene.currentBattle.battleType === BattleType.TRAINER ||
- globalScene.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
+ globalScene.currentBattle.battleType === BattleType.TRAINER
+ || globalScene.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) {
const trainerName = globalScene.currentBattle.trainer?.getName(
!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER,
@@ -278,9 +278,9 @@ export class SummonPhase extends PartyMemberPokemonPhase {
pokemon.resetTurnData();
if (
- !this.loaded ||
- [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) ||
- globalScene.currentBattle.waveIndex % 10 === 1
+ !this.loaded
+ || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)
+ || globalScene.currentBattle.waveIndex % 10 === 1
) {
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
this.queuePostSummon();
diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts
index a431d973a02..8b03f5ec5ce 100644
--- a/src/phases/switch-phase.ts
+++ b/src/phases/switch-phase.ts
@@ -1,8 +1,9 @@
import { globalScene } from "#app/global-scene";
+import { DynamicPhaseType } from "#enums/dynamic-phase-type";
import { SwitchType } from "#enums/switch-type";
import { UiMode } from "#enums/ui-mode";
import { BattlePhase } from "#phases/battle-phase";
-import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
/**
* Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase}
@@ -37,7 +38,10 @@ export class SwitchPhase extends BattlePhase {
super.start();
// Skip modal switch if impossible (no remaining party members that aren't in battle)
- if (this.isModal && !globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) {
+ if (
+ this.isModal
+ && globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length === 0
+ ) {
return super.end();
}
@@ -54,9 +58,9 @@ export class SwitchPhase extends BattlePhase {
// Check if there is any space still in field
if (
- this.isModal &&
- globalScene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length >=
- globalScene.currentBattle.getBattlerCount()
+ this.isModal
+ && globalScene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length
+ >= globalScene.currentBattle.getBattlerCount()
) {
return super.end();
}
@@ -75,8 +79,11 @@ export class SwitchPhase extends BattlePhase {
if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) {
// Remove any pre-existing PostSummonPhase under the same field index.
// Pre-existing PostSummonPhases may occur when this phase is invoked during a prompt to switch at the start of a wave.
- globalScene.phaseManager.tryRemovePhase(
+ // TODO: Separate the animations from `SwitchSummonPhase` and co. into another phase and use that on initial switch - this is a band-aid fix
+ globalScene.phaseManager.tryRemoveDynamicPhase(
+ DynamicPhaseType.POST_SUMMON,
p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex,
+ "all",
);
const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType;
globalScene.phaseManager.unshiftNew("SwitchSummonPhase", switchType, fieldIndex, slotIndex, this.doReturn);
diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts
index b7460e77569..ac47068c619 100644
--- a/src/phases/switch-summon-phase.ts
+++ b/src/phases/switch-summon-phase.ts
@@ -56,9 +56,9 @@ export class SwitchSummonPhase extends SummonPhase {
}
if (
- !this.doReturn ||
- (this.slotIndex !== -1 &&
- !(this.player ? globalScene.getPlayerParty() : globalScene.getEnemyParty())[this.slotIndex])
+ !this.doReturn
+ || (this.slotIndex !== -1
+ && !(this.player ? globalScene.getPlayerParty() : globalScene.getEnemyParty())[this.slotIndex])
) {
if (this.player) {
this.switchAndSummon();
@@ -141,14 +141,14 @@ export class SwitchSummonPhase extends SummonPhase {
if (
!globalScene.findModifier(
m =>
- m instanceof SwitchEffectTransferModifier &&
- (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id,
+ m instanceof SwitchEffectTransferModifier
+ && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id,
)
) {
const batonPassModifier = globalScene.findModifier(
m =>
- m instanceof SwitchEffectTransferModifier &&
- (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id,
+ m instanceof SwitchEffectTransferModifier
+ && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id,
) as SwitchEffectTransferModifier;
if (batonPassModifier) {
@@ -214,9 +214,9 @@ export class SwitchSummonPhase extends SummonPhase {
// Compensate for turn spent summoning/forced switch if switched out pokemon is not fainted.
// Needed as we increment turn counters in `TurnEndPhase`.
if (
- currentCommand === Command.POKEMON ||
- lastPokemonIsForceSwitchedAndNotFainted ||
- lastPokemonHasForceSwitchAbAttr
+ currentCommand === Command.POKEMON
+ || lastPokemonIsForceSwitchedAndNotFainted
+ || lastPokemonHasForceSwitchAbAttr
) {
pokemon.tempSummonData.turnCount--;
pokemon.tempSummonData.waveTurnCount--;
diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts
index 15d92ba2812..97f6bf0d837 100644
--- a/src/phases/title-phase.ts
+++ b/src/phases/title-phase.ts
@@ -14,10 +14,10 @@ import { Unlockables } from "#enums/unlockables";
import { getBiomeKey } from "#field/arena";
import type { Modifier } from "#modifiers/modifier";
import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type";
-import type { SessionSaveData } from "#system/game-data";
import { vouchers } from "#system/voucher";
-import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler";
+import type { SessionSaveData } from "#types/save-data";
+import type { OptionSelectConfig, OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import { SaveSlotUiMode } from "#ui/handlers/save-slot-select-ui-handler";
import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#utils/common";
import i18next from "i18next";
@@ -125,7 +125,7 @@ export class TitlePhase extends Phase {
});
globalScene.ui.showText(i18next.t("menu:selectGameMode"), null, () =>
globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
- options: options,
+ options,
}),
);
return true;
@@ -161,7 +161,7 @@ export class TitlePhase extends Phase {
},
);
const config: OptionSelectConfig = {
- options: options,
+ options,
noCancel: true,
yOffset: 47,
};
@@ -177,6 +177,9 @@ export class TitlePhase extends Phase {
.then((success: boolean) => {
if (success) {
this.loaded = true;
+ if (loggedInUser) {
+ loggedInUser.lastSessionSlot = slotId;
+ }
globalScene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end());
} else {
this.end();
@@ -241,11 +244,11 @@ export class TitlePhase extends Phase {
regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER);
- const modifiers: Modifier[] = Array(3)
+ const modifiers: Modifier[] = new Array(3)
.fill(null)
.map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier())
.concat(
- Array(3)
+ new Array(3)
.fill(null)
.map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier()),
)
@@ -319,8 +322,8 @@ export class TitlePhase extends Phase {
}
if (
- globalScene.currentBattle.battleType !== BattleType.TRAINER &&
- (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily)
+ globalScene.currentBattle.battleType !== BattleType.TRAINER
+ && (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily)
) {
const minPartySize = globalScene.currentBattle.double ? 2 : 1;
if (availablePartyMembers > minPartySize) {
diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts
index 6f92dbe496d..909c1535140 100644
--- a/src/phases/trainer-victory-phase.ts
+++ b/src/phases/trainer-victory-phase.ts
@@ -27,35 +27,34 @@ export class TrainerVictoryPhase extends BattlePhase {
const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct?
// Validate Voucher for boss trainers
- if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
- if (
- !globalScene.validateVoucher(vouchers[TrainerType[trainerType]]) &&
- globalScene.currentBattle.trainer?.config.isBoss
- ) {
- if (timedEventManager.getUpgradeUnlockedVouchers()) {
- globalScene.phaseManager.unshiftNew(
- "ModifierRewardPhase",
- [
- modifierTypes.VOUCHER_PLUS,
- modifierTypes.VOUCHER_PLUS,
- modifierTypes.VOUCHER_PLUS,
- modifierTypes.VOUCHER_PREMIUM,
- ][vouchers[TrainerType[trainerType]].voucherType],
- );
- } else {
- globalScene.phaseManager.unshiftNew(
- "ModifierRewardPhase",
- [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][
- vouchers[TrainerType[trainerType]].voucherType
- ],
- );
- }
+ if (
+ vouchers.hasOwnProperty(TrainerType[trainerType])
+ && !globalScene.validateVoucher(vouchers[TrainerType[trainerType]])
+ && globalScene.currentBattle.trainer?.config.isBoss
+ ) {
+ if (timedEventManager.getUpgradeUnlockedVouchers()) {
+ globalScene.phaseManager.unshiftNew(
+ "ModifierRewardPhase",
+ [
+ modifierTypes.VOUCHER_PLUS,
+ modifierTypes.VOUCHER_PLUS,
+ modifierTypes.VOUCHER_PLUS,
+ modifierTypes.VOUCHER_PREMIUM,
+ ][vouchers[TrainerType[trainerType]].voucherType],
+ );
+ } else {
+ globalScene.phaseManager.unshiftNew(
+ "ModifierRewardPhase",
+ [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][
+ vouchers[TrainerType[trainerType]].voucherType
+ ],
+ );
}
}
// Breeders in Space achievement
if (
- globalScene.arena.biomeType === BiomeId.SPACE &&
- (trainerType === TrainerType.BREEDER || trainerType === TrainerType.EXPERT_POKEMON_BREEDER)
+ globalScene.arena.biomeType === BiomeId.SPACE
+ && (trainerType === TrainerType.BREEDER || trainerType === TrainerType.EXPERT_POKEMON_BREEDER)
) {
globalScene.validateAchv(achvs.BREEDERS_IN_SPACE);
}
@@ -87,7 +86,7 @@ export class TrainerVictoryPhase extends BattlePhase {
showMessageOrEnd();
};
let showMessageOrEnd = () => this.end();
- if (victoryMessages?.length) {
+ if (victoryMessages?.length > 0) {
if (globalScene.currentBattle.trainer?.config.hasCharSprite && !globalScene.ui.shouldSkipDialogue(message)) {
const originalFunc = showMessageOrEnd;
showMessageOrEnd = () =>
diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts
index b2ab096102c..d1b1f01c130 100644
--- a/src/phases/turn-init-phase.ts
+++ b/src/phases/turn-init-phase.ts
@@ -25,13 +25,13 @@ export class TurnInitPhase extends FieldPhase {
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
- if (!allowedPokemon.length) {
+ if (allowedPokemon.length === 0) {
// If there are no longer any legal pokemon in the party, game over.
globalScene.phaseManager.clearPhaseQueue();
globalScene.phaseManager.unshiftNew("GameOverPhase");
} else if (
- allowedPokemon.length >= globalScene.currentBattle.getBattlerCount() ||
- (globalScene.currentBattle.double && !allowedPokemon[0].isActive(true))
+ allowedPokemon.length >= globalScene.currentBattle.getBattlerCount()
+ || (globalScene.currentBattle.double && !allowedPokemon[0].isActive(true))
) {
// If there is at least one pokemon in the back that is legal to switch in, force a switch.
p.switchOut();
diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts
index 8fc7a763c8f..1733901d527 100644
--- a/src/phases/turn-start-phase.ts
+++ b/src/phases/turn-start-phase.ts
@@ -72,7 +72,7 @@ export class TurnStartPhase extends FieldPhase {
applyAbAttrs("PreventBypassSpeedChanceAbAttr", {
pokemon: p,
bypass: bypassSpeed,
- canCheckHeldItems: canCheckHeldItems,
+ canCheckHeldItems,
});
if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
@@ -179,12 +179,11 @@ export class TurnStartPhase extends FieldPhase {
// https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-64#post-9244179
phaseManager.pushNew("WeatherEffectPhase");
+ phaseManager.pushNew("PositionalTagPhase");
phaseManager.pushNew("BerryPhase");
- /** Add a new phase to check who should be taking status damage */
phaseManager.pushNew("CheckStatusEffectPhase", moveOrder);
- phaseManager.pushNew("PositionalTagPhase");
phaseManager.pushNew("TurnEndPhase");
/*
@@ -227,8 +226,8 @@ export class TurnStartPhase extends FieldPhase {
// TODO: This seems somewhat dubious
const move =
- pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) ??
- new PokemonMove(queuedMove.move);
+ pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp())
+ ?? new PokemonMove(queuedMove.move);
if (move.getMove().hasAttr("MoveHeaderAttr")) {
globalScene.phaseManager.unshiftNew("MoveHeaderPhase", pokemon, move);
diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts
index 81db543001b..27cd787a08d 100644
--- a/src/phases/weather-effect-phase.ts
+++ b/src/phases/weather-effect-phase.ts
@@ -48,9 +48,9 @@ export class WeatherEffectPhase extends CommonAnimPhase {
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
if (
- cancelled.value ||
- pokemon.getTag(BattlerTagType.UNDERGROUND) ||
- pokemon.getTag(BattlerTagType.UNDERWATER)
+ cancelled.value
+ || pokemon.getTag(BattlerTagType.UNDERGROUND)
+ || pokemon.getTag(BattlerTagType.UNDERWATER)
) {
return;
}
@@ -63,9 +63,9 @@ export class WeatherEffectPhase extends CommonAnimPhase {
this.executeForAll((pokemon: Pokemon) => {
const immune =
- !pokemon ||
- !!pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length ||
- pokemon.switchOutStatus;
+ !pokemon
+ || pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length > 0
+ || pokemon.switchOutStatus;
if (!immune) {
inflictDamage(pokemon);
}
diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts
index b20bacf6a5e..9132fd585ec 100644
--- a/src/pipelines/field-sprite.ts
+++ b/src/pipelines/field-sprite.ts
@@ -9,7 +9,7 @@ export class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPi
constructor(game: Phaser.Game, config?: Phaser.Types.Renderer.WebGL.WebGLPipelineConfig) {
super(
config || {
- game: game,
+ game,
name: "field-sprite",
fragShader: fieldSpriteFragShader,
vertShader: spriteVertShader,
diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts
index 8d38eda562d..65f8007e2f8 100644
--- a/src/pipelines/sprite.ts
+++ b/src/pipelines/sprite.ts
@@ -13,7 +13,7 @@ export class SpritePipeline extends FieldSpritePipeline {
constructor(game: Phaser.Game) {
super(game, {
- game: game,
+ game,
name: "sprite",
fragShader: spriteFragShader,
vertShader: spriteVertShader,
@@ -52,9 +52,9 @@ export class SpritePipeline extends FieldSpritePipeline {
const ignoreOverride = data["ignoreOverride"] as boolean;
const isEntityObj =
- sprite.parentContainer instanceof Pokemon ||
- sprite.parentContainer instanceof Trainer ||
- sprite.parentContainer instanceof MysteryEncounterIntroVisuals;
+ sprite.parentContainer instanceof Pokemon
+ || sprite.parentContainer instanceof Trainer
+ || sprite.parentContainer instanceof MysteryEncounterIntroVisuals;
const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer;
const position = isEntityObj ? [sprite.parentContainer.x, sprite.parentContainer.y] : [sprite.x, sprite.y];
if (field) {
@@ -65,8 +65,8 @@ export class SpritePipeline extends FieldSpritePipeline {
-(sprite.width - sprite.frame.width) / 2 + sprite.frame.x + (!ignoreFieldPos ? sprite.x - field.x : 0);
if (sprite.originY === 0.5) {
position[1] +=
- (sprite.height / 2) * ((isEntityObj ? sprite.parentContainer : sprite).scale - 1) +
- (!ignoreFieldPos ? sprite.y - field.y : 0);
+ (sprite.height / 2) * ((isEntityObj ? sprite.parentContainer : sprite).scale - 1)
+ + (!ignoreFieldPos ? sprite.y - field.y : 0);
}
this.set1f("teraTime", (this.game.getTime() % 500000) / 500000);
this.set3fv(
@@ -90,9 +90,9 @@ export class SpritePipeline extends FieldSpritePipeline {
if (globalScene.fusionPaletteSwaps) {
const spriteColors = ((ignoreOverride && data["spriteColorsBase"]) || data["spriteColors"] || []) as number[][];
- const fusionSpriteColors = ((ignoreOverride && data["fusionSpriteColorsBase"]) ||
- data["fusionSpriteColors"] ||
- []) as number[][];
+ const fusionSpriteColors = ((ignoreOverride && data["fusionSpriteColorsBase"])
+ || data["fusionSpriteColors"]
+ || []) as number[][];
const emptyColors = [0, 0, 0, 0];
const flatSpriteColors: number[] = [];
@@ -132,14 +132,14 @@ export class SpritePipeline extends FieldSpritePipeline {
const flatVariantColors: number[] = [];
if (
- (sprite.parentContainer instanceof Pokemon ? sprite.parentContainer.shiny : !!data["shiny"]) &&
- (variantColors =
+ (sprite.parentContainer instanceof Pokemon ? sprite.parentContainer.shiny : !!data["shiny"])
+ && (variantColors =
variantColorCache[
sprite.parentContainer instanceof Pokemon
? sprite.parentContainer.getSprite().texture.key
: data["spriteKey"]
- ]) &&
- variantColors.hasOwnProperty(variant)
+ ])
+ && variantColors.hasOwnProperty(variant)
) {
const baseColors = Object.keys(variantColors[variant]);
for (let c = 0; c < 32; c++) {
@@ -197,9 +197,9 @@ export class SpritePipeline extends FieldSpritePipeline {
const yShadowOffset = (sprite.pipelineData["yShadowOffset"] as number) ?? 0;
if (hasShadow) {
const isEntityObj =
- sprite.parentContainer instanceof Pokemon ||
- sprite.parentContainer instanceof Trainer ||
- sprite.parentContainer instanceof MysteryEncounterIntroVisuals;
+ sprite.parentContainer instanceof Pokemon
+ || sprite.parentContainer instanceof Trainer
+ || sprite.parentContainer instanceof MysteryEncounterIntroVisuals;
const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer;
const fieldScaleRatio = field.scale / 6;
const baseY = ((isEntityObj ? sprite.parentContainer.y : sprite.y + sprite.height) * 6) / fieldScaleRatio;
diff --git a/src/plugins/api/pokerogue-daily-api.ts b/src/plugins/api/pokerogue-daily-api.ts
index 5ea3846e60e..dfde4720730 100644
--- a/src/plugins/api/pokerogue-daily-api.ts
+++ b/src/plugins/api/pokerogue-daily-api.ts
@@ -1,6 +1,6 @@
import { ApiBase } from "#api/api-base";
import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/api/pokerogue-daily-api";
-import type { RankingEntry } from "#ui/daily-run-scoreboard";
+import type { RankingEntry } from "#ui/containers/daily-run-scoreboard";
/**
* A wrapper for daily-run PokéRogue API requests.
diff --git a/src/plugins/api/pokerogue-session-savedata-api.ts b/src/plugins/api/pokerogue-session-savedata-api.ts
index 39fa292f9f1..e1c67ef7245 100644
--- a/src/plugins/api/pokerogue-session-savedata-api.ts
+++ b/src/plugins/api/pokerogue-session-savedata-api.ts
@@ -1,5 +1,4 @@
import { ApiBase } from "#api/api-base";
-import type { SessionSaveData } from "#system/game-data";
import type {
ClearSessionSavedataRequest,
ClearSessionSavedataResponse,
@@ -8,6 +7,7 @@ import type {
NewClearSessionSavedataRequest,
UpdateSessionSavedataRequest,
} from "#types/api/pokerogue-session-save-data-api";
+import type { SessionSaveData } from "#types/save-data";
/**
* A wrapper for PokéRogue session savedata API requests.
@@ -16,8 +16,8 @@ export class PokerogueSessionSavedataApi extends ApiBase {
//#region Public
/**
- * Mark a session as cleared aka "newclear".\
- * *This is **NOT** the same as {@linkcode clear | clear()}.*
+ * Mark a session as cleared aka "newclear". \
+ * _This is **NOT** the same as {@linkcode clear | clear()}._
* @param params The {@linkcode NewClearSessionSavedataRequest} to send
* @returns The raw savedata as `string`.
* @throws Error if the request fails
@@ -82,6 +82,7 @@ export class PokerogueSessionSavedataApi extends ApiBase {
try {
const urlSearchParams = this.toUrlSearchParams(params);
const response = await this.doGet(`/savedata/session/delete?${urlSearchParams}`);
+ console.debug("%cSending a request to delete session in slot %d", "color: blue", params.slot);
if (response.ok) {
return null;
@@ -94,8 +95,8 @@ export class PokerogueSessionSavedataApi extends ApiBase {
}
/**
- * Clears the session savedata of the given slot.\
- * *This is **NOT** the same as {@linkcode newclear | newclear()}.*
+ * Clears the session savedata of the given slot. \
+ * _This is **NOT** the same as {@linkcode newclear | newclear()}._
* @param params The {@linkcode ClearSessionSavedataRequest} to send
* @param sessionData The {@linkcode SessionSaveData} object
*/
diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts
index 62fc73a10a3..b6a5bacc7ba 100644
--- a/src/plugins/i18n.ts
+++ b/src/plugins/i18n.ts
@@ -121,8 +121,8 @@ async function initFonts(language: string | undefined) {
}
/**
- * I18n money formatter with. (useful for BBCode coloring of text)\
- * *If you don't want the BBCode tag applied, just use 'number' formatter*
+ * I18n money formatter with. (useful for BBCode coloring of text) \
+ * _If you don't want the BBCode tag applied, just use 'number' formatter_
* @example Input: `{{myMoneyValue, money}}`
* Output: `@[MONEY]{₽100,000,000}`
* @param amount the money amount
diff --git a/src/system/achv.ts b/src/system/achv.ts
index 4e9bb54c304..1a66dc83878 100644
--- a/src/system/achv.ts
+++ b/src/system/achv.ts
@@ -519,9 +519,9 @@ export const achvs = {
"reviver_seed",
100,
c =>
- c instanceof FreshStartChallenge &&
- c.value === 1 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof FreshStartChallenge
+ && c.value === 1
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -546,9 +546,9 @@ export const achvs = {
"ribbon_gen1",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 1 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 1
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -558,9 +558,9 @@ export const achvs = {
"ribbon_gen2",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 2 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 2
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -570,9 +570,9 @@ export const achvs = {
"ribbon_gen3",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 3 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 3
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -582,9 +582,9 @@ export const achvs = {
"ribbon_gen4",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 4 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 4
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -594,9 +594,9 @@ export const achvs = {
"ribbon_gen5",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 5 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 5
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -606,9 +606,9 @@ export const achvs = {
"ribbon_gen6",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 6 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 6
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -618,9 +618,9 @@ export const achvs = {
"ribbon_gen7",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 7 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 7
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -630,9 +630,9 @@ export const achvs = {
"ribbon_gen8",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 8 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 8
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -642,9 +642,9 @@ export const achvs = {
"ribbon_gen9",
100,
c =>
- c instanceof SingleGenerationChallenge &&
- c.value === 9 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleGenerationChallenge
+ && c.value === 9
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -654,9 +654,9 @@ export const achvs = {
"silk_scarf",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 1 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 1
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -666,9 +666,9 @@ export const achvs = {
"black_belt",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 2 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 2
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -678,9 +678,9 @@ export const achvs = {
"sharp_beak",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 3 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 3
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -690,9 +690,9 @@ export const achvs = {
"poison_barb",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 4 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 4
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -702,9 +702,9 @@ export const achvs = {
"soft_sand",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 5 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 5
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -714,9 +714,9 @@ export const achvs = {
"hard_stone",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 6 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 6
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -726,9 +726,9 @@ export const achvs = {
"silver_powder",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 7 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 7
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -738,9 +738,9 @@ export const achvs = {
"spell_tag",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 8 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 8
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -750,9 +750,9 @@ export const achvs = {
"metal_coat",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 9 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 9
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -762,9 +762,9 @@ export const achvs = {
"charcoal",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 10 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 10
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -774,9 +774,9 @@ export const achvs = {
"mystic_water",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 11 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 11
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -786,9 +786,9 @@ export const achvs = {
"miracle_seed",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 12 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 12
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -798,9 +798,9 @@ export const achvs = {
"magnet",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 13 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 13
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -810,9 +810,9 @@ export const achvs = {
"twisted_spoon",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 14 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 14
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -822,9 +822,9 @@ export const achvs = {
"never_melt_ice",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 15 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 15
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -834,9 +834,9 @@ export const achvs = {
"dragon_fang",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 16 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 16
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -846,9 +846,9 @@ export const achvs = {
"black_glasses",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 17 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 17
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -858,9 +858,9 @@ export const achvs = {
"fairy_feather",
100,
c =>
- c instanceof SingleTypeChallenge &&
- c.value === 18 &&
- !globalScene.gameMode.challenges.some(
+ c instanceof SingleTypeChallenge
+ && c.value === 18
+ && !globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
@@ -877,9 +877,9 @@ export const achvs = {
"cracked_pot",
50,
c =>
- c instanceof FlipStatChallenge &&
- c.value > 0 &&
- globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0),
+ c instanceof FlipStatChallenge
+ && c.value > 0
+ && globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0),
).setSecret(),
BREEDERS_IN_SPACE: new Achv("breedersInSpace", "breedersInSpace.description", "moon_stone", 50).setSecret(),
};
diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts
index b2a04f96a55..18620e15223 100644
--- a/src/system/arena-data.ts
+++ b/src/system/arena-data.ts
@@ -5,14 +5,14 @@ import { Terrain } from "#data/terrain";
import { Weather } from "#data/weather";
import type { BiomeId } from "#enums/biome-id";
import { Arena } from "#field/arena";
-import type { ArenaTagTypeData } from "#types/arena-tags";
+import type { ArenaTagData } from "#types/arena-tags";
import type { NonFunctionProperties } from "#types/type-helpers";
export interface SerializedArenaData {
biome: BiomeId;
weather: NonFunctionProperties | null;
terrain: NonFunctionProperties | null;
- tags?: ArenaTagTypeData[];
+ tags?: ArenaTagData[];
positionalTags: SerializedPositionalTag[];
playerTerasUsed?: number;
}
@@ -31,7 +31,7 @@ export class ArenaData {
// is not yet an instance of `ArenaTag`
this.tags =
source.tags
- ?.map((t: ArenaTag | ArenaTagTypeData) => loadArenaTag(t))
+ ?.map((t: ArenaTag | ArenaTagData) => loadArenaTag(t))
?.filter((tag): tag is SerializableArenaTag => tag instanceof SerializableArenaTag) ?? [];
this.playerTerasUsed = source.playerTerasUsed ?? 0;
diff --git a/src/system/game-data.ts b/src/system/game-data.ts
index 3a4dafb2de2..247f8c3660a 100644
--- a/src/system/game-data.ts
+++ b/src/system/game-data.ts
@@ -1,6 +1,5 @@
import { pokerogueApi } from "#api/pokerogue-api";
import { clientSessionId, loggedInUser, updateUserInfo } from "#app/account";
-import type { PokeballCounts } from "#app/battle-scene";
import { defaultStarterSpecies, saveKey } from "#app/constants";
import { getGameMode } from "#app/game-mode";
import { globalScene } from "#app/global-scene";
@@ -24,11 +23,9 @@ import { Device } from "#enums/devices";
import { DexAttr } from "#enums/dex-attr";
import { GameDataType } from "#enums/game-data-type";
import { GameModes } from "#enums/game-modes";
-import type { MoveId } from "#enums/move-id";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Nature } from "#enums/nature";
import { PlayerGender } from "#enums/player-gender";
-import type { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect";
import { TrainerVariant } from "#enums/trainer-variant";
@@ -62,12 +59,26 @@ import {
import { VoucherType, vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config";
import type { DexData, DexEntry } from "#types/dex-data";
-import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler";
+import type {
+ AchvUnlocks,
+ DexAttrProps,
+ RunHistoryData,
+ SeenDialogues,
+ SessionSaveData,
+ StarterData,
+ SystemSaveData,
+ TutorialFlags,
+ Unlocks,
+ VoucherCounts,
+ VoucherUnlocks,
+} from "#types/save-data";
+import { RUN_HISTORY_LIMIT } from "#ui/handlers/run-history-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common";
import { decrypt, encrypt } from "#utils/data";
import { getEnumKeys } from "#utils/enums";
import { getPokemonSpecies } from "#utils/pokemon-utils";
+import { isBeta } from "#utils/utility-vars";
import { AES, enc } from "crypto-js";
import i18next from "i18next";
@@ -93,132 +104,6 @@ function getDataTypeKey(dataType: GameDataType, slotId = 0): string {
}
}
-// TODO: Move all these exported interfaces to @types
-export interface SystemSaveData {
- trainerId: number;
- secretId: number;
- gender: PlayerGender;
- dexData: DexData;
- starterData: StarterData;
- gameStats: GameStats;
- unlocks: Unlocks;
- achvUnlocks: AchvUnlocks;
- voucherUnlocks: VoucherUnlocks;
- voucherCounts: VoucherCounts;
- eggs: EggData[];
- gameVersion: string;
- timestamp: number;
- eggPity: number[];
- unlockPity: number[];
-}
-
-export interface SessionSaveData {
- seed: string;
- playTime: number;
- gameMode: GameModes;
- party: PokemonData[];
- enemyParty: PokemonData[];
- modifiers: PersistentModifierData[];
- enemyModifiers: PersistentModifierData[];
- arena: ArenaData;
- pokeballCounts: PokeballCounts;
- money: number;
- score: number;
- waveIndex: number;
- battleType: BattleType;
- trainer: TrainerData;
- gameVersion: string;
- /** The player-chosen name of the run */
- name: string;
- timestamp: number;
- challenges: ChallengeData[];
- mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
- mysteryEncounterSaveData: MysteryEncounterSaveData;
- /**
- * Counts the amount of pokemon fainted in your party during the current arena encounter.
- */
- playerFaints: number;
-}
-
-interface Unlocks {
- [key: number]: boolean;
-}
-
-export interface AchvUnlocks {
- [key: string]: number;
-}
-
-export interface VoucherUnlocks {
- [key: string]: number;
-}
-
-export interface VoucherCounts {
- [type: string]: number;
-}
-
-export type StarterMoveset = [MoveId] | [MoveId, MoveId] | [MoveId, MoveId, MoveId] | [MoveId, MoveId, MoveId, MoveId];
-
-export interface StarterFormMoveData {
- [key: number]: StarterMoveset;
-}
-
-export interface StarterMoveData {
- [key: number]: StarterMoveset | StarterFormMoveData;
-}
-
-export interface StarterAttributes {
- nature?: number;
- ability?: number;
- variant?: number;
- form?: number;
- female?: boolean;
- shiny?: boolean;
- favorite?: boolean;
- nickname?: string;
- tera?: PokemonType;
-}
-
-export interface DexAttrProps {
- shiny: boolean;
- female: boolean;
- variant: Variant;
- formIndex: number;
-}
-
-export type RunHistoryData = Record;
-
-export interface RunEntry {
- entry: SessionSaveData;
- isVictory: boolean;
- /*Automatically set to false at the moment - implementation TBD*/
- isFavorite: boolean;
-}
-
-export interface StarterDataEntry {
- moveset: StarterMoveset | StarterFormMoveData | null;
- eggMoves: number;
- candyCount: number;
- friendship: number;
- abilityAttr: number;
- passiveAttr: number;
- valueReduction: number;
- classicWinCount: number;
-}
-
-export interface StarterData {
- [key: number]: StarterDataEntry;
-}
-
-// TODO: Rework into a bitmask
-export type TutorialFlags = {
- [key in Tutorial]: boolean;
-};
-
-// TODO: Rework into a bitmask
-export interface SeenDialogues {
- [key: string]: boolean;
-}
-
const systemShortKeys = {
seenAttr: "$sa",
caughtAttr: "$ca",
@@ -367,10 +252,10 @@ export class GameData {
if (!bypassLogin) {
pokerogueApi.savedata.system.get({ clientSessionId }).then(saveDataOrErr => {
if (
- typeof saveDataOrErr === "number" ||
- !saveDataOrErr ||
- saveDataOrErr.length === 0 ||
- saveDataOrErr[0] !== "{"
+ typeof saveDataOrErr === "number"
+ || !saveDataOrErr
+ || saveDataOrErr.length === 0
+ || saveDataOrErr[0] !== "{"
) {
if (saveDataOrErr === 404) {
globalScene.phaseManager.queueMessage(
@@ -419,7 +304,15 @@ export class GameData {
}
}
- console.debug(systemData);
+ if (isLocal || isBeta) {
+ try {
+ console.debug(
+ this.parseSystemData(JSON.stringify(systemData, (_, v: any) => (typeof v === "bigint" ? v.toString() : v))),
+ );
+ } catch (err) {
+ console.debug("Attempt to log system data failed:", err);
+ }
+ }
localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemDataStr, bypassLogin));
@@ -590,7 +483,7 @@ export class GameData {
const timestamp = runEntry.timestamp.toString();
runHistoryData[timestamp] = {
entry: runEntry,
- isVictory: isVictory,
+ isVictory,
isFavorite: false,
};
localStorage.setItem(
@@ -945,45 +838,46 @@ export class GameData {
} as SessionSaveData;
}
- getSession(slotId: number): Promise {
- // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
- return new Promise(async (resolve, reject) => {
- if (slotId < 0) {
- return resolve(null);
+ async getSession(slotId: number): Promise {
+ const { promise, resolve, reject } = Promise.withResolvers();
+ if (slotId < 0) {
+ resolve(null);
+ return promise;
+ }
+ const handleSessionData = async (sessionDataStr: string) => {
+ try {
+ const sessionData = this.parseSessionData(sessionDataStr);
+ resolve(sessionData);
+ } catch (err) {
+ reject(err);
+ return;
}
- const handleSessionData = async (sessionDataStr: string) => {
- try {
- const sessionData = this.parseSessionData(sessionDataStr);
- resolve(sessionData);
- } catch (err) {
- reject(err);
- return;
- }
- };
+ };
- if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`)) {
- pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId }).then(async response => {
- if (!response || response?.length === 0 || response?.[0] !== "{") {
- console.error(response);
- return resolve(null);
- }
+ if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`)) {
+ const response = await pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId });
- localStorage.setItem(
- `sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
- encrypt(response, bypassLogin),
- );
-
- await handleSessionData(response);
- });
- } else {
- const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
- if (sessionData) {
- await handleSessionData(decrypt(sessionData, bypassLogin));
- } else {
- return resolve(null);
- }
+ if (!response || response?.length === 0 || response?.[0] !== "{") {
+ console.error(response);
+ resolve(null);
+ return promise;
}
- });
+
+ localStorage.setItem(
+ `sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
+ encrypt(response, bypassLogin),
+ );
+
+ await handleSessionData(response);
+ return promise;
+ }
+ const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
+ if (sessionData) {
+ await handleSessionData(decrypt(sessionData, bypassLogin));
+ return promise;
+ }
+ resolve(null);
+ return promise;
}
async renameSession(slotId: number, newName: string): Promise {
@@ -1028,166 +922,177 @@ export class GameData {
return !(success !== null && !success);
}
- loadSession(slotId: number, sessionData?: SessionSaveData): Promise {
- // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
- return new Promise(async (resolve, reject) => {
- try {
- const initSessionFromData = async (sessionData: SessionSaveData) => {
- console.debug(sessionData);
-
- globalScene.gameMode = getGameMode(sessionData.gameMode || GameModes.CLASSIC);
- if (sessionData.challenges) {
- globalScene.gameMode.challenges = sessionData.challenges.map(c => c.toChallenge());
+ async loadSession(slotId: number, sessionData?: SessionSaveData): Promise {
+ const { promise, resolve, reject } = Promise.withResolvers();
+ try {
+ const initSessionFromData = (fromSession: SessionSaveData) => {
+ if (isLocal || isBeta) {
+ try {
+ console.debug(
+ this.parseSessionData(
+ JSON.stringify(fromSession, (_, v: any) => (typeof v === "bigint" ? v.toString() : v)),
+ ),
+ );
+ } catch (err) {
+ console.debug("Attempt to log session data failed:", err);
}
-
- globalScene.setSeed(sessionData.seed || globalScene.game.config.seed[0]);
- globalScene.resetSeed();
-
- console.log("Seed:", globalScene.seed);
-
- globalScene.sessionPlayTime = sessionData.playTime || 0;
- globalScene.lastSavePlayTime = 0;
-
- const loadPokemonAssets: Promise[] = [];
-
- const party = globalScene.getPlayerParty();
- party.splice(0, party.length);
-
- for (const p of sessionData.party) {
- const pokemon = p.toPokemon() as PlayerPokemon;
- pokemon.setVisible(false);
- loadPokemonAssets.push(pokemon.loadAssets(false));
- party.push(pokemon);
- }
-
- Object.keys(globalScene.pokeballCounts).forEach((key: string) => {
- globalScene.pokeballCounts[key] = sessionData.pokeballCounts[key] || 0;
- });
- if (Overrides.POKEBALL_OVERRIDE.active) {
- globalScene.pokeballCounts = Overrides.POKEBALL_OVERRIDE.pokeballs;
- }
-
- globalScene.money = Math.floor(sessionData.money || 0);
- globalScene.updateMoneyText();
-
- if (globalScene.money > this.gameStats.highestMoney) {
- this.gameStats.highestMoney = globalScene.money;
- }
-
- globalScene.score = sessionData.score;
- globalScene.updateScoreText();
-
- globalScene.mysteryEncounterSaveData = new MysteryEncounterSaveData(sessionData.mysteryEncounterSaveData);
-
- globalScene.newArena(sessionData.arena.biome, sessionData.playerFaints);
-
- const battleType = sessionData.battleType || 0;
- const trainerConfig = sessionData.trainer ? trainerConfigs[sessionData.trainer.trainerType] : null;
- const mysteryEncounterType =
- sessionData.mysteryEncounterType !== -1 ? sessionData.mysteryEncounterType : undefined;
- const battle = globalScene.newBattle(
- sessionData.waveIndex,
- battleType,
- sessionData.trainer,
- battleType === BattleType.TRAINER
- ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE
- : sessionData.enemyParty.length > 1,
- mysteryEncounterType,
- );
- battle.enemyLevels = sessionData.enemyParty.map(p => p.level);
-
- globalScene.arena.init();
-
- sessionData.enemyParty.forEach((enemyData, e) => {
- const enemyPokemon = enemyData.toPokemon(
- battleType,
- e,
- sessionData.trainer?.variant === TrainerVariant.DOUBLE,
- ) as EnemyPokemon;
- battle.enemyParty[e] = enemyPokemon;
- if (battleType === BattleType.WILD) {
- battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
- }
-
- loadPokemonAssets.push(enemyPokemon.loadAssets());
- });
-
- globalScene.arena.weather = sessionData.arena.weather;
- globalScene.arena.eventTarget.dispatchEvent(
- new WeatherChangedEvent(
- WeatherType.NONE,
- globalScene.arena.weather?.weatherType!,
- globalScene.arena.weather?.turnsLeft!,
- ),
- ); // TODO: is this bang correct?
-
- globalScene.arena.terrain = sessionData.arena.terrain;
- globalScene.arena.eventTarget.dispatchEvent(
- new TerrainChangedEvent(
- TerrainType.NONE,
- globalScene.arena.terrain?.terrainType!,
- globalScene.arena.terrain?.turnsLeft!,
- ),
- ); // TODO: is this bang correct?
-
- globalScene.arena.playerTerasUsed = sessionData.arena.playerTerasUsed;
-
- globalScene.arena.tags = sessionData.arena.tags;
- if (globalScene.arena.tags) {
- for (const tag of globalScene.arena.tags) {
- if (tag instanceof EntryHazardTag) {
- const { tagType, side, turnCount, layers, maxLayers } = tag as EntryHazardTag;
- globalScene.arena.eventTarget.dispatchEvent(
- new TagAddedEvent(tagType, side, turnCount, layers, maxLayers),
- );
- } else {
- globalScene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount));
- }
- }
- }
-
- globalScene.arena.positionalTagManager.tags = sessionData.arena.positionalTags.map(tag =>
- loadPositionalTag(tag),
- );
-
- if (globalScene.modifiers.length) {
- console.warn("Existing modifiers not cleared on session load, deleting...");
- globalScene.modifiers = [];
- }
- for (const modifierData of sessionData.modifiers) {
- const modifier = modifierData.toModifier(Modifier[modifierData.className]);
- if (modifier) {
- globalScene.addModifier(modifier, true);
- }
- }
- globalScene.updateModifiers(true);
-
- for (const enemyModifierData of sessionData.enemyModifiers) {
- const modifier = enemyModifierData.toModifier(Modifier[enemyModifierData.className]);
- if (modifier) {
- globalScene.addEnemyModifier(modifier, true);
- }
- }
-
- globalScene.updateModifiers(false);
-
- Promise.all(loadPokemonAssets).then(() => resolve(true));
- };
- if (sessionData) {
- initSessionFromData(sessionData);
- } else {
- this.getSession(slotId)
- .then(data => data && initSessionFromData(data))
- .catch(err => {
- reject(err);
- return;
- });
}
- } catch (err) {
- reject(err);
- return;
+
+ globalScene.gameMode = getGameMode(fromSession.gameMode || GameModes.CLASSIC);
+ if (fromSession.challenges) {
+ globalScene.gameMode.challenges = fromSession.challenges.map(c => c.toChallenge());
+ }
+
+ globalScene.setSeed(fromSession.seed || globalScene.game.config.seed[0]);
+ globalScene.resetSeed();
+
+ console.log("Seed:", globalScene.seed);
+
+ globalScene.sessionPlayTime = fromSession.playTime || 0;
+ globalScene.lastSavePlayTime = 0;
+
+ const loadPokemonAssets: Promise[] = [];
+
+ const party = globalScene.getPlayerParty();
+ party.splice(0, party.length);
+
+ for (const p of fromSession.party) {
+ const pokemon = p.toPokemon() as PlayerPokemon;
+ pokemon.setVisible(false);
+ loadPokemonAssets.push(pokemon.loadAssets(false));
+ party.push(pokemon);
+ }
+
+ Object.keys(globalScene.pokeballCounts).forEach((key: string) => {
+ globalScene.pokeballCounts[key] = fromSession.pokeballCounts[key] || 0;
+ });
+ if (Overrides.POKEBALL_OVERRIDE.active) {
+ globalScene.pokeballCounts = Overrides.POKEBALL_OVERRIDE.pokeballs;
+ }
+
+ globalScene.money = Math.floor(fromSession.money || 0);
+ globalScene.updateMoneyText();
+
+ if (globalScene.money > this.gameStats.highestMoney) {
+ this.gameStats.highestMoney = globalScene.money;
+ }
+
+ globalScene.score = fromSession.score;
+ globalScene.updateScoreText();
+
+ globalScene.mysteryEncounterSaveData = new MysteryEncounterSaveData(fromSession.mysteryEncounterSaveData);
+
+ globalScene.newArena(fromSession.arena.biome, fromSession.playerFaints);
+
+ const battleType = fromSession.battleType || 0;
+ const trainerConfig = fromSession.trainer ? trainerConfigs[fromSession.trainer.trainerType] : null;
+ const mysteryEncounterType =
+ fromSession.mysteryEncounterType !== -1 ? fromSession.mysteryEncounterType : undefined;
+ const battle = globalScene.newBattle(
+ fromSession.waveIndex,
+ battleType,
+ fromSession.trainer,
+ battleType === BattleType.TRAINER
+ ? trainerConfig?.doubleOnly || fromSession.trainer?.variant === TrainerVariant.DOUBLE
+ : fromSession.enemyParty.length > 1,
+ mysteryEncounterType,
+ );
+ battle.enemyLevels = fromSession.enemyParty.map(p => p.level);
+
+ globalScene.arena.init();
+
+ fromSession.enemyParty.forEach((enemyData, e) => {
+ const enemyPokemon = enemyData.toPokemon(
+ battleType,
+ e,
+ fromSession.trainer?.variant === TrainerVariant.DOUBLE,
+ ) as EnemyPokemon;
+ battle.enemyParty[e] = enemyPokemon;
+ if (battleType === BattleType.WILD) {
+ battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
+ }
+
+ loadPokemonAssets.push(enemyPokemon.loadAssets());
+ });
+
+ globalScene.arena.weather = fromSession.arena.weather;
+ globalScene.arena.eventTarget.dispatchEvent(
+ new WeatherChangedEvent(
+ WeatherType.NONE,
+ globalScene.arena.weather?.weatherType!,
+ globalScene.arena.weather?.turnsLeft!,
+ ),
+ ); // TODO: is this bang correct?
+
+ globalScene.arena.terrain = fromSession.arena.terrain;
+ globalScene.arena.eventTarget.dispatchEvent(
+ new TerrainChangedEvent(
+ TerrainType.NONE,
+ globalScene.arena.terrain?.terrainType!,
+ globalScene.arena.terrain?.turnsLeft!,
+ ),
+ ); // TODO: is this bang correct?
+
+ globalScene.arena.playerTerasUsed = fromSession.arena.playerTerasUsed;
+
+ globalScene.arena.tags = fromSession.arena.tags;
+ if (globalScene.arena.tags) {
+ for (const tag of globalScene.arena.tags) {
+ if (tag instanceof EntryHazardTag) {
+ const { tagType, side, turnCount, layers, maxLayers } = tag as EntryHazardTag;
+ globalScene.arena.eventTarget.dispatchEvent(
+ new TagAddedEvent(tagType, side, turnCount, layers, maxLayers),
+ );
+ } else {
+ globalScene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount));
+ }
+ }
+ }
+
+ globalScene.arena.positionalTagManager.tags = fromSession.arena.positionalTags.map(tag =>
+ loadPositionalTag(tag),
+ );
+
+ if (globalScene.modifiers.length > 0) {
+ console.warn("Existing modifiers not cleared on session load, deleting...");
+ globalScene.modifiers = [];
+ }
+ for (const modifierData of fromSession.modifiers) {
+ const modifier = modifierData.toModifier(Modifier[modifierData.className]);
+ if (modifier) {
+ globalScene.addModifier(modifier, true);
+ }
+ }
+ globalScene.updateModifiers(true);
+
+ for (const enemyModifierData of fromSession.enemyModifiers) {
+ const modifier = enemyModifierData.toModifier(Modifier[enemyModifierData.className]);
+ if (modifier) {
+ globalScene.addEnemyModifier(modifier, true);
+ }
+ }
+
+ globalScene.updateModifiers(false);
+
+ Promise.all(loadPokemonAssets).then(() => resolve(true));
+ };
+ if (sessionData) {
+ initSessionFromData(sessionData);
+ } else {
+ this.getSession(slotId)
+ .then(data => {
+ return data && initSessionFromData(data);
+ })
+ .catch(err => {
+ reject(err);
+ return;
+ });
}
- });
+ } catch (err) {
+ reject(err);
+ }
+
+ return promise;
}
/**
@@ -1326,8 +1231,8 @@ export class GameData {
}
if (
- md instanceof Modifier.EnemyAttackStatusEffectChanceModifier &&
- (md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP)
+ md instanceof Modifier.EnemyAttackStatusEffectChanceModifier
+ && (md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP)
) {
// Discard any old "sleep/freeze chance tokens".
// TODO: make this migrate script
@@ -1394,7 +1299,7 @@ export class GameData {
system: systemData,
session: sessionData,
sessionSlotId: globalScene.sessionSlotId,
- clientSessionId: clientSessionId,
+ clientSessionId,
};
localStorage.setItem(
@@ -1470,8 +1375,9 @@ export class GameData {
});
}
+ // TODO: this is a really shit way of checking JSON validity
promise.then(response => {
- if (typeof response === "number" || !response?.length || response[0] !== "{") {
+ if (typeof response !== "string" || response.length === 0 || response.charAt(0) !== "{") {
console.error(response);
resolve(false);
return;
@@ -1515,6 +1421,7 @@ export class GameData {
switch (dataType) {
case GameDataType.SYSTEM: {
dataStr = this.convertSystemDataStr(dataStr);
+ dataStr = dataStr.replace(/"playTime":\d+/, `"playTime":${this.gameStats.playTime + 60}`);
const systemData = this.parseSystemData(dataStr);
valid = !!systemData.dexData && !!systemData.timestamp;
break;
@@ -1645,7 +1552,7 @@ export class GameData {
globalScene.executeWithSeedOffset(
() => {
const neutralNatures = [Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY];
- for (let s = 0; s < defaultStarterSpecies.length; s++) {
+ for (const _ of defaultStarterSpecies) {
defaultStarterNatures.push(randSeedItem(neutralNatures));
}
},
@@ -1691,8 +1598,8 @@ export class GameData {
setPokemonSeen(pokemon: Pokemon, incrementCount = true, trainer = false): void {
// Some Mystery Encounters block updates to these stats
if (
- globalScene.currentBattle?.isBattleMysteryEncounter() &&
- globalScene.currentBattle.mysteryEncounter?.preventGameStatsUpdates
+ globalScene.currentBattle?.isBattleMysteryEncounter()
+ && globalScene.currentBattle.mysteryEncounter?.preventGameStatsUpdates
) {
return;
}
@@ -1972,16 +1879,14 @@ export class GameData {
let message = prependSpeciesToMessage ? species.getName() + " " : "";
message +=
eggMoveIndex === 3
- ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName })
- : i18next.t("egg:eggMoveUnlock", { moveName: moveName });
+ ? i18next.t("egg:rareEggMoveUnlock", { moveName })
+ : i18next.t("egg:eggMoveUnlock", { moveName });
globalScene.ui.showText(message, null, () => resolve(true), null, true);
});
}
- /**
- * Checks whether the root species of a given {@PokemonSpecies} has been unlocked in the dex
- */
+ /** Return whether the root species of a given `PokemonSpecies` has been unlocked in the dex */
isRootSpeciesUnlocked(species: PokemonSpecies): boolean {
return !!this.dexData[species.getRootSpeciesId()]?.caughtAttr;
}
@@ -2196,9 +2101,9 @@ export class GameData {
for (const s of starterIds) {
const dexAttr = dexData[s].caughtAttr;
starterData[s].abilityAttr =
- (dexAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0) |
- (dexAttr & DexAttr.VARIANT_2 ? AbilityAttr.ABILITY_2 : 0) |
- (dexAttr & DexAttr.VARIANT_3 ? AbilityAttr.ABILITY_HIDDEN : 0);
+ (dexAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0)
+ | (dexAttr & DexAttr.VARIANT_2 ? AbilityAttr.ABILITY_2 : 0)
+ | (dexAttr & DexAttr.VARIANT_3 ? AbilityAttr.ABILITY_HIDDEN : 0);
if (dexAttr) {
if (!(dexAttr & DexAttr.DEFAULT_VARIANT)) {
dexData[s].caughtAttr ^= DexAttr.DEFAULT_VARIANT;
diff --git a/src/system/settings/settings-gamepad.ts b/src/system/settings/settings-gamepad.ts
index c334159cc3c..f16928f8c6c 100644
--- a/src/system/settings/settings-gamepad.ts
+++ b/src/system/settings/settings-gamepad.ts
@@ -104,18 +104,16 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole
case SettingGamepad.Button_Speed_Up:
case SettingGamepad.Button_Slow_Down:
case SettingGamepad.Button_Submit:
- if (value) {
- if (globalScene.ui) {
- const cancelHandler = (success = false): boolean => {
- globalScene.ui.revertMode();
- (globalScene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings();
- return success;
- };
- globalScene.ui.setOverlayMode(UiMode.GAMEPAD_BINDING, {
- target: setting,
- cancelHandler: cancelHandler,
- });
- }
+ if (value && globalScene.ui) {
+ const cancelHandler = (success = false): boolean => {
+ globalScene.ui.revertMode();
+ (globalScene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings();
+ return success;
+ };
+ globalScene.ui.setOverlayMode(UiMode.GAMEPAD_BINDING, {
+ target: setting,
+ cancelHandler,
+ });
}
break;
case SettingGamepad.Controller:
diff --git a/src/system/settings/settings-keyboard.ts b/src/system/settings/settings-keyboard.ts
index 07f4f86bdb0..b5e7fc0be2f 100644
--- a/src/system/settings/settings-keyboard.ts
+++ b/src/system/settings/settings-keyboard.ts
@@ -167,18 +167,16 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo
case SettingKeyboard.Alt_Button_Speed_Up:
case SettingKeyboard.Alt_Button_Slow_Down:
case SettingKeyboard.Alt_Button_Submit:
- if (value) {
- if (globalScene.ui) {
- const cancelHandler = (success = false): boolean => {
- globalScene.ui.revertMode();
- (globalScene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings();
- return success;
- };
- globalScene.ui.setOverlayMode(UiMode.KEYBOARD_BINDING, {
- target: setting,
- cancelHandler: cancelHandler,
- });
- }
+ if (value && globalScene.ui) {
+ const cancelHandler = (success = false): boolean => {
+ globalScene.ui.revertMode();
+ (globalScene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings();
+ return success;
+ };
+ globalScene.ui.setOverlayMode(UiMode.KEYBOARD_BINDING, {
+ target: setting,
+ cancelHandler,
+ });
}
break;
}
diff --git a/src/system/settings/settings-language.ts b/src/system/settings/settings-language.ts
new file mode 100644
index 00000000000..3fd90477e65
--- /dev/null
+++ b/src/system/settings/settings-language.ts
@@ -0,0 +1,101 @@
+import { globalScene } from "#app/global-scene";
+import type { SettingsDisplayUiHandler } from "#ui/settings-display-ui-handler";
+import i18next from "i18next";
+
+const cancelHandler = () => {
+ globalScene.ui.revertMode();
+ const handler = globalScene.ui.getHandler();
+ // Reset the cursor to the current language, if in the settings menu
+ if (handler && typeof (handler as SettingsDisplayUiHandler).setOptionCursor === "function") {
+ (handler as SettingsDisplayUiHandler).setOptionCursor(-1, 0, true);
+ }
+};
+
+const changeLocaleHandler = (locale: string): boolean => {
+ try {
+ i18next.changeLanguage(locale);
+ localStorage.setItem("prLang", locale);
+ cancelHandler();
+ // Reload the whole game to apply the new locale since also some constants are translated
+ window.location.reload();
+ return true;
+ } catch (error) {
+ console.error("Error changing locale:", error);
+ return false;
+ }
+};
+
+export const languageOptions = [
+ {
+ label: "English",
+ handler: () => changeLocaleHandler("en"),
+ },
+ {
+ label: "Español (ES)",
+ handler: () => changeLocaleHandler("es-ES"),
+ },
+ {
+ label: "Español (LATAM)",
+ handler: () => changeLocaleHandler("es-MX"),
+ },
+ {
+ label: "Français",
+ handler: () => changeLocaleHandler("fr"),
+ },
+ {
+ label: "Deutsch",
+ handler: () => changeLocaleHandler("de"),
+ },
+ {
+ label: "Italiano",
+ handler: () => changeLocaleHandler("it"),
+ },
+ {
+ label: "Português (BR)",
+ handler: () => changeLocaleHandler("pt-BR"),
+ },
+ {
+ label: "한국어",
+ handler: () => changeLocaleHandler("ko"),
+ },
+ {
+ label: "日本語",
+ handler: () => changeLocaleHandler("ja"),
+ },
+ {
+ label: "简体中文",
+ handler: () => changeLocaleHandler("zh-CN"),
+ },
+ {
+ label: "繁體中文",
+ handler: () => changeLocaleHandler("zh-TW"),
+ },
+ {
+ label: "Català (Needs Help)",
+ handler: () => changeLocaleHandler("ca"),
+ },
+ {
+ label: "Türkçe (Needs Help)",
+ handler: () => changeLocaleHandler("tr"),
+ },
+ {
+ label: "Русский (Needs Help)",
+ handler: () => changeLocaleHandler("ru"),
+ },
+ {
+ label: "Dansk (Needs Help)",
+ handler: () => changeLocaleHandler("da"),
+ },
+ {
+ label: "Română (Needs Help)",
+ handler: () => changeLocaleHandler("ro"),
+ },
+ {
+ label: "Tagalog (Needs Help)",
+ handler: () => changeLocaleHandler("tl"),
+ },
+ {
+ label: i18next.t("settings:back"),
+ handler: () => cancelHandler(),
+ },
+];
diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts
index 86305b3f7ed..78b6044b0fc 100644
--- a/src/system/settings/settings.ts
+++ b/src/system/settings/settings.ts
@@ -6,10 +6,10 @@ import { PlayerGender } from "#enums/player-gender";
import { ShopCursorTarget } from "#enums/shop-cursor-target";
import { UiMode } from "#enums/ui-mode";
import { CandyUpgradeNotificationChangedEvent } from "#events/battle-scene";
-import type { SettingsUiHandler } from "#ui/settings-ui-handler";
import { updateWindowType } from "#ui/ui-theme";
import { isLocal } from "#utils/common";
import i18next from "i18next";
+import { languageOptions } from "./settings-language";
const VOLUME_OPTIONS: SettingOption[] = [
{
@@ -120,6 +120,14 @@ export interface Setting {
default: number;
type: SettingType;
requireReload?: boolean;
+ /**
+ * Specifies the behavior when navigating left/right at the boundaries of the option
+ *
+ * - `true`: the cursor will stay on the boundary instead of moving
+ * - `false`: the cursor will wrap to the other end of the options list
+ * @defaultValue `false`
+ */
+ clamp?: boolean;
/** Whether the setting can be activated or not */
activatable?: boolean;
/** Determines whether the setting should be hidden from the UI */
@@ -230,6 +238,7 @@ export const Setting: Array = [
],
default: 3,
type: SettingType.GENERAL,
+ clamp: false,
},
{
key: SettingKeys.HP_Bar_Speed,
@@ -639,6 +648,7 @@ export const Setting: Array = [
options: VOLUME_OPTIONS,
default: 5,
type: SettingType.AUDIO,
+ clamp: true,
},
{
key: SettingKeys.BGM_Volume,
@@ -646,6 +656,7 @@ export const Setting: Array = [
options: VOLUME_OPTIONS,
default: 10,
type: SettingType.AUDIO,
+ clamp: true,
},
{
key: SettingKeys.Field_Volume,
@@ -653,6 +664,7 @@ export const Setting: Array = [
options: VOLUME_OPTIONS,
default: 10,
type: SettingType.AUDIO,
+ clamp: true,
},
{
key: SettingKeys.SE_Volume,
@@ -660,6 +672,7 @@ export const Setting: Array = [
options: VOLUME_OPTIONS,
default: 10,
type: SettingType.AUDIO,
+ clamp: true,
},
{
key: SettingKeys.UI_Volume,
@@ -667,6 +680,7 @@ export const Setting: Array = [
options: VOLUME_OPTIONS,
default: 10,
type: SettingType.AUDIO,
+ clamp: true,
},
{
key: SettingKeys.Battle_Music,
@@ -896,104 +910,12 @@ export function setSetting(setting: string, value: number): boolean {
globalScene.typeHints = Setting[index].options[value].value === "On";
break;
case SettingKeys.Language:
- if (value) {
- if (globalScene.ui) {
- const cancelHandler = () => {
- globalScene.ui.revertMode();
- (globalScene.ui.getHandler() as SettingsUiHandler).setOptionCursor(-1, 0, true);
- };
- const changeLocaleHandler = (locale: string): boolean => {
- try {
- i18next.changeLanguage(locale);
- localStorage.setItem("prLang", locale);
- cancelHandler();
- // Reload the whole game to apply the new locale since also some constants are translated
- window.location.reload();
- return true;
- } catch (error) {
- console.error("Error changing locale:", error);
- return false;
- }
- };
- globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
- options: [
- {
- label: "English",
- handler: () => changeLocaleHandler("en"),
- },
- {
- label: "Español (ES)",
- handler: () => changeLocaleHandler("es-ES"),
- },
- {
- label: "Español (LATAM)",
- handler: () => changeLocaleHandler("es-MX"),
- },
- {
- label: "Français",
- handler: () => changeLocaleHandler("fr"),
- },
- {
- label: "Deutsch",
- handler: () => changeLocaleHandler("de"),
- },
- {
- label: "Italiano",
- handler: () => changeLocaleHandler("it"),
- },
- {
- label: "Português (BR)",
- handler: () => changeLocaleHandler("pt-BR"),
- },
- {
- label: "한국어",
- handler: () => changeLocaleHandler("ko"),
- },
- {
- label: "日本語",
- handler: () => changeLocaleHandler("ja"),
- },
- {
- label: "简体中文",
- handler: () => changeLocaleHandler("zh-CN"),
- },
- {
- label: "繁體中文",
- handler: () => changeLocaleHandler("zh-TW"),
- },
- {
- label: "Català (Needs Help)",
- handler: () => changeLocaleHandler("ca"),
- },
- {
- label: "Türkçe (Needs Help)",
- handler: () => changeLocaleHandler("tr"),
- },
- {
- label: "Русский (Needs Help)",
- handler: () => changeLocaleHandler("ru"),
- },
- {
- label: "Dansk (Needs Help)",
- handler: () => changeLocaleHandler("da"),
- },
- {
- label: "Română (Needs Help)",
- handler: () => changeLocaleHandler("ro"),
- },
- {
- label: "Tagalog (Needs Help)",
- handler: () => changeLocaleHandler("tl"),
- },
- {
- label: i18next.t("settings:back"),
- handler: () => cancelHandler(),
- },
- ],
- maxOptions: 7,
- });
- return false;
- }
+ if (value && globalScene.ui) {
+ globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
+ options: languageOptions,
+ maxOptions: 7,
+ });
+ return false;
}
break;
case SettingKeys.Shop_Overlay_Opacity:
diff --git a/src/system/version-migration/version-converter.ts b/src/system/version-migration/version-converter.ts
index 6dde611ce84..269e577ca3f 100644
--- a/src/system/version-migration/version-converter.ts
+++ b/src/system/version-migration/version-converter.ts
@@ -1,7 +1,7 @@
/** biome-ignore-all lint/performance/noNamespaceImport: Convenience */
import { version } from "#package.json";
-import type { SessionSaveData, SystemSaveData } from "#system/game-data";
+import type { SessionSaveData, SystemSaveData } from "#types/save-data";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SettingsSaveMigrator } from "#types/settings-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
diff --git a/src/system/version-migration/versions/v1_0_4.ts b/src/system/version-migration/versions/v1_0_4.ts
index 2c50e05d40f..8229b9320d5 100644
--- a/src/system/version-migration/versions/v1_0_4.ts
+++ b/src/system/version-migration/versions/v1_0_4.ts
@@ -3,8 +3,8 @@ import { allSpecies } from "#data/data-lists";
import { CustomPokemonData } from "#data/pokemon-data";
import { AbilityAttr } from "#enums/ability-attr";
import { DexAttr } from "#enums/dex-attr";
-import type { SessionSaveData, SystemSaveData } from "#system/game-data";
import { SettingKeys } from "#system/settings";
+import type { SessionSaveData, SystemSaveData } from "#types/save-data";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SettingsSaveMigrator } from "#types/settings-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
@@ -35,9 +35,9 @@ const fixLegendaryStats: SystemSaveMigrator = {
version: "1.0.4",
migrate: (data: SystemSaveData): void => {
if (
- data.gameStats &&
- data.gameStats.legendaryPokemonCaught !== undefined &&
- data.gameStats.subLegendaryPokemonCaught === undefined
+ data.gameStats
+ && data.gameStats.legendaryPokemonCaught !== undefined
+ && data.gameStats.subLegendaryPokemonCaught === undefined
) {
data.gameStats.subLegendaryPokemonSeen = 0;
data.gameStats.subLegendaryPokemonCaught = 0;
diff --git a/src/system/version-migration/versions/v1_10_0.ts b/src/system/version-migration/versions/v1_10_0.ts
index 5c13acd4508..eab0b0cc78e 100644
--- a/src/system/version-migration/versions/v1_10_0.ts
+++ b/src/system/version-migration/versions/v1_10_0.ts
@@ -2,7 +2,7 @@ import type { BattlerIndex } from "#enums/battler-index";
import type { MoveId } from "#enums/move-id";
import type { MoveResult } from "#enums/move-result";
import { MoveUseMode } from "#enums/move-use-mode";
-import type { SessionSaveData } from "#system/game-data";
+import type { SessionSaveData } from "#types/save-data";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { TurnMove } from "#types/turn-move";
diff --git a/src/system/version-migration/versions/v1_7_0.ts b/src/system/version-migration/versions/v1_7_0.ts
index 69c640756ea..6d365cf31ac 100644
--- a/src/system/version-migration/versions/v1_7_0.ts
+++ b/src/system/version-migration/versions/v1_7_0.ts
@@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene";
import { DexAttr } from "#enums/dex-attr";
-import type { SessionSaveData, SystemSaveData } from "#system/game-data";
+import type { SessionSaveData, SystemSaveData } from "#types/save-data";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { isNullOrUndefined } from "#utils/common";
diff --git a/src/system/version-migration/versions/v1_8_3.ts b/src/system/version-migration/versions/v1_8_3.ts
index 4907b4e5e57..0d6fd87a56c 100644
--- a/src/system/version-migration/versions/v1_8_3.ts
+++ b/src/system/version-migration/versions/v1_8_3.ts
@@ -1,6 +1,6 @@
import { DexAttr } from "#enums/dex-attr";
import { SpeciesId } from "#enums/species-id";
-import type { SystemSaveData } from "#system/game-data";
+import type { SystemSaveData } from "#types/save-data";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { getPokemonSpecies } from "#utils/pokemon-utils";
diff --git a/src/system/version-migration/versions/v1_9_0.ts b/src/system/version-migration/versions/v1_9_0.ts
index fb24b9af837..eee60571884 100644
--- a/src/system/version-migration/versions/v1_9_0.ts
+++ b/src/system/version-migration/versions/v1_9_0.ts
@@ -1,7 +1,7 @@
import { MoveId } from "#enums/move-id";
import { PokemonMove } from "#moves/pokemon-move";
-import type { SessionSaveData } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
+import type { SessionSaveData } from "#types/save-data";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
/**
@@ -22,9 +22,9 @@ const migratePartyData: SessionSaveMigrator = {
pkmnData.summonData.moveset &&= pkmnData.summonData.moveset.filter(m => !!m).map(m => PokemonMove.loadMove(m));
if (
- pkmnData.customPokemonData &&
- "hitsRecCount" in pkmnData.customPokemonData &&
- typeof pkmnData.customPokemonData["hitsRecCount"] === "number"
+ pkmnData.customPokemonData
+ && "hitsRecCount" in pkmnData.customPokemonData
+ && typeof pkmnData.customPokemonData["hitsRecCount"] === "number"
) {
// transfer old hit count stat to battleData.
pkmnData.battleData.hitCount = pkmnData.customPokemonData["hitsRecCount"];
diff --git a/src/system/voucher.ts b/src/system/voucher.ts
index 79e95f0b07b..18188269e71 100644
--- a/src/system/voucher.ts
+++ b/src/system/voucher.ts
@@ -103,9 +103,9 @@ export function initVouchers() {
const bossTrainerTypes = Object.keys(trainerConfigs).filter(
tt =>
- trainerConfigs[tt].isBoss &&
- trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL &&
- trainerConfigs[tt].hasVoucher,
+ trainerConfigs[tt].isBoss
+ && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL
+ && trainerConfigs[tt].hasVoucher,
);
for (const trainerType of bossTrainerTypes) {
diff --git a/src/touch-controls.ts b/src/touch-controls.ts
index faee9ea232e..d031af9ef4c 100644
--- a/src/touch-controls.ts
+++ b/src/touch-controls.ts
@@ -5,9 +5,9 @@ import type Phaser from "phaser";
const repeatInputDelayMillis = 250;
export class TouchControl {
- events: Phaser.Events.EventEmitter;
+ readonly events: Phaser.Events.EventEmitter;
private buttonLock: string[] = [];
- private inputInterval: NodeJS.Timeout[] = [];
+ private readonly inputInterval: NodeJS.Timeout[] = [];
/** Whether touch controls are disabled */
private disabled = false;
/** Whether the last touch event has finished before disabling */
@@ -61,12 +61,46 @@ export class TouchControl {
* event, removes the keydown state, and removes the 'active' class from the node and the last touched element.
*/
bindKey(node: HTMLElement, key: string) {
- node.addEventListener("touchstart", event => {
+ node.addEventListener("touchstart", (event: TouchEvent) => {
+ // Handle touch events for touch devices
+ this.touchButtonDown(node, key);
+ event.preventDefault();
+
+ // prevent pointer event from also firing (undefined just sets presence of custom attribute)
+ if (event.currentTarget instanceof HTMLElement) {
+ event.currentTarget.dataset.skipPointerEvent = undefined;
+ }
+ });
+ node.addEventListener("pointerdown", event => {
+ const currentTarget = event.currentTarget;
+ if (currentTarget instanceof HTMLElement && "skipPointerDown" in currentTarget.dataset) {
+ return;
+ }
event.preventDefault();
this.touchButtonDown(node, key);
});
- node.addEventListener("touchend", event => {
+ node.addEventListener("touchcancel", (event: TouchEvent) => {
+ if (event.currentTarget instanceof HTMLElement && "skipPointerDown" in event.currentTarget.dataset) {
+ delete event.currentTarget.dataset.skipPointerEvent;
+ }
+ });
+
+ node.addEventListener("touchend", (event: TouchEvent) => {
+ event.preventDefault();
+ this.touchButtonUp(node, key, event.target?.["id"]);
+ if (event.currentTarget instanceof HTMLElement && "skipPointerDown" in event.currentTarget.dataset) {
+ // allow pointer event to once again fire
+ delete event.currentTarget.dataset.skipPointerEvent;
+ event.currentTarget.dataset.skipPointerUp = undefined;
+ }
+ });
+
+ node.addEventListener("pointerup", event => {
+ if (event.currentTarget instanceof HTMLElement && "skipPointerUp" in event.currentTarget.dataset) {
+ delete event.currentTarget.dataset.skipPointerUp;
+ return;
+ }
event.preventDefault();
this.touchButtonUp(node, key, event.target?.["id"]);
});
@@ -124,14 +158,14 @@ export class TouchControl {
case "keydown":
this.events.emit("input_down", {
controller_type: "keyboard",
- button: button,
+ button,
isTouch: true,
});
break;
case "keyup":
this.events.emit("input_up", {
controller_type: "keyboard",
- button: button,
+ button,
isTouch: true,
});
break;
@@ -143,7 +177,7 @@ export class TouchControl {
* {@link https://stackoverflow.com/a/39778831/4622620|Source}
*
* Prevent zoom on specified element
- * @param {HTMLElement} element
+ * @param element
*/
preventElementZoom(element: HTMLElement | null): void {
if (!element) {
@@ -207,8 +241,8 @@ export function isMobile(): boolean {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
a,
- ) ||
- /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
+ )
+ || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
a.substr(0, 4),
)
) {
diff --git a/src/tutorial.ts b/src/tutorial.ts
index 018d0927da0..5ab0be116f8 100644
--- a/src/tutorial.ts
+++ b/src/tutorial.ts
@@ -1,8 +1,8 @@
import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides";
import { UiMode } from "#enums/ui-mode";
-import { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
-import type { UiHandler } from "#ui/ui-handler";
+import { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
+import type { UiHandler } from "#ui/handlers/ui-handler";
import i18next from "i18next";
export enum Tutorial {
diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts
index 71c5ac1049e..5c4a530b39a 100644
--- a/src/ui-inputs.ts
+++ b/src/ui-inputs.ts
@@ -3,17 +3,17 @@ import type { InputsController } from "#app/inputs-controller";
import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode";
import { Setting, SettingKeys, settingIndex } from "#system/settings";
-import type { MessageUiHandler } from "#ui/message-ui-handler";
-import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler";
-import { PokedexUiHandler } from "#ui/pokedex-ui-handler";
-import { RunInfoUiHandler } from "#ui/run-info-ui-handler";
+import type { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { PokedexPageUiHandler } from "#ui/handlers/pokedex-page-ui-handler";
+import { PokedexUiHandler } from "#ui/handlers/pokedex-ui-handler";
+import { RunInfoUiHandler } from "#ui/handlers/run-info-ui-handler";
+import { StarterSelectUiHandler } from "#ui/handlers/starter-select-ui-handler";
import { SettingsAudioUiHandler } from "#ui/settings-audio-ui-handler";
import { SettingsDisplayUiHandler } from "#ui/settings-display-ui-handler";
import { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler";
import { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler";
import { SettingsUiHandler } from "#ui/settings-ui-handler";
-import { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";
-import type Phaser from "phaser";
+import Phaser from "phaser";
type ActionKeys = Record void>;
@@ -103,23 +103,23 @@ export class UiInputs {
getActionsKeyUp(): ActionKeys {
const actions: ActionKeys = {
- [Button.UP]: () => undefined,
- [Button.DOWN]: () => undefined,
- [Button.LEFT]: () => undefined,
- [Button.RIGHT]: () => undefined,
- [Button.SUBMIT]: () => undefined,
- [Button.ACTION]: () => undefined,
- [Button.CANCEL]: () => undefined,
- [Button.MENU]: () => undefined,
+ [Button.UP]: () => {},
+ [Button.DOWN]: () => {},
+ [Button.LEFT]: () => {},
+ [Button.RIGHT]: () => {},
+ [Button.SUBMIT]: () => {},
+ [Button.ACTION]: () => {},
+ [Button.CANCEL]: () => {},
+ [Button.MENU]: () => {},
[Button.STATS]: () => this.buttonStats(false),
- [Button.CYCLE_SHINY]: () => undefined,
- [Button.CYCLE_FORM]: () => undefined,
- [Button.CYCLE_GENDER]: () => undefined,
- [Button.CYCLE_ABILITY]: () => undefined,
- [Button.CYCLE_NATURE]: () => undefined,
+ [Button.CYCLE_SHINY]: () => {},
+ [Button.CYCLE_FORM]: () => {},
+ [Button.CYCLE_GENDER]: () => {},
+ [Button.CYCLE_ABILITY]: () => {},
+ [Button.CYCLE_NATURE]: () => {},
[Button.CYCLE_TERA]: () => this.buttonInfo(false),
- [Button.SPEED_UP]: () => undefined,
- [Button.SLOW_DOWN]: () => undefined,
+ [Button.SPEED_UP]: () => {},
+ [Button.SLOW_DOWN]: () => {},
};
return actions;
}
@@ -224,25 +224,26 @@ export class UiInputs {
buttonSpeedChange(up = true): void {
const settingGameSpeed = settingIndex(SettingKeys.Game_Speed);
+ const settingOptions = Setting[settingGameSpeed].options;
+ let currentSetting = settingOptions.findIndex(item => item.value === globalScene.gameSpeed.toString());
+ // if current setting is -1, then the current game speed is not a valid option, so default to index 5 (3x)
+ if (currentSetting === -1) {
+ currentSetting = 5;
+ }
+ let direction: number;
if (up && globalScene.gameSpeed < 5) {
- globalScene.gameData.saveSetting(
- SettingKeys.Game_Speed,
- Setting[settingGameSpeed].options.findIndex(item => item.label === `${globalScene.gameSpeed}x`) + 1,
- );
- if (globalScene.ui?.getMode() === UiMode.SETTINGS) {
- (globalScene.ui.getHandler() as SettingsUiHandler).show([]);
- }
+ direction = 1;
} else if (!up && globalScene.gameSpeed > 1) {
- globalScene.gameData.saveSetting(
- SettingKeys.Game_Speed,
- Math.max(
- Setting[settingGameSpeed].options.findIndex(item => item.label === `${globalScene.gameSpeed}x`) - 1,
- 0,
- ),
- );
- if (globalScene.ui?.getMode() === UiMode.SETTINGS) {
- (globalScene.ui.getHandler() as SettingsUiHandler).show([]);
- }
+ direction = -1;
+ } else {
+ return;
+ }
+ globalScene.gameData.saveSetting(
+ SettingKeys.Game_Speed,
+ Phaser.Math.Clamp(currentSetting + direction, 0, settingOptions.length - 1),
+ );
+ if (globalScene.ui?.getMode() === UiMode.SETTINGS) {
+ (globalScene.ui.getHandler() as SettingsUiHandler).show([]);
}
}
}
diff --git a/src/ui/battle-info/battle-info.ts b/src/ui/battle-info/battle-info.ts
index 810d0c7c328..66e0830a731 100644
--- a/src/ui/battle-info/battle-info.ts
+++ b/src/ui/battle-info/battle-info.ts
@@ -5,6 +5,7 @@ import { PokemonType } from "#enums/pokemon-type";
import { Stat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { TextStyle } from "#enums/text-style";
+import { UiTheme } from "#enums/ui-theme";
import type { Pokemon } from "#field/pokemon";
import { getVariantTint } from "#sprites/variant";
import { addTextObject } from "#ui/text";
@@ -71,6 +72,7 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
protected splicedIcon: Phaser.GameObjects.Sprite;
protected statusIndicator: Phaser.GameObjects.Sprite;
protected levelContainer: Phaser.GameObjects.Container;
+ protected hpLabel: Phaser.GameObjects.Image;
protected hpBar: Phaser.GameObjects.Image;
protected levelNumbersContainer: Phaser.GameObjects.Container;
protected type1Icon: Phaser.GameObjects.Sprite;
@@ -177,7 +179,7 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
}
const statLabel = globalScene.add
- .sprite(statX, statY, "pbinfo_stat", Stat[s])
+ .sprite(statX, statY, getLocalizedSpriteKey("pbinfo_stat"), Stat[s])
.setName("icon_stat_label_" + i.toString())
.setOrigin(0);
statLabels.push(statLabel);
@@ -258,14 +260,19 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
.setName("container_level");
this.add(this.levelContainer);
- const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
+ const levelOverlay = globalScene.add.image(5.5, 0, getLocalizedSpriteKey("overlay_lv")).setOrigin(1, 0.5);
this.levelContainer.add(levelOverlay);
this.hpBar = globalScene.add.image(posParams.hpBarX, posParams.hpBarY, "overlay_hp").setName("hp_bar").setOrigin(0);
this.add(this.hpBar);
+ this.hpLabel = globalScene.add
+ .image(posParams.hpBarX - 1, posParams.hpBarY - 3, getLocalizedSpriteKey("overlay_hp_label"))
+ .setOrigin(1, 0);
+ this.add(this.hpLabel);
+
this.levelNumbersContainer = globalScene.add
- .container(9.5, globalScene.uiTheme ? 0 : -0.5)
+ .container(9.5, globalScene.uiTheme === UiTheme.LEGACY ? 0 : -0.5)
.setName("container_level");
this.levelContainer.add(this.levelNumbersContainer);
@@ -287,9 +294,6 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
2.5,
);
this.splicedIcon.setVisible(pokemon.isFusion(true));
- if (!this.splicedIcon.visible) {
- return;
- }
this.splicedIcon
.on("pointerover", () =>
globalScene.ui.showTooltip(
@@ -311,11 +315,11 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
this.shinyIcon.setPositionRelative(
this.nameText,
- xOffset +
- this.genderText.displayWidth +
- 1 +
- (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
- (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
+ xOffset
+ + this.genderText.displayWidth
+ + 1
+ + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0)
+ + (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
this.shinyIcon
@@ -323,6 +327,10 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
.setVisible(pokemon.isShiny())
.setTint(getVariantTint(baseVariant));
+ this.shinyIcon
+ .on("pointerover", () => globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor))
+ .on("pointerout", () => globalScene.ui.hideTooltip());
+
if (!this.shinyIcon.visible) {
return;
}
@@ -335,10 +343,6 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
}
shinyDescriptor += ")";
}
-
- this.shinyIcon
- .on("pointerover", () => globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor))
- .on("pointerout", () => globalScene.ui.hideTooltip());
}
initInfo(pokemon: Pokemon) {
@@ -502,19 +506,19 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
.setVisible(isFusion)
.setPositionRelative(
this.nameText,
- this.nameText.displayWidth +
- this.genderText.displayWidth +
- 1 +
- (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
+ this.nameText.displayWidth
+ + this.genderText.displayWidth
+ + 1
+ + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
1.5,
);
this.shinyIcon.setPositionRelative(
this.nameText,
- this.nameText.displayWidth +
- this.genderText.displayWidth +
- 1 +
- (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
- (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
+ this.nameText.displayWidth
+ + this.genderText.displayWidth
+ + 1
+ + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0)
+ + (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
}
@@ -551,7 +555,7 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
targets: this.hpBar,
ease: "Sine.easeOut",
scaleX: pokemon.getHpRatio(true),
- duration: duration,
+ duration,
onUpdate: () => {
this.onHpTweenUpdate(pokemon);
},
@@ -631,12 +635,12 @@ export abstract class BattleInfo extends Phaser.GameObjects.Container {
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
- nameTextWidth >
- (this.player || !this.boss ? 60 : 98) -
- ((gender !== Gender.GENDERLESS ? 6 : 0) +
- (pokemon.fusionSpecies ? 8 : 0) +
- (pokemon.isShiny() ? 8 : 0) +
- (Math.min(pokemon.level.toString().length, 3) - 3) * 8)
+ nameTextWidth
+ > (this.player || !this.boss ? 60 : 98)
+ - ((gender !== Gender.GENDERLESS ? 6 : 0)
+ + (pokemon.fusionSpecies ? 8 : 0)
+ + (pokemon.isShiny() ? 8 : 0)
+ + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)
) {
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
nameSizeTest.setText(displayName);
diff --git a/src/ui/battle-info/enemy-battle-info.ts b/src/ui/battle-info/enemy-battle-info.ts
index d426a49df5c..ad72afedc38 100644
--- a/src/ui/battle-info/enemy-battle-info.ts
+++ b/src/ui/battle-info/enemy-battle-info.ts
@@ -1,12 +1,14 @@
import { globalScene } from "#app/global-scene";
import { Stat } from "#enums/stat";
import { TextStyle } from "#enums/text-style";
+import { UiTheme } from "#enums/ui-theme";
import type { EnemyPokemon } from "#field/pokemon";
-import { BattleFlyout } from "#ui/battle-flyout";
import type { BattleInfoParamList } from "#ui/battle-info";
import { BattleInfo } from "#ui/battle-info";
+import { BattleFlyout } from "#ui/containers/battle-flyout";
import { addTextObject } from "#ui/text";
import { addWindow, WindowVariant } from "#ui/ui-theme";
+import { getLocalizedSpriteKey } from "#utils/common";
import i18next from "i18next";
import type { GameObjects } from "phaser";
@@ -36,7 +38,7 @@ export class EnemyBattleInfo extends BattleInfo {
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-15, -15.5, "pbinfo_enemy_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-15, -2.5, "pbinfo_enemy_type2").setName("icon_type_2").setOrigin(0);
- this.type3Icon = globalScene.add.sprite(0, 15.5, "pbinfo_enemy_type3").setName("icon_type_3").setOrigin(0);
+ this.type3Icon = globalScene.add.sprite(0, -15.5, "pbinfo_enemy_type").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
@@ -111,10 +113,13 @@ export class EnemyBattleInfo extends BattleInfo {
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
const opponentPokemonDexAttr = pokemon.getDexAttr();
if (
- globalScene.gameMode.isClassic &&
- globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
- globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
+ globalScene.gameMode.isClassic
+ && globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0
+ && globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
+ // move the ribbon to the left if there is no owned icon
+ const championRibbonX = this.ownedIcon.visible ? 8 : 0;
+ this.championRibbon.setPositionRelative(this.nameText, championRibbonX, 11.75);
this.championRibbon.setVisible(true);
}
@@ -180,12 +185,15 @@ export class EnemyBattleInfo extends BattleInfo {
this.ownedIcon,
this.championRibbon,
this.statusIndicator,
- this.levelContainer,
this.statValuesContainer,
].map(e => (e.x += 48 * (boss ? -1 : 1)));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
+ this.hpLabel.x += 38 * (boss ? -1 : 1);
+ this.hpLabel.y += 1 * (this.boss ? -1 : 1);
+ this.hpLabel.setTexture(getLocalizedSpriteKey(`overlay_hp_label${boss ? "_boss" : ""}`));
+ this.levelContainer.x += 2 * (boss ? -1 : 1);
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
}
@@ -195,12 +203,12 @@ export class EnemyBattleInfo extends BattleInfo {
}
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
- while (this.hpBarSegmentDividers.length) {
+ while (this.hpBarSegmentDividers.length > 0) {
this.hpBarSegmentDividers.pop()?.destroy();
}
if (this.boss && this.bossSegments > 1) {
- const uiTheme = globalScene.uiTheme;
+ const isLegacyUiTheme = globalScene.uiTheme === UiTheme.LEGACY;
const maxHp = pokemon.getMaxHp();
for (let s = 1; s < this.bossSegments; s++) {
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
@@ -208,14 +216,14 @@ export class EnemyBattleInfo extends BattleInfo {
0,
0,
1,
- this.hpBar.height - (uiTheme ? 0 : 1),
+ this.hpBar.height - (isLegacyUiTheme ? 0 : 1),
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
);
divider.setOrigin(0.5, 0).setName("hpBar_divider_" + s.toString());
this.add(divider);
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
- divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
+ divider.setPositionRelative(this.hpBar, dividerX, isLegacyUiTheme ? 0 : 1);
this.hpBarSegmentDividers.push(divider);
}
}
diff --git a/src/ui/battle-info/player-battle-info.ts b/src/ui/battle-info/player-battle-info.ts
index 998f7cbb41f..ec6332c54f3 100644
--- a/src/ui/battle-info/player-battle-info.ts
+++ b/src/ui/battle-info/player-battle-info.ts
@@ -5,10 +5,12 @@ import { Stat } from "#enums/stat";
import type { PlayerPokemon } from "#field/pokemon";
import type { BattleInfoParamList } from "#ui/battle-info";
import { BattleInfo } from "#ui/battle-info";
+import { getLocalizedSpriteKey } from "#utils/common";
export class PlayerBattleInfo extends BattleInfo {
protected player: true = true;
protected hpNumbersContainer: Phaser.GameObjects.Container;
+ protected expBarLabel: Phaser.GameObjects.Image;
override get statOrder(): Stat[] {
return [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
@@ -21,7 +23,7 @@ export class PlayerBattleInfo extends BattleInfo {
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-139, -17, "pbinfo_player_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-139, -1, "pbinfo_player_type2").setName("icon_type_2").setOrigin(0);
- this.type3Icon = globalScene.add.sprite(-154, -17, "pbinfo_player_type3").setName("icon_type_3").setOrigin(0);
+ this.type3Icon = globalScene.add.sprite(-154, -17, "pbinfo_player_type").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
@@ -46,6 +48,12 @@ export class PlayerBattleInfo extends BattleInfo {
// hp number container must be beneath the stat container for overlay to display properly
this.addAt(this.hpNumbersContainer, this.getIndex(this.statsContainer));
+ const expBarLabel = globalScene.add
+ .image(-91, 20, getLocalizedSpriteKey("overlay_exp_label"))
+ .setName("overlay_exp_label")
+ .setOrigin(1, 1);
+ this.add(expBarLabel);
+
const expBar = globalScene.add.image(-98, 18, "overlay_exp").setName("overlay_exp").setOrigin(0);
this.add(expBar);
@@ -60,6 +68,7 @@ export class PlayerBattleInfo extends BattleInfo {
expBar.setMask(expMask);
+ this.expBarLabel = expBarLabel;
this.expBar = expBar;
this.expMaskRect = expMaskRect;
}
@@ -108,7 +117,7 @@ export class PlayerBattleInfo extends BattleInfo {
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
- const toggledElements = [this.hpNumbersContainer, this.expBar];
+ const toggledElements = [this.hpNumbersContainer, this.expBarLabel, this.expBar];
toggledElements.forEach(el => el.setVisible(!mini));
}
@@ -138,10 +147,10 @@ export class PlayerBattleInfo extends BattleInfo {
);
let duration =
this.visible && !instant
- ? ((levelExp - this.lastLevelExp) / relLevelExp) *
- BattleInfo.EXP_GAINS_DURATION_BASE *
- durationMultiplier *
- levelDurationMultiplier
+ ? ((levelExp - this.lastLevelExp) / relLevelExp)
+ * BattleInfo.EXP_GAINS_DURATION_BASE
+ * durationMultiplier
+ * levelDurationMultiplier
: 0;
const speed = globalScene.expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
@@ -162,7 +171,7 @@ export class PlayerBattleInfo extends BattleInfo {
targets: this.expMaskRect,
ease: "Sine.easeIn",
x: ratio * 510,
- duration: duration,
+ duration,
onComplete: () => {
if (!globalScene) {
return resolve();
diff --git a/src/ui/ability-bar.ts b/src/ui/containers/ability-bar.ts
similarity index 96%
rename from src/ui/ability-bar.ts
rename to src/ui/containers/ability-bar.ts
index 4b868d4e66c..2495bf190a5 100644
--- a/src/ui/ability-bar.ts
+++ b/src/ui/containers/ability-bar.ts
@@ -67,7 +67,7 @@ export class AbilityBar extends Phaser.GameObjects.Container {
}
public async showAbility(pokemonName: string, abilityName: string, passive = false, player = true): Promise {
- const text = `${i18next.t("fightUiHandler:abilityFlyInText", { pokemonName: pokemonName, passive: passive ? i18next.t("fightUiHandler:passive") : "", abilityName: abilityName })}`;
+ const text = `${i18next.t("fightUiHandler:abilityFlyInText", { pokemonName, passive: passive ? i18next.t("fightUiHandler:passive") : "", abilityName })}`;
this.screenRight = globalScene.scaledCanvas.width;
if (player !== this.player) {
// Move the bar if it has changed from the player to enemy side (or vice versa)
diff --git a/src/ui/achv-bar.ts b/src/ui/containers/achv-bar.ts
similarity index 99%
rename from src/ui/achv-bar.ts
rename to src/ui/containers/achv-bar.ts
index 0f71bcbfde0..5a3e16d6088 100644
--- a/src/ui/achv-bar.ts
+++ b/src/ui/containers/achv-bar.ts
@@ -142,7 +142,7 @@ export class AchvBar extends Phaser.GameObjects.Container {
onComplete: () => {
this.shown = false;
this.setVisible(false);
- if (this.queue.length) {
+ if (this.queue.length > 0) {
const shifted = this.queue.shift();
shifted && this.showAchv(shifted);
}
diff --git a/src/ui/arena-flyout.ts b/src/ui/containers/arena-flyout.ts
similarity index 98%
rename from src/ui/arena-flyout.ts
rename to src/ui/containers/arena-flyout.ts
index da062f5c96f..3555694760d 100644
--- a/src/ui/arena-flyout.ts
+++ b/src/ui/containers/arena-flyout.ts
@@ -15,8 +15,8 @@ import {
} from "#events/arena";
import type { TurnEndEvent } from "#events/battle-scene";
import { BattleSceneEventType } from "#events/battle-scene";
+import { TimeOfDayWidget } from "#ui/containers/time-of-day-widget";
import { addTextObject } from "#ui/text";
-import { TimeOfDayWidget } from "#ui/time-of-day-widget";
import { addWindow, WindowVariant } from "#ui/ui-theme";
import { fixedInt } from "#utils/common";
import { toCamelCase, toTitleCase } from "#utils/strings";
@@ -241,9 +241,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
- for (let i = 0; i < this.fieldEffectInfo.length; i++) {
- const fieldEffectInfo = this.fieldEffectInfo[i];
-
+ for (const fieldEffectInfo of this.fieldEffectInfo) {
// Creates a proxy object to decide which text object needs to be updated
let textObject: Phaser.GameObjects.Text;
switch (fieldEffectInfo.effectType) {
@@ -389,9 +387,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
const fieldEffectInfo: ArenaEffectInfo[] = [];
this.fieldEffectInfo.forEach(i => fieldEffectInfo.push(i));
- for (let i = 0; i < fieldEffectInfo.length; i++) {
- const info = fieldEffectInfo[i];
-
+ for (const info of fieldEffectInfo) {
if (info.maxDuration === 0) {
continue;
}
diff --git a/src/ui/base-stats-overlay.ts b/src/ui/containers/base-stats-overlay.ts
similarity index 96%
rename from src/ui/base-stats-overlay.ts
rename to src/ui/containers/base-stats-overlay.ts
index b3cccf34298..9ddc8533feb 100644
--- a/src/ui/base-stats-overlay.ts
+++ b/src/ui/containers/base-stats-overlay.ts
@@ -70,7 +70,7 @@ export class BaseStatsOverlay extends Phaser.GameObjects.Container implements In
show(values: number[], total: number): boolean {
for (let i = 0; i < 6; i++) {
this.statsLabels[i].setText(
- i18next.t(`pokemonInfo:stat.${toCamelCase(shortStats[i])}Shortened`) + ": " + `${values[i]}`,
+ i18next.t(`pokemonInfo:stat.${toCamelCase(shortStats[i])}Shortened`) + `: ${values[i]}`,
);
// This accounts for base stats up to 200, might not be enough.
// TODO: change color based on value.
@@ -78,7 +78,7 @@ export class BaseStatsOverlay extends Phaser.GameObjects.Container implements In
this.statsRectangles[i].setSize(values[i] / 2, 5);
}
- this.statsTotalLabel.setText(i18next.t("pokedexUiHandler:baseTotal") + ": " + `${total}`);
+ this.statsTotalLabel.setText(`${i18next.t("pokedexUiHandler:baseTotal")}: ${total}`);
this.setVisible(true);
this.active = true;
diff --git a/src/ui/battle-flyout.ts b/src/ui/containers/battle-flyout.ts
similarity index 97%
rename from src/ui/battle-flyout.ts
rename to src/ui/containers/battle-flyout.ts
index 0a67dc9ad37..c0c118b8560 100644
--- a/src/ui/battle-flyout.ts
+++ b/src/ui/containers/battle-flyout.ts
@@ -177,9 +177,9 @@ export class BattleFlyout extends Phaser.GameObjects.Container {
private onBerryUsed(event: Event) {
const berryUsedEvent = event as BerryUsedEvent;
if (
- !berryUsedEvent ||
- berryUsedEvent.berryModifier.pokemonId !== this.pokemon?.id ||
- berryUsedEvent.berryModifier.berryType !== BerryType.LEPPA
+ !berryUsedEvent
+ || berryUsedEvent.berryModifier.pokemonId !== this.pokemon?.id
+ || berryUsedEvent.berryModifier.berryType !== BerryType.LEPPA
) {
// We only care about Leppa berries
return;
diff --git a/src/ui/bgm-bar.ts b/src/ui/containers/bgm-bar.ts
similarity index 95%
rename from src/ui/bgm-bar.ts
rename to src/ui/containers/bgm-bar.ts
index f24f372a804..9c9e761e26b 100644
--- a/src/ui/bgm-bar.ts
+++ b/src/ui/containers/bgm-bar.ts
@@ -53,7 +53,7 @@ export class BgmBar extends Phaser.GameObjects.Container {
/*
* Set the BGM Name to the BGM bar.
- * @param {string} bgmName The name of the BGM to set.
+ * @param bgmName The name of the BGM to set.
*/
setBgmToBgmBar(bgmName: string): void {
this.musicText.setText(`${i18next.t("bgmName:music")}${this.getRealBgmName(bgmName)}`);
@@ -71,7 +71,7 @@ export class BgmBar extends Phaser.GameObjects.Container {
/*
Show or hide the BGM bar.
- @param {boolean} visible Whether to show or hide the BGM bar.
+ @param visible Whether to show or hide the BGM bar.
*/
public toggleBgmBar(visible: boolean): void {
/*
diff --git a/src/ui/candy-bar.ts b/src/ui/containers/candy-bar.ts
similarity index 100%
rename from src/ui/candy-bar.ts
rename to src/ui/containers/candy-bar.ts
diff --git a/src/ui/char-sprite.ts b/src/ui/containers/char-sprite.ts
similarity index 100%
rename from src/ui/char-sprite.ts
rename to src/ui/containers/char-sprite.ts
diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/containers/daily-run-scoreboard.ts
similarity index 96%
rename from src/ui/daily-run-scoreboard.ts
rename to src/ui/containers/daily-run-scoreboard.ts
index 9391d02859c..456c8edde01 100644
--- a/src/ui/daily-run-scoreboard.ts
+++ b/src/ui/containers/daily-run-scoreboard.ts
@@ -202,8 +202,8 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container {
* The method fetches the total page count if necessary, followed by fetching the rankings for the specified category
* and page. It updates the UI with the fetched rankings or shows an appropriate message if no rankings are found.
*
- * @param {ScoreboardCategory} [category=this.category] - The category to fetch rankings for. Defaults to the current category.
- * @param {number} [page=this.page] - The page number to fetch. Defaults to the current page.
+ * @param [category=this.category] - The category to fetch rankings for. Defaults to the current category.
+ * @param [page=this.page] - The page number to fetch. Defaults to the current page.
*/
update(category: ScoreboardCategory = this.category, page: number = this.page) {
if (this.isUpdating) {
@@ -249,7 +249,7 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container {
/**
* Sets the state of the navigation buttons.
- * @param {boolean} [enabled=true] - Whether the buttons should be enabled or disabled.
+ * @param [enabled=true] - Whether the buttons should be enabled or disabled.
*/
setButtonsState(enabled = true) {
const buttons = [
diff --git a/src/ui/dropdown.ts b/src/ui/containers/dropdown.ts
similarity index 96%
rename from src/ui/dropdown.ts
rename to src/ui/containers/dropdown.ts
index c13d1ab6482..bf589085d2e 100644
--- a/src/ui/dropdown.ts
+++ b/src/ui/containers/dropdown.ts
@@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
-import { ScrollBar } from "#ui/scroll-bar";
+import { ScrollBar } from "#ui/containers/scroll-bar";
import { addTextObject } from "#ui/text";
import { addWindow, WindowVariant } from "#ui/ui-theme";
import i18next from "i18next";
@@ -504,13 +504,11 @@ export class DropDown extends Phaser.GameObjects.Container {
if (index === 0) {
// we are on the All option > put all other options to the newState
this.setAllOptions(newState);
- } else {
+ } else if (newState === DropDownState.ON && this.checkForAllOn()) {
// select the "all" option if all others are selected, other unselect it
- if (newState === DropDownState.ON && this.checkForAllOn()) {
- this.options[0].setOptionState(DropDownState.ON);
- } else {
- this.options[0].setOptionState(DropDownState.OFF);
- }
+ this.options[0].setOptionState(DropDownState.ON);
+ } else {
+ this.options[0].setOptionState(DropDownState.OFF);
}
} else if (this.dropDownType === DropDownType.SINGLE) {
if (option.state === DropDownState.OFF) {
@@ -612,8 +610,8 @@ export class DropDown extends Phaser.GameObjects.Container {
const compareValues = (keys: string[]): boolean => {
return (
- currentValues.length === this.defaultSettings.length &&
- currentValues.every((value, index) => keys.every(key => value[key] === this.defaultSettings[index][key]))
+ currentValues.length === this.defaultSettings.length
+ && currentValues.every((value, index) => keys.every(key => value[key] === this.defaultSettings[index][key]))
);
};
@@ -653,10 +651,8 @@ export class DropDown extends Phaser.GameObjects.Container {
this.options[i].setDirection(SortDirection.ASC);
this.options[i].toggle.setVisible(true);
}
- } else {
- if (this.defaultSettings[i]) {
- this.options[i].setOptionState(this.defaultSettings[i]["state"]);
- }
+ } else if (this.defaultSettings[i]) {
+ this.options[i].setOptionState(this.defaultSettings[i]["state"]);
}
}
@@ -699,11 +695,11 @@ export class DropDown extends Phaser.GameObjects.Container {
autoSize(): void {
let maxWidth = 0;
let x = 0;
- for (let i = 0; i < this.options.length; i++) {
- const optionWidth = this.options[i].getWidth();
+ for (const option of this.options) {
+ const optionWidth = option.getWidth();
if (optionWidth > maxWidth) {
maxWidth = optionWidth;
- x = this.options[i].getCurrentLabelX() ?? 0;
+ x = option.getCurrentLabelX() ?? 0;
}
}
this.window.width = maxWidth + x - this.window.x + 9;
diff --git a/src/ui/egg-counter-container.ts b/src/ui/containers/egg-counter-container.ts
similarity index 93%
rename from src/ui/egg-counter-container.ts
rename to src/ui/containers/egg-counter-container.ts
index da394e73b28..d080dd66a68 100644
--- a/src/ui/egg-counter-container.ts
+++ b/src/ui/containers/egg-counter-container.ts
@@ -2,14 +2,11 @@ import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
import type { EggCountChangedEvent } from "#events/egg";
import { EggEventType } from "#events/egg";
-import type { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler";
+import type { EggHatchSceneUiHandler } from "#ui/handlers/egg-hatch-scene-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
-/**
- * A container that displays the count of hatching eggs.
- * @extends Phaser.GameObjects.Container
- */
+/** A container that displays the count of hatching eggs */
export class EggCounterContainer extends Phaser.GameObjects.Container {
private readonly WINDOW_DEFAULT_WIDTH = 37;
private readonly WINDOW_MEDIUM_WIDTH = 42;
@@ -27,7 +24,7 @@ export class EggCounterContainer extends Phaser.GameObjects.Container {
super(globalScene, 0, 0);
this.eggCount = eggCount;
- const uiHandler = globalScene.ui.getHandler() as EggHatchSceneHandler;
+ const uiHandler = globalScene.ui.getHandler() as EggHatchSceneUiHandler;
uiHandler.eventTarget.addEventListener(EggEventType.EGG_COUNT_CHANGED, this.onEggCountChangedEvent);
this.setup();
diff --git a/src/ui/filter-bar.ts b/src/ui/containers/filter-bar.ts
similarity index 90%
rename from src/ui/filter-bar.ts
rename to src/ui/containers/filter-bar.ts
index ea227655a97..bbca38c3f53 100644
--- a/src/ui/filter-bar.ts
+++ b/src/ui/containers/filter-bar.ts
@@ -2,9 +2,9 @@ import { globalScene } from "#app/global-scene";
import type { DropDownColumn } from "#enums/drop-down-column";
import { TextStyle } from "#enums/text-style";
import type { UiTheme } from "#enums/ui-theme";
-import type { DropDown } from "#ui/dropdown";
-import { DropDownType } from "#ui/dropdown";
-import type { StarterContainer } from "#ui/starter-container";
+import type { DropDown } from "#ui/containers/dropdown";
+import { DropDownType } from "#ui/containers/dropdown";
+import type { StarterContainer } from "#ui/containers/starter-container";
import { addTextObject, getTextColor } from "#ui/text";
import { addWindow, WindowVariant } from "#ui/ui-theme";
@@ -97,9 +97,9 @@ export class FilterBar extends Phaser.GameObjects.Container {
updateFilterLabels(): void {
for (let i = 0; i < this.numFilters; i++) {
if (this.dropDowns[i].hasDefaultValues()) {
- this.labels[i].setColor(getTextColor(TextStyle.TOOLTIP_CONTENT, false, globalScene.uiTheme));
+ this.labels[i].setColor(getTextColor(TextStyle.TOOLTIP_CONTENT, false));
} else {
- this.labels[i].setColor(getTextColor(TextStyle.STATS_LABEL, false, globalScene.uiTheme));
+ this.labels[i].setColor(getTextColor(TextStyle.STATS_LABEL, false));
}
}
}
@@ -130,22 +130,20 @@ export class FilterBar extends Phaser.GameObjects.Container {
* Move the leftmost dropdown to the left of the FilterBar instead of below it
*/
offsetHybridFilters(): void {
- for (let i = 0; i < this.dropDowns.length; i++) {
- if (this.dropDowns[i].dropDownType === DropDownType.HYBRID) {
- this.dropDowns[i].autoSize();
- this.dropDowns[i].x = -this.dropDowns[i].getWidth();
- this.dropDowns[i].y = 0;
+ for (const dropDown of this.dropDowns) {
+ if (dropDown.dropDownType === DropDownType.HYBRID) {
+ dropDown.autoSize();
+ dropDown.x = -dropDown.getWidth();
+ dropDown.y = 0;
}
}
}
setCursor(cursor: number): void {
- if (this.lastCursor > -1) {
- if (this.dropDowns[this.lastCursor].visible) {
- this.dropDowns[this.lastCursor].setVisible(false);
- this.dropDowns[cursor].setVisible(true);
- this.dropDowns[cursor].resetCursor();
- }
+ if (this.lastCursor > -1 && this.dropDowns[this.lastCursor].visible) {
+ this.dropDowns[this.lastCursor].setVisible(false);
+ this.dropDowns[cursor].setVisible(true);
+ this.dropDowns[cursor].resetCursor();
}
this.cursorObj.setPosition(this.labels[cursor].x - this.cursorOffset + 2, 6);
diff --git a/src/ui/filter-text.ts b/src/ui/containers/filter-text.ts
similarity index 95%
rename from src/ui/filter-text.ts
rename to src/ui/containers/filter-text.ts
index d5809292c25..0d15aca8530 100644
--- a/src/ui/filter-text.ts
+++ b/src/ui/containers/filter-text.ts
@@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
import type { UiTheme } from "#enums/ui-theme";
-import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
-import type { StarterContainer } from "#ui/starter-container";
+import type { StarterContainer } from "#ui/containers/starter-container";
+import type { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
import { addTextObject, getTextColor } from "#ui/text";
import type { UI } from "#ui/ui";
import { addWindow, WindowVariant } from "#ui/ui-theme";
@@ -24,14 +24,12 @@ export class FilterText extends Phaser.GameObjects.Container {
private rows: FilterTextRow[] = [];
public cursorObj: Phaser.GameObjects.Image;
public numFilters = 0;
- private lastCursor = -1;
private uiTheme: UiTheme;
private menuMessageBoxContainer: Phaser.GameObjects.Container;
private dialogueMessageBox: Phaser.GameObjects.NineSlice;
message: any;
private readonly textPadding = 8;
- private readonly defaultWordWrapWidth = 1224;
private onChange: () => void;
@@ -158,7 +156,6 @@ export class FilterText extends Phaser.GameObjects.Container {
const cursorOffset = 8;
this.cursorObj.setPosition(cursorOffset, this.labels[cursor].y + 3);
- this.lastCursor = cursor;
}
/**
@@ -167,9 +164,9 @@ export class FilterText extends Phaser.GameObjects.Container {
updateFilterLabels(): void {
for (let i = 0; i < this.numFilters; i++) {
if (this.selections[i].text === this.defaultText) {
- this.labels[i].setColor(getTextColor(TextStyle.TOOLTIP_CONTENT, false, globalScene.uiTheme));
+ this.labels[i].setColor(getTextColor(TextStyle.TOOLTIP_CONTENT, false));
} else {
- this.labels[i].setColor(getTextColor(TextStyle.STATS_LABEL, false, globalScene.uiTheme));
+ this.labels[i].setColor(getTextColor(TextStyle.STATS_LABEL, false));
}
}
}
diff --git a/src/ui/hatched-pokemon-container.ts b/src/ui/containers/hatched-pokemon-container.ts
similarity index 95%
rename from src/ui/hatched-pokemon-container.ts
rename to src/ui/containers/hatched-pokemon-container.ts
index a5919348a94..456ffd923a1 100644
--- a/src/ui/hatched-pokemon-container.ts
+++ b/src/ui/containers/hatched-pokemon-container.ts
@@ -4,8 +4,8 @@ import { Gender } from "#data/gender";
import type { PokemonSpecies } from "#data/pokemon-species";
import { DexAttr } from "#enums/dex-attr";
import { getVariantTint } from "#sprites/variant";
-import type { PokemonIconAnimHandler } from "#ui/pokemon-icon-anim-handler";
-import { PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
+import type { PokemonIconAnimHelper } from "#ui/utils/pokemon-icon-anim-helper";
+import { PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
/**
* A container for a Pokemon's sprite and icons to get displayed in the egg summary screen
@@ -81,9 +81,9 @@ export class HatchedPokemonContainer extends Phaser.GameObjects.Container {
* Animates the pokemon icon if it has a new form or shiny variant
*
* @param hatchData the {@linkcode EggHatchData} to base the icons on
- * @param iconAnimHandler the {@linkcode PokemonIconAnimHandler} to use to animate the sprites
+ * @param iconAnimHandler the {@linkcode PokemonIconAnimHelper} to use to animate the sprites
*/
- updateAndAnimate(hatchData: EggHatchData, iconAnimHandler: PokemonIconAnimHandler) {
+ updateAndAnimate(hatchData: EggHatchData, iconAnimHandler: PokemonIconAnimHelper) {
const displayPokemon = hatchData.pokemon;
this.species = displayPokemon.species;
diff --git a/src/ui/move-info-overlay.ts b/src/ui/containers/move-info-overlay.ts
similarity index 99%
rename from src/ui/move-info-overlay.ts
rename to src/ui/containers/move-info-overlay.ts
index f98630260db..b321fcfc041 100644
--- a/src/ui/move-info-overlay.ts
+++ b/src/ui/containers/move-info-overlay.ts
@@ -41,8 +41,6 @@ const GLOBAL_SCALE = 6;
export class MoveInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle {
public active = false;
- private move: Move;
-
private desc: Phaser.GameObjects.Text;
private descScroll: Phaser.Tweens.Tween | null = null;
@@ -177,7 +175,6 @@ export class MoveInfoOverlay extends Phaser.GameObjects.Container implements Inf
if (!globalScene.enableMoveInfo) {
return false; // move infos have been disabled // TODO:: is `false` correct? i used to be `undeefined`
}
- this.move = move;
this.pow.setText(move.power >= 0 ? move.power.toString() : "---");
this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---");
this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---");
diff --git a/src/ui/party-exp-bar.ts b/src/ui/containers/party-exp-bar.ts
similarity index 100%
rename from src/ui/party-exp-bar.ts
rename to src/ui/containers/party-exp-bar.ts
diff --git a/src/ui/pokeball-tray.ts b/src/ui/containers/pokeball-tray.ts
similarity index 95%
rename from src/ui/pokeball-tray.ts
rename to src/ui/containers/pokeball-tray.ts
index b1522af0e27..1a89e22f294 100644
--- a/src/ui/pokeball-tray.ts
+++ b/src/ui/containers/pokeball-tray.ts
@@ -35,9 +35,9 @@ export class PokeballTray extends Phaser.GameObjects.Container {
.fill(null)
.map((_, i) =>
globalScene.add.sprite(
- (this.player ? -83 : 76) +
- globalScene.scaledCanvas.width * (this.player ? -1 : 1) +
- 10 * i * (this.player ? 1 : -1),
+ (this.player ? -83 : 76)
+ + globalScene.scaledCanvas.width * (this.player ? -1 : 1)
+ + 10 * i * (this.player ? 1 : -1),
-8,
"pb_tray_ball",
"empty",
diff --git a/src/ui/pokedex-info-overlay.ts b/src/ui/containers/pokedex-info-overlay.ts
similarity index 100%
rename from src/ui/pokedex-info-overlay.ts
rename to src/ui/containers/pokedex-info-overlay.ts
diff --git a/src/ui/pokedex-mon-container.ts b/src/ui/containers/pokedex-mon-container.ts
similarity index 100%
rename from src/ui/pokedex-mon-container.ts
rename to src/ui/containers/pokedex-mon-container.ts
diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/containers/pokemon-hatch-info-container.ts
similarity index 99%
rename from src/ui/pokemon-hatch-info-container.ts
rename to src/ui/containers/pokemon-hatch-info-container.ts
index bb1cc22e9fd..ad09eb39d42 100644
--- a/src/ui/pokemon-hatch-info-container.ts
+++ b/src/ui/containers/pokemon-hatch-info-container.ts
@@ -9,11 +9,11 @@ import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { TextStyle } from "#enums/text-style";
import type { PlayerPokemon } from "#field/pokemon";
-import { PokemonInfoContainer } from "#ui/pokemon-info-container";
import { addTextObject } from "#ui/text";
import { padInt, rgbHexToRgba } from "#utils/common";
import { getPokemonSpeciesForm } from "#utils/pokemon-utils";
import { argbFromRgba } from "@material/material-color-utilities";
+import { PokemonInfoContainer } from "./pokemon-info-container";
/**
* Class for the hatch info summary of each pokemon
diff --git a/src/ui/pokemon-info-container.ts b/src/ui/containers/pokemon-info-container.ts
similarity index 94%
rename from src/ui/pokemon-info-container.ts
rename to src/ui/containers/pokemon-info-container.ts
index 3b2349348a8..6072b610430 100644
--- a/src/ui/pokemon-info-container.ts
+++ b/src/ui/containers/pokemon-info-container.ts
@@ -6,15 +6,15 @@ import { PokemonType } from "#enums/pokemon-type";
import { TextStyle } from "#enums/text-style";
import type { Pokemon } from "#field/pokemon";
import { getVariantTint } from "#sprites/variant";
-import type { StarterDataEntry } from "#system/game-data";
import type { DexEntry } from "#types/dex-data";
-import { ConfirmUiHandler } from "#ui/confirm-ui-handler";
-import { StatsContainer } from "#ui/stats-container";
+import type { StarterDataEntry } from "#types/save-data";
+import { ConfirmUiHandler } from "#ui/handlers/confirm-ui-handler";
import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { fixedInt, getShinyDescriptor } from "#utils/common";
import i18next from "i18next";
import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
+import { StatsContainer } from "./stats-container";
interface LanguageSetting {
infoContainerTextSize: string;
@@ -274,8 +274,8 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container {
const newGender = BigInt(1 << pokemon.gender) * DexAttr.MALE;
this.pokemonGenderNewText.setText("(+)");
- this.pokemonGenderNewText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonGenderNewText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonGenderNewText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonGenderNewText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
this.pokemonGenderNewText.setVisible((newGender & caughtAttr) === BigInt(0));
} else {
this.pokemonGenderNewText.setVisible(false);
@@ -290,11 +290,11 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container {
const newForm = BigInt(1 << pokemon.formIndex) * DexAttr.DEFAULT_FORM;
if ((newForm & caughtAttr) === BigInt(0)) {
- this.pokemonFormLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonFormLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonFormLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonFormLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
} else {
- this.pokemonFormLabelText.setColor(getTextColor(TextStyle.WINDOW, false, globalScene.uiTheme));
- this.pokemonFormLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true, globalScene.uiTheme));
+ this.pokemonFormLabelText.setColor(getTextColor(TextStyle.WINDOW, false));
+ this.pokemonFormLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true));
}
this.pokemonFormText.setText(
@@ -320,31 +320,31 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container {
const abilityTextStyle = pokemon.abilityIndex === 2 ? TextStyle.MONEY : TextStyle.WINDOW;
this.pokemonAbilityText.setText(pokemon.getAbility(true).name);
- this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, globalScene.uiTheme));
- this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, globalScene.uiTheme));
+ this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false));
+ this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true));
// Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(starterEntry.abilityAttr);
if (!playerOwnsThisAbility) {
- this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
} else {
- this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.WINDOW, false, globalScene.uiTheme));
- this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true, globalScene.uiTheme));
+ this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.WINDOW, false));
+ this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true));
}
- this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, globalScene.uiTheme));
+ this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false));
const dexNatures = dexEntry.natureAttr;
const newNature = 1 << (pokemon.nature + 1);
if (!(dexNatures & newNature)) {
- this.pokemonNatureLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonNatureLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonNatureLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonNatureLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
} else {
- this.pokemonNatureLabelText.setColor(getTextColor(TextStyle.WINDOW, false, globalScene.uiTheme));
- this.pokemonNatureLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true, globalScene.uiTheme));
+ this.pokemonNatureLabelText.setColor(getTextColor(TextStyle.WINDOW, false));
+ this.pokemonNatureLabelText.setShadowColor(getTextColor(TextStyle.WINDOW, true));
}
const isFusion = pokemon.isFusion();
@@ -373,16 +373,16 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container {
const newVariant = BigInt(1 << (pokemon.variant + 4));
this.pokemonShinyNewIcon.setText("(+)");
- this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
const newShinyOrVariant = (newShiny & caughtAttr) === BigInt(0) || (newVariant & caughtAttr) === BigInt(0);
this.pokemonShinyNewIcon.setVisible(!!newShinyOrVariant);
} else if ((caughtAttr & DexAttr.NON_SHINY) === BigInt(0) && (caughtAttr & DexAttr.SHINY) === DexAttr.SHINY) {
//If the player has *only* caught any shiny variant of this species, not a non-shiny
this.pokemonShinyNewIcon.setVisible(true);
this.pokemonShinyNewIcon.setText("(+)");
- this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme));
- this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme));
+ this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false));
+ this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true));
} else {
this.pokemonShinyNewIcon.setVisible(false);
}
diff --git a/src/ui/saving-icon-handler.ts b/src/ui/containers/saving-icon-handler.ts
similarity index 95%
rename from src/ui/saving-icon-handler.ts
rename to src/ui/containers/saving-icon-handler.ts
index 00c8b8b526c..aad37bca97f 100644
--- a/src/ui/saving-icon-handler.ts
+++ b/src/ui/containers/saving-icon-handler.ts
@@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { fixedInt } from "#utils/common";
-export class SavingIconHandler extends Phaser.GameObjects.Container {
+export class SavingIconContainer extends Phaser.GameObjects.Container {
private icon: Phaser.GameObjects.Sprite;
private animActive: boolean;
diff --git a/src/ui/scroll-bar.ts b/src/ui/containers/scroll-bar.ts
similarity index 100%
rename from src/ui/scroll-bar.ts
rename to src/ui/containers/scroll-bar.ts
diff --git a/src/ui/starter-container.ts b/src/ui/containers/starter-container.ts
similarity index 100%
rename from src/ui/starter-container.ts
rename to src/ui/containers/starter-container.ts
diff --git a/src/ui/stats-container.ts b/src/ui/containers/stats-container.ts
similarity index 94%
rename from src/ui/stats-container.ts
rename to src/ui/containers/stats-container.ts
index e9af5eed3e3..fa53c8c8fd4 100644
--- a/src/ui/stats-container.ts
+++ b/src/ui/containers/stats-container.ts
@@ -82,10 +82,10 @@ export class StatsContainer extends Phaser.GameObjects.Container {
for (const s of PERMANENT_STATS) {
const statLabel = addTextObject(
ivChartBg.x + ivChartSize * ivChartStatCoordMultipliers[s][0] * 1.325 + (this.showDiff ? 0 : ivLabelOffset[s]),
- ivChartBg.y +
- ivChartSize * ivChartStatCoordMultipliers[s][1] * 1.325 -
- 4 +
- (this.showDiff ? 0 : ivChartLabelyOffset[s]),
+ ivChartBg.y
+ + ivChartSize * ivChartStatCoordMultipliers[s][1] * 1.325
+ - 4
+ + (this.showDiff ? 0 : ivChartLabelyOffset[s]),
i18next.t(getStatKey(s)),
TextStyle.STATS_HEXAGON,
);
@@ -113,7 +113,7 @@ export class StatsContainer extends Phaser.GameObjects.Container {
(ivs[ivChartStatIndexes[i]] / 31) * ivChartSize * ivChartStatCoordMultipliers[ivChartStatIndexes[i]][1],
]);
const lastIvChartData = this.statsIvsCache || defaultIvChartData;
- const perfectIVColor: string = getTextColor(TextStyle.SUMMARY_GOLD, false, globalScene.uiTheme);
+ const perfectIVColor: string = getTextColor(TextStyle.SUMMARY_GOLD, false);
this.statsIvsCache = ivChartData.slice(0);
this.ivStatValueTexts.map((t: BBCodeText, i: number) => {
@@ -127,7 +127,7 @@ export class StatsContainer extends Phaser.GameObjects.Container {
}
if (this.showDiff && originalIvs) {
if (originalIvs[i] < ivs[i]) {
- label += ` ([color=${getTextColor(TextStyle.SUMMARY_BLUE, false, globalScene.uiTheme)}][shadow=${getTextColor(TextStyle.SUMMARY_BLUE, true, globalScene.uiTheme)}]+${ivs[i] - originalIvs[i]}[/shadow][/color])`;
+ label += ` ([color=${getTextColor(TextStyle.SUMMARY_BLUE, false)}][shadow=${getTextColor(TextStyle.SUMMARY_BLUE, true)}]+${ivs[i] - originalIvs[i]}[/shadow][/color])`;
} else {
label += " (-)";
}
diff --git a/src/ui/time-of-day-widget.ts b/src/ui/containers/time-of-day-widget.ts
similarity index 100%
rename from src/ui/time-of-day-widget.ts
rename to src/ui/containers/time-of-day-widget.ts
diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/handlers/abstract-binding-ui-handler.ts
similarity index 91%
rename from src/ui/settings/abstract-binding-ui-handler.ts
rename to src/ui/handlers/abstract-binding-ui-handler.ts
index 2c8d0eb63ba..d106ff6f914 100644
--- a/src/ui/settings/abstract-binding-ui-handler.ts
+++ b/src/ui/handlers/abstract-binding-ui-handler.ts
@@ -2,9 +2,9 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { NavigationManager } from "#ui/navigation-menu";
-import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
+import { addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import i18next from "i18next";
@@ -222,16 +222,16 @@ export abstract class AbstractBindingUiHandler extends UiHandler {
setCursor(cursor: number): boolean {
this.cursor = cursor;
if (cursor === 1) {
- this.actionLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
- this.actionLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
- this.cancelLabel.setColor(this.getTextColor(TextStyle.WINDOW));
- this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
+ this.actionLabel.setColor(getTextColor(TextStyle.SETTINGS_SELECTED));
+ this.actionLabel.setShadowColor(getTextColor(TextStyle.SETTINGS_SELECTED, true));
+ this.cancelLabel.setColor(getTextColor(TextStyle.WINDOW));
+ this.cancelLabel.setShadowColor(getTextColor(TextStyle.WINDOW, true));
return true;
}
- this.actionLabel.setColor(this.getTextColor(TextStyle.WINDOW));
- this.actionLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
- this.cancelLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
- this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
+ this.actionLabel.setColor(getTextColor(TextStyle.WINDOW));
+ this.actionLabel.setShadowColor(getTextColor(TextStyle.WINDOW, true));
+ this.cancelLabel.setColor(getTextColor(TextStyle.SETTINGS_SELECTED));
+ this.cancelLabel.setShadowColor(getTextColor(TextStyle.SETTINGS_SELECTED, true));
return true;
}
diff --git a/src/ui/abstract-option-select-ui-handler.ts b/src/ui/handlers/abstract-option-select-ui-handler.ts
similarity index 93%
rename from src/ui/abstract-option-select-ui-handler.ts
rename to src/ui/handlers/abstract-option-select-ui-handler.ts
index b7279bc2d30..1e102010e4a 100644
--- a/src/ui/abstract-option-select-ui-handler.ts
+++ b/src/ui/handlers/abstract-option-select-ui-handler.ts
@@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addBBCodeTextObject, getTextColor, getTextStyleOptions } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
import { fixedInt, rgbHexToRgba } from "#utils/common";
import { argbFromRgba } from "@material/material-color-utilities";
@@ -81,7 +81,7 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
this.optionSelectIcons = [];
- this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale;
+ this.scale = getTextStyleOptions(TextStyle.WINDOW).scale;
this.setCursor(0);
}
@@ -105,7 +105,7 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
}
}
- if (this.optionSelectIcons?.length) {
+ if (this.optionSelectIcons?.length > 0) {
this.optionSelectIcons.map(i => i.destroy());
this.optionSelectIcons.splice(0, this.optionSelectIcons.length);
}
@@ -124,8 +124,8 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
optionsForWidth
.map(o =>
o.item
- ? `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true, globalScene.uiTheme)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false, globalScene.uiTheme)}] ${o.label}[/color][/shadow]`
- : `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true, globalScene.uiTheme)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false, globalScene.uiTheme)}]${o.label}[/color][/shadow]`,
+ ? `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false)}] ${o.label}[/color][/shadow]`
+ : `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false)}]${o.label}[/color][/shadow]`,
)
.join("\n"),
TextStyle.WINDOW,
@@ -149,8 +149,8 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
this.textContent = optionsWithScroll
.map(o =>
o.item
- ? `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true, globalScene.uiTheme)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false, globalScene.uiTheme)}] ${o.label}[/color][/shadow]`
- : `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true, globalScene.uiTheme)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false, globalScene.uiTheme)}]${o.label}[/color][/shadow]`,
+ ? `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false)}] ${o.label}[/color][/shadow]`
+ : `[shadow=${getTextColor(o.style ?? this.defaultTextStyle, true)}][color=${getTextColor(o.style ?? TextStyle.WINDOW, false)}]${o.label}[/color][/shadow]`,
)
.join("\n");
this.optionSelectText.setText(this.textContent);
@@ -184,7 +184,7 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
}
show(args: any[]): boolean {
- if (!args.length || !args[0].hasOwnProperty("options") || !args[0].options.length) {
+ if (args.length === 0 || !args[0].hasOwnProperty("options") || args[0].options.length === 0) {
return false;
}
@@ -315,8 +315,8 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler {
const optionStartIndex = this.scrollCursor;
const optionEndIndex = Math.min(
optionsScrollTotal,
- optionStartIndex +
- (!optionStartIndex || this.scrollCursor + (this.config.maxOptions - 1) >= optionsScrollTotal
+ optionStartIndex
+ + (!optionStartIndex || this.scrollCursor + (this.config.maxOptions - 1) >= optionsScrollTotal
? this.config.maxOptions - 1
: this.config.maxOptions - 2),
);
diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/handlers/achvs-ui-handler.ts
similarity index 98%
rename from src/ui/achvs-ui-handler.ts
rename to src/ui/handlers/achvs-ui-handler.ts
index b0f49d13c86..d7cf82e78ad 100644
--- a/src/ui/achvs-ui-handler.ts
+++ b/src/ui/handlers/achvs-ui-handler.ts
@@ -5,11 +5,11 @@ import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
import type { Achv } from "#system/achv";
import { achvs, getAchievementDescription } from "#system/achv";
-import type { AchvUnlocks, VoucherUnlocks } from "#system/game-data";
import type { Voucher } from "#system/voucher";
import { getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#system/voucher";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { ScrollBar } from "#ui/scroll-bar";
+import type { AchvUnlocks, VoucherUnlocks } from "#types/save-data";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import i18next from "i18next";
diff --git a/src/ui/admin-ui-handler.ts b/src/ui/handlers/admin-ui-handler.ts
similarity index 93%
rename from src/ui/admin-ui-handler.ts
rename to src/ui/handlers/admin-ui-handler.ts
index e577368363d..9ca30e35313 100644
--- a/src/ui/admin-ui-handler.ts
+++ b/src/ui/handlers/admin-ui-handler.ts
@@ -3,9 +3,10 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
+import { getTextColor } from "#ui/text";
import { toTitleCase } from "#utils/strings";
type AdminUiHandlerService = "discord" | "google";
@@ -17,8 +18,6 @@ export class AdminUiHandler extends FormModalUiHandler {
private config: ModalConfig;
private readonly buttonGap = 10;
- // http response from the server when a username isn't found in the server
- private readonly httpUserNotFoundErrorCode: number = 404;
private readonly ERR_REQUIRED_FIELD = (field: string) => {
if (field === "username") {
return `${toTitleCase(field)} is required`;
@@ -129,11 +128,11 @@ export class AdminUiHandler extends FormModalUiHandler {
this.errorMessage.setPosition(10, (hasTitle ? 31 : 5) + 20 * (fields.length - 1) + 16 + this.getButtonTopMargin()); // sets the position of the message dynamically
if (isMessageError) {
- this.errorMessage.setColor(this.getTextColor(TextStyle.SUMMARY_PINK));
- this.errorMessage.setShadowColor(this.getTextColor(TextStyle.SUMMARY_PINK, true));
+ this.errorMessage.setColor(getTextColor(TextStyle.SUMMARY_PINK));
+ this.errorMessage.setShadowColor(getTextColor(TextStyle.SUMMARY_PINK, true));
} else {
- this.errorMessage.setColor(this.getTextColor(TextStyle.SUMMARY_GREEN));
- this.errorMessage.setShadowColor(this.getTextColor(TextStyle.SUMMARY_GREEN, true));
+ this.errorMessage.setColor(getTextColor(TextStyle.SUMMARY_GREEN));
+ this.errorMessage.setShadowColor(getTextColor(TextStyle.SUMMARY_GREEN, true));
}
if (super.show(args)) {
@@ -321,7 +320,7 @@ export class AdminUiHandler extends FormModalUiHandler {
});
if (errorType || !adminInfo) {
// error - if adminInfo.status === this.httpUserNotFoundErrorCode that means the username can't be found in the db
- return { adminSearchResult: adminSearchResult, error: true, errorType };
+ return { adminSearchResult, error: true, errorType };
}
// success
return { adminSearchResult: adminInfo, error: false };
@@ -373,10 +372,10 @@ export class AdminUiHandler extends FormModalUiHandler {
if (errorType) {
// error - if response.status === this.httpUserNotFoundErrorCode that means the username can't be found in the db
- return { adminSearchResult: adminSearchResult, error: true, errorType };
+ return { adminSearchResult, error: true, errorType };
}
// success!
- return { adminSearchResult: adminSearchResult, error: false };
+ return { adminSearchResult, error: false };
} catch (err) {
console.error(err);
return { error: true, errorType: err };
@@ -420,9 +419,9 @@ export class AdminUiHandler extends FormModalUiHandler {
* and if either of these conditions are met, the element is destroyed.
*/
if (
- itemsToRemove.some(iTR => mC[i].name.includes(iTR)) ||
- (mC[i].type === "Container" &&
- (mC[i] as Phaser.GameObjects.Container).list.find(m => m.type === "rexInputText"))
+ itemsToRemove.some(iTR => mC[i].name.includes(iTR))
+ || (mC[i].type === "Container"
+ && (mC[i] as Phaser.GameObjects.Container).list.find(m => m.type === "rexInputText"))
) {
removeArray.push(mC[i]);
}
diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/handlers/autocomplete-ui-handler.ts
similarity index 94%
rename from src/ui/autocomplete-ui-handler.ts
rename to src/ui/handlers/autocomplete-ui-handler.ts
index 337b17048dc..914fe23a123 100644
--- a/src/ui/autocomplete-ui-handler.ts
+++ b/src/ui/handlers/autocomplete-ui-handler.ts
@@ -1,6 +1,6 @@
import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode";
-import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
+import { AbstractOptionSelectUiHandler } from "#ui/handlers/abstract-option-select-ui-handler";
export class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler {
modalContainer: Phaser.GameObjects.Container;
diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/handlers/awaitable-ui-handler.ts
similarity index 96%
rename from src/ui/awaitable-ui-handler.ts
rename to src/ui/handlers/awaitable-ui-handler.ts
index e8513b4acc1..9dcd3377da2 100644
--- a/src/ui/awaitable-ui-handler.ts
+++ b/src/ui/handlers/awaitable-ui-handler.ts
@@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import type { UiMode } from "#enums/ui-mode";
-import { UiHandler } from "#ui/ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
export abstract class AwaitableUiHandler extends UiHandler {
protected awaitingActionInput: boolean;
diff --git a/src/ui/ball-ui-handler.ts b/src/ui/handlers/ball-ui-handler.ts
similarity index 97%
rename from src/ui/ball-ui-handler.ts
rename to src/ui/handlers/ball-ui-handler.ts
index 99dabd893df..3d1868c207e 100644
--- a/src/ui/ball-ui-handler.ts
+++ b/src/ui/handlers/ball-ui-handler.ts
@@ -5,8 +5,8 @@ import { Command } from "#enums/command";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
import type { CommandPhase } from "#phases/command-phase";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addTextObject, getTextStyleOptions } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
import i18next from "i18next";
@@ -26,7 +26,7 @@ export class BallUiHandler extends UiHandler {
setup() {
const ui = this.getUi();
- this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale;
+ this.scale = getTextStyleOptions(TextStyle.WINDOW).scale;
let optionsTextContent = "";
diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/handlers/battle-message-ui-handler.ts
similarity index 94%
rename from src/ui/battle-message-ui-handler.ts
rename to src/ui/handlers/battle-message-ui-handler.ts
index 2ecca06172b..6912109c7e7 100644
--- a/src/ui/battle-message-ui-handler.ts
+++ b/src/ui/handlers/battle-message-ui-handler.ts
@@ -3,7 +3,7 @@ import { Button } from "#enums/buttons";
import { getStatKey, PERMANENT_STATS } from "#enums/stat";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import { MessageUiHandler } from "#ui/message-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import i18next from "i18next";
@@ -153,16 +153,12 @@ export class BattleMessageUiHandler extends MessageUiHandler {
processInput(button: Button): boolean {
const ui = this.getUi();
- if (this.awaitingActionInput) {
- if (button === Button.CANCEL || button === Button.ACTION) {
- if (this.onActionInput) {
- ui.playSelect();
- const originalOnActionInput = this.onActionInput;
- this.onActionInput = null;
- originalOnActionInput();
- return true;
- }
- }
+ if (this.awaitingActionInput && (button === Button.CANCEL || button === Button.ACTION) && this.onActionInput) {
+ ui.playSelect();
+ const originalOnActionInput = this.onActionInput;
+ this.onActionInput = null;
+ originalOnActionInput();
+ return true;
}
return false;
@@ -245,7 +241,6 @@ export class BattleMessageUiHandler extends MessageUiHandler {
getIvDescriptor(value: number, typeIv: number, pokemonId: number): string {
const starterSpecies = globalScene.getPokemonById(pokemonId)!.species.getRootSpeciesId(); // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists
const starterIvs: number[] = globalScene.gameData.dexData[starterSpecies].ivs;
- const uiTheme = globalScene.uiTheme; // Assuming uiTheme is accessible
// Function to wrap text in color based on comparison
const coloredText = (text: string, isBetter: boolean, ivValue) => {
@@ -259,8 +254,8 @@ export class BattleMessageUiHandler extends MessageUiHandler {
} else {
textStyle = TextStyle.WINDOW;
}
- const color = getTextColor(textStyle, false, uiTheme);
- return `[color=${color}][shadow=${getTextColor(textStyle, true, uiTheme)}]${text}[/shadow][/color]`;
+ const color = getTextColor(textStyle, false);
+ return `[color=${color}][shadow=${getTextColor(textStyle, true)}]${text}[/shadow][/color]`;
};
if (value > 30) {
diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/handlers/challenges-select-ui-handler.ts
similarity index 81%
rename from src/ui/challenges-select-ui-handler.ts
rename to src/ui/handlers/challenges-select-ui-handler.ts
index b37ddcc6a5f..5cc91285a74 100644
--- a/src/ui/challenges-select-ui-handler.ts
+++ b/src/ui/handlers/challenges-select-ui-handler.ts
@@ -5,8 +5,8 @@ import { Challenges } from "#enums/challenges";
import { Color, ShadowColor } from "#enums/color";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
import { getLocalizedSpriteKey } from "#utils/common";
import i18next from "i18next";
@@ -185,10 +185,10 @@ export class GameChallengesUiHandler extends UiHandler {
this.valuesContainer.add(value);
this.challengeLabels[i] = {
- label: label,
- value: value,
- leftArrow: leftArrow,
- rightArrow: rightArrow,
+ label,
+ value,
+ leftArrow,
+ rightArrow,
};
}
@@ -272,17 +272,17 @@ export class GameChallengesUiHandler extends UiHandler {
challengeLabel.leftArrow.setVisible(challenge.value !== 0);
challengeLabel.rightArrow.setPositionRelative(
challengeLabel.leftArrow,
- Math.max(this.monoTypeValue.width, this.widestTextBox) +
- challengeLabel.leftArrow.displayWidth +
- 2 * this.arrowSpacing,
+ Math.max(this.monoTypeValue.width, this.widestTextBox)
+ + challengeLabel.leftArrow.displayWidth
+ + 2 * this.arrowSpacing,
0,
);
challengeLabel.rightArrow.setVisible(challenge.value !== challenge.maxValue);
// this check looks to make sure that the arrows and value textbox don't take up too much space that they'll clip the right edge of the options background
if (
- challengeLabel.rightArrow.x + challengeLabel.rightArrow.width + this.optionsBg.rightWidth + this.arrowSpacing >
- this.optionsWidth
+ challengeLabel.rightArrow.x + challengeLabel.rightArrow.width + this.optionsBg.rightWidth + this.arrowSpacing
+ > this.optionsWidth
) {
// if we go out of bounds of the box, set the x position as far right as we can without going past the box, with this.arrowSpacing to allow a small gap between the arrow and border
challengeLabel.rightArrow.setX(this.optionsWidth - this.arrowSpacing - this.optionsBg.rightWidth);
@@ -383,14 +383,14 @@ export class GameChallengesUiHandler extends UiHandler {
this.updateChallengeArrows(this.startCursor.visible);
} else {
globalScene.phaseManager.toTitleScreen();
- globalScene.phaseManager.getCurrentPhase()?.end();
+ globalScene.phaseManager.getCurrentPhase().end();
}
success = true;
} else if (button === Button.SUBMIT || button === Button.ACTION) {
if (this.hasSelectedChallenge) {
if (this.startCursor.visible) {
globalScene.phaseManager.unshiftNew("SelectStarterPhase");
- globalScene.phaseManager.getCurrentPhase()?.end();
+ globalScene.phaseManager.getCurrentPhase().end();
} else {
this.startCursor.setVisible(true);
this.cursorObj?.setVisible(false);
@@ -400,75 +400,73 @@ export class GameChallengesUiHandler extends UiHandler {
} else {
success = false;
}
- } else {
- if (this.cursorObj?.visible && !this.startCursor.visible) {
- switch (button) {
- case Button.UP:
- if (this.cursor === 0) {
- if (this.scrollCursor === 0) {
- // When at the top of the menu and pressing UP, move to the bottommost item.
- if (globalScene.gameMode.challenges.length > rowsToDisplay) {
- // If there are more than 9 challenges, scroll to the bottom
- // First, set the cursor to the last visible element, preparing for the scroll to the end.
- const successA = this.setCursor(rowsToDisplay - 1);
- // Then, adjust the scroll to display the bottommost elements of the menu.
- const successB = this.setScrollCursor(globalScene.gameMode.challenges.length - rowsToDisplay);
- success = successA && successB; // success is just there to play the little validation sound effect
- } else {
- // If there are 9 or less challenges, just move to the bottom one
- success = this.setCursor(globalScene.gameMode.challenges.length - 1);
- }
- } else {
- success = this.setScrollCursor(this.scrollCursor - 1);
- }
- } else {
- success = this.setCursor(this.cursor - 1);
- }
- if (success) {
- this.updateText();
- }
- break;
- case Button.DOWN:
- if (this.cursor === rowsToDisplay - 1) {
- if (this.scrollCursor < globalScene.gameMode.challenges.length - rowsToDisplay) {
- // When at the bottom and pressing DOWN, scroll if possible.
- success = this.setScrollCursor(this.scrollCursor + 1);
- } else {
- // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item.
- // First, set the cursor to the first visible element, preparing for the scroll to the top.
- const successA = this.setCursor(0);
- // Then, adjust the scroll to display the topmost elements of the menu.
- const successB = this.setScrollCursor(0);
+ } else if (this.cursorObj?.visible && !this.startCursor.visible) {
+ switch (button) {
+ case Button.UP:
+ if (this.cursor === 0) {
+ if (this.scrollCursor === 0) {
+ // When at the top of the menu and pressing UP, move to the bottommost item.
+ if (globalScene.gameMode.challenges.length > rowsToDisplay) {
+ // If there are more than 9 challenges, scroll to the bottom
+ // First, set the cursor to the last visible element, preparing for the scroll to the end.
+ const successA = this.setCursor(rowsToDisplay - 1);
+ // Then, adjust the scroll to display the bottommost elements of the menu.
+ const successB = this.setScrollCursor(globalScene.gameMode.challenges.length - rowsToDisplay);
success = successA && successB; // success is just there to play the little validation sound effect
+ } else {
+ // If there are 9 or less challenges, just move to the bottom one
+ success = this.setCursor(globalScene.gameMode.challenges.length - 1);
}
- } else if (
- globalScene.gameMode.challenges.length < rowsToDisplay &&
- this.cursor === globalScene.gameMode.challenges.length - 1
- ) {
- // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item.
- success = this.setCursor(0);
} else {
- success = this.setCursor(this.cursor + 1);
+ success = this.setScrollCursor(this.scrollCursor - 1);
}
- if (success) {
- this.updateText();
+ } else {
+ success = this.setCursor(this.cursor - 1);
+ }
+ if (success) {
+ this.updateText();
+ }
+ break;
+ case Button.DOWN:
+ if (this.cursor === rowsToDisplay - 1) {
+ if (this.scrollCursor < globalScene.gameMode.challenges.length - rowsToDisplay) {
+ // When at the bottom and pressing DOWN, scroll if possible.
+ success = this.setScrollCursor(this.scrollCursor + 1);
+ } else {
+ // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item.
+ // First, set the cursor to the first visible element, preparing for the scroll to the top.
+ const successA = this.setCursor(0);
+ // Then, adjust the scroll to display the topmost elements of the menu.
+ const successB = this.setScrollCursor(0);
+ success = successA && successB; // success is just there to play the little validation sound effect
}
- break;
- case Button.LEFT:
- // Moves the option cursor left, if possible.
- success = this.getActiveChallenge().decreaseValue();
- if (success) {
- this.updateText();
- }
- break;
- case Button.RIGHT:
- // Moves the option cursor right, if possible.
- success = this.getActiveChallenge().increaseValue();
- if (success) {
- this.updateText();
- }
- break;
- }
+ } else if (
+ globalScene.gameMode.challenges.length < rowsToDisplay
+ && this.cursor === globalScene.gameMode.challenges.length - 1
+ ) {
+ // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item.
+ success = this.setCursor(0);
+ } else {
+ success = this.setCursor(this.cursor + 1);
+ }
+ if (success) {
+ this.updateText();
+ }
+ break;
+ case Button.LEFT:
+ // Moves the option cursor left, if possible.
+ success = this.getActiveChallenge().decreaseValue();
+ if (success) {
+ this.updateText();
+ }
+ break;
+ case Button.RIGHT:
+ // Moves the option cursor right, if possible.
+ success = this.getActiveChallenge().increaseValue();
+ if (success) {
+ this.updateText();
+ }
+ break;
}
}
diff --git a/src/ui/change-password-form-ui-handler.ts b/src/ui/handlers/change-password-form-ui-handler.ts
similarity index 94%
rename from src/ui/change-password-form-ui-handler.ts
rename to src/ui/handlers/change-password-form-ui-handler.ts
index eccc67ffb04..f4fdf349978 100644
--- a/src/ui/change-password-form-ui-handler.ts
+++ b/src/ui/handlers/change-password-form-ui-handler.ts
@@ -1,9 +1,9 @@
import { globalScene } from "#app/global-scene";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import { UiMode } from "#enums/ui-mode";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import i18next from "i18next";
export class ChangePasswordFormUiHandler extends FormModalUiHandler {
diff --git a/src/ui/command-ui-handler.ts b/src/ui/handlers/command-ui-handler.ts
similarity index 96%
rename from src/ui/command-ui-handler.ts
rename to src/ui/handlers/command-ui-handler.ts
index b702bcd0803..de5e51a4210 100644
--- a/src/ui/command-ui-handler.ts
+++ b/src/ui/handlers/command-ui-handler.ts
@@ -9,9 +9,9 @@ import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
import { TerastallizeAccessModifier } from "#modifiers/modifier";
import type { CommandPhase } from "#phases/command-phase";
-import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import { PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import i18next from "i18next";
export class CommandUiHandler extends UiHandler {
@@ -68,7 +68,7 @@ export class CommandUiHandler extends UiHandler {
show(args: any[]): boolean {
super.show(args);
- this.fieldIndex = args.length ? (args[0] as number) : 0;
+ this.fieldIndex = args.length > 0 ? (args[0] as number) : 0;
this.commandsContainer.setVisible(true);
@@ -198,7 +198,7 @@ export class CommandUiHandler extends UiHandler {
}
canTera(): boolean {
- const hasTeraMod = !!globalScene.getModifiers(TerastallizeAccessModifier).length;
+ const hasTeraMod = globalScene.getModifiers(TerastallizeAccessModifier).length > 0;
const activePokemon = globalScene.getField()[this.fieldIndex];
const isBlockedForm =
activePokemon.isMega() || activePokemon.isMax() || activePokemon.hasSpecies(SpeciesId.NECROZMA, "ultra");
diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/handlers/confirm-ui-handler.ts
similarity index 89%
rename from src/ui/confirm-ui-handler.ts
rename to src/ui/handlers/confirm-ui-handler.ts
index 529d1bd8bbb..77f1f182514 100644
--- a/src/ui/confirm-ui-handler.ts
+++ b/src/ui/handlers/confirm-ui-handler.ts
@@ -1,8 +1,8 @@
import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode";
-import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
-import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectConfig } from "#ui/handlers/abstract-option-select-ui-handler";
+import { AbstractOptionSelectUiHandler } from "#ui/handlers/abstract-option-select-ui-handler";
import i18next from "i18next";
export class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
@@ -21,12 +21,12 @@ export class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
show(args: any[]): boolean {
if (
- args.length === 5 &&
- args[0] instanceof Function &&
- args[1] instanceof Function &&
- args[2] instanceof Function &&
- args[3] instanceof Function &&
- args[4] === "fullParty"
+ args.length === 5
+ && args[0] instanceof Function
+ && args[1] instanceof Function
+ && args[2] instanceof Function
+ && args[3] instanceof Function
+ && args[4] === "fullParty"
) {
const config: OptionSelectConfig = {
options: [
diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/handlers/egg-gacha-ui-handler.ts
similarity index 99%
rename from src/ui/egg-gacha-ui-handler.ts
rename to src/ui/handlers/egg-gacha-ui-handler.ts
index e54ee445aee..bd96b4d9392 100644
--- a/src/ui/egg-gacha-ui-handler.ts
+++ b/src/ui/handlers/egg-gacha-ui-handler.ts
@@ -9,7 +9,7 @@ import { GachaType } from "#enums/gacha-types";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
import { getVoucherTypeIcon, VoucherType } from "#system/voucher";
-import { MessageUiHandler } from "#ui/message-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addTextObject, getEggTierTextTint, getTextStyleOptions } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { fixedInt, randSeedShuffle } from "#utils/common";
@@ -170,7 +170,7 @@ export class EggGachaUiHandler extends MessageUiHandler {
setup() {
this.gachaCursor = 0;
- this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale;
+ this.scale = getTextStyleOptions(TextStyle.WINDOW).scale;
const ui = this.getUi();
@@ -525,7 +525,7 @@ export class EggGachaUiHandler extends MessageUiHandler {
}
const eggSprite = globalScene.add.sprite(127, 75, "egg", `egg_${eggs[i].getKey()}`).setScale(0.5);
gachaContainer.addAt(eggSprite, 2);
- // biome-ignore lint/nursery/noAwaitInLoop: The point of this loop is to play the animations, one after another
+ // biome-ignore lint/performance/noAwaitInLoops: The point of this loop is to play the animations, one after another
await this.doPullAnim(eggSprite, i).finally(() => gachaContainer.remove(eggSprite, true));
}
diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/handlers/egg-hatch-scene-ui-handler.ts
similarity index 90%
rename from src/ui/egg-hatch-scene-handler.ts
rename to src/ui/handlers/egg-hatch-scene-ui-handler.ts
index 6536ef2af80..2fff9980de3 100644
--- a/src/ui/egg-hatch-scene-handler.ts
+++ b/src/ui/handlers/egg-hatch-scene-ui-handler.ts
@@ -1,9 +1,9 @@
import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode";
-import { UiHandler } from "#ui/ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
-export class EggHatchSceneHandler extends UiHandler {
+export class EggHatchSceneUiHandler extends UiHandler {
public eggHatchContainer: Phaser.GameObjects.Container;
/**
@@ -45,7 +45,7 @@ export class EggHatchSceneHandler extends UiHandler {
processInput(button: Button): boolean {
if (button === Button.ACTION || button === Button.CANCEL) {
const phase = globalScene.phaseManager.getCurrentPhase();
- if (phase?.is("EggHatchPhase") && phase.trySkip()) {
+ if (phase.is("EggHatchPhase") && phase.trySkip()) {
return true;
}
}
diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/handlers/egg-list-ui-handler.ts
similarity index 92%
rename from src/ui/egg-list-ui-handler.ts
rename to src/ui/handlers/egg-list-ui-handler.ts
index 2516903f631..e4d1d5b10e9 100644
--- a/src/ui/egg-list-ui-handler.ts
+++ b/src/ui/handlers/egg-list-ui-handler.ts
@@ -2,12 +2,12 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
-import { ScrollBar } from "#ui/scroll-bar";
-import { ScrollableGridUiHandler } from "#ui/scrollable-grid-handler";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
+import { PokemonIconAnimHelper, PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
+import { ScrollableGridHelper } from "#ui/utils/scrollable-grid-helper";
import i18next from "i18next";
export class EggListUiHandler extends MessageUiHandler {
@@ -25,9 +25,9 @@ export class EggListUiHandler extends MessageUiHandler {
private eggListMessageBoxContainer: Phaser.GameObjects.Container;
private cursorObj: Phaser.GameObjects.Image;
- private scrollGridHandler: ScrollableGridUiHandler;
+ private scrollGridHandler: ScrollableGridHelper;
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
constructor() {
super(UiMode.EGG_LIST);
@@ -45,7 +45,7 @@ export class EggListUiHandler extends MessageUiHandler {
const eggListBg = globalScene.add.image(0, 0, "egg_list_bg").setOrigin(0);
- this.iconAnimHandler = new PokemonIconAnimHandler();
+ this.iconAnimHandler = new PokemonIconAnimHelper();
this.iconAnimHandler.setup();
this.eggNameText = addTextObject(8, 68, "", TextStyle.SUMMARY).setOrigin(0);
@@ -64,7 +64,7 @@ export class EggListUiHandler extends MessageUiHandler {
const scrollBar = new ScrollBar(310, 5, 4, 170, this.ROWS);
- this.scrollGridHandler = new ScrollableGridUiHandler(this, this.ROWS, this.COLUMNS)
+ this.scrollGridHandler = new ScrollableGridHelper(this, this.ROWS, this.COLUMNS)
.withScrollBar(scrollBar)
.withUpdateGridCallBack(() => this.updateEggIcons())
.withUpdateSingleElementCallback((i: number) => this.setEggDetails(i));
diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/handlers/egg-summary-ui-handler.ts
similarity index 91%
rename from src/ui/egg-summary-ui-handler.ts
rename to src/ui/handlers/egg-summary-ui-handler.ts
index c66075dd910..1097615d83d 100644
--- a/src/ui/egg-summary-ui-handler.ts
+++ b/src/ui/handlers/egg-summary-ui-handler.ts
@@ -3,12 +3,12 @@ import { getEggTierForSpecies } from "#data/egg";
import type { EggHatchData } from "#data/egg-hatch-data";
import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode";
-import { HatchedPokemonContainer } from "#ui/hatched-pokemon-container";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { PokemonHatchInfoContainer } from "#ui/pokemon-hatch-info-container";
-import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
-import { ScrollBar } from "#ui/scroll-bar";
-import { ScrollableGridUiHandler } from "#ui/scrollable-grid-handler";
+import { HatchedPokemonContainer } from "#ui/containers/hatched-pokemon-container";
+import { PokemonHatchInfoContainer } from "#ui/containers/pokemon-hatch-info-container";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { PokemonIconAnimHelper, PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
+import { ScrollableGridHelper } from "#ui/utils/scrollable-grid-helper";
const iconContainerX = 112;
const iconContainerY = 9;
@@ -34,11 +34,11 @@ export class EggSummaryUiHandler extends MessageUiHandler {
/** hatch info container that displays the current pokemon / hatch (main element on left hand side) */
private infoContainer: PokemonHatchInfoContainer;
/** handles jumping animations for the pokemon sprite icons */
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
private eggHatchBg: Phaser.GameObjects.Image;
private eggHatchData: EggHatchData[];
- private scrollGridHandler: ScrollableGridUiHandler;
+ private scrollGridHandler: ScrollableGridHelper;
private cursorObj: Phaser.GameObjects.Image;
/** used to add a delay before which it is not possible to exit the summary */
@@ -67,7 +67,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
this.eggHatchContainer.setVisible(false);
ui.add(this.eggHatchContainer);
- this.iconAnimHandler = new PokemonIconAnimHandler();
+ this.iconAnimHandler = new PokemonIconAnimHelper();
this.iconAnimHandler.setup();
this.eggHatchBg = globalScene.add.image(0, 0, "egg_summary_bg");
@@ -97,7 +97,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
);
this.summaryContainer.add(scrollBar);
- this.scrollGridHandler = new ScrollableGridUiHandler(this, numRows, numCols)
+ this.scrollGridHandler = new ScrollableGridHelper(this, numRows, numCols)
.withScrollBar(scrollBar)
.withUpdateGridCallBack(() => this.updatePokemonIcons())
.withUpdateSingleElementCallback((i: number) => this.infoContainer.showHatchInfo(this.eggHatchData[i]));
@@ -144,7 +144,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
*/
show(args: EggHatchData[][]): boolean {
super.show(args);
- if (args.length >= 1) {
+ if (args.length > 0) {
// sort the egg hatch data by egg tier then by species number (then by order hatched)
this.eggHatchData = args[0].sort(function sortHatchData(a: EggHatchData, b: EggHatchData) {
const speciesA = a.pokemon.species;
@@ -222,7 +222,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
if (button === Button.CANCEL) {
if (!this.blockExit) {
const phase = globalScene.phaseManager.getCurrentPhase();
- if (phase?.is("EggSummaryPhase")) {
+ if (phase.is("EggSummaryPhase")) {
phase.end();
}
success = true;
diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/handlers/evolution-scene-ui-handler.ts
similarity index 81%
rename from src/ui/evolution-scene-handler.ts
rename to src/ui/handlers/evolution-scene-ui-handler.ts
index 0333594dc36..a1783544a07 100644
--- a/src/ui/evolution-scene-handler.ts
+++ b/src/ui/handlers/evolution-scene-ui-handler.ts
@@ -2,10 +2,10 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import { MessageUiHandler } from "#ui/message-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addTextObject } from "#ui/text";
-export class EvolutionSceneHandler extends MessageUiHandler {
+export class EvolutionSceneUiHandler extends MessageUiHandler {
public evolutionContainer: Phaser.GameObjects.Container;
public messageBg: Phaser.GameObjects.Image;
public messageContainer: Phaser.GameObjects.Container;
@@ -62,16 +62,12 @@ export class EvolutionSceneHandler extends MessageUiHandler {
}
const ui = this.getUi();
- if (this.awaitingActionInput) {
- if (button === Button.CANCEL || button === Button.ACTION) {
- if (this.onActionInput) {
- ui.playSelect();
- const originalOnActionInput = this.onActionInput;
- this.onActionInput = null;
- originalOnActionInput();
- return true;
- }
- }
+ if (this.awaitingActionInput && (button === Button.CANCEL || button === Button.ACTION) && this.onActionInput) {
+ ui.playSelect();
+ const originalOnActionInput = this.onActionInput;
+ this.onActionInput = null;
+ originalOnActionInput();
+ return true;
}
return false;
diff --git a/src/ui/fight-ui-handler.ts b/src/ui/handlers/fight-ui-handler.ts
similarity index 97%
rename from src/ui/fight-ui-handler.ts
rename to src/ui/handlers/fight-ui-handler.ts
index 1d856079939..9dd00a90b66 100644
--- a/src/ui/fight-ui-handler.ts
+++ b/src/ui/handlers/fight-ui-handler.ts
@@ -12,9 +12,9 @@ import { UiMode } from "#enums/ui-mode";
import type { EnemyPokemon, Pokemon } from "#field/pokemon";
import type { PokemonMove } from "#moves/pokemon-move";
import type { CommandPhase } from "#phases/command-phase";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
-import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import { UiHandler } from "#ui/handlers/ui-handler";
+import { addTextObject, getTextColor } from "#ui/text";
import { fixedInt, getLocalizedSpriteKey, padInt } from "#utils/common";
import i18next from "i18next";
@@ -284,7 +284,7 @@ export class FightUiHandler extends UiHandler implements InfoToggle {
const ppColorStyle = FightUiHandler.ppRatioToColor(pp / maxPP);
// Changes the text color and shadow according to the determined TextStyle
- this.ppText.setColor(this.getTextColor(ppColorStyle, false)).setShadowColor(this.getTextColor(ppColorStyle, true));
+ this.ppText.setColor(getTextColor(ppColorStyle, false)).setShadowColor(getTextColor(ppColorStyle, true));
this.moveInfoOverlay.show(pokemonMove.getMove());
pokemon.getOpponents().forEach(opponent => {
@@ -372,12 +372,12 @@ export class FightUiHandler extends UiHandler implements InfoToggle {
*/
private getMoveColor(pokemon: Pokemon, pokemonMove: PokemonMove): string | undefined {
if (!globalScene.typeHints) {
- return undefined;
+ return;
}
const opponents = pokemon.getOpponents();
if (opponents.length <= 0) {
- return undefined;
+ return;
}
const moveColors = opponents
@@ -394,7 +394,7 @@ export class FightUiHandler extends UiHandler implements InfoToggle {
.sort((a, b) => b - a)
.map(effectiveness => {
if (pokemonMove.getMove().category === MoveCategory.STATUS && effectiveness !== 0) {
- return undefined;
+ return;
}
return getTypeDamageMultiplierColor(effectiveness ?? 0, "offense");
});
diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/handlers/form-modal-ui-handler.ts
similarity index 87%
rename from src/ui/form-modal-ui-handler.ts
rename to src/ui/handlers/form-modal-ui-handler.ts
index 5c547465de9..af1d8653df7 100644
--- a/src/ui/form-modal-ui-handler.ts
+++ b/src/ui/handlers/form-modal-ui-handler.ts
@@ -2,9 +2,9 @@ import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
-import type { ModalConfig } from "#ui/modal-ui-handler";
-import { ModalUiHandler } from "#ui/modal-ui-handler";
-import { addTextInputObject, addTextObject } from "#ui/text";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
+import { ModalUiHandler } from "#ui/handlers/modal-ui-handler";
+import { addTextInputObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow, WindowVariant } from "#ui/ui-theme";
import { fixedInt } from "#utils/common";
import type InputText from "phaser3-rex-plugins/plugins/inputtext";
@@ -41,11 +41,11 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
getHeight(config?: ModalConfig): number {
return (
- 20 * this.getInputFieldConfigs().length +
- (this.getModalTitle() ? 26 : 0) +
- ((config as FormModalConfig)?.errorMessage ? 12 : 0) +
- this.getButtonTopMargin() +
- 28
+ 20 * this.getInputFieldConfigs().length
+ + (this.getModalTitle() ? 26 : 0)
+ + ((config as FormModalConfig)?.errorMessage ? 12 : 0)
+ + this.getButtonTopMargin()
+ + 28
);
}
@@ -64,7 +64,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
const hasTitle = !!this.getModalTitle();
- if (config.length >= 1) {
+ if (config.length > 0) {
this.updateFields(config, hasTitle);
}
@@ -78,8 +78,8 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
wordWrap: { width: 850 },
},
);
- this.errorMessage.setColor(this.getTextColor(TextStyle.SUMMARY_PINK));
- this.errorMessage.setShadowColor(this.getTextColor(TextStyle.SUMMARY_PINK, true));
+ this.errorMessage.setColor(getTextColor(TextStyle.SUMMARY_PINK));
+ this.errorMessage.setShadowColor(getTextColor(TextStyle.SUMMARY_PINK, true));
this.errorMessage.setVisible(false);
this.modalContainer.add(this.errorMessage);
}
@@ -100,7 +100,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
label.name = "formLabel" + f;
this.formLabels.push(label);
- this.modalContainer.add(this.formLabels[this.formLabels.length - 1]);
+ this.modalContainer.add(label);
const inputWidth = label.width < 320 ? 80 : 80 - (label.width - 320) / 5.5;
const inputContainer = globalScene.add.container(70 + (80 - inputWidth), (hasTitle ? 28 : 2) + 20 * f);
@@ -133,9 +133,14 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
const config = args[0] as FormModalConfig;
- this.submitAction = config.buttonActions.length ? config.buttonActions[0] : null;
+ this.submitAction = config.buttonActions.length > 0 ? config.buttonActions[0] : null;
this.cancelAction = config.buttonActions[1] ?? null;
+ // Auto focus the first input field after a short delay, to prevent accidental inputs
+ setTimeout(() => {
+ this.inputs[0].setFocus();
+ }, 50);
+
// #region: Override button pointerDown
// Override the pointerDown event for the buttonBgs to call the `submitAction` and `cancelAction`
// properties that we set above, allowing their behavior to change after this method terminates
diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/handlers/game-stats-ui-handler.ts
similarity index 99%
rename from src/ui/game-stats-ui-handler.ts
rename to src/ui/handlers/game-stats-ui-handler.ts
index be41e4d21b9..9ffb7346b4d 100644
--- a/src/ui/game-stats-ui-handler.ts
+++ b/src/ui/handlers/game-stats-ui-handler.ts
@@ -7,8 +7,8 @@ import { PlayerGender } from "#enums/player-gender";
import { TextStyle } from "#enums/text-style";
import { UiTheme } from "#enums/ui-theme";
import type { GameData } from "#system/game-data";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
import { formatFancyLargeNumber, getPlayTimeString } from "#utils/common";
import { toTitleCase } from "#utils/strings";
@@ -529,7 +529,7 @@ export function initStatsKeys() {
displayStats[key] = {
label_key: label,
sourceFunc: gameData => gameData.gameStats[key].toString(),
- hidden: hidden,
+ hidden,
};
} else if (displayStats[key] === null) {
displayStats[key] = {
diff --git a/src/ui/loading-modal-ui-handler.ts b/src/ui/handlers/loading-modal-ui-handler.ts
similarity index 92%
rename from src/ui/loading-modal-ui-handler.ts
rename to src/ui/handlers/loading-modal-ui-handler.ts
index de00d911c47..9b401e17f91 100644
--- a/src/ui/loading-modal-ui-handler.ts
+++ b/src/ui/handlers/loading-modal-ui-handler.ts
@@ -1,6 +1,6 @@
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
-import { ModalUiHandler } from "#ui/modal-ui-handler";
+import { ModalUiHandler } from "#ui/handlers/modal-ui-handler";
import { addTextObject } from "#ui/text";
import i18next from "i18next";
diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/handlers/login-form-ui-handler.ts
similarity index 83%
rename from src/ui/login-form-ui-handler.ts
rename to src/ui/handlers/login-form-ui-handler.ts
index 0f55faba5c4..0634ae36ba8 100644
--- a/src/ui/login-form-ui-handler.ts
+++ b/src/ui/handlers/login-form-ui-handler.ts
@@ -2,10 +2,11 @@ import { pokerogueApi } from "#api/pokerogue-api";
import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import { languageOptions } from "#system/settings-language";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { fixedInt } from "#utils/common";
@@ -31,6 +32,7 @@ export class LoginFormUiHandler extends FormModalUiHandler {
private discordImage: Phaser.GameObjects.Image;
private usernameInfoImage: Phaser.GameObjects.Image;
private saveDownloadImage: Phaser.GameObjects.Image;
+ private changeLanguageImage: Phaser.GameObjects.Image;
private externalPartyContainer: Phaser.GameObjects.Container;
private infoContainer: Phaser.GameObjects.Container;
private externalPartyBg: Phaser.GameObjects.NineSlice;
@@ -82,8 +84,14 @@ export class LoginFormUiHandler extends FormModalUiHandler {
scale: 0.75,
});
+ this.changeLanguageImage = this.buildInteractableImage("language_icon", "change-language-icon", {
+ x: 40,
+ scale: 0.5,
+ });
+
this.infoContainer.add(this.usernameInfoImage);
this.infoContainer.add(this.saveDownloadImage);
+ this.infoContainer.add(this.changeLanguageImage);
this.getUi().add(this.infoContainer);
this.infoContainer.setVisible(false);
this.infoContainer.disableInteractive();
@@ -163,13 +171,18 @@ export class LoginFormUiHandler extends FormModalUiHandler {
const [usernameInput, passwordInput] = this.inputs;
- pokerogueApi.account.login({ username: usernameInput.text, password: passwordInput.text }).then(error => {
- if (!error && originalLoginAction) {
- originalLoginAction();
- } else {
- onFail(error);
- }
- });
+ pokerogueApi.account
+ .login({
+ username: usernameInput.text,
+ password: passwordInput.text,
+ })
+ .then(error => {
+ if (!error && originalLoginAction) {
+ originalLoginAction();
+ } else {
+ onFail(error);
+ }
+ });
}
};
@@ -185,9 +198,13 @@ export class LoginFormUiHandler extends FormModalUiHandler {
this.infoContainer.setVisible(false);
this.setMouseCursorStyle("default"); //reset cursor
- [this.discordImage, this.googleImage, this.usernameInfoImage, this.saveDownloadImage].forEach(img =>
- img.off("pointerdown"),
- );
+ [
+ this.discordImage,
+ this.googleImage,
+ this.usernameInfoImage,
+ this.saveDownloadImage,
+ this.changeLanguageImage,
+ ].forEach(img => img.off("pointerdown"));
}
private processExternalProvider(config: ModalConfig): void {
@@ -206,6 +223,7 @@ export class LoginFormUiHandler extends FormModalUiHandler {
this.getUi().moveTo(this.infoContainer, this.getUi().length - 1);
this.usernameInfoImage.setPositionRelative(this.infoContainer, 0, 0);
this.saveDownloadImage.setPositionRelative(this.infoContainer, 20, 0);
+ this.changeLanguageImage.setPositionRelative(this.infoContainer, 40, 0);
this.discordImage.on("pointerdown", () => {
const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`);
@@ -234,9 +252,9 @@ export class LoginFormUiHandler extends FormModalUiHandler {
const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0);
if (dataKeys.length > 0 && dataKeys.length <= 2) {
const options: OptionSelectItem[] = [];
- for (let i = 0; i < dataKeys.length; i++) {
+ for (const key of dataKeys) {
options.push({
- label: dataKeys[i].replace(keyToFind, ""),
+ label: key.replace(keyToFind, ""),
handler: () => {
globalScene.ui.revertMode();
this.infoContainer.disableInteractive();
@@ -245,7 +263,7 @@ export class LoginFormUiHandler extends FormModalUiHandler {
});
}
globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
- options: options,
+ options,
delay: 1000,
});
this.infoContainer.setInteractive(
@@ -261,7 +279,7 @@ export class LoginFormUiHandler extends FormModalUiHandler {
}
});
- this.saveDownloadImage.on("pointerdown", () => {
+ this.saveDownloadImage.on("pointerdown", async () => {
// find all data_ and sessionData keys, put them in a .txt file and download everything in a single zip
const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage
const keyToFind = "data_";
@@ -270,25 +288,32 @@ export class LoginFormUiHandler extends FormModalUiHandler {
const sessionKeys = localStorageKeys.filter(ls => ls.indexOf(sessionKeyToFind) >= 0);
if (dataKeys.length > 0 || sessionKeys.length > 0) {
const zip = new JSZip();
- for (let i = 0; i < dataKeys.length; i++) {
- zip.file(dataKeys[i] + ".prsv", localStorage.getItem(dataKeys[i])!);
+ for (const dataKey of dataKeys) {
+ zip.file(dataKey + ".prsv", localStorage.getItem(dataKey)!);
}
- for (let i = 0; i < sessionKeys.length; i++) {
- zip.file(sessionKeys[i] + ".prsv", localStorage.getItem(sessionKeys[i])!);
+ for (const sessionKey of sessionKeys) {
+ zip.file(sessionKey + ".prsv", localStorage.getItem(sessionKey)!);
}
- zip.generateAsync({ type: "blob" }).then(content => {
- const url = URL.createObjectURL(content);
- const a = document.createElement("a");
- a.href = url;
- a.download = "pokerogue_saves.zip";
- a.click();
- URL.revokeObjectURL(url);
- });
+ const content = await zip.generateAsync({ type: "blob" });
+ const url = URL.createObjectURL(content);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = "pokerogue_saves.zip";
+ a.click();
+ URL.revokeObjectURL(url);
} else {
return onFail(this.ERR_NO_SAVES);
}
});
+ this.changeLanguageImage.on("pointerdown", () => {
+ globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
+ options: languageOptions,
+ maxOptions: 7,
+ delay: 1000,
+ });
+ });
+
this.externalPartyContainer.setAlpha(0);
globalScene.tweens.add({
targets: this.externalPartyContainer,
diff --git a/src/ui/menu-ui-handler.ts b/src/ui/handlers/menu-ui-handler.ts
similarity index 97%
rename from src/ui/menu-ui-handler.ts
rename to src/ui/handlers/menu-ui-handler.ts
index 4f6d6ede488..df1908bae39 100644
--- a/src/ui/menu-ui-handler.ts
+++ b/src/ui/handlers/menu-ui-handler.ts
@@ -7,11 +7,10 @@ import { Button } from "#enums/buttons";
import { GameDataType } from "#enums/game-data-type";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { AdminMode, getAdminModeName } from "#ui/admin-ui-handler";
-import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
-import { BgmBar } from "#ui/bgm-bar";
-import { MessageUiHandler } from "#ui/message-ui-handler";
+import { BgmBar } from "#ui/containers/bgm-bar";
+import type { OptionSelectConfig, OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addTextObject, getTextStyleOptions } from "#ui/text";
import { addWindow, WindowVariant } from "#ui/ui-theme";
import { fixedInt, isLocal, sessionIdKey } from "#utils/common";
@@ -20,6 +19,7 @@ import { getEnumValues } from "#utils/enums";
import { toCamelCase } from "#utils/strings";
import { isBeta } from "#utils/utility-vars";
import i18next from "i18next";
+import { AdminMode, getAdminModeName } from "./admin-ui-handler";
enum MenuOptions {
GAME_SETTINGS,
@@ -126,7 +126,7 @@ export class MenuUiHandler extends MessageUiHandler {
const ui = this.getUi();
this.excludedMenus = () => [
{
- condition: !!globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase"),
+ condition: globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase"),
options: [MenuOptions.EGG_GACHA],
},
{ condition: bypassLogin, options: [MenuOptions.LOG_OUT] },
@@ -145,7 +145,7 @@ export class MenuUiHandler extends MessageUiHandler {
);
this.optionSelectText.setLineSpacing(12);
- this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale;
+ this.scale = getTextStyleOptions(TextStyle.WINDOW).scale;
this.menuBg = addWindow(
globalScene.scaledCanvas.width - (this.optionSelectText.displayWidth + 25),
0,
@@ -495,7 +495,7 @@ export class MenuUiHandler extends MessageUiHandler {
},
});
globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, {
- options: options,
+ options,
delay: 0,
});
return true;
@@ -578,7 +578,7 @@ export class MenuUiHandler extends MessageUiHandler {
success = true;
break;
case MenuOptions.EGG_LIST:
- if (globalScene.gameData.eggs.length) {
+ if (globalScene.gameData.eggs.length > 0) {
ui.revertMode();
ui.setOverlayMode(UiMode.EGG_LIST);
success = true;
@@ -599,11 +599,11 @@ export class MenuUiHandler extends MessageUiHandler {
break;
case MenuOptions.MANAGE_DATA:
if (
- !bypassLogin &&
- !this.manageDataConfig.options.some(
+ !bypassLogin
+ && !this.manageDataConfig.options.some(
o =>
- o.label === i18next.t("menuUiHandler:linkDiscord") ||
- o.label === i18next.t("menuUiHandler:unlinkDiscord"),
+ o.label === i18next.t("menuUiHandler:linkDiscord")
+ || o.label === i18next.t("menuUiHandler:unlinkDiscord"),
)
) {
this.manageDataConfig.options.splice(
diff --git a/src/ui/message-ui-handler.ts b/src/ui/handlers/message-ui-handler.ts
similarity index 95%
rename from src/ui/message-ui-handler.ts
rename to src/ui/handlers/message-ui-handler.ts
index 57f054c9bf9..b8e3f983cca 100644
--- a/src/ui/message-ui-handler.ts
+++ b/src/ui/handlers/message-ui-handler.ts
@@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene";
import type { UiMode } from "#enums/ui-mode";
-import { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
+import { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
import { getFrameMs } from "#utils/common";
export abstract class MessageUiHandler extends AwaitableUiHandler {
@@ -108,17 +108,17 @@ export abstract class MessageUiHandler extends AwaitableUiHandler {
const textWords = text.split(" ");
let lastLineCount = 1;
let newText = "";
- for (let w = 0; w < textWords.length; w++) {
- const nextWordText = newText ? `${newText} ${textWords[w]}` : textWords[w];
+ for (const textWord of textWords) {
+ const nextWordText = newText ? `${newText} ${textWord}` : textWord;
- if (textWords[w].includes("\n")) {
+ if (textWord.includes("\n")) {
newText = nextWordText;
lastLineCount++;
} else {
const lineCount = this.message.runWordWrap(nextWordText).split(/\n/g).length;
if (lineCount > lastLineCount) {
lastLineCount = lineCount;
- newText = `${newText}\n${textWords[w]}`;
+ newText = `${newText}\n${textWord}`;
} else {
newText = nextWordText;
}
@@ -151,7 +151,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler {
this.pendingPrompt = true;
}
this.textTimer = globalScene.time.addEvent({
- delay: delay,
+ delay,
callback: () => {
const charIndex = text.length - this.textTimer?.repeatCount!; // TODO: is this bang correct?
const charVar = charVarMap.get(charIndex);
@@ -222,7 +222,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler {
showPrompt(callback?: Function | null, callbackDelay?: number | null) {
const wrappedTextLines = this.message.runWordWrap(this.message.text).split(/\n/g);
const textLinesCount = wrappedTextLines.length;
- const lastTextLine = wrappedTextLines[wrappedTextLines.length - 1];
+ const lastTextLine = wrappedTextLines.at(-1) ?? "";
const lastLineTest = globalScene.add.text(0, 0, lastTextLine, {
font: "96px emerald",
});
diff --git a/src/ui/modal-ui-handler.ts b/src/ui/handlers/modal-ui-handler.ts
similarity index 98%
rename from src/ui/modal-ui-handler.ts
rename to src/ui/handlers/modal-ui-handler.ts
index 51a6a21a29c..e6eecece9a2 100644
--- a/src/ui/modal-ui-handler.ts
+++ b/src/ui/handlers/modal-ui-handler.ts
@@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import type { Button } from "#enums/buttons";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow, WindowVariant } from "#ui/ui-theme";
export interface ModalConfig {
@@ -97,7 +97,7 @@ export abstract class ModalUiHandler extends UiHandler {
}
show(args: any[]): boolean {
- if (args.length >= 1 && "buttonActions" in args[0]) {
+ if (args.length > 0 && "buttonActions" in args[0]) {
super.show(args);
if (args[0].hasOwnProperty("fadeOut") && typeof args[0].fadeOut === "function") {
const [marginTop, marginRight, marginBottom, marginLeft] = this.getMargin();
diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/handlers/modifier-select-ui-handler.ts
similarity index 94%
rename from src/ui/modifier-select-ui-handler.ts
rename to src/ui/handlers/modifier-select-ui-handler.ts
index a070b522050..a721bf9e7db 100644
--- a/src/ui/modifier-select-ui-handler.ts
+++ b/src/ui/handlers/modifier-select-ui-handler.ts
@@ -11,8 +11,8 @@ import { UiMode } from "#enums/ui-mode";
import { HealShopCostModifier, LockModifierTiersModifier, PokemonHeldItemModifier } from "#modifiers/modifier";
import type { ModifierTypeOption } from "#modifiers/modifier-type";
import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "#modifiers/modifier-type";
-import { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
import { addTextObject, getModifierTierTextTint, getTextColor, getTextStyleOptions } from "#ui/text";
import { formatMoney, NumberHolder } from "#utils/common";
import i18next from "i18next";
@@ -65,7 +65,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
- const styleOptions = getTextStyleOptions(TextStyle.PARTY, globalScene.uiTheme).styleOptions;
+ const styleOptions = getTextStyleOptions(TextStyle.PARTY).styleOptions;
if (context) {
context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily;
@@ -179,7 +179,8 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
this.player = args[0];
const partyHasHeldItem =
- this.player && !!globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable).length;
+ this.player
+ && globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable).length > 0;
const canLockRarities = !!globalScene.findModifier(m => m instanceof LockModifierTiersModifier);
this.transferButtonContainer.setVisible(false);
@@ -480,12 +481,10 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
}
} else if (this.cursor) {
success = this.setCursor(this.cursor - 1);
+ } else if (this.rowCursor === 1 && this.options.length === 0) {
+ success = false;
} else {
- if (this.rowCursor === 1 && this.options.length === 0) {
- success = false;
- } else {
- success = this.setCursor(this.getRowItems(this.rowCursor) - 1);
- }
+ success = this.setCursor(this.getRowItems(this.rowCursor) - 1);
}
break;
case Button.RIGHT:
@@ -514,12 +513,10 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
}
} else if (this.cursor < this.getRowItems(this.rowCursor) - 1) {
success = this.setCursor(this.cursor + 1);
+ } else if (this.rowCursor === 1 && this.options.length === 0) {
+ success = this.setRowCursor(0);
} else {
- if (this.rowCursor === 1 && this.options.length === 0) {
- success = this.setRowCursor(0);
- } else {
- success = this.setCursor(0);
- }
+ success = this.setCursor(0);
}
break;
}
@@ -541,8 +538,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
this.modifierContainer.add(this.cursorObj);
}
- const options =
- this.rowCursor === 1 ? this.options : this.shopOptionsRows[this.shopOptionsRows.length - (this.rowCursor - 1)];
+ const options = this.rowCursor === 1 ? this.options : this.shopOptionsRows.at(-(this.rowCursor - 1))!;
this.cursorObj.setScale(this.rowCursor === 1 ? 2 : this.rowCursor >= 2 ? 1.5 : 1);
@@ -554,8 +550,8 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
this.cursorObj.setScale(1.25);
this.cursorObj.setPosition(
globalScene.scaledCanvas.width / 3 + 23,
- -globalScene.scaledCanvas.height / 2 -
- (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
+ -globalScene.scaledCanvas.height / 2
+ - (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
);
ui.showText(i18next.t("modifierSelectUiHandler:continueNextWaveDescription"));
return ret;
@@ -566,16 +562,16 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
// Cursor on free items
this.cursorObj.setPosition(
sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 20,
- -globalScene.scaledCanvas.height / 2 -
- (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
+ -globalScene.scaledCanvas.height / 2
+ - (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
);
} else {
// Cursor on paying items
this.cursorObj.setPosition(
sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 16,
- -globalScene.scaledCanvas.height / 2 -
- globalScene.game.canvas.height / 32 -
- (-14 + 28 * (this.rowCursor - (this.shopOptionsRows.length - 1))),
+ -globalScene.scaledCanvas.height / 2
+ - globalScene.game.canvas.height / 32
+ - (-14 + 28 * (this.rowCursor - (this.shopOptionsRows.length - 1))),
);
}
@@ -657,7 +653,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
case 1:
return this.options.length;
default:
- return this.shopOptionsRows[this.shopOptionsRows.length - (rowCursor - 1)].length;
+ return this.shopOptionsRows.at(-(rowCursor - 1))!.length;
}
}
@@ -686,14 +682,14 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
const formattedMoney = formatMoney(globalScene.moneyFormat, this.rerollCost);
this.rerollCostText.setText(i18next.t("modifierSelectUiHandler:rerollCost", { formattedMoney }));
- this.rerollCostText.setColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED));
- this.rerollCostText.setShadowColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED, true));
+ this.rerollCostText.setColor(getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED));
+ this.rerollCostText.setShadowColor(getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED, true));
}
updateLockRaritiesText(): void {
const textStyle = globalScene.lockModifierTiers ? TextStyle.SUMMARY_BLUE : TextStyle.PARTY;
- this.lockRarityButtonText.setColor(this.getTextColor(textStyle));
- this.lockRarityButtonText.setShadowColor(this.getTextColor(textStyle, true));
+ this.lockRarityButtonText.setColor(getTextColor(textStyle));
+ this.lockRarityButtonText.setShadowColor(getTextColor(textStyle, true));
}
clear() {
@@ -747,7 +743,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
duration: 250,
ease: "Cubic.easeIn",
onComplete: () => {
- if (!this.options.length) {
+ if (this.options.length === 0) {
container.setVisible(false);
} else {
container.setAlpha(1);
@@ -1042,7 +1038,7 @@ class ModifierOption extends Phaser.GameObjects.Container {
const formattedMoney = formatMoney(globalScene.moneyFormat, cost);
this.itemCostText.setText(i18next.t("modifierSelectUiHandler:itemCost", { formattedMoney }));
- this.itemCostText.setColor(getTextColor(textStyle, false, globalScene.uiTheme));
- this.itemCostText.setShadowColor(getTextColor(textStyle, true, globalScene.uiTheme));
+ this.itemCostText.setColor(getTextColor(textStyle, false));
+ this.itemCostText.setShadowColor(getTextColor(textStyle, true));
}
}
diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/handlers/mystery-encounter-ui-handler.ts
similarity index 93%
rename from src/ui/mystery-encounter-ui-handler.ts
rename to src/ui/handlers/mystery-encounter-ui-handler.ts
index 37f77cf43b9..9bc6f0681ee 100644
--- a/src/ui/mystery-encounter-ui-handler.ts
+++ b/src/ui/handlers/mystery-encounter-ui-handler.ts
@@ -9,9 +9,9 @@ import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { OptionSelectSettings } from "#mystery-encounters/encounter-phase-utils";
import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option";
import type { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { PartyUiMode } from "#ui/party-ui-handler";
+import { PartyUiMode } from "#ui/handlers/party-ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addBBCodeTextObject, getBBCodeFrag } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow, WindowVariant } from "#ui/ui-theme";
import { fixedInt, isNullOrUndefined } from "#utils/common";
import i18next from "i18next";
@@ -150,20 +150,18 @@ export class MysteryEncounterUiHandler extends UiHandler {
}, 300);
});
} else if (
- this.blockInput ||
- (!this.optionsMeetsReqs[cursor] &&
- (selected.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT ||
- selected.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL))
+ this.blockInput
+ || (!this.optionsMeetsReqs[cursor]
+ && (selected.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT
+ || selected.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL))
) {
success = false;
+ } else if (
+ (globalScene.phaseManager.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)
+ ) {
+ success = true;
} else {
- if (
- (globalScene.phaseManager.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)
- ) {
- success = true;
- } else {
- ui.playError();
- }
+ ui.playError();
}
} else {
// TODO: If we need to handle cancel option? Maybe default logic to leave/run from encounter idk
@@ -306,9 +304,9 @@ export class MysteryEncounterUiHandler extends UiHandler {
for (let i = 0; i < this.optionsContainer.length - 1; i++) {
const optionMode = this.encounterOptions[i].optionMode;
if (
- !this.optionsMeetsReqs[i] &&
- (optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT ||
- optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
+ !this.optionsMeetsReqs[i]
+ && (optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT
+ || optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
) {
continue;
}
@@ -409,10 +407,10 @@ export class MysteryEncounterUiHandler extends UiHandler {
: optionDialogue.buttonLabel;
let text: string | null;
if (
- option.hasRequirements() &&
- this.optionsMeetsReqs[i] &&
- (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL ||
- option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
+ option.hasRequirements()
+ && this.optionsMeetsReqs[i]
+ && (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL
+ || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
) {
// Options with special requirements that are met are automatically colored green
text = getEncounterText(label, TextStyle.ME_OPTION_SPECIAL);
@@ -425,9 +423,9 @@ export class MysteryEncounterUiHandler extends UiHandler {
}
if (
- !this.optionsMeetsReqs[i] &&
- (option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT ||
- option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
+ !this.optionsMeetsReqs[i]
+ && (option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT
+ || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
) {
optionText.setAlpha(0.5);
}
@@ -580,10 +578,10 @@ export class MysteryEncounterUiHandler extends UiHandler {
const cursorOption = this.encounterOptions[cursor];
const optionDialogue = cursorOption.dialogue!;
if (
- !this.optionsMeetsReqs[cursor] &&
- (cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT ||
- cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) &&
- optionDialogue.disabledButtonTooltip
+ !this.optionsMeetsReqs[cursor]
+ && (cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT
+ || cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
+ && optionDialogue.disabledButtonTooltip
) {
text = getEncounterText(optionDialogue.disabledButtonTooltip, TextStyle.TOOLTIP_CONTENT);
} else {
@@ -596,18 +594,18 @@ export class MysteryEncounterUiHandler extends UiHandler {
text = text.replace(
/(\(\+\)[^([]*)/gi,
substring =>
- "[/color][/shadow]" +
- getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) +
- "[/color][/shadow]" +
- primaryStyleString,
+ "[/color][/shadow]"
+ + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN)
+ + "[/color][/shadow]"
+ + primaryStyleString,
);
text = text.replace(
/(\(-\)[^([]*)/gi,
substring =>
- "[/color][/shadow]" +
- getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) +
- "[/color][/shadow]" +
- primaryStyleString,
+ "[/color][/shadow]"
+ + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE)
+ + "[/color][/shadow]"
+ + primaryStyleString,
);
}
diff --git a/src/ui/party-ui-handler.ts b/src/ui/handlers/party-ui-handler.ts
similarity index 89%
rename from src/ui/party-ui-handler.ts
rename to src/ui/handlers/party-ui-handler.ts
index d3c67f66ec9..cc2bb093b07 100644
--- a/src/ui/party-ui-handler.ts
+++ b/src/ui/handlers/party-ui-handler.ts
@@ -21,11 +21,11 @@ import type { PokemonMove } from "#moves/pokemon-move";
import type { CommandPhase } from "#phases/command-phase";
import { getVariantTint } from "#sprites/variant";
import type { TurnMove } from "#types/turn-move";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
-import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
+import { PokemonIconAnimHelper, PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, getLocalizedSpriteKey, randInt } from "#utils/common";
import { toCamelCase, toTitleCase } from "#utils/strings";
@@ -201,7 +201,7 @@ export class PartyUiHandler extends MessageUiHandler {
private tmMoveId: MoveId;
private showMovePp: boolean;
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
private blockInput: boolean;
@@ -320,7 +320,7 @@ export class PartyUiHandler extends MessageUiHandler {
this.optionsContainer = globalScene.add.container(globalScene.scaledCanvas.width - 1, -1);
partyContainer.add(this.optionsContainer);
- this.iconAnimHandler = new PokemonIconAnimHandler();
+ this.iconAnimHandler = new PokemonIconAnimHelper();
this.iconAnimHandler.setup();
const partyDiscardModeButton = new PartyDiscardModeButton(DISCARD_BUTTON_X, DISCARD_BUTTON_Y, this);
@@ -342,7 +342,7 @@ export class PartyUiHandler extends MessageUiHandler {
}
show(args: any[]): boolean {
- if (!args.length || this.active) {
+ if (args.length === 0 || this.active) {
return false;
}
@@ -441,7 +441,7 @@ export class PartyUiHandler extends MessageUiHandler {
ui.setMode(UiMode.PARTY);
this.showText(
i18next.t("partyUiHandler:wasReverted", {
- fusionName: fusionName,
+ fusionName,
pokemonName: pokemon.getName(false),
}),
undefined,
@@ -562,8 +562,11 @@ export class PartyUiHandler extends MessageUiHandler {
private processModifierTransferModeInput(pokemon: PlayerPokemon) {
const ui = this.getUi();
const option = this.options[this.optionsCursor];
+ const allItems = this.getTransferrableItemsFromPokemon(pokemon);
+ // get the index of the "All" option.
+ const allCursorIndex = allItems.length;
- if (option === PartyOption.TRANSFER) {
+ if (this.transferMode && option === PartyOption.TRANSFER) {
return this.processTransferOption();
}
@@ -573,36 +576,62 @@ export class PartyUiHandler extends MessageUiHandler {
let ableToTransferText: string;
for (let p = 0; p < globalScene.getPlayerParty().length; p++) {
- // this for look goes through each of the party pokemon
+ // this for loop goes through each of the party pokemon
const newPokemon = globalScene.getPlayerParty()[p];
// this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon `p`
// this returns `undefined` if the new pokemon doesn't have the item at all, otherwise it returns the `pokemonHeldItemModifier` for that item
- const matchingModifier = globalScene.findModifier(
- m =>
- m.is("PokemonHeldItemModifier") &&
- m.pokemonId === newPokemon.id &&
- m.matchType(this.getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor]),
- ) as PokemonHeldItemModifier;
+ const matchingModifiers: (PokemonHeldItemModifier | undefined)[] = [];
+ if (this.transferOptionCursor === allCursorIndex) {
+ // if "All" is selected, check all items
+ for (const item of allItems) {
+ matchingModifiers.push(
+ globalScene.findModifier(
+ m => m.is("PokemonHeldItemModifier") && m.pokemonId === newPokemon.id && m.matchType(item),
+ ) as PokemonHeldItemModifier | undefined,
+ );
+ }
+ } else {
+ // otherwise only check the selected item
+ matchingModifiers.push(
+ globalScene.findModifier(
+ m =>
+ m.is("PokemonHeldItemModifier")
+ && m.pokemonId === newPokemon.id
+ && m.matchType(allItems[this.transferOptionCursor]),
+ ) as PokemonHeldItemModifier | undefined,
+ );
+ }
+ const hasMatchingModifier = matchingModifiers.some(m => m !== undefined); // checks if any items match
const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us
if (p !== this.transferCursor) {
// this skips adding the able/not able labels on the pokemon doing the transfer
- if (matchingModifier) {
+ if (hasMatchingModifier) {
// if matchingModifier exists then the item exists on the new pokemon
- if (matchingModifier.getMaxStackCount() === matchingModifier.stackCount) {
- // checks to see if the stack of items is at max stack; if so, set the description label to "Not able"
- ableToTransferText = i18next.t("partyUiHandler:notAble");
- } else {
- // if the pokemon isn't at max stack, make the label "Able"
- ableToTransferText = i18next.t("partyUiHandler:able");
+ ableToTransferText = i18next.t("partyUiHandler:notAble"); // start with not able
+ /**
+ * The amount of items that can be transferred in the `All` option
+ */
+ let ableAmount = 0;
+ for (const modifier of matchingModifiers) {
+ if (!modifier || modifier.getCountUnderMax() > 0) {
+ // if the modifier doesn't exist, or the stack count isn't at max, then we can transfer at least 1 stack
+ ableToTransferText = i18next.t("partyUiHandler:able");
+ ableAmount++;
+ }
}
+ // only show the amount if an item can be transferred and there are multiple items
+ ableToTransferText += ableAmount && matchingModifiers.length > 1 ? ` (${ableAmount})` : "";
} else {
- // if matchingModifier doesn't exist, that means the pokemon doesn't have any of the item, and we need to show "Able"
+ // if no item matches, that means the pokemon doesn't have any of the item, and we need to show "Able"
ableToTransferText = i18next.t("partyUiHandler:able");
+ // only show the amount if there are multiple items
+ ableToTransferText += matchingModifiers.length > 1 ? ` (${matchingModifiers.length})` : "";
}
} else {
// this else relates to the transfer pokemon. We set the text to be blank so there's no "Able"/"Not able" text
ableToTransferText = "";
}
+ partySlot.slotHpLabel.setVisible(false);
partySlot.slotHpBar.setVisible(false);
partySlot.slotHpOverlay.setVisible(false);
partySlot.slotHpText.setVisible(false);
@@ -613,39 +642,51 @@ export class PartyUiHandler extends MessageUiHandler {
ui.playSelect();
return true;
}
+
+ if (option === PartyOption.SUMMARY) {
+ return this.processSummaryOption(pokemon);
+ }
+ if (option === PartyOption.POKEDEX) {
+ return this.processPokedexOption(pokemon);
+ }
+ if (option === PartyOption.UNPAUSE_EVOLUTION) {
+ return this.processUnpauseEvolutionOption(pokemon);
+ }
+ if (option === PartyOption.RENAME) {
+ return this.processRenameOption(pokemon);
+ }
+
return false;
}
// TODO: Might need to check here for when this.transferMode is active.
private processModifierTransferModeLeftRightInput(button: Button) {
+ if (!this.isItemManageMode()) {
+ return false;
+ }
let success = false;
const option = this.options[this.optionsCursor];
if (button === Button.LEFT) {
/** Decrease quantity for the current item and update UI */
- if (this.isItemManageMode()) {
- this.transferQuantities[option] =
- this.transferQuantities[option] === 1
- ? this.transferQuantitiesMax[option]
- : this.transferQuantities[option] - 1;
- this.updateOptions();
- success = this.setCursor(
- this.optionsCursor,
- ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
- }
+ this.transferQuantities[option] =
+ this.transferQuantities[option] === 1
+ ? this.transferQuantitiesMax[option]
+ : this.transferQuantities[option] - 1;
+ this.updateOptions();
+ success = this.setCursor(
+ this.optionsCursor,
+ ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
-
if (button === Button.RIGHT) {
/** Increase quantity for the current item and update UI */
- if (this.isItemManageMode()) {
- this.transferQuantities[option] =
- this.transferQuantities[option] === this.transferQuantitiesMax[option]
- ? 1
- : this.transferQuantities[option] + 1;
- this.updateOptions();
- success = this.setCursor(
- this.optionsCursor,
- ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
- }
+ this.transferQuantities[option] =
+ this.transferQuantities[option] === this.transferQuantitiesMax[option]
+ ? 1
+ : this.transferQuantities[option] + 1;
+ this.updateOptions();
+ success = this.setCursor(
+ this.optionsCursor,
+ ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
return success;
}
@@ -819,9 +860,9 @@ export class PartyUiHandler extends MessageUiHandler {
// This is only relevant for PartyUiMode.CHECK
// TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check?
if (
- option >= PartyOption.FORM_CHANGE_ITEM &&
- globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase") &&
- this.partyUiMode === PartyUiMode.CHECK
+ option >= PartyOption.FORM_CHANGE_ITEM
+ && globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase")
+ && this.partyUiMode === PartyUiMode.CHECK
) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM];
@@ -871,8 +912,8 @@ export class PartyUiHandler extends MessageUiHandler {
// This is used when switching out using the Pokemon command (possibly holding a Baton held item). In this case there is no callback.
if (
- (option === PartyOption.PASS_BATON || option === PartyOption.SEND_OUT) &&
- this.partyUiMode === PartyUiMode.SWITCH
+ (option === PartyOption.PASS_BATON || option === PartyOption.SEND_OUT)
+ && this.partyUiMode === PartyUiMode.SWITCH
) {
this.clearOptions();
(globalScene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(
@@ -894,8 +935,8 @@ export class PartyUiHandler extends MessageUiHandler {
PartyOption.MOVE_3,
PartyOption.MOVE_4,
PartyOption.SELECT,
- ].includes(option) &&
- this.selectCallback
+ ].includes(option)
+ && this.selectCallback
) {
this.clearOptions();
const selectCallback = this.selectCallback;
@@ -934,10 +975,8 @@ export class PartyUiHandler extends MessageUiHandler {
return this.moveOptionCursor(button);
}
- if (button === Button.LEFT || button === Button.RIGHT) {
- if (this.isItemManageMode()) {
- return this.processModifierTransferModeLeftRightInput(button);
- }
+ if ((button === Button.LEFT || button === Button.RIGHT) && this.isItemManageMode()) {
+ return this.processModifierTransferModeLeftRightInput(button);
}
return false;
@@ -1003,15 +1042,15 @@ export class PartyUiHandler extends MessageUiHandler {
const ui = this.getUi();
if (this.cursor < 6) {
if (
- (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) ||
- this.partyUiMode === PartyUiMode.DISCARD
+ (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode)
+ || this.partyUiMode === PartyUiMode.DISCARD
) {
/** Initialize item quantities for the selected Pokemon */
const itemModifiers = globalScene.findModifiers(
m =>
- m.is("PokemonHeldItemModifier") &&
- m.isTransferable &&
- m.pokemonId === globalScene.getPlayerParty()[this.cursor].id,
+ m.is("PokemonHeldItemModifier")
+ && m.isTransferable
+ && m.pokemonId === globalScene.getPlayerParty()[this.cursor].id,
) as PokemonHeldItemModifier[];
this.transferQuantities = itemModifiers.map(item => item.getStackCount());
this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount());
@@ -1021,7 +1060,8 @@ export class PartyUiHandler extends MessageUiHandler {
}
// Toggle item transfer mode to discard items or vice versa
- if (this.cursor === 7) {
+ // Prevent changing mode, when currently transfering an item
+ if (this.cursor === 7 && !this.transferMode) {
switch (this.partyUiMode) {
case PartyUiMode.DISCARD:
this.partyUiMode = PartyUiMode.MODIFIER_TRANSFER;
@@ -1052,8 +1092,8 @@ export class PartyUiHandler extends MessageUiHandler {
private processPartyCancelInput(): boolean {
const ui = this.getUi();
if (
- (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) &&
- this.transferMode
+ (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE)
+ && this.transferMode
) {
this.clearTransfer();
ui.playSelect();
@@ -1219,11 +1259,9 @@ export class PartyUiHandler extends MessageUiHandler {
isScroll = true;
this.optionsScrollCursor++;
}
- } else {
- if (!cursor && this.optionsScrollCursor) {
- isScroll = true;
- this.optionsScrollCursor--;
- }
+ } else if (!cursor && this.optionsScrollCursor) {
+ isScroll = true;
+ this.optionsScrollCursor--;
}
if (isScroll && this.optionsScrollCursor === 1) {
this.optionsScrollCursor += isDown ? 1 : -1;
@@ -1328,8 +1366,8 @@ export class PartyUiHandler extends MessageUiHandler {
private allowBatonModifierSwitch(): boolean {
return !!(
- this.partyUiMode !== PartyUiMode.FAINT_SWITCH &&
- globalScene.findModifier(
+ this.partyUiMode !== PartyUiMode.FAINT_SWITCH
+ && globalScene.findModifier(
m => m.is("SwitchEffectTransferModifier") && m.pokemonId === globalScene.getPlayerField()[this.fieldIndex].id,
)
);
@@ -1340,9 +1378,9 @@ export class PartyUiHandler extends MessageUiHandler {
private isBatonPassMove(): boolean {
const lastMove: TurnMove | undefined = globalScene.getPlayerField()[this.fieldIndex].getLastXMoves()[0];
return (
- this.partyUiMode === PartyUiMode.FAINT_SWITCH &&
- lastMove?.result === MoveResult.SUCCESS &&
- allMoves[lastMove.move].getAttrs("ForceSwitchOutAttr")[0]?.isBatonPass()
+ this.partyUiMode === PartyUiMode.FAINT_SWITCH
+ && lastMove?.result === MoveResult.SUCCESS
+ && allMoves[lastMove.move].getAttrs("ForceSwitchOutAttr")[0]?.isBatonPass()
);
}
@@ -1359,7 +1397,7 @@ export class PartyUiHandler extends MessageUiHandler {
for (let m = 0; m < learnableMoves.length; m++) {
this.options.push(m);
}
- if (learnableMoves?.length) {
+ if (learnableMoves?.length > 0) {
// show the move overlay with info for the first move
this.moveInfoOverlay.show(allMoves[learnableMoves[0]]);
}
@@ -1388,8 +1426,10 @@ export class PartyUiHandler extends MessageUiHandler {
this.options.push(PartyOption.RENAME);
if (
- pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) ||
- (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId))
+ pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId)
+ || (pokemon.isFusion()
+ && pokemon.fusionSpecies
+ && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId))
) {
this.options.push(PartyOption.UNPAUSE_EVOLUTION);
}
@@ -1422,7 +1462,7 @@ export class PartyUiHandler extends MessageUiHandler {
updateOptions(): void {
const pokemon = globalScene.getPlayerParty()[this.cursor];
- if (this.options.length) {
+ if (this.options.length > 0) {
this.options.splice(0, this.options.length);
this.optionsContainer.removeAll(true);
this.eraseOptionsCursor();
@@ -1513,7 +1553,7 @@ export class PartyUiHandler extends MessageUiHandler {
break;
case PartyUiMode.CHECK:
this.addCommonOptions(pokemon);
- if (globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase")) {
+ if (globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase")) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
for (let i = 0; i < formChangeItemModifiers.length; i++) {
this.options.push(PartyOption.FORM_CHANGE_ITEM + i);
@@ -1568,14 +1608,12 @@ export class PartyUiHandler extends MessageUiHandler {
this.optionsContainer.add(this.optionsBg);
- const optionStartIndex = 0;
- const optionEndIndex = this.options.length;
-
let widestOptionWidth = 0;
const optionTexts: BBCodeText[] = [];
- for (let o = optionStartIndex; o < optionEndIndex; o++) {
- const option = this.options[this.options.length - (o + 1)];
+ // TODO: Refactor this iteration to not be fucking bizarre
+ for (let o = 0; o < this.options.length; o++) {
+ const option = this.options.at(-(o + 1))!;
let altText = false;
let optionName: string;
if (option === PartyOption.SCROLL_UP) {
@@ -1583,10 +1621,10 @@ export class PartyUiHandler extends MessageUiHandler {
} else if (option === PartyOption.SCROLL_DOWN) {
optionName = "↓";
} else if (
- (this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER &&
- (this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER || this.transferMode) &&
- this.partyUiMode !== PartyUiMode.DISCARD) ||
- option === PartyOption.CANCEL
+ (this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER
+ && (this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER || this.transferMode)
+ && this.partyUiMode !== PartyUiMode.DISCARD)
+ || option === PartyOption.CANCEL
) {
switch (option) {
case PartyOption.MOVE_1:
@@ -1609,13 +1647,11 @@ export class PartyUiHandler extends MessageUiHandler {
const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM];
optionName = `${modifier.active ? i18next.t("partyUiHandler:deactivate") : i18next.t("partyUiHandler:activate")} ${modifier.type.name}`;
} else if (option === PartyOption.UNPAUSE_EVOLUTION) {
- optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:unpausedEvolution") : i18next.t("partyUiHandler:pauseEvolution")}`;
+ optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:unpauseEvolution") : i18next.t("partyUiHandler:pauseEvolution")}`;
+ } else if (this.localizedOptions.includes(option)) {
+ optionName = i18next.t(`partyUiHandler:${toCamelCase(PartyOption[option])}`);
} else {
- if (this.localizedOptions.includes(option)) {
- optionName = i18next.t(`partyUiHandler:${toCamelCase(PartyOption[option])}`);
- } else {
- optionName = toTitleCase(PartyOption[option]);
- }
+ optionName = toTitleCase(PartyOption[option]);
}
break;
}
@@ -1630,6 +1666,8 @@ export class PartyUiHandler extends MessageUiHandler {
.find(plm => plm[1] === move);
} else if (option === PartyOption.ALL) {
optionName = i18next.t("partyUiHandler:all");
+ // add the number of items to the `all` option
+ optionName += ` (${this.getTransferrableItemsFromPokemon(pokemon).length})`;
} else {
const itemModifiers = this.getItemModifiers(pokemon);
const itemModifier = itemModifiers[option];
@@ -1648,11 +1686,11 @@ export class PartyUiHandler extends MessageUiHandler {
const itemModifiers = this.getItemModifiers(pokemon);
const itemModifier = itemModifiers[option];
if (
- this.isItemManageMode() &&
- this.transferQuantitiesMax[option] > 1 &&
- !this.transferMode &&
- itemModifier !== undefined &&
- itemModifier.type.name === optionName
+ this.isItemManageMode()
+ && this.transferQuantitiesMax[option] > 1
+ && !this.transferMode
+ && itemModifier !== undefined
+ && itemModifier.type.name === optionName
) {
let amountText = ` (${this.transferQuantities[option]})`;
@@ -1691,11 +1729,12 @@ export class PartyUiHandler extends MessageUiHandler {
this.transferMode = false;
this.transferAll = false;
this.partySlots[this.transferCursor].setTransfer(false);
- for (let i = 0; i < this.partySlots.length; i++) {
- this.partySlots[i].slotDescriptionLabel.setVisible(false);
- this.partySlots[i].slotHpBar.setVisible(true);
- this.partySlots[i].slotHpOverlay.setVisible(true);
- this.partySlots[i].slotHpText.setVisible(true);
+ for (const partySlot of this.partySlots) {
+ partySlot.slotDescriptionLabel.setVisible(false);
+ partySlot.slotHpLabel.setVisible(true);
+ partySlot.slotHpBar.setVisible(true);
+ partySlot.slotHpOverlay.setVisible(true);
+ partySlot.slotHpText.setVisible(true);
}
}
@@ -1727,42 +1766,42 @@ export class PartyUiHandler extends MessageUiHandler {
getReleaseMessage(pokemonName: string): string {
const rand = randInt(128);
if (rand < 20) {
- return i18next.t("partyUiHandler:goodbye", { pokemonName: pokemonName });
+ return i18next.t("partyUiHandler:goodbye", { pokemonName });
}
if (rand < 40) {
- return i18next.t("partyUiHandler:byebye", { pokemonName: pokemonName });
+ return i18next.t("partyUiHandler:byebye", { pokemonName });
}
if (rand < 60) {
- return i18next.t("partyUiHandler:farewell", { pokemonName: pokemonName });
+ return i18next.t("partyUiHandler:farewell", { pokemonName });
}
if (rand < 80) {
- return i18next.t("partyUiHandler:soLong", { pokemonName: pokemonName });
+ return i18next.t("partyUiHandler:soLong", { pokemonName });
}
if (rand < 100) {
return i18next.t("partyUiHandler:thisIsWhereWePart", {
- pokemonName: pokemonName,
+ pokemonName,
});
}
if (rand < 108) {
return i18next.t("partyUiHandler:illMissYou", {
- pokemonName: pokemonName,
+ pokemonName,
});
}
if (rand < 116) {
return i18next.t("partyUiHandler:illNeverForgetYou", {
- pokemonName: pokemonName,
+ pokemonName,
});
}
if (rand < 124) {
return i18next.t("partyUiHandler:untilWeMeetAgain", {
- pokemonName: pokemonName,
+ pokemonName,
});
}
if (rand < 127) {
- return i18next.t("partyUiHandler:sayonara", { pokemonName: pokemonName });
+ return i18next.t("partyUiHandler:sayonara", { pokemonName });
}
return i18next.t("partyUiHandler:smellYaLater", {
- pokemonName: pokemonName,
+ pokemonName,
});
}
@@ -1793,9 +1832,9 @@ export class PartyUiHandler extends MessageUiHandler {
getOptionsCursorWithScroll(): number {
return (
- this.optionsCursor +
- this.optionsScrollCursor +
- (this.options && this.options[0] === PartyOption.SCROLL_UP ? -1 : 0)
+ this.optionsCursor
+ + this.optionsScrollCursor
+ + (this.options && this.options[0] === PartyOption.SCROLL_UP ? -1 : 0)
);
}
@@ -1845,6 +1884,7 @@ class PartySlot extends Phaser.GameObjects.Container {
private slotBg: Phaser.GameObjects.Image;
private slotPb: Phaser.GameObjects.Sprite;
public slotName: Phaser.GameObjects.Text;
+ public slotHpLabel: Phaser.GameObjects.Image;
public slotHpBar: Phaser.GameObjects.Image;
public slotHpOverlay: Phaser.GameObjects.Sprite;
public slotHpText: Phaser.GameObjects.Text;
@@ -1852,12 +1892,12 @@ class PartySlot extends Phaser.GameObjects.Container {
private slotBgKey: string;
private pokemonIcon: Phaser.GameObjects.Container;
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
constructor(
slotIndex: number,
pokemon: PlayerPokemon,
- iconAnimHandler: PokemonIconAnimHandler,
+ iconAnimHandler: PokemonIconAnimHelper,
partyUiMode: PartyUiMode,
tmMoveId: MoveId,
) {
@@ -1997,7 +2037,7 @@ class PartySlot extends Phaser.GameObjects.Container {
this.slotName.setOrigin(0);
const slotLevelLabel = globalScene.add
- .image(0, 0, "party_slot_overlay_lv")
+ .image(0, 0, getLocalizedSpriteKey("party_slot_overlay_lv"))
.setPositionRelative(this.slotBg, levelLabelPosition.x, levelLabelPosition.y)
.setOrigin(0);
@@ -2040,12 +2080,13 @@ class PartySlot extends Phaser.GameObjects.Container {
if (this.pokemon.isShiny()) {
const doubleShiny = this.pokemon.isDoubleShiny(false);
+ const largeIconTint = doubleShiny ? this.pokemon.getBaseVariant() : this.pokemon.getVariant();
const shinyStar = globalScene.add
.image(0, 0, `shiny_star_small${doubleShiny ? "_1" : ""}`)
.setOrigin(0)
.setPositionRelative(this.slotName, shinyIconToNameOffset.x, shinyIconToNameOffset.y)
- .setTint(getVariantTint(this.pokemon.getBaseVariant()));
+ .setTint(getVariantTint(largeIconTint));
slotInfoContainer.add(shinyStar);
if (doubleShiny) {
@@ -2058,6 +2099,12 @@ class PartySlot extends Phaser.GameObjects.Container {
}
}
+ this.slotHpLabel = globalScene.add
+ .image(0, 0, getLocalizedSpriteKey("party_slot_overlay_hp"))
+ .setOrigin(1, 0)
+ .setVisible(false)
+ .setPositionRelative(this.slotBg, hpBarPosition.x + 15, hpBarPosition.y);
+
this.slotHpBar = globalScene.add
.image(0, 0, "party_slot_hp_bar")
.setOrigin(0)
@@ -2087,14 +2134,22 @@ class PartySlot extends Phaser.GameObjects.Container {
.setVisible(false)
.setPositionRelative(this.slotBg, descriptionLabelPosition.x, descriptionLabelPosition.y);
- slotInfoContainer.add([this.slotHpBar, this.slotHpOverlay, this.slotHpText, this.slotDescriptionLabel]);
+ slotInfoContainer.add([
+ this.slotHpLabel,
+ this.slotHpBar,
+ this.slotHpOverlay,
+ this.slotHpText,
+ this.slotDescriptionLabel,
+ ]);
if (partyUiMode !== PartyUiMode.TM_MODIFIER) {
this.slotDescriptionLabel.setVisible(false);
+ this.slotHpLabel.setVisible(true);
this.slotHpBar.setVisible(true);
this.slotHpOverlay.setVisible(true);
this.slotHpText.setVisible(true);
} else {
+ this.slotHpLabel.setVisible(false);
this.slotHpBar.setVisible(false);
this.slotHpOverlay.setVisible(false);
this.slotHpText.setVisible(false);
diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/handlers/pokedex-page-ui-handler.ts
similarity index 94%
rename from src/ui/pokedex-page-ui-handler.ts
rename to src/ui/handlers/pokedex-page-ui-handler.ts
index 706f139b8bb..659c3488bc1 100644
--- a/src/ui/pokedex-page-ui-handler.ts
+++ b/src/ui/handlers/pokedex-page-ui-handler.ts
@@ -43,15 +43,15 @@ import { TimeOfDay } from "#enums/time-of-day";
import { UiMode } from "#enums/ui-mode";
import type { Variant } from "#sprites/variant";
import { getVariantIcon, getVariantTint } from "#sprites/variant";
-import type { StarterAttributes } from "#system/game-data";
import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { BaseStatsOverlay } from "#ui/base-stats-overlay";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
-import { PokedexInfoOverlay } from "#ui/pokedex-info-overlay";
-import { StatsContainer } from "#ui/stats-container";
+import type { StarterAttributes } from "#types/save-data";
+import { BaseStatsOverlay } from "#ui/containers/base-stats-overlay";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import { PokedexInfoOverlay } from "#ui/containers/pokedex-info-overlay";
+import { StatsContainer } from "#ui/containers/stats-container";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { addBBCodeTextObject, addTextObject, getTextColor, getTextStyleOptions } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { BooleanHolder, getLocalizedSpriteKey, isNullOrUndefined, padInt, rgbHexToRgba } from "#utils/common";
@@ -175,6 +175,7 @@ enum MenuOptions {
export class PokedexPageUiHandler extends MessageUiHandler {
private starterSelectContainer: Phaser.GameObjects.Container;
private shinyOverlay: Phaser.GameObjects.Image;
+ private starterDexNoLabel: Phaser.GameObjects.Image;
private pokemonNumberText: Phaser.GameObjects.Text;
private pokemonSprite: Phaser.GameObjects.Sprite;
private pokemonNameText: Phaser.GameObjects.Text;
@@ -259,8 +260,6 @@ export class PokedexPageUiHandler extends MessageUiHandler {
private instructionRowX = 0;
private instructionRowY = 0;
private instructionRowTextOffset = 9;
- private filterInstructionRowX = 0;
- private filterInstructionRowY = 0;
private starterAttributes: StarterAttributes;
private savedStarterAttributes: StarterAttributes;
@@ -324,8 +323,12 @@ export class PokedexPageUiHandler extends MessageUiHandler {
});
this.starterSelectContainer.add(this.pokemonSprite);
- this.shinyOverlay = globalScene.add.image(6, 6, "summary_overlay_shiny");
- this.shinyOverlay.setOrigin(0, 0);
+ this.starterDexNoLabel = globalScene.add.image(6, 14, getLocalizedSpriteKey("summary_dexnb_label")); // Pixel text 'No'
+ this.starterDexNoLabel.setOrigin(0, 1);
+ this.starterSelectContainer.add(this.starterDexNoLabel);
+
+ this.shinyOverlay = globalScene.add.image(6, 111, getLocalizedSpriteKey("summary_dexnb_label_overlay_shiny")); // Pixel text 'No' shiny
+ this.shinyOverlay.setOrigin(0, 1);
this.shinyOverlay.setVisible(false);
this.starterSelectContainer.add(this.shinyOverlay);
@@ -657,7 +660,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
i18next.t("pokedexUiHandler:showEvolutions"),
];
- this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale;
+ this.scale = getTextStyleOptions(TextStyle.WINDOW).scale;
this.menuBg = addWindow(
globalScene.scaledCanvas.width - 83,
0,
@@ -705,10 +708,10 @@ export class PokedexPageUiHandler extends MessageUiHandler {
show(args: any[]): boolean {
// Allow the use of candies if we are in one of the whitelisted phases
this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes(
- globalScene.phaseManager.getCurrentPhase()?.phaseName ?? "",
+ globalScene.phaseManager.getCurrentPhase().phaseName,
);
- if (args.length >= 1 && args[0] === "refresh") {
+ if (args.length > 0 && args[0] === "refresh") {
return false;
}
this.species = args[0];
@@ -759,19 +762,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
.map(o => {
const label = i18next.t(`pokedexUiHandler:${toCamelCase(`menu${MenuOptions[o]}`)}`);
const isDark =
- !isSeen ||
- (!isStarterCaught && (o === MenuOptions.TOGGLE_IVS || o === MenuOptions.NATURES)) ||
- (this.tmMoves.length < 1 && o === MenuOptions.TM_MOVES);
- const color = getTextColor(
- isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE,
- false,
- globalScene.uiTheme,
- );
- const shadow = getTextColor(
- isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE,
- true,
- globalScene.uiTheme,
- );
+ !isSeen
+ || (!isStarterCaught && (o === MenuOptions.TOGGLE_IVS || o === MenuOptions.NATURES))
+ || (this.tmMoves.length === 0 && o === MenuOptions.TM_MOVES);
+ const color = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, false);
+ const shadow = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, true);
return `[shadow=${shadow}][color=${color}]${label}[/color][/shadow]`;
})
.join("\n");
@@ -787,9 +782,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
let formKey = this.species?.forms.length > 0 ? this.species.forms[this.formIndex].formKey : "";
this.isFormGender = formKey === "male" || formKey === "female";
if (
- this.isFormGender &&
- ((this.savedStarterAttributes.female === true && formKey === "male") ||
- (this.savedStarterAttributes.female === false && formKey === "female"))
+ this.isFormGender
+ && ((this.savedStarterAttributes.female === true && formKey === "male")
+ || (this.savedStarterAttributes.female === false && formKey === "female"))
) {
this.formIndex = (this.formIndex + 1) % 2;
formKey = this.species.forms[this.formIndex].formKey;
@@ -808,9 +803,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
// If this form has a specific set of moves, we get them.
this.levelMoves =
- formIndex > 0 &&
- pokemonFormLevelMoves.hasOwnProperty(species.speciesId) &&
- pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)
+ formIndex > 0
+ && pokemonFormLevelMoves.hasOwnProperty(species.speciesId)
+ && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)
? pokemonFormLevelMoves[species.speciesId][formIndex]
: pokemonSpeciesLevelMoves[species.speciesId];
this.ability1 = form.ability1;
@@ -884,17 +879,13 @@ export class PokedexPageUiHandler extends MessageUiHandler {
: [];
this.prevolutions = preEvolutions.filter(
e =>
- e.speciesId === species.speciesId &&
- (((e.evoFormKey === "" || e.evoFormKey === null) &&
- // This takes care of Cosplay Pikachu (Pichu is not shown)
- (preSpecies.forms.some(form => form.formKey === species.forms[formIndex]?.formKey) ||
- // This takes care of Gholdengo
- (preSpecies.forms.length > 0 && species.forms.length === 0) ||
- // This takes care of everything else
- (preSpecies.forms.length === 0 &&
- (species.forms.length === 0 || species.forms[formIndex]?.formKey === "")))) ||
- // This takes care of Burmy, Shellos etc
- e.evoFormKey === species.forms[formIndex]?.formKey),
+ e.speciesId === species.speciesId
+ && (((e.evoFormKey === "" || e.evoFormKey === null) // This takes care of Cosplay Pikachu (Pichu is not shown)
+ && (preSpecies.forms.some(form => form.formKey === species.forms[formIndex]?.formKey) // This takes care of Gholdengo
+ || (preSpecies.forms.length > 0 && species.forms.length === 0) // This takes care of everything else
+ || (preSpecies.forms.length === 0
+ && (species.forms.length === 0 || species.forms[formIndex]?.formKey === "")))) // This takes care of Burmy, Shellos etc
+ || e.evoFormKey === species.forms[formIndex]?.formKey),
);
}
@@ -995,7 +986,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const formIndex = otherFormIndex !== undefined ? otherFormIndex : this.formIndex;
const caughtAttr = this.isCaught(species);
- if (caughtAttr && (!species.forms.length || species.forms.length === 1)) {
+ if (caughtAttr && (species.forms.length === 0 || species.forms.length === 1)) {
return true;
}
@@ -1046,9 +1037,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
!!(hasShiny && caughtAttr & DexAttr.VARIANT_3),
];
if (
- starterAttributes.variant === undefined ||
- Number.isNaN(starterAttributes.variant) ||
- starterAttributes.variant < 0
+ starterAttributes.variant === undefined
+ || Number.isNaN(starterAttributes.variant)
+ || starterAttributes.variant < 0
) {
starterAttributes.variant = 0;
} else if (!this.unlockedVariants[starterAttributes.variant]) {
@@ -1064,17 +1055,15 @@ export class PokedexPageUiHandler extends MessageUiHandler {
if (starterAttributes.female !== undefined) {
if (
- (starterAttributes.female && !(caughtAttr & DexAttr.FEMALE)) ||
- (!starterAttributes.female && !(caughtAttr & DexAttr.MALE))
+ (starterAttributes.female && !(caughtAttr & DexAttr.FEMALE))
+ || (!starterAttributes.female && !(caughtAttr & DexAttr.MALE))
) {
starterAttributes.female = !starterAttributes.female;
}
- } else {
- if (caughtAttr & DexAttr.FEMALE) {
- starterAttributes.female = true;
- } else if (caughtAttr & DexAttr.MALE) {
- starterAttributes.female = false;
- }
+ } else if (caughtAttr & DexAttr.FEMALE) {
+ starterAttributes.female = true;
+ } else if (caughtAttr & DexAttr.MALE) {
+ starterAttributes.female = false;
}
return starterAttributes;
@@ -1105,7 +1094,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
this.message.setY(singleLine ? -22 : -37);
}
- this.starterSelectMessageBoxContainer.setVisible(!!text?.length);
+ this.starterSelectMessageBoxContainer.setVisible(text?.length > 0);
}
/**
@@ -1361,7 +1350,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
case MenuOptions.TM_MOVES:
if (!isSeen) {
error = true;
- } else if (this.tmMoves.length < 1) {
+ } else if (this.tmMoves.length === 0) {
ui.showText(i18next.t("pokedexUiHandler:noTmMoves"));
error = true;
} else {
@@ -1484,7 +1473,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
supportHover: true,
maxOptions: 8,
yOffset: 19,
@@ -1517,14 +1506,14 @@ export class PokedexPageUiHandler extends MessageUiHandler {
this.biomes.map(b => {
options.push({
label:
- i18next.t(`biome:${toCamelCase(BiomeId[b.biome])}`) +
- " - " +
- i18next.t(`biome:${toCamelCase(BiomePoolTier[b.tier])}`) +
- (b.tod.length === 1 && b.tod[0] === -1
+ i18next.t(`biome:${toCamelCase(BiomeId[b.biome])}`)
+ + " - "
+ + i18next.t(`biome:${toCamelCase(BiomePoolTier[b.tier])}`)
+ + (b.tod.length === 1 && b.tod[0] === -1
? ""
- : " (" +
- b.tod.map(tod => i18next.t(`biome:${toCamelCase(TimeOfDay[tod])}`)).join(", ") +
- ")"),
+ : " ("
+ + b.tod.map(tod => i18next.t(`biome:${toCamelCase(TimeOfDay[tod])}`)).join(", ")
+ + ")"),
handler: () => false,
});
});
@@ -1538,14 +1527,14 @@ export class PokedexPageUiHandler extends MessageUiHandler {
this.preBiomes.map(b => {
options.push({
label:
- i18next.t(`biome:${toCamelCase(BiomeId[b.biome])}`) +
- " - " +
- i18next.t(`biome:${toCamelCase(BiomePoolTier[b.tier])}`) +
- (b.tod.length === 1 && b.tod[0] === -1
+ i18next.t(`biome:${toCamelCase(BiomeId[b.biome])}`)
+ + " - "
+ + i18next.t(`biome:${toCamelCase(BiomePoolTier[b.tier])}`)
+ + (b.tod.length === 1 && b.tod[0] === -1
? ""
- : " (" +
- b.tod.map(tod => i18next.t(`biome:${toCamelCase(TimeOfDay[tod])}`)).join(", ") +
- ")"),
+ : " ("
+ + b.tod.map(tod => i18next.t(`biome:${toCamelCase(TimeOfDay[tod])}`)).join(", ")
+ + ")"),
handler: () => false,
});
});
@@ -1563,7 +1552,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
supportHover: true,
maxOptions: 8,
yOffset: 19,
@@ -1586,9 +1575,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const options: any[] = [];
if (
- (!this.prevolutions || this.prevolutions?.length === 0) &&
- (!this.evolutions || this.evolutions?.length === 0) &&
- (!this.battleForms || this.battleForms?.length === 0)
+ (!this.prevolutions || this.prevolutions?.length === 0)
+ && (!this.evolutions || this.evolutions?.length === 0)
+ && (!this.battleForms || this.battleForms?.length === 0)
) {
ui.showText(i18next.t("pokedexUiHandler:noEvolutions"));
ui.playError();
@@ -1710,7 +1699,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
if (conditionText) {
options.push({
- label: label,
+ label,
style: isFormCaught ? TextStyle.WINDOW : TextStyle.SHADOW_TEXT,
handler: () => {
this.previousSpecies.push(this.species);
@@ -1747,7 +1736,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
supportHover: true,
maxOptions: 8,
yOffset: 19,
@@ -1784,7 +1773,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
options: natures
.map((n: Nature, _i: number) => {
const option: OptionSelectItem = {
- label: getNatureName(n, true, true, true, globalScene.uiTheme),
+ label: getNatureName(n, true, true, true),
handler: () => {
return false;
},
@@ -1841,10 +1830,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
if (this.isCaught() & DexAttr.VARIANT_2) {
break;
}
- } else {
- if (this.isCaught() & DexAttr.VARIANT_3) {
- break;
- }
+ } else if (this.isCaught() & DexAttr.VARIANT_3) {
+ break;
}
} while (newVariant !== props.variant);
@@ -2043,7 +2030,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
},
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
yOffset: 47,
});
success = true;
@@ -2206,8 +2193,6 @@ export class PokedexPageUiHandler extends MessageUiHandler {
updateInstructions(): void {
this.instructionRowX = 0;
this.instructionRowY = 0;
- this.filterInstructionRowX = 0;
- this.filterInstructionRowY = 0;
this.hideInstructions();
this.instructionsContainer.removeAll();
this.filterInstructionsContainer.removeAll();
@@ -2307,8 +2292,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[this.starterId];
return (
- starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[this.starterId]) &&
- !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
+ starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[this.starterId])
+ && !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
);
}
@@ -2321,9 +2306,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[this.starterId];
return (
- starterData.candyCount >=
- getValueReductionCandyCounts(speciesStarterCosts[this.starterId])[starterData.valueReduction] &&
- starterData.valueReduction < valueReductionMax
+ starterData.candyCount
+ >= getValueReductionCandyCounts(speciesStarterCosts[this.starterId])[starterData.valueReduction]
+ && starterData.valueReduction < valueReductionMax
);
}
@@ -2364,10 +2349,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
// Set default attributes if for some reason starterAttributes does not exist or attributes missing
const props: StarterAttributes = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
- if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) {
- if (props.shiny) {
- props.variant = starterAttributes.variant as Variant;
- }
+ if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant) && props.shiny) {
+ props.variant = starterAttributes.variant as Variant;
}
props.form = starterAttributes?.form ?? props.form;
props.female = starterAttributes?.female ?? props.female;
@@ -2441,11 +2424,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
// We will only update the sprite if there is a change to form, shiny/variant
// or gender for species with gender sprite differences
const shouldUpdateSprite =
- (species?.genderDiffs && !isNullOrUndefined(female)) ||
- !isNullOrUndefined(formIndex) ||
- !isNullOrUndefined(shiny) ||
- !isNullOrUndefined(variant) ||
- forceUpdate;
+ (species?.genderDiffs && !isNullOrUndefined(female))
+ || !isNullOrUndefined(formIndex)
+ || !isNullOrUndefined(shiny)
+ || !isNullOrUndefined(variant)
+ || forceUpdate;
if (this.activeTooltip === "CANDY") {
if (this.species && this.pokemonCandyContainer.visible) {
@@ -2512,10 +2495,10 @@ export class PokedexPageUiHandler extends MessageUiHandler {
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
this.pokemonNumberText.setColor(
- this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false),
+ getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false),
);
this.pokemonNumberText.setShadowColor(
- this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true),
+ getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true),
);
const assetLoadCancelled = new BooleanHolder(false);
@@ -2723,8 +2706,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
}
} else {
this.shinyOverlay.setVisible(false);
- this.pokemonNumberText.setColor(this.getTextColor(TextStyle.SUMMARY));
- this.pokemonNumberText.setShadowColor(this.getTextColor(TextStyle.SUMMARY, true));
+ this.pokemonNumberText.setColor(getTextColor(TextStyle.SUMMARY));
+ this.pokemonNumberText.setShadowColor(getTextColor(TextStyle.SUMMARY, true));
this.pokemonGenderText.setText("");
this.setTypeIcons(null, null);
}
@@ -2757,9 +2740,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
let props = 0n;
const species = allSpecies.find(sp => sp.speciesId === speciesId);
const caughtAttr =
- globalScene.gameData.dexData[speciesId].caughtAttr &
- globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr &
- (species?.getFullUnlocksData() ?? 0n);
+ globalScene.gameData.dexData[speciesId].caughtAttr
+ & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr
+ & (species?.getFullUnlocksData() ?? 0n);
/* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props
* It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props
@@ -2774,23 +2757,21 @@ export class PokedexPageUiHandler extends MessageUiHandler {
* If they're not there, it enables shiny state by default if any shiny was caught
*/
if (
- this.starterAttributes?.shiny ||
- ((caughtAttr & DexAttr.SHINY) > 0n && this.starterAttributes?.shiny !== false)
+ this.starterAttributes?.shiny
+ || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterAttributes?.shiny !== false)
) {
props += DexAttr.SHINY;
if (this.starterAttributes?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterAttributes?.variant)) * DexAttr.DEFAULT_VARIANT;
- } else {
- /* This calculates the correct variant if there's no starter preferences for it.
+ /* This chunk calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
- if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
- props += DexAttr.VARIANT_3;
- } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
- props += DexAttr.VARIANT_2;
- } else {
- props += DexAttr.DEFAULT_VARIANT;
- }
+ } else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
+ props += DexAttr.VARIANT_3;
+ } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
+ props += DexAttr.VARIANT_2;
+ } else {
+ props += DexAttr.DEFAULT_VARIANT;
}
} else {
props += DexAttr.NON_SHINY;
diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/handlers/pokedex-scan-ui-handler.ts
similarity index 90%
rename from src/ui/pokedex-scan-ui-handler.ts
rename to src/ui/handlers/pokedex-scan-ui-handler.ts
index 4f606cbcbb0..bb3cec5bb56 100644
--- a/src/ui/pokedex-scan-ui-handler.ts
+++ b/src/ui/handlers/pokedex-scan-ui-handler.ts
@@ -1,11 +1,11 @@
import { allAbilities, allMoves, allSpecies } from "#data/data-lists";
import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { FilterTextRow } from "#ui/filter-text";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import { FilterTextRow } from "#ui/containers/filter-text";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import { isNullOrUndefined } from "#utils/common";
import i18next from "i18next";
@@ -106,14 +106,10 @@ export class PokedexScanUiHandler extends FormModalUiHandler {
this.reduceKeys();
- setTimeout(() => {
- input.setFocus(); // Focus after a short delay to avoid unwanted input
- }, 50);
-
input.on("keydown", (inputObject, evt: KeyboardEvent) => {
if (
- ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) &&
- ui.getMode() === UiMode.AUTO_COMPLETE
+ ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase())
+ && ui.getMode() === UiMode.AUTO_COMPLETE
) {
// Delete autocomplete list and recovery focus.
inputObject.on("blur", () => inputObject.node.focus(), { once: true });
@@ -148,7 +144,7 @@ export class PokedexScanUiHandler extends FormModalUiHandler {
if (options.length > 0) {
const modalOpts = {
- options: options,
+ options,
maxOptions: 5,
modalContainer: this.modalContainer,
};
diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/handlers/pokedex-ui-handler.ts
similarity index 91%
rename from src/ui/pokedex-ui-handler.ts
rename to src/ui/handlers/pokedex-ui-handler.ts
index 01046bfef9c..3e37ccba563 100644
--- a/src/ui/pokedex-ui-handler.ts
+++ b/src/ui/handlers/pokedex-ui-handler.ts
@@ -28,21 +28,29 @@ import { PokemonType } from "#enums/pokemon-type";
import type { SpeciesId } from "#enums/species-id";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
+import { UiTheme } from "#enums/ui-theme";
import type { Variant } from "#sprites/variant";
import { getVariantIcon, getVariantTint } from "#sprites/variant";
-import type { DexAttrProps, StarterAttributes } from "#system/game-data";
import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data";
-import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
-import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown";
-import { FilterBar } from "#ui/filter-bar";
-import { FilterText, FilterTextRow } from "#ui/filter-text";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { PokedexMonContainer } from "#ui/pokedex-mon-container";
-import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
-import { ScrollBar } from "#ui/scroll-bar";
-import { addTextObject } from "#ui/text";
+import type { DexAttrProps, StarterAttributes } from "#types/save-data";
+import {
+ DropDown,
+ DropDownLabel,
+ DropDownOption,
+ DropDownState,
+ DropDownType,
+ SortCriteria,
+} from "#ui/containers/dropdown";
+import { FilterBar } from "#ui/containers/filter-bar";
+import { FilterText, FilterTextRow } from "#ui/containers/filter-text";
+import { PokedexMonContainer } from "#ui/containers/pokedex-mon-container";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import type { OptionSelectConfig } from "#ui/handlers/abstract-option-select-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
+import { PokemonIconAnimHelper, PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
import { BooleanHolder, fixedInt, getLocalizedSpriteKey, padInt, randIntRange, rgbHexToRgba } from "#utils/common";
import type { StarterPreferences } from "#utils/data";
import { loadStarterPreferences } from "#utils/data";
@@ -147,7 +155,7 @@ function calcStarterPosition(index: number): { x: number; y: number } {
const x = (index % 9) * 18;
const y = yOffset + Math.floor(index / 9) * height;
- return { x: x, y: y };
+ return { x, y };
}
interface SpeciesDetails {
@@ -190,7 +198,7 @@ export class PokedexUiHandler extends MessageUiHandler {
public cursorObj: Phaser.GameObjects.Image;
private pokerusCursorObjs: Phaser.GameObjects.Image[];
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
private starterPreferences: StarterPreferences;
@@ -470,11 +478,11 @@ export class PokedexUiHandler extends MessageUiHandler {
// Offset the generation filter dropdown to avoid covering the filtered pokemon
this.filterBar.offsetHybridFilters();
- if (!globalScene.uiTheme) {
+ if (globalScene.uiTheme === UiTheme.DEFAULT) {
pokemonContainerWindow.setVisible(false);
}
- this.iconAnimHandler = new PokemonIconAnimHandler();
+ this.iconAnimHandler = new PokemonIconAnimHelper();
this.iconAnimHandler.setup();
this.pokemonNumberText = addTextObject(6, 141, "", TextStyle.SUMMARY);
@@ -641,7 +649,7 @@ export class PokedexUiHandler extends MessageUiHandler {
this.pokerusSpecies = getPokerusStarters();
// When calling with "refresh", we do not reset the cursor and filters
- if (args.length >= 1 && args[0] === "refresh") {
+ if (args.length > 0 && args[0] === "refresh") {
return false;
}
@@ -713,20 +721,21 @@ export class PokedexUiHandler extends MessageUiHandler {
hasShiny && caughtAttr & DexAttr.VARIANT_3,
];
if (
- Number.isNaN(starterAttributes.variant) ||
- starterAttributes.variant < 0 ||
- !unlockedVariants[starterAttributes.variant]
+ Number.isNaN(starterAttributes.variant)
+ || starterAttributes.variant < 0
+ || !unlockedVariants[starterAttributes.variant]
) {
// variant value is invalid or requested variant wasn't unlocked, purging setting
starterAttributes.variant = undefined;
}
}
- if (starterAttributes.female !== undefined) {
- if (!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)) {
- // requested gender wasn't unlocked, purging setting
- starterAttributes.female = undefined;
- }
+ if (
+ starterAttributes.female !== undefined
+ && !(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)
+ ) {
+ // requested gender wasn't unlocked, purging setting
+ starterAttributes.female = undefined;
}
if (starterAttributes.ability !== undefined) {
@@ -750,9 +759,9 @@ export class PokedexUiHandler extends MessageUiHandler {
const selectedForm = starterAttributes.form;
if (
- selectedForm !== undefined &&
- (!species.forms[selectedForm]?.isStarterSelectable ||
- !(caughtAttr & globalScene.gameData.getFormAttr(selectedForm)))
+ selectedForm !== undefined
+ && (!species.forms[selectedForm]?.isStarterSelectable
+ || !(caughtAttr & globalScene.gameData.getFormAttr(selectedForm)))
) {
// requested form wasn't unlocked/isn't a starter form, purging setting
starterAttributes.form = undefined;
@@ -802,7 +811,7 @@ export class PokedexUiHandler extends MessageUiHandler {
this.message.setY(singleLine ? -22 : -37);
}
- this.starterSelectMessageBoxContainer.setVisible(!!text?.length);
+ this.starterSelectMessageBoxContainer.setVisible(text?.length > 0);
}
isSeen(species: PokemonSpecies, dexEntry: DexEntry, seenFilter?: boolean): boolean {
@@ -852,8 +861,8 @@ export class PokedexUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)];
return (
- starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[this.getStarterSpeciesId(speciesId)]) &&
- !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
+ starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[this.getStarterSpeciesId(speciesId)])
+ && !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
);
}
@@ -867,8 +876,8 @@ export class PokedexUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)];
return (
- starterData.candyCount >=
- getValueReductionCandyCounts(speciesStarterCosts[this.getStarterSpeciesId(speciesId)])[
+ starterData.candyCount
+ >= getValueReductionCandyCounts(speciesStarterCosts[this.getStarterSpeciesId(speciesId)])[
starterData.valueReduction
] && starterData.valueReduction < valueReductionMax
);
@@ -929,9 +938,9 @@ export class PokedexUiHandler extends MessageUiHandler {
};
if (
- this.isPassiveAvailable(species.speciesId) ||
- (globalScene.candyUpgradeNotification === 2 &&
- (this.isValueReductionAvailable(species.speciesId) || this.isSameSpeciesEggAvailable(species.speciesId)))
+ this.isPassiveAvailable(species.speciesId)
+ || (globalScene.candyUpgradeNotification === 2
+ && (this.isValueReductionAvailable(species.speciesId) || this.isSameSpeciesEggAvailable(species.speciesId)))
) {
const chain = globalScene.tweens.chain(tweenChain);
if (!startPaused) {
@@ -948,9 +957,9 @@ export class PokedexUiHandler extends MessageUiHandler {
const slotVisible = !!species?.speciesId;
if (
- !species ||
- globalScene.candyUpgradeNotification === 0 ||
- species.speciesId !== species.getRootSpeciesId(false)
+ !species
+ || globalScene.candyUpgradeNotification === 0
+ || species.speciesId !== species.getRootSpeciesId(false)
) {
starter.candyUpgradeIcon.setVisible(false);
starter.candyUpgradeOverlayIcon.setVisible(false);
@@ -1017,8 +1026,8 @@ export class PokedexUiHandler extends MessageUiHandler {
this.updateStarters();
success = true;
} else if (
- this.filterTextMode &&
- !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)
+ this.filterTextMode
+ && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)
) {
this.filterText.resetSelection(this.filterTextCursor);
success = true;
@@ -1206,78 +1215,76 @@ export class PokedexUiHandler extends MessageUiHandler {
break;
}
}
+ } else if (button === Button.ACTION) {
+ ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices);
+ success = true;
} else {
- if (button === Button.ACTION) {
- ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices);
- success = true;
- } else {
- switch (button) {
- case Button.UP:
- if (currentRow > 0) {
- if (this.scrollCursor > 0 && currentRow - this.scrollCursor === 0) {
- this.scrollCursor--;
- this.updateScroll();
- success = this.setCursor(this.cursor);
- } else {
- success = this.setCursor(this.cursor - 9);
- }
- } else {
- this.filterBarCursor = this.filterBar.getNearestFilter(this.pokemonContainers[this.cursor]);
- this.setFilterMode(true);
- success = true;
- }
- break;
- case Button.DOWN:
- if (currentRow < numOfRows - 1 && this.cursor + 9 < this.filteredPokemonData.length) {
- // not last row
- if (currentRow - this.scrollCursor === 8) {
- // last row of visible pokemon
- this.scrollCursor++;
- this.updateScroll();
- success = this.setCursor(this.cursor);
- } else {
- success = this.setCursor(this.cursor + 9);
- }
- } else if (numOfRows > 1) {
- // DOWN from last row of pokemon > Wrap around to first row
- this.scrollCursor = 0;
+ switch (button) {
+ case Button.UP:
+ if (currentRow > 0) {
+ if (this.scrollCursor > 0 && currentRow - this.scrollCursor === 0) {
+ this.scrollCursor--;
this.updateScroll();
- success = this.setCursor(this.cursor % 9);
+ success = this.setCursor(this.cursor);
} else {
- // DOWN from single row of pokemon > Go to filters
- this.filterBarCursor = this.filterBar.getNearestFilter(this.pokemonContainers[this.cursor]);
- this.setFilterMode(true);
- success = true;
+ success = this.setCursor(this.cursor - 9);
}
- break;
- case Button.LEFT:
- if (this.cursor % 9 !== 0) {
- success = this.setCursor(this.cursor - 1);
- } else {
- // LEFT from filtered pokemon, on the left edge
- this.filterTextCursor = this.filterText.getNearestFilter(this.pokemonContainers[this.cursor]);
- this.setFilterTextMode(true);
- success = true;
- }
- break;
- case Button.RIGHT:
- // is not right edge
- if (this.cursor % 9 < (currentRow < numOfRows - 1 ? 8 : (numberOfStarters - 1) % 9)) {
- success = this.setCursor(this.cursor + 1);
- } else {
- // RIGHT from filtered pokemon, on the right edge
- this.filterTextCursor = this.filterText.getNearestFilter(this.pokemonContainers[this.cursor]);
- this.setFilterTextMode(true);
- success = true;
- }
- break;
- case Button.CYCLE_FORM: {
- const species = this.pokemonContainers[this.cursor].species;
- if (this.canShowFormTray) {
- success = this.openFormTray(species);
- }
- break;
+ } else {
+ this.filterBarCursor = this.filterBar.getNearestFilter(this.pokemonContainers[this.cursor]);
+ this.setFilterMode(true);
+ success = true;
}
+ break;
+ case Button.DOWN:
+ if (currentRow < numOfRows - 1 && this.cursor + 9 < this.filteredPokemonData.length) {
+ // not last row
+ if (currentRow - this.scrollCursor === 8) {
+ // last row of visible pokemon
+ this.scrollCursor++;
+ this.updateScroll();
+ success = this.setCursor(this.cursor);
+ } else {
+ success = this.setCursor(this.cursor + 9);
+ }
+ } else if (numOfRows > 1) {
+ // DOWN from last row of pokemon > Wrap around to first row
+ this.scrollCursor = 0;
+ this.updateScroll();
+ success = this.setCursor(this.cursor % 9);
+ } else {
+ // DOWN from single row of pokemon > Go to filters
+ this.filterBarCursor = this.filterBar.getNearestFilter(this.pokemonContainers[this.cursor]);
+ this.setFilterMode(true);
+ success = true;
+ }
+ break;
+ case Button.LEFT:
+ if (this.cursor % 9 !== 0) {
+ success = this.setCursor(this.cursor - 1);
+ } else {
+ // LEFT from filtered pokemon, on the left edge
+ this.filterTextCursor = this.filterText.getNearestFilter(this.pokemonContainers[this.cursor]);
+ this.setFilterTextMode(true);
+ success = true;
+ }
+ break;
+ case Button.RIGHT:
+ // is not right edge
+ if (this.cursor % 9 < (currentRow < numOfRows - 1 ? 8 : (numberOfStarters - 1) % 9)) {
+ success = this.setCursor(this.cursor + 1);
+ } else {
+ // RIGHT from filtered pokemon, on the right edge
+ this.filterTextCursor = this.filterText.getNearestFilter(this.pokemonContainers[this.cursor]);
+ this.setFilterTextMode(true);
+ success = true;
+ }
+ break;
+ case Button.CYCLE_FORM: {
+ const species = this.pokemonContainers[this.cursor].species;
+ if (this.canShowFormTray) {
+ success = this.openFormTray(species);
+ }
+ break;
}
}
}
@@ -1348,8 +1355,8 @@ export class PokedexUiHandler extends MessageUiHandler {
// Returns true if one of the forms has the requested move
hasFormLevelMove(form: PokemonForm, selectedMove: string): boolean {
if (
- !pokemonFormLevelMoves.hasOwnProperty(form.speciesId) ||
- !pokemonFormLevelMoves[form.speciesId].hasOwnProperty(form.formIndex)
+ !pokemonFormLevelMoves.hasOwnProperty(form.speciesId)
+ || !pokemonFormLevelMoves[form.speciesId].hasOwnProperty(form.formIndex)
) {
return false;
}
@@ -1375,17 +1382,17 @@ export class PokedexUiHandler extends MessageUiHandler {
const props = this.getSanitizedProps(globalScene.gameData.getSpeciesDexAttrProps(species, currentDexAttr));
const data: ContainerData = {
- species: species,
+ species,
cost: globalScene.gameData.getSpeciesStarterValue(starterId),
- props: props,
+ props,
};
// First, ensure you have the caught attributes for the species else default to bigint 0
// TODO: This might be removed depending on how accessible we want the pokedex function to be
const caughtAttr =
- (globalScene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0)) &
- (globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)]?.caughtAttr || BigInt(0)) &
- species.getFullUnlocksData();
+ (globalScene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0))
+ & (globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)]?.caughtAttr || BigInt(0))
+ & species.getFullUnlocksData();
const starterData = globalScene.gameData.starterData[starterId];
const isStarterProgressable = speciesEggMoves.hasOwnProperty(starterId);
@@ -1462,8 +1469,8 @@ export class PokedexUiHandler extends MessageUiHandler {
// If both fields have been set to the same ability, show both ability and passive
const fitsAbilities =
- (fitsAbility1 && (fitsPassive2 || selectedAbility2 === this.filterText.defaultText)) ||
- (fitsAbility2 && (fitsPassive1 || selectedAbility1 === this.filterText.defaultText));
+ (fitsAbility1 && (fitsPassive2 || selectedAbility2 === this.filterText.defaultText))
+ || (fitsAbility2 && (fitsPassive1 || selectedAbility1 === this.filterText.defaultText));
if (fitsPassive1 || fitsPassive2) {
if (fitsPassive1) {
@@ -1472,12 +1479,10 @@ export class PokedexUiHandler extends MessageUiHandler {
} else {
data.passive1 = false;
}
+ } else if (starterData.passiveAttr > 0) {
+ data.passive2 = true;
} else {
- if (starterData.passiveAttr > 0) {
- data.passive2 = true;
- } else {
- data.passive2 = false;
- }
+ data.passive2 = false;
}
}
@@ -1505,8 +1510,8 @@ export class PokedexUiHandler extends MessageUiHandler {
}
const showNoBiome = !!(biomes.length === 0 && this.filterBar.getVals(DropDownColumn.BIOME).length === 36);
const fitsBiome =
- this.filterBar.getVals(DropDownColumn.BIOME).some(item => biomes.includes(indexToBiome.get(item) ?? "")) ||
- showNoBiome;
+ this.filterBar.getVals(DropDownColumn.BIOME).some(item => biomes.includes(indexToBiome.get(item) ?? ""))
+ || showNoBiome;
// Caught / Shiny filter
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
@@ -1693,23 +1698,23 @@ export class PokedexUiHandler extends MessageUiHandler {
});
if (
- fitsName &&
- fitsAbilities &&
- fitsMoves &&
- fitsGen &&
- fitsBiome &&
- fitsType &&
- fitsCaught &&
- fitsPassive &&
- fitsCostReduction &&
- fitsStarter &&
- fitsFavorite &&
- fitsWin &&
- fitsHA &&
- fitsSeen &&
- fitsEncountered &&
- fitsEgg &&
- fitsPokerus
+ fitsName
+ && fitsAbilities
+ && fitsMoves
+ && fitsGen
+ && fitsBiome
+ && fitsType
+ && fitsCaught
+ && fitsPassive
+ && fitsCostReduction
+ && fitsStarter
+ && fitsFavorite
+ && fitsWin
+ && fitsHA
+ && fitsSeen
+ && fitsEncountered
+ && fitsEgg
+ && fitsPokerus
) {
this.filteredPokemonData.push(data);
}
@@ -1735,26 +1740,26 @@ export class PokedexUiHandler extends MessageUiHandler {
}
case SortCriteria.IV: {
const avgIVsA =
- globalScene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0) /
- globalScene.gameData.dexData[a.species.speciesId].ivs.length;
+ globalScene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0)
+ / globalScene.gameData.dexData[a.species.speciesId].ivs.length;
const avgIVsB =
- globalScene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0) /
- globalScene.gameData.dexData[b.species.speciesId].ivs.length;
+ globalScene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0)
+ / globalScene.gameData.dexData[b.species.speciesId].ivs.length;
return (avgIVsA - avgIVsB) * -sort.dir;
}
case SortCriteria.NAME:
return a.species.name.localeCompare(b.species.name) * -sort.dir;
case SortCriteria.CAUGHT:
return (
- (globalScene.gameData.dexData[a.species.speciesId].caughtCount -
- globalScene.gameData.dexData[b.species.speciesId].caughtCount) *
- -sort.dir
+ (globalScene.gameData.dexData[a.species.speciesId].caughtCount
+ - globalScene.gameData.dexData[b.species.speciesId].caughtCount)
+ * -sort.dir
);
case SortCriteria.HATCHED:
return (
- (globalScene.gameData.dexData[this.getStarterSpeciesId(a.species.speciesId)].hatchedCount -
- globalScene.gameData.dexData[this.getStarterSpeciesId(b.species.speciesId)].hatchedCount) *
- -sort.dir
+ (globalScene.gameData.dexData[this.getStarterSpeciesId(a.species.speciesId)].hatchedCount
+ - globalScene.gameData.dexData[this.getStarterSpeciesId(b.species.speciesId)].hatchedCount)
+ * -sort.dir
);
default:
break;
@@ -1799,9 +1804,9 @@ export class PokedexUiHandler extends MessageUiHandler {
const speciesId = data.species.speciesId;
const dexEntry = globalScene.gameData.dexData[speciesId];
const caughtAttr =
- dexEntry.caughtAttr &
- globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr &
- data.species.getFullUnlocksData();
+ dexEntry.caughtAttr
+ & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr
+ & data.species.getFullUnlocksData();
if (caughtAttr & data.species.getFullUnlocksData() || globalScene.dexForDevs) {
container.icon.clearTint();
@@ -2062,7 +2067,7 @@ export class PokedexUiHandler extends MessageUiHandler {
const species = this.lastSpecies;
const formIndex = this.trayForms[cursor].formIndex;
- this.setSpeciesDetails(species, { formIndex: formIndex });
+ this.setSpeciesDetails(species, { formIndex });
return changed;
}
@@ -2120,8 +2125,8 @@ export class PokedexUiHandler extends MessageUiHandler {
}
if (
- species &&
- (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr || globalScene.dexForDevs)
+ species
+ && (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr || globalScene.dexForDevs)
) {
this.pokemonNumberText.setText(i18next.t("pokedexUiHandler:pokemonNumber") + padInt(species.speciesId, 4));
@@ -2186,9 +2191,9 @@ export class PokedexUiHandler extends MessageUiHandler {
if (species) {
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const caughtAttr =
- dexEntry.caughtAttr &
- globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)].caughtAttr &
- species.getFullUnlocksData();
+ dexEntry.caughtAttr
+ & globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)].caughtAttr
+ & species.getFullUnlocksData();
if (caughtAttr) {
const props = this.getSanitizedProps(
@@ -2314,8 +2319,8 @@ export class PokedexUiHandler extends MessageUiHandler {
break;
}
if (baseStarterValue - starterValue > 0) {
- starter.label.setColor(this.getTextColor(textStyle));
- starter.label.setShadowColor(this.getTextColor(textStyle, true));
+ starter.label.setColor(getTextColor(textStyle));
+ starter.label.setShadowColor(getTextColor(textStyle, true));
}
}
@@ -2358,17 +2363,17 @@ export class PokedexUiHandler extends MessageUiHandler {
let props = 0n;
const species = allSpecies.find(sp => sp.speciesId === speciesId);
const caughtAttr =
- globalScene.gameData.dexData[speciesId].caughtAttr &
- globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr &
- (species?.getFullUnlocksData() ?? 0n);
+ globalScene.gameData.dexData[speciesId].caughtAttr
+ & globalScene.gameData.dexData[this.getStarterSpeciesId(speciesId)].caughtAttr
+ & (species?.getFullUnlocksData() ?? 0n);
/* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props
* It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props
* If neither of these pass, we add DexAttr.MALE to our temp props
*/
if (
- this.starterPreferences[speciesId]?.female ||
- ((caughtAttr & DexAttr.FEMALE) > 0n && (caughtAttr & DexAttr.MALE) === 0n)
+ this.starterPreferences[speciesId]?.female
+ || ((caughtAttr & DexAttr.FEMALE) > 0n && (caughtAttr & DexAttr.MALE) === 0n)
) {
props += DexAttr.FEMALE;
} else {
@@ -2378,23 +2383,21 @@ export class PokedexUiHandler extends MessageUiHandler {
* If they're not there, it enables shiny state by default if any shiny was caught
*/
if (
- this.starterPreferences[speciesId]?.shiny ||
- ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)
+ this.starterPreferences[speciesId]?.shiny
+ || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)
) {
props += DexAttr.SHINY;
if (this.starterPreferences[speciesId]?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
- } else {
+ } else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
/* This calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
- if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
- props += DexAttr.VARIANT_3;
- } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
- props += DexAttr.VARIANT_2;
- } else {
- props += DexAttr.DEFAULT_VARIANT;
- }
+ props += DexAttr.VARIANT_3;
+ } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
+ props += DexAttr.VARIANT_2;
+ } else {
+ props += DexAttr.DEFAULT_VARIANT;
}
} else {
props += DexAttr.NON_SHINY;
diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/handlers/registration-form-ui-handler.ts
similarity index 94%
rename from src/ui/registration-form-ui-handler.ts
rename to src/ui/handlers/registration-form-ui-handler.ts
index 2c8080d534d..d424e44b455 100644
--- a/src/ui/registration-form-ui-handler.ts
+++ b/src/ui/handlers/registration-form-ui-handler.ts
@@ -2,9 +2,9 @@ import { pokerogueApi } from "#api/pokerogue-api";
import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import { addTextObject } from "#ui/text";
import i18next from "i18next";
diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/handlers/rename-form-ui-handler.ts
similarity index 87%
rename from src/ui/rename-form-ui-handler.ts
rename to src/ui/handlers/rename-form-ui-handler.ts
index 9da5b0e8554..f1d9ae3c981 100644
--- a/src/ui/rename-form-ui-handler.ts
+++ b/src/ui/handlers/rename-form-ui-handler.ts
@@ -1,7 +1,7 @@
import type { PlayerPokemon } from "#field/pokemon";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import i18next from "i18next";
export class RenameFormUiHandler extends FormModalUiHandler {
diff --git a/src/ui/rename-run-ui-handler.ts b/src/ui/handlers/rename-run-ui-handler.ts
similarity index 89%
rename from src/ui/rename-run-ui-handler.ts
rename to src/ui/handlers/rename-run-ui-handler.ts
index a94b7b08fb9..43094d3cf60 100644
--- a/src/ui/rename-run-ui-handler.ts
+++ b/src/ui/handlers/rename-run-ui-handler.ts
@@ -1,6 +1,5 @@
import i18next from "i18next";
-import type { InputFieldConfig } from "./form-modal-ui-handler";
-import { FormModalUiHandler } from "./form-modal-ui-handler";
+import { FormModalUiHandler, type InputFieldConfig } from "./form-modal-ui-handler";
import type { ModalConfig } from "./modal-ui-handler";
export class RenameRunFormUiHandler extends FormModalUiHandler {
@@ -37,7 +36,7 @@ export class RenameRunFormUiHandler extends FormModalUiHandler {
if (!super.show(args)) {
return false;
}
- if (this.inputs?.length) {
+ if (this.inputs?.length > 0) {
this.inputs.forEach(input => {
input.text = "";
});
diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/handlers/run-history-ui-handler.ts
similarity index 96%
rename from src/ui/run-history-ui-handler.ts
rename to src/ui/handlers/run-history-ui-handler.ts
index 6f4d9024832..baf1577a2b4 100644
--- a/src/ui/run-history-ui-handler.ts
+++ b/src/ui/handlers/run-history-ui-handler.ts
@@ -6,10 +6,10 @@ import { PlayerGender } from "#enums/player-gender";
import { TextStyle } from "#enums/text-style";
import { TrainerVariant } from "#enums/trainer-variant";
import { UiMode } from "#enums/ui-mode";
-import type { RunEntry } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { RunDisplayMode } from "#ui/run-info-ui-handler";
+import type { RunEntry } from "#types/save-data";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { RunDisplayMode } from "#ui/handlers/run-info-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { fixedInt, formatLargeNumber } from "#utils/common";
@@ -32,8 +32,6 @@ export class RunHistoryUiHandler extends MessageUiHandler {
private runsContainer: Phaser.GameObjects.Container;
private runs: RunEntryContainer[];
- private runSelectCallback: RunSelectCallback | null;
-
private scrollCursor = 0;
private cursorObj: Phaser.GameObjects.NineSlice | null;
@@ -118,7 +116,6 @@ export class RunHistoryUiHandler extends MessageUiHandler {
success = true;
return success;
}
- this.runSelectCallback = null;
success = true;
globalScene.ui.revertMode();
} else if (this.runs.length > 0) {
@@ -235,7 +232,6 @@ export class RunHistoryUiHandler extends MessageUiHandler {
this.runSelectContainer.setVisible(false);
this.setScrollCursor(0);
this.clearCursor();
- this.runSelectCallback = null;
this.clearRuns();
}
@@ -258,13 +254,11 @@ export class RunHistoryUiHandler extends MessageUiHandler {
* entryData: the data of an individual run
*/
class RunEntryContainer extends Phaser.GameObjects.Container {
- private slotId: number;
public entryData: RunEntry;
constructor(entryData: RunEntry, slotId: number) {
super(globalScene, 0, slotId * 56);
- this.slotId = slotId;
this.entryData = entryData;
this.setup(this.entryData);
@@ -329,8 +323,8 @@ class RunEntryContainer extends Phaser.GameObjects.Container {
});
this.add(enemyContainer);
} else if (
- data.battleType === BattleType.TRAINER ||
- (data.battleType === BattleType.MYSTERY_ENCOUNTER && data.trainer)
+ data.battleType === BattleType.TRAINER
+ || (data.battleType === BattleType.MYSTERY_ENCOUNTER && data.trainer)
) {
// Defeats from Trainers show the trainer's title and name
const tObj = data.trainer.toTrainer();
diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/handlers/run-info-ui-handler.ts
similarity index 95%
rename from src/ui/run-info-ui-handler.ts
rename to src/ui/handlers/run-info-ui-handler.ts
index 5fc0f37c72d..93ace112148 100644
--- a/src/ui/run-info-ui-handler.ts
+++ b/src/ui/handlers/run-info-ui-handler.ts
@@ -19,11 +19,11 @@ import { UiMode } from "#enums/ui-mode";
import * as Modifier from "#modifiers/modifier";
import { getLuckString, getLuckTextTint } from "#modifiers/modifier-type";
import { getVariantTint } from "#sprites/variant";
-import type { SessionSaveData } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
import { SettingKeyboard } from "#system/settings-keyboard";
+import type { SessionSaveData } from "#types/save-data";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
import { toCamelCase } from "#utils/strings";
@@ -178,7 +178,7 @@ export class RunInfoUiHandler extends UiHandler {
const headerBg = addWindow(0, 0, globalScene.scaledCanvas.width - 2, 24);
headerBg.setOrigin(0, 0);
this.runContainer.add(headerBg);
- if (this.runInfo.modifiers.length !== 0) {
+ if (this.runInfo.modifiers.length > 0) {
const headerBgCoords = headerBg.getTopRight();
const abilityButtonContainer = globalScene.add.container(0, 0);
const abilityButtonText = addTextObject(8, 0, i18next.t("runHistory:viewHeldItems"), TextStyle.WINDOW, {
@@ -282,8 +282,8 @@ export class RunInfoUiHandler extends UiHandler {
const enemyContainer = globalScene.add.container(0, 0);
// Wild - Single and Doubles
if (
- this.runInfo.battleType === BattleType.WILD ||
- (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && !this.runInfo.trainer)
+ this.runInfo.battleType === BattleType.WILD
+ || (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && !this.runInfo.trainer)
) {
switch (this.runInfo.enemyParty.length) {
case 1:
@@ -296,8 +296,8 @@ export class RunInfoUiHandler extends UiHandler {
break;
}
} else if (
- this.runInfo.battleType === BattleType.TRAINER ||
- (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && this.runInfo.trainer)
+ this.runInfo.battleType === BattleType.TRAINER
+ || (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && this.runInfo.trainer)
) {
this.parseTrainerDefeat(enemyContainer);
}
@@ -344,7 +344,7 @@ export class RunInfoUiHandler extends UiHandler {
}
const boxString = i18next
.t(trainerObj.variant !== TrainerVariant.DOUBLE ? "battle:trainerAppeared" : "battle:trainerAppearedDouble", {
- trainerName: trainerName,
+ trainerName,
})
.replace(/\n/g, " ");
const descContainer = globalScene.add.container(0, 0);
@@ -363,8 +363,8 @@ export class RunInfoUiHandler extends UiHandler {
subSprite.setScale(0.65);
subSprite.setPosition(34, 46);
const mysteryEncounterTitle = i18next.t(
- globalScene.getMysteryEncounter(this.runInfo.mysteryEncounterType as MysteryEncounterType, true)
- .localizationKey + ":title",
+ globalScene.getMysteryEncounter(this.runInfo.mysteryEncounterType as MysteryEncounterType, true).localizationKey
+ + ":title",
);
const descContainer = globalScene.add.container(0, 0);
const textBox = addTextObject(0, 0, mysteryEncounterTitle, TextStyle.WINDOW, {
@@ -600,8 +600,8 @@ export class RunInfoUiHandler extends UiHandler {
// If the player achieves a personal best in Endless, the mode text will be tinted similarly to SSS luck to celebrate their achievement.
if (
- (this.runInfo.gameMode === GameModes.ENDLESS || this.runInfo.gameMode === GameModes.SPLICED_ENDLESS) &&
- this.runInfo.waveIndex === globalScene.gameData.gameStats.highestEndlessWave
+ (this.runInfo.gameMode === GameModes.ENDLESS || this.runInfo.gameMode === GameModes.SPLICED_ENDLESS)
+ && this.runInfo.waveIndex === globalScene.gameData.gameStats.highestEndlessWave
) {
modeText.appendText(` [${i18next.t("runHistory:personalBest")}]`);
modeText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969);
@@ -613,12 +613,12 @@ export class RunInfoUiHandler extends UiHandler {
const lineSpacing = i18next.resolvedLanguage === "ja" ? 3 : 3;
const runInfoText = addBBCodeTextObject(7, 0, "", TextStyle.WINDOW, {
fontSize: "50px",
- lineSpacing: lineSpacing,
+ lineSpacing,
});
const runTime = getPlayTimeString(this.runInfo.playTime);
runInfoText.appendText(`${i18next.t("runHistory:runLength")}: ${runTime}`, false);
const runMoney = formatMoney(globalScene.moneyFormat, this.runInfo.money);
- const moneyTextColor = getTextColor(TextStyle.MONEY_WINDOW, false, globalScene.uiTheme);
+ const moneyTextColor = getTextColor(TextStyle.MONEY_WINDOW, false);
runInfoText.appendText(
`[color=${moneyTextColor}]${i18next.t("battleScene:moneyOwned", { formattedMoney: runMoney })}[/color]`,
);
@@ -648,7 +648,7 @@ export class RunInfoUiHandler extends UiHandler {
// Player Held Items
// A max of 20 items can be displayed. A + sign will be added if the run's held items pushes past this maximum to show the user that there are more.
- if (this.runInfo.modifiers.length) {
+ if (this.runInfo.modifiers.length > 0) {
let visibleModifierIndex = 0;
const modifierIconsContainer = globalScene.add.container(
@@ -685,37 +685,38 @@ export class RunInfoUiHandler extends UiHandler {
/**
* This function parses the Challenges section of the Run Entry and returns a list of active challenge.
- * @return string[] of active challenge names
+ * @returns string[] of active challenge names
*/
private challengeParser(): string[] {
const rules: string[] = [];
- for (let i = 0; i < this.runInfo.challenges.length; i++) {
- if (this.runInfo.challenges[i].value !== 0) {
- switch (this.runInfo.challenges[i].id) {
- case Challenges.SINGLE_GENERATION:
- rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`));
- break;
- case Challenges.SINGLE_TYPE: {
- const typeRule = PokemonType[this.runInfo.challenges[i].value - 1];
- const typeTextColor = `[color=${TypeColor[typeRule]}]`;
- const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`;
- const typeText =
- typeTextColor +
- typeShadowColor +
- i18next.t(`pokemonInfo:type.${toCamelCase(typeRule)}`)! +
- "[/color]" +
- "[/shadow]";
- rules.push(typeText);
- break;
- }
- case Challenges.INVERSE_BATTLE:
- rules.push(i18next.t("challenges:inverseBattle.shortName"));
- break;
- default: {
- const localizationKey = toCamelCase(Challenges[this.runInfo.challenges[i].id]);
- rules.push(i18next.t(`challenges:${localizationKey}.name`));
- break;
- }
+ for (const chal of this.runInfo.challenges) {
+ if (chal.value === 0) {
+ continue;
+ }
+
+ switch (chal.id) {
+ case Challenges.SINGLE_GENERATION:
+ rules.push(i18next.t(`runHistory:challengeMonoGen${chal.value}`));
+ break;
+ case Challenges.SINGLE_TYPE: {
+ const typeRule = PokemonType[chal.value - 1];
+ const typeTextColor = `[color=${TypeColor[typeRule]}]`;
+ const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`;
+ const typeText =
+ typeTextColor
+ + typeShadowColor
+ + i18next.t(`pokemonInfo:type.${toCamelCase(typeRule)}`)!
+ + "[/color][/shadow]";
+ rules.push(typeText);
+ break;
+ }
+ case Challenges.INVERSE_BATTLE:
+ rules.push(i18next.t("challenges:inverseBattle.shortName"));
+ break;
+ default: {
+ const localizationKey = toCamelCase(Challenges[chal.id]);
+ rules.push(i18next.t(`challenges:${localizationKey}.name`));
+ break;
}
}
}
@@ -779,7 +780,7 @@ export class RunInfoUiHandler extends UiHandler {
const lineSpacing = i18next.resolvedLanguage === "ja" ? 3 : 3;
const pokeInfoText = addBBCodeTextObject(0, 0, pName, TextStyle.SUMMARY, {
fontSize: textContainerFontSize,
- lineSpacing: lineSpacing,
+ lineSpacing,
});
pokeInfoText.appendText(
`${i18next.t("saveSlotSelectUiHandler:lv")}${formatFancyLargeNumber(pokemon.level, 1)} - ${pNatureName}`,
@@ -811,7 +812,7 @@ export class RunInfoUiHandler extends UiHandler {
// Column 1: HP Atk Def
const pokeStatText1 = addBBCodeTextObject(-5, 0, hp, TextStyle.SUMMARY, {
fontSize: textContainerFontSize,
- lineSpacing: lineSpacing,
+ lineSpacing,
});
pokeStatText1.appendText(atk);
pokeStatText1.appendText(def);
@@ -819,7 +820,7 @@ export class RunInfoUiHandler extends UiHandler {
// Column 2: SpAtk SpDef Speed
const pokeStatText2 = addBBCodeTextObject(25, 0, spatk, TextStyle.SUMMARY, {
fontSize: textContainerFontSize,
- lineSpacing: lineSpacing,
+ lineSpacing,
});
pokeStatText2.appendText(spdef);
pokeStatText2.appendText(speed);
@@ -893,7 +894,7 @@ export class RunInfoUiHandler extends UiHandler {
this.runInfo.gameMode === GameModes.SPLICED_ENDLESS || this.runInfo.gameMode === GameModes.ENDLESS ? 0.25 : 0.5;
const heldItemsContainer = globalScene.add.container(-82, 2);
const heldItemsList: Modifier.PokemonHeldItemModifier[] = [];
- if (this.runInfo.modifiers.length) {
+ if (this.runInfo.modifiers.length > 0) {
for (const m of this.runInfo.modifiers) {
const modifier = m.toModifier(this.modifiersModule[m.className]);
if (modifier instanceof Modifier.PokemonHeldItemModifier && modifier.pokemonId === pokemon.id) {
@@ -912,8 +913,8 @@ export class RunInfoUiHandler extends UiHandler {
}
const itemIcon = item?.getIcon(true);
if (
- item?.stackCount < item?.getMaxHeldItemCount(pokemon) &&
- itemIcon.list[1] instanceof Phaser.GameObjects.BitmapText
+ item?.stackCount < item?.getMaxHeldItemCount(pokemon)
+ && itemIcon.list[1] instanceof Phaser.GameObjects.BitmapText
) {
itemIcon.list[1].clearTint();
}
@@ -1150,7 +1151,7 @@ export class RunInfoUiHandler extends UiHandler {
}
break;
case Button.CYCLE_ABILITY:
- if (this.runInfo.modifiers.length !== 0 && this.pageMode === RunInfoUiMode.MAIN) {
+ if (this.runInfo.modifiers.length > 0 && this.pageMode === RunInfoUiMode.MAIN) {
if (this.partyVisibility) {
this.showParty(false);
} else {
diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/handlers/save-slot-select-ui-handler.ts
similarity index 97%
rename from src/ui/save-slot-select-ui-handler.ts
rename to src/ui/handlers/save-slot-select-ui-handler.ts
index e9f9c5a0038..5115ab23578 100644
--- a/src/ui/save-slot-select-ui-handler.ts
+++ b/src/ui/handlers/save-slot-select-ui-handler.ts
@@ -6,11 +6,11 @@ import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
import * as Modifier from "#modifiers/modifier";
-import type { SessionSaveData } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
-import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { RunDisplayMode } from "#ui/run-info-ui-handler";
+import type { SessionSaveData } from "#types/save-data";
+import type { OptionSelectConfig } from "#ui/handlers/abstract-option-select-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { RunDisplayMode } from "#ui/handlers/run-info-ui-handler";
import { addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { fixedInt, formatLargeNumber, getPlayTimeString, isNullOrUndefined } from "#utils/common";
@@ -181,7 +181,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
ui.setOverlayMode(
UiMode.CONFIRM,
() => {
- globalScene.gameData.tryClearSession(cursor).then(response => {
+ globalScene.gameData.deleteSession(cursor).then(response => {
if (response[0] === false) {
globalScene.reset(true);
} else {
@@ -293,8 +293,8 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
} else if (this.scrollCursor < SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN) {
success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition);
} else if (
- this.cursor === SLOTS_ON_SCREEN - 1 &&
- this.scrollCursor === SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN
+ this.cursor === SLOTS_ON_SCREEN - 1
+ && this.scrollCursor === SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN
) {
this.setScrollCursor(0);
this.revertSessionSlot(SLOTS_ON_SCREEN - 1);
@@ -357,7 +357,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
this.message.setY(-37);
}
- this.saveSlotSelectMessageBoxContainer.setVisible(!!text?.length);
+ this.saveSlotSelectMessageBoxContainer.setVisible(text?.length > 0);
}
/**
diff --git a/src/ui/session-reload-modal-ui-handler.ts b/src/ui/handlers/session-reload-modal-ui-handler.ts
similarity index 88%
rename from src/ui/session-reload-modal-ui-handler.ts
rename to src/ui/handlers/session-reload-modal-ui-handler.ts
index 1f5a205f990..33c18b1974a 100644
--- a/src/ui/session-reload-modal-ui-handler.ts
+++ b/src/ui/handlers/session-reload-modal-ui-handler.ts
@@ -1,7 +1,7 @@
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
-import type { ModalConfig } from "#ui/modal-ui-handler";
-import { ModalUiHandler } from "#ui/modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
+import { ModalUiHandler } from "#ui/handlers/modal-ui-handler";
import { addTextObject } from "#ui/text";
export class SessionReloadModalUiHandler extends ModalUiHandler {
diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/handlers/starter-select-ui-handler.ts
similarity index 94%
rename from src/ui/starter-select-ui-handler.ts
rename to src/ui/handlers/starter-select-ui-handler.ts
index 25d5277b4c2..7c67ed13d4b 100644
--- a/src/ui/starter-select-ui-handler.ts
+++ b/src/ui/handlers/starter-select-ui-handler.ts
@@ -40,26 +40,34 @@ import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
+import { UiTheme } from "#enums/ui-theme";
import type { CandyUpgradeNotificationChangedEvent } from "#events/battle-scene";
import { BattleSceneEventType } from "#events/battle-scene";
import type { Variant } from "#sprites/variant";
import { getVariantIcon, getVariantTint } from "#sprites/variant";
import { achvs } from "#system/achv";
-import type { DexAttrProps, StarterAttributes, StarterDataEntry, StarterMoveset } from "#system/game-data";
import { RibbonData } from "#system/ribbons/ribbon-data";
import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown";
-import { FilterBar } from "#ui/filter-bar";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { MoveInfoOverlay } from "#ui/move-info-overlay";
-import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
-import { ScrollBar } from "#ui/scroll-bar";
-import { StarterContainer } from "#ui/starter-container";
-import { StatsContainer } from "#ui/stats-container";
-import { addBBCodeTextObject, addTextObject } from "#ui/text";
+import type { DexAttrProps, StarterAttributes, StarterDataEntry, StarterMoveset } from "#types/save-data";
+import {
+ DropDown,
+ DropDownLabel,
+ DropDownOption,
+ DropDownState,
+ DropDownType,
+ SortCriteria,
+} from "#ui/containers/dropdown";
+import { FilterBar } from "#ui/containers/filter-bar";
+import { MoveInfoOverlay } from "#ui/containers/move-info-overlay";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { StarterContainer } from "#ui/containers/starter-container";
+import { StatsContainer } from "#ui/containers/stats-container";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
+import { PokemonIconAnimHelper, PokemonIconAnimMode } from "#ui/utils/pokemon-icon-anim-helper";
import { applyChallenges, checkStarterValidForChallenge } from "#utils/challenge-utils";
import {
BooleanHolder,
@@ -72,7 +80,7 @@ import {
rgbHexToRgba,
} from "#utils/common";
import type { StarterPreferences } from "#utils/data";
-import { loadStarterPreferences, saveStarterPreferences } from "#utils/data";
+import { deepCopy, loadStarterPreferences, saveStarterPreferences } from "#utils/data";
import { getPokemonSpeciesForm, getPokerusStarters } from "#utils/pokemon-utils";
import { toCamelCase, toTitleCase } from "#utils/strings";
import { argbFromRgba } from "@material/material-color-utilities";
@@ -206,7 +214,7 @@ function calcStarterPosition(index: number, scrollCursor = 0): { x: number; y: n
const x = (index % 9) * 18;
const y = yOffset + (Math.floor(index / 9) - scrollCursor) * height;
- return { x: x, y: y };
+ return { x, y };
}
/**
@@ -339,7 +347,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
private teraLabel: Phaser.GameObjects.Text;
private goFilterLabel: Phaser.GameObjects.Text;
/** Group holding the UI elements appearing in the instructionsContainer */
- /* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group`
+ /* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group`
private instructionElemGroup: Phaser.GameObjects.Group;
*/
@@ -390,7 +398,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
private startCursorObj: Phaser.GameObjects.NineSlice;
private randomCursorObj: Phaser.GameObjects.NineSlice;
- private iconAnimHandler: PokemonIconAnimHandler;
+ private iconAnimHandler: PokemonIconAnimHelper;
//variables to keep track of the dynamically rendered list of instruction prompts for starter select
private instructionRowX = 0;
@@ -432,8 +440,15 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const bgColor = globalScene.add.rectangle(0, 0, sWidth, sHeight, 0x006860).setOrigin(0);
+ const starterDexNoLabel = globalScene.add
+ .image(6, 14, getLocalizedSpriteKey("summary_dexnb_label"))
+ .setOrigin(0, 1); // Pixel text 'No'
+
const starterSelectBg = globalScene.add.image(0, 0, "starter_select_bg").setOrigin(0);
- this.shinyOverlay = globalScene.add.image(6, 6, "summary_overlay_shiny").setOrigin(0).setVisible(false);
+ this.shinyOverlay = globalScene.add
+ .image(6, 111, getLocalizedSpriteKey("summary_dexnb_label_overlay_shiny"))
+ .setOrigin(0, 1)
+ .setVisible(false); // Pixel text 'No' shiny
const starterContainerWindow = addWindow(speciesContainerX, filterBarHeight + 1, 175, 161);
const starterContainerBg = globalScene.add
@@ -592,11 +607,11 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// Offset the generation filter dropdown to avoid covering the filtered pokemon
this.filterBar.offsetHybridFilters();
- if (!globalScene.uiTheme) {
+ if (globalScene.uiTheme === UiTheme.DEFAULT) {
starterContainerWindow.setVisible(false);
}
- this.iconAnimHandler = new PokemonIconAnimHandler();
+ this.iconAnimHandler = new PokemonIconAnimHelper();
this.iconAnimHandler.setup();
this.pokemonSprite = globalScene.add.sprite(53, 63, "pkmn__sub");
@@ -1070,6 +1085,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.starterSelectContainer.add([
bgColor,
starterSelectBg,
+ starterDexNoLabel,
this.shinyOverlay,
starterContainerBg,
addWindow(
@@ -1141,14 +1157,15 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.allowTera = globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id);
- if (args.length >= 1 && args[0] instanceof Function) {
+ if (args.length > 0 && args[0] instanceof Function) {
super.show(args);
this.starterSelectCallback = args[0] as StarterSelectCallback;
this.starterSelectContainer.setVisible(true);
this.starterPreferences = loadStarterPreferences();
- this.originalStarterPreferences = loadStarterPreferences();
+ // Deep copy the JSON (avoid re-loading from disk)
+ this.originalStarterPreferences = deepCopy(this.starterPreferences);
this.allSpecies.forEach((species, s) => {
const icon = this.starterContainers[s].icon;
@@ -1212,6 +1229,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
preferences: StarterPreferences,
ignoreChallenge = false,
): StarterAttributes {
+ // if preferences for the species is undefined, set it to an empty object
+ preferences[species.speciesId] ??= {};
const starterAttributes = preferences[species.speciesId];
const { dexEntry, starterDataEntry: starterData } = this.getSpeciesData(species.speciesId, !ignoreChallenge);
@@ -1240,20 +1259,21 @@ export class StarterSelectUiHandler extends MessageUiHandler {
hasShiny && caughtAttr & DexAttr.VARIANT_3,
];
if (
- Number.isNaN(starterAttributes.variant) ||
- starterAttributes.variant < 0 ||
- !unlockedVariants[starterAttributes.variant]
+ Number.isNaN(starterAttributes.variant)
+ || starterAttributes.variant < 0
+ || !unlockedVariants[starterAttributes.variant]
) {
// variant value is invalid or requested variant wasn't unlocked, purging setting
starterAttributes.variant = undefined;
}
}
- if (starterAttributes.female !== undefined) {
- if (!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)) {
- // requested gender wasn't unlocked, purging setting
- starterAttributes.female = undefined;
- }
+ if (
+ starterAttributes.female !== undefined
+ && !(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)
+ ) {
+ // requested gender wasn't unlocked, purging setting
+ starterAttributes.female = undefined;
}
if (starterAttributes.ability !== undefined) {
@@ -1277,9 +1297,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const selectedForm = starterAttributes.form;
if (
- selectedForm !== undefined &&
- (!species.forms[selectedForm]?.isStarterSelectable ||
- !(caughtAttr & globalScene.gameData.getFormAttr(selectedForm)))
+ selectedForm !== undefined
+ && (!species.forms[selectedForm]?.isStarterSelectable
+ || !(caughtAttr & globalScene.gameData.getFormAttr(selectedForm)))
) {
// requested form wasn't unlocked/isn't a starter form, purging setting
starterAttributes.form = undefined;
@@ -1357,7 +1377,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.message.setY(singleLine ? -22 : -37);
}
- this.starterSelectMessageBoxContainer.setVisible(!!text?.length);
+ this.starterSelectMessageBoxContainer.setVisible(text?.length > 0);
}
/**
@@ -1385,8 +1405,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[speciesId];
return (
- starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[speciesId]) &&
- !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
+ starterData.candyCount >= getPassiveCandyCount(speciesStarterCosts[speciesId])
+ && !(starterData.passiveAttr & PassiveAttr.UNLOCKED)
);
}
@@ -1400,9 +1420,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const starterData = globalScene.gameData.starterData[speciesId];
return (
- starterData.candyCount >=
- getValueReductionCandyCounts(speciesStarterCosts[speciesId])[starterData.valueReduction] &&
- starterData.valueReduction < valueReductionMax
+ starterData.candyCount >= getValueReductionCandyCounts(speciesStarterCosts[speciesId])[starterData.valueReduction]
+ && starterData.valueReduction < valueReductionMax
);
}
@@ -1459,9 +1478,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
};
if (
- this.isPassiveAvailable(species.speciesId) ||
- (globalScene.candyUpgradeNotification === 2 &&
- (this.isValueReductionAvailable(species.speciesId) || this.isSameSpeciesEggAvailable(species.speciesId)))
+ this.isPassiveAvailable(species.speciesId)
+ || (globalScene.candyUpgradeNotification === 2
+ && (this.isValueReductionAvailable(species.speciesId) || this.isSameSpeciesEggAvailable(species.speciesId)))
) {
const chain = globalScene.tweens.chain(tweenChain);
if (!startPaused) {
@@ -1478,9 +1497,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const slotVisible = !!species?.speciesId;
if (
- !species ||
- globalScene.candyUpgradeNotification === 0 ||
- species.speciesId !== species.getRootSpeciesId(false)
+ !species
+ || globalScene.candyUpgradeNotification === 0
+ || species.speciesId !== species.getRootSpeciesId(false)
) {
starter.candyUpgradeIcon.setVisible(false);
starter.candyUpgradeOverlayIcon.setVisible(false);
@@ -1581,8 +1600,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.filterBar.toggleDropDown(this.filterBarCursor);
success = true;
} else if (
- this.filterMode &&
- !this.filterBar.getFilter(this.filterBar.getColumn(this.filterBarCursor)).hasDefaultValues()
+ this.filterMode
+ && !this.filterBar.getFilter(this.filterBar.getColumn(this.filterBarCursor)).hasDefaultValues()
) {
if (this.filterBar.getColumn(this.filterBarCursor) === DropDownColumn.CAUGHT) {
this.resetCaughtDropdown();
@@ -1594,7 +1613,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} else if (this.statsMode) {
this.toggleStatsMode(false);
success = true;
- } else if (this.starterSpecies.length) {
+ } else if (this.starterSpecies.length > 0) {
this.popStarter(this.starterSpecies.length - 1);
success = true;
this.updateInstructions();
@@ -1607,6 +1626,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
if (!this.filterMode) {
this.startCursorObj.setVisible(false);
this.starterIconsCursorObj.setVisible(false);
+ this.randomCursorObj.setVisible(false);
this.setSpecies(null);
this.filterBarCursor = 0;
this.setFilterMode(true);
@@ -1828,9 +1848,15 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// The persistent starter data to apply e.g. candy upgrades
const persistentStarterData = globalScene.gameData.starterData[this.lastSpecies.speciesId];
// The sanitized starter preferences
- let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId];
- // The original starter preferences
- const originalStarterAttributes = this.originalStarterPreferences[this.lastSpecies.speciesId];
+ if (this.starterPreferences[this.lastSpecies.speciesId] === undefined) {
+ this.starterPreferences[this.lastSpecies.speciesId] = {};
+ }
+ if (this.originalStarterPreferences[this.lastSpecies.speciesId] === undefined) {
+ this.originalStarterPreferences[this.lastSpecies.speciesId] = {};
+ }
+ // Bangs are safe here due to the above check
+ const starterAttributes = this.starterPreferences[this.lastSpecies.speciesId]!;
+ const originalStarterAttributes = this.originalStarterPreferences[this.lastSpecies.speciesId]!;
// this gets the correct pokemon cursor depending on whether you're in the starter screen or the party icons
if (!this.starterIconsCursorObj.visible) {
@@ -1872,10 +1898,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
);
const newCost = globalScene.gameData.getSpeciesStarterValue(this.lastSpecies.speciesId);
if (
- !isDupe &&
- isValidForChallenge &&
- currentPartyValue + newCost <= this.getValueLimit() &&
- this.starterSpecies.length < PLAYER_PARTY_MAX_SIZE
+ !isDupe
+ && isValidForChallenge
+ && currentPartyValue + newCost <= this.getValueLimit()
+ && this.starterSpecies.length < PLAYER_PARTY_MAX_SIZE
) {
options = [
{
@@ -2048,12 +2074,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
options: natures
.map((n: Nature, _i: number) => {
const option: OptionSelectItem = {
- label: getNatureName(n, true, true, true, globalScene.uiTheme),
+ label: getNatureName(n, true, true, true),
handler: () => {
- // update default nature in starter save data
- if (!starterAttributes) {
- starterAttributes = this.starterPreferences[this.lastSpecies.speciesId] = {};
- }
starterAttributes.nature = n;
originalStarterAttributes.nature = starterAttributes.nature;
this.clearText();
@@ -2095,27 +2117,21 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const passiveAttr = starterData.passiveAttr;
if (passiveAttr & PassiveAttr.UNLOCKED) {
// this is for enabling and disabling the passive
- if (!(passiveAttr & PassiveAttr.ENABLED)) {
- options.push({
- label: i18next.t("starterSelectUiHandler:enablePassive"),
- handler: () => {
- starterData.passiveAttr |= PassiveAttr.ENABLED;
- ui.setMode(UiMode.STARTER_SELECT);
- this.setSpeciesDetails(this.lastSpecies);
- return true;
- },
- });
- } else {
- options.push({
- label: i18next.t("starterSelectUiHandler:disablePassive"),
- handler: () => {
- starterData.passiveAttr ^= PassiveAttr.ENABLED;
- ui.setMode(UiMode.STARTER_SELECT);
- this.setSpeciesDetails(this.lastSpecies);
- return true;
- },
- });
- }
+ const label = i18next.t(
+ passiveAttr & PassiveAttr.ENABLED
+ ? "starterSelectUiHandler:disablePassive"
+ : "starterSelectUiHandler:enablePassive",
+ );
+ options.push({
+ label,
+ handler: () => {
+ starterData.passiveAttr ^= PassiveAttr.ENABLED;
+ persistentStarterData.passiveAttr ^= PassiveAttr.ENABLED;
+ ui.setMode(UiMode.STARTER_SELECT);
+ this.setSpeciesDetails(this.lastSpecies);
+ return true;
+ },
+ });
}
// if container.favorite is false, show the favorite option
const isFavorite = starterAttributes?.favorite ?? false;
@@ -2322,7 +2338,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
},
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
yOffset: 47,
});
};
@@ -2369,7 +2385,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
},
});
ui.setModeWithoutClear(UiMode.OPTION_SELECT, {
- options: options,
+ options,
yOffset: 47,
});
success = true;
@@ -2419,11 +2435,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// TODO: is this bang correct?
break;
}
- } else {
- if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) {
- // TODO: is this bang correct?
- break;
- }
+ } else if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) {
+ // TODO: is this bang correct?
+ break;
}
} while (newVariant !== props.variant);
starterAttributes.variant = newVariant; // store the selected variant
@@ -2458,8 +2472,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
do {
newFormIndex = (newFormIndex + 1) % formCount;
if (
- this.lastSpecies.forms[newFormIndex].isStarterSelectable &&
- this.speciesStarterDexEntry!.caughtAttr! & globalScene.gameData.getFormAttr(newFormIndex)
+ this.lastSpecies.forms[newFormIndex].isStarterSelectable
+ && this.speciesStarterDexEntry!.caughtAttr! & globalScene.gameData.getFormAttr(newFormIndex)
) {
// TODO: are those bangs correct?
break;
@@ -2504,10 +2518,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
newAbilityIndex = (newAbilityIndex + 1) % abilityCount;
}
break;
- } else {
- if (abilityAttr & AbilityAttr.ABILITY_HIDDEN) {
- break;
- }
+ } else if (abilityAttr & AbilityAttr.ABILITY_HIDDEN) {
+ break;
}
} while (newAbilityIndex !== this.abilityCursor);
starterAttributes.ability = newAbilityIndex; // store the selected ability
@@ -3008,10 +3020,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.filteredStarterContainers = [];
this.validStarterContainers = [];
- // biome-ignore-start lint/nursery/useIterableCallbackReturn: benign
+ // biome-ignore-start lint/suspicious/useIterableCallbackReturn: benign
this.pokerusCursorObjs.forEach(cursor => cursor.setVisible(false));
this.starterCursorObjs.forEach(cursor => cursor.setVisible(false));
- // biome-ignore-end lint/nursery/useIterableCallbackReturn: benign
+ // biome-ignore-end lint/suspicious/useIterableCallbackReturn: benign
this.filterBar.updateFilterLabels();
@@ -3058,8 +3070,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
// this updates icons for previously saved pokemon
- for (let i = 0; i < this.validStarterContainers.length; i++) {
- const currentFilteredContainer = this.validStarterContainers[i];
+ for (const currentFilteredContainer of this.validStarterContainers) {
const starterSprite = currentFilteredContainer.icon as Phaser.GameObjects.Sprite;
const currentDexAttr = this.getCurrentDexProps(currentFilteredContainer.species.speciesId);
@@ -3191,8 +3202,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// HA Filter
const speciesHasHiddenAbility =
- container.species.abilityHidden !== container.species.ability1 &&
- container.species.abilityHidden !== AbilityId.NONE;
+ container.species.abilityHidden !== container.species.ability1
+ && container.species.abilityHidden !== AbilityId.NONE;
const hasHA = starterData.abilityAttr & AbilityAttr.ABILITY_HIDDEN;
const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) {
@@ -3234,16 +3245,16 @@ export class StarterSelectUiHandler extends MessageUiHandler {
});
if (
- fitsGen &&
- fitsType &&
- fitsCaught &&
- fitsPassive &&
- fitsCostReduction &&
- fitsFavorite &&
- fitsWin &&
- fitsHA &&
- fitsEgg &&
- fitsPokerus
+ fitsGen
+ && fitsType
+ && fitsCaught
+ && fitsPassive
+ && fitsCostReduction
+ && fitsFavorite
+ && fitsWin
+ && fitsHA
+ && fitsEgg
+ && fitsPokerus
) {
this.filteredStarterContainers.push(container);
}
@@ -3267,26 +3278,26 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
case SortCriteria.IV: {
const avgIVsA =
- globalScene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0) /
- globalScene.gameData.dexData[a.species.speciesId].ivs.length;
+ globalScene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0)
+ / globalScene.gameData.dexData[a.species.speciesId].ivs.length;
const avgIVsB =
- globalScene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0) /
- globalScene.gameData.dexData[b.species.speciesId].ivs.length;
+ globalScene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0)
+ / globalScene.gameData.dexData[b.species.speciesId].ivs.length;
return (avgIVsA - avgIVsB) * -sort.dir;
}
case SortCriteria.NAME:
return a.species.name.localeCompare(b.species.name) * -sort.dir;
case SortCriteria.CAUGHT:
return (
- (globalScene.gameData.dexData[a.species.speciesId].caughtCount -
- globalScene.gameData.dexData[b.species.speciesId].caughtCount) *
- -sort.dir
+ (globalScene.gameData.dexData[a.species.speciesId].caughtCount
+ - globalScene.gameData.dexData[b.species.speciesId].caughtCount)
+ * -sort.dir
);
case SortCriteria.HATCHED:
return (
- (globalScene.gameData.dexData[a.species.speciesId].hatchedCount -
- globalScene.gameData.dexData[b.species.speciesId].hatchedCount) *
- -sort.dir
+ (globalScene.gameData.dexData[a.species.speciesId].hatchedCount
+ - globalScene.gameData.dexData[b.species.speciesId].hatchedCount)
+ * -sort.dir
);
}
return 0;
@@ -3414,8 +3425,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
if (species) {
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
+ // Bang is correct due to the `?` before variant
const variant = this.starterPreferences[species.speciesId]?.variant
- ? (this.starterPreferences[species.speciesId].variant as Variant)
+ ? (this.starterPreferences[species.speciesId]!.variant as Variant)
: defaultProps.variant;
const tint = getVariantTint(variant);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant)).setTint(tint);
@@ -3640,15 +3652,19 @@ export class StarterSelectUiHandler extends MessageUiHandler {
if (starterIndex > -1) {
props = globalScene.gameData.getSpeciesDexAttrProps(species, this.starterAttr[starterIndex]);
- this.setSpeciesDetails(species, {
- shiny: props.shiny,
- formIndex: props.formIndex,
- female: props.female,
- variant: props.variant,
- abilityIndex: this.starterAbilityIndexes[starterIndex],
- natureIndex: this.starterNatures[starterIndex],
- teraType: this.starterTeras[starterIndex],
- });
+ this.setSpeciesDetails(
+ species,
+ {
+ shiny: props.shiny,
+ formIndex: props.formIndex,
+ female: props.female,
+ variant: props.variant,
+ abilityIndex: this.starterAbilityIndexes[starterIndex],
+ natureIndex: this.starterNatures[starterIndex],
+ teraType: this.starterTeras[starterIndex],
+ },
+ false,
+ );
} else {
const defaultAbilityIndex =
starterAttributes?.ability ?? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
@@ -3657,23 +3673,25 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const defaultNature =
starterAttributes?.nature || globalScene.gameData.getSpeciesDefaultNature(species, dexEntry);
props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
- if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) {
- if (props.shiny) {
- props.variant = starterAttributes.variant as Variant;
- }
+ if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant) && props.shiny) {
+ props.variant = starterAttributes.variant as Variant;
}
props.formIndex = starterAttributes?.form ?? props.formIndex;
props.female = starterAttributes?.female ?? props.female;
- this.setSpeciesDetails(species, {
- shiny: props.shiny,
- formIndex: props.formIndex,
- female: props.female,
- variant: props.variant,
- abilityIndex: defaultAbilityIndex,
- natureIndex: defaultNature,
- teraType: starterAttributes?.tera,
- });
+ this.setSpeciesDetails(
+ species,
+ {
+ shiny: props.shiny,
+ formIndex: props.formIndex,
+ female: props.female,
+ variant: props.variant,
+ abilityIndex: defaultAbilityIndex,
+ natureIndex: defaultNature,
+ teraType: starterAttributes?.tera,
+ },
+ false,
+ );
}
if (!isNullOrUndefined(props.formIndex)) {
@@ -3710,15 +3728,19 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const defaultNature = globalScene.gameData.getSpeciesDefaultNature(species);
const props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
- this.setSpeciesDetails(species, {
- shiny: props.shiny,
- formIndex: props.formIndex,
- female: props.female,
- variant: props.variant,
- abilityIndex: defaultAbilityIndex,
- natureIndex: defaultNature,
- forSeen: true,
- });
+ this.setSpeciesDetails(
+ species,
+ {
+ shiny: props.shiny,
+ formIndex: props.formIndex,
+ female: props.female,
+ variant: props.variant,
+ abilityIndex: defaultAbilityIndex,
+ natureIndex: defaultNature,
+ forSeen: true,
+ },
+ false,
+ );
this.pokemonSprite.setTint(0x808080);
}
} else {
@@ -3740,15 +3762,19 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonFormText.setVisible(false);
this.teraIcon.setVisible(false);
- this.setSpeciesDetails(species!, {
- // TODO: is this bang correct?
- shiny: false,
- formIndex: 0,
- female: false,
- variant: 0,
- abilityIndex: 0,
- natureIndex: 0,
- });
+ this.setSpeciesDetails(
+ species!,
+ {
+ // TODO: is this bang correct?
+ shiny: false,
+ formIndex: 0,
+ female: false,
+ variant: 0,
+ abilityIndex: 0,
+ natureIndex: 0,
+ },
+ false,
+ );
this.pokemonSprite.clearTint();
}
}
@@ -3770,7 +3796,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
return { dexEntry: { ...copiedDexEntry }, starterDataEntry: { ...copiedStarterDataEntry } };
}
- setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void {
+ setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}, save = true): void {
let { shiny, formIndex, female, variant, abilityIndex, natureIndex, teraType } = options;
const forSeen: boolean = options.forSeen ?? false;
const oldProps = species ? globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
@@ -3790,10 +3816,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// We will only update the sprite if there is a change to form, shiny/variant
// or gender for species with gender sprite differences
const shouldUpdateSprite =
- (species?.genderDiffs && !isNullOrUndefined(female)) ||
- !isNullOrUndefined(formIndex) ||
- !isNullOrUndefined(shiny) ||
- !isNullOrUndefined(variant);
+ (species?.genderDiffs && !isNullOrUndefined(female))
+ || !isNullOrUndefined(formIndex)
+ || !isNullOrUndefined(shiny)
+ || !isNullOrUndefined(variant);
const isFreshStartChallenge = globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
@@ -3885,10 +3911,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
this.pokemonNumberText.setColor(
- this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false),
+ getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, false),
);
this.pokemonNumberText.setShadowColor(
- this.getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true),
+ getTextColor(shiny ? TextStyle.SUMMARY_DEX_NUM_GOLD : TextStyle.SUMMARY_DEX_NUM, true),
);
if (forSeen ? this.speciesStarterDexEntry?.seenAttr : this.speciesStarterDexEntry?.caughtAttr) {
@@ -3970,10 +3996,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
.filter(f => f).length > 1;
this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
this.canCycleTera =
- !this.statsMode &&
- this.allowTera &&
- !isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2) &&
- !globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
+ !this.statsMode
+ && this.allowTera
+ && !isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2)
+ && !globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
}
if (dexEntry.caughtAttr && species.malePercent !== null) {
@@ -3997,8 +4023,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1);
this.pokemonAbilityText
.setText(ability.name)
- .setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD))
- .setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true));
+ .setColor(getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD))
+ .setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true));
const passiveAttr = starterDataEntry.passiveAttr;
const passiveAbility = allAbilities[this.lastSpecies.getPassiveAbility(formIndex)];
@@ -4027,14 +4053,14 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonPassiveLabelText
.setVisible(!isFreshStartChallenge)
- .setColor(this.getTextColor(TextStyle.SUMMARY_ALT))
- .setShadowColor(this.getTextColor(TextStyle.SUMMARY_ALT, true));
+ .setColor(getTextColor(TextStyle.SUMMARY_ALT))
+ .setShadowColor(getTextColor(TextStyle.SUMMARY_ALT, true));
this.pokemonPassiveText
.setVisible(!isFreshStartChallenge)
.setText(passiveAbility.name)
- .setColor(this.getTextColor(textStyle))
+ .setColor(getTextColor(textStyle))
.setAlpha(textAlpha)
- .setShadowColor(this.getTextColor(textStyle, true));
+ .setShadowColor(getTextColor(textStyle, true));
if (this.activeTooltip === "PASSIVE") {
globalScene.ui.editTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`);
@@ -4066,15 +4092,13 @@ export class StarterSelectUiHandler extends MessageUiHandler {
globalScene.ui.hideTooltip();
}
- this.pokemonNatureText.setText(
- getNatureName(natureIndex as unknown as Nature, true, true, false, globalScene.uiTheme),
- );
+ this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false));
let levelMoves: LevelMoves;
if (
- pokemonFormLevelMoves.hasOwnProperty(species.speciesId) &&
- formIndex &&
- pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)
+ pokemonFormLevelMoves.hasOwnProperty(species.speciesId)
+ && formIndex
+ && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)
) {
levelMoves = pokemonFormLevelMoves[species.speciesId][formIndex];
} else {
@@ -4135,8 +4159,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} else {
this.shinyOverlay.setVisible(false);
this.pokemonNumberText
- .setColor(this.getTextColor(TextStyle.SUMMARY))
- .setShadowColor(this.getTextColor(TextStyle.SUMMARY, true));
+ .setColor(getTextColor(TextStyle.SUMMARY))
+ .setShadowColor(getTextColor(TextStyle.SUMMARY, true));
this.pokemonGenderText.setText("");
this.pokemonAbilityText.setText("");
this.pokemonPassiveText.setText("");
@@ -4182,7 +4206,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.updateInstructions();
- saveStarterPreferences(this.originalStarterPreferences);
+ if (save) {
+ saveStarterPreferences(this.originalStarterPreferences);
+ }
}
setTypeIcons(type1: PokemonType | null, type2: PokemonType | null): void {
@@ -4276,7 +4302,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
textStyle = TextStyle.SUMMARY_GOLD;
break;
}
- starter.label.setColor(this.getTextColor(textStyle)).setShadowColor(this.getTextColor(textStyle, true));
+ starter.label.setColor(getTextColor(textStyle)).setShadowColor(getTextColor(textStyle, true));
}
tryUpdateValue(add?: number, addingToParty?: boolean): boolean {
@@ -4296,8 +4322,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
this.valueLimitLabel
.setText(`${newValueStr}/${valueLimit}`)
- .setColor(this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK))
- .setShadowColor(this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK, true));
+ .setColor(getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK))
+ .setShadowColor(getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK, true));
if (overLimit) {
globalScene.time.delayedCall(fixedInt(500), () => this.tryUpdateValue());
return false;
@@ -4394,7 +4420,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
globalScene.phaseManager.pushNew("EncounterPhase");
}
this.clearText();
- globalScene.phaseManager.getCurrentPhase()?.end();
+ globalScene.phaseManager.getCurrentPhase().end();
},
cancel,
null,
@@ -4405,7 +4431,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
tryStart(manualTrigger = false): boolean {
- if (!this.starterSpecies.length) {
+ if (this.starterSpecies.length === 0) {
return false;
}
@@ -4471,13 +4497,13 @@ export class StarterSelectUiHandler extends MessageUiHandler {
return true;
}
- /* This block checks to see if your party is valid
+ /**
+ * This block checks to see if your party is valid
* It checks each pokemon against the challenge - noting that due to monotype challenges it needs to check the pokemon while ignoring their evolutions/form change items
*/
isPartyValid(): boolean {
let canStart = false;
- for (let s = 0; s < this.starterSpecies.length; s++) {
- const species = this.starterSpecies[s];
+ for (const species of this.starterSpecies) {
const isValidForChallenge = checkStarterValidForChallenge(
species,
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
@@ -4505,8 +4531,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
* If neither of these pass, we add DexAttr.MALE to our temp props
*/
if (
- this.starterPreferences[speciesId]?.female ||
- ((caughtAttr & DexAttr.FEMALE) > 0n && (caughtAttr & DexAttr.MALE) === 0n)
+ this.starterPreferences[speciesId]?.female
+ || ((caughtAttr & DexAttr.FEMALE) > 0n && (caughtAttr & DexAttr.MALE) === 0n)
) {
props += DexAttr.FEMALE;
} else {
@@ -4516,23 +4542,21 @@ export class StarterSelectUiHandler extends MessageUiHandler {
* If they're not there, it enables shiny state by default if any shiny was caught
*/
if (
- this.starterPreferences[speciesId]?.shiny ||
- ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)
+ this.starterPreferences[speciesId]?.shiny
+ || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)
) {
props += DexAttr.SHINY;
if (this.starterPreferences[speciesId]?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
- } else {
+ } else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
/* This calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
- if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
- props += DexAttr.VARIANT_3;
- } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
- props += DexAttr.VARIANT_2;
- } else {
- props += DexAttr.DEFAULT_VARIANT;
- }
+ props += DexAttr.VARIANT_3;
+ } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
+ props += DexAttr.VARIANT_2;
+ } else {
+ props += DexAttr.DEFAULT_VARIANT;
}
} else {
props += DexAttr.NON_SHINY;
@@ -4573,10 +4597,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
);
const formIndex = props.formIndex;
this.canCycleTera =
- !this.statsMode &&
- this.allowTera &&
- !isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2) &&
- !globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
+ !this.statsMode
+ && this.allowTera
+ && !isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2)
+ && !globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
this.updateInstructions();
}
}
@@ -4618,6 +4642,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
clear(): void {
super.clear();
+ saveStarterPreferences(this.originalStarterPreferences);
+
this.clearStarterPreferences();
this.cursor = -1;
this.hideInstructions();
@@ -4627,7 +4653,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.starterSelectContainer.setVisible(false);
this.blockInput = false;
- while (this.starterSpecies.length) {
+ while (this.starterSpecies.length > 0) {
this.popStarter(this.starterSpecies.length - 1);
}
diff --git a/src/ui/summary-ui-handler.ts b/src/ui/handlers/summary-ui-handler.ts
similarity index 83%
rename from src/ui/summary-ui-handler.ts
rename to src/ui/handlers/summary-ui-handler.ts
index b6447f03587..e73c5bae431 100644
--- a/src/ui/summary-ui-handler.ts
+++ b/src/ui/handlers/summary-ui-handler.ts
@@ -25,8 +25,8 @@ import type { PokemonMove } from "#moves/pokemon-move";
import type { Variant } from "#sprites/variant";
import { getVariantTint } from "#sprites/variant";
import { achvs } from "#system/achv";
-import { addBBCodeTextObject, addTextObject, getBBCodeFrag } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
+import { addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "#ui/text";
import {
fixedInt,
formatStat,
@@ -68,6 +68,7 @@ export class SummaryUiHandler extends UiHandler {
private summaryUiMode: SummaryUiMode;
private summaryContainer: Phaser.GameObjects.Container;
+ private summaryContainerDexNoLabel: Phaser.GameObjects.Image;
private tabSprite: Phaser.GameObjects.Sprite;
private shinyOverlay: Phaser.GameObjects.Image;
private numberText: Phaser.GameObjects.Text;
@@ -94,12 +95,15 @@ export class SummaryUiHandler extends UiHandler {
private passiveContainer: abilityContainer;
private summaryPageContainer: Phaser.GameObjects.Container;
private movesContainer: Phaser.GameObjects.Container;
+ private movesContainerMovesTitle: Phaser.GameObjects.Image;
+ private movesContainerDescriptionsTitle: Phaser.GameObjects.Image;
private moveDescriptionText: Phaser.GameObjects.Text;
private moveCursorObj: Phaser.GameObjects.Sprite | null;
private selectedMoveCursorObj: Phaser.GameObjects.Sprite | null;
private moveRowsContainer: Phaser.GameObjects.Container;
private extraMoveRowContainer: Phaser.GameObjects.Container;
private moveEffectContainer: Phaser.GameObjects.Container;
+ private moveEffectContainerTitle: Phaser.GameObjects.Image;
private movePowerText: Phaser.GameObjects.Text;
private moveAccuracyText: Phaser.GameObjects.Text;
private moveCategoryIcon: Phaser.GameObjects.Sprite;
@@ -111,6 +115,10 @@ export class SummaryUiHandler extends UiHandler {
private permStatsContainer: Phaser.GameObjects.Container;
private ivContainer: Phaser.GameObjects.Container;
private statsContainer: Phaser.GameObjects.Container;
+ private statsContainerItemTitle: Phaser.GameObjects.Image;
+ private statsContainerStatsTitle: Phaser.GameObjects.Image;
+ private statsContainerExpTitle: Phaser.GameObjects.Image;
+ private statsContainerExpBarTitle: Phaser.GameObjects.Image;
private descriptionScrollTween: Phaser.Tweens.Tween | null;
private moveCursorBlinkTimer: Phaser.Time.TimerEvent | null;
@@ -144,15 +152,19 @@ export class SummaryUiHandler extends UiHandler {
summaryBg.setOrigin(0, 1);
this.summaryContainer.add(summaryBg);
- this.tabSprite = globalScene.add.sprite(134, -summaryBg.displayHeight + 16, "summary_tabs_1");
- this.tabSprite.setOrigin(1, 1);
+ this.tabSprite = globalScene.add.sprite(81, -summaryBg.displayHeight + 16, getLocalizedSpriteKey("summary_tabs_1")); // Pixel text 'STATUS' tab
+ this.tabSprite.setOrigin(0, 1);
this.summaryContainer.add(this.tabSprite);
const summaryLabel = addTextObject(4, -165, i18next.t("pokemonSummary:pokemonInfo"), TextStyle.SUMMARY_HEADER);
summaryLabel.setOrigin(0, 1);
this.summaryContainer.add(summaryLabel);
- this.shinyOverlay = globalScene.add.image(6, -54, "summary_overlay_shiny");
+ this.summaryContainerDexNoLabel = globalScene.add.image(6, -151, getLocalizedSpriteKey("summary_dexnb_label")); // Pixel text 'No'
+ this.summaryContainerDexNoLabel.setOrigin(0, 1);
+ this.summaryContainer.add(this.summaryContainerDexNoLabel);
+
+ this.shinyOverlay = globalScene.add.image(6, -54, getLocalizedSpriteKey("summary_dexnb_label_overlay_shiny")); // Pixel text 'No' shiny
this.shinyOverlay.setOrigin(0, 1);
this.shinyOverlay.setVisible(false);
this.summaryContainer.add(this.shinyOverlay);
@@ -281,6 +293,10 @@ export class SummaryUiHandler extends UiHandler {
moveEffectBg.setOrigin(0, 0);
this.moveEffectContainer.add(moveEffectBg);
+ this.moveEffectContainerTitle = globalScene.add.image(7, 7, getLocalizedSpriteKey("summary_moves_effect_title")); // Pixel text 'EFFECT'
+ this.moveEffectContainerTitle.setOrigin(0, 0.5);
+ this.moveEffectContainer.add(this.moveEffectContainerTitle);
+
const moveEffectLabels = addTextObject(8, 12, i18next.t("pokemonSummary:powerAccuracyCategory"), TextStyle.SUMMARY);
moveEffectLabels.setLineSpacing(9);
moveEffectLabels.setOrigin(0, 0);
@@ -345,9 +361,9 @@ export class SummaryUiHandler extends UiHandler {
this.candyOverlay.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1])));
this.numberText.setText(padInt(this.pokemon.species.speciesId, 4));
- this.numberText.setColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
+ this.numberText.setColor(getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
this.numberText.setShadowColor(
- this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true),
+ getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true),
);
const spriteKey = this.pokemon.getSpriteKey(true);
try {
@@ -389,8 +405,8 @@ export class SummaryUiHandler extends UiHandler {
}
if (
- globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
- globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
+ globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()].classicWinCount > 0
+ && globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
this.championRibbon.setVisible(true);
} else {
@@ -430,20 +446,21 @@ export class SummaryUiHandler extends UiHandler {
this.friendshipShadow.setCrop(0, 0, 16, 16 - 16 * ((this.pokemon?.friendship || 0) / 255));
const doubleShiny = this.pokemon.isDoubleShiny(false);
- const baseVariant = this.pokemon.getBaseVariant(doubleShiny);
+ const bigIconVariant = doubleShiny ? this.pokemon.getBaseVariant(doubleShiny) : this.pokemon.getVariant();
this.shinyIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth + (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0) + 1,
3,
);
- this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`);
- this.shinyIcon.setVisible(this.pokemon.isShiny(false));
- this.shinyIcon.setTint(getVariantTint(baseVariant));
+ this.shinyIcon
+ .setTexture(`shiny_star${doubleShiny ? "_1" : ""}`)
+ .setVisible(this.pokemon.isShiny(false))
+ .setTint(getVariantTint(bigIconVariant));
if (this.shinyIcon.visible) {
let shinyDescriptor = "";
- if (doubleShiny || baseVariant) {
- shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
+ if (doubleShiny || bigIconVariant) {
+ shinyDescriptor = " (" + getShinyDescriptor(bigIconVariant);
if (doubleShiny) {
shinyDescriptor += "/" + getShinyDescriptor(this.pokemon.fusionVariant);
}
@@ -515,33 +532,31 @@ export class SummaryUiHandler extends UiHandler {
if (this.pokemon && this.moveCursor < this.pokemon.moveset.length) {
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
this.moveSelectFunction?.(this.moveCursor);
+ } else if (this.selectedMoveIndex === -1) {
+ this.selectedMoveIndex = this.moveCursor;
+ this.setCursor(this.moveCursor);
} else {
- if (this.selectedMoveIndex === -1) {
- this.selectedMoveIndex = this.moveCursor;
- this.setCursor(this.moveCursor);
- } else {
- if (this.selectedMoveIndex !== this.moveCursor) {
- const tempMove = this.pokemon?.moveset[this.selectedMoveIndex];
- this.pokemon.moveset[this.selectedMoveIndex] = this.pokemon.moveset[this.moveCursor];
- this.pokemon.moveset[this.moveCursor] = tempMove;
+ if (this.selectedMoveIndex !== this.moveCursor) {
+ const tempMove = this.pokemon?.moveset[this.selectedMoveIndex];
+ this.pokemon.moveset[this.selectedMoveIndex] = this.pokemon.moveset[this.moveCursor];
+ this.pokemon.moveset[this.moveCursor] = tempMove;
- const selectedMoveRow = this.moveRowsContainer.getAt(
- this.selectedMoveIndex,
- ) as Phaser.GameObjects.Container;
- const switchMoveRow = this.moveRowsContainer.getAt(this.moveCursor) as Phaser.GameObjects.Container;
+ const selectedMoveRow = this.moveRowsContainer.getAt(
+ this.selectedMoveIndex,
+ ) as Phaser.GameObjects.Container;
+ const switchMoveRow = this.moveRowsContainer.getAt(this.moveCursor) as Phaser.GameObjects.Container;
- this.moveRowsContainer.moveTo(selectedMoveRow, this.moveCursor);
- this.moveRowsContainer.moveTo(switchMoveRow, this.selectedMoveIndex);
+ this.moveRowsContainer.moveTo(selectedMoveRow, this.moveCursor);
+ this.moveRowsContainer.moveTo(switchMoveRow, this.selectedMoveIndex);
- selectedMoveRow.setY(this.moveCursor * 16);
- switchMoveRow.setY(this.selectedMoveIndex * 16);
- }
+ selectedMoveRow.setY(this.moveCursor * 16);
+ switchMoveRow.setY(this.selectedMoveIndex * 16);
+ }
- this.selectedMoveIndex = -1;
- if (this.selectedMoveCursorObj) {
- this.selectedMoveCursorObj.destroy();
- this.selectedMoveCursorObj = null;
- }
+ this.selectedMoveIndex = -1;
+ if (this.selectedMoveCursorObj) {
+ this.selectedMoveCursorObj.destroy();
+ this.selectedMoveCursorObj = null;
}
}
success = true;
@@ -575,78 +590,76 @@ export class SummaryUiHandler extends UiHandler {
break;
}
}
- } else {
- if (button === Button.ACTION) {
- if (this.cursor === Page.MOVES) {
- this.showMoveSelect();
- success = true;
- } else if (this.cursor === Page.PROFILE && this.pokemon?.hasPassive()) {
- // if we're on the PROFILE page and this pokemon has a passive unlocked..
- // Since abilities are displayed by default, all we need to do is toggle visibility on all elements to show passives
- this.abilityContainer.nameText?.setVisible(!this.abilityContainer.descriptionText?.visible);
- this.abilityContainer.descriptionText?.setVisible(!this.abilityContainer.descriptionText.visible);
- this.abilityContainer.labelImage.setVisible(!this.abilityContainer.labelImage.visible);
-
- this.passiveContainer.nameText?.setVisible(!this.passiveContainer.descriptionText?.visible);
- this.passiveContainer.descriptionText?.setVisible(!this.passiveContainer.descriptionText.visible);
- this.passiveContainer.labelImage.setVisible(!this.passiveContainer.labelImage.visible);
- } else if (this.cursor === Page.STATS) {
- //Show IVs
- this.permStatsContainer.setVisible(!this.permStatsContainer.visible);
- this.ivContainer.setVisible(!this.ivContainer.visible);
- }
- } else if (button === Button.CANCEL) {
- if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
- this.hideMoveSelect();
- } else {
- if (this.selectCallback instanceof Function) {
- const selectCallback = this.selectCallback;
- this.selectCallback = null;
- selectCallback();
- }
-
- if (!fromPartyMode) {
- ui.setMode(UiMode.MESSAGE);
- } else {
- ui.setMode(UiMode.PARTY);
- }
- }
+ } else if (button === Button.ACTION) {
+ if (this.cursor === Page.MOVES) {
+ this.showMoveSelect();
success = true;
+ } else if (this.cursor === Page.PROFILE && this.pokemon?.hasPassive()) {
+ // if we're on the PROFILE page and this pokemon has a passive unlocked..
+ // Since abilities are displayed by default, all we need to do is toggle visibility on all elements to show passives
+ this.abilityContainer.nameText?.setVisible(!this.abilityContainer.descriptionText?.visible);
+ this.abilityContainer.descriptionText?.setVisible(!this.abilityContainer.descriptionText.visible);
+ this.abilityContainer.labelImage.setVisible(!this.abilityContainer.labelImage.visible);
+
+ this.passiveContainer.nameText?.setVisible(!this.passiveContainer.descriptionText?.visible);
+ this.passiveContainer.descriptionText?.setVisible(!this.passiveContainer.descriptionText.visible);
+ this.passiveContainer.labelImage.setVisible(!this.passiveContainer.labelImage.visible);
+ } else if (this.cursor === Page.STATS) {
+ //Show IVs
+ this.permStatsContainer.setVisible(!this.permStatsContainer.visible);
+ this.ivContainer.setVisible(!this.ivContainer.visible);
+ }
+ } else if (button === Button.CANCEL) {
+ if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
+ this.hideMoveSelect();
} else {
- const pages = getEnumValues(Page);
- switch (button) {
- case Button.UP:
- case Button.DOWN: {
- if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
- break;
- }
- if (!fromPartyMode) {
- break;
- }
- const isDown = button === Button.DOWN;
- const party = globalScene.getPlayerParty();
- const partyMemberIndex = this.pokemon ? party.indexOf(this.pokemon) : -1;
- if ((isDown && partyMemberIndex < party.length - 1) || (!isDown && partyMemberIndex)) {
- const page = this.cursor;
- this.clear();
- this.show([party[partyMemberIndex + (isDown ? 1 : -1)], this.summaryUiMode, page]);
- }
+ if (this.selectCallback instanceof Function) {
+ const selectCallback = this.selectCallback;
+ this.selectCallback = null;
+ selectCallback();
+ }
+
+ if (!fromPartyMode) {
+ ui.setMode(UiMode.MESSAGE);
+ } else {
+ ui.setMode(UiMode.PARTY);
+ }
+ }
+ success = true;
+ } else {
+ const pages = getEnumValues(Page);
+ switch (button) {
+ case Button.UP:
+ case Button.DOWN: {
+ if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
break;
}
- case Button.LEFT:
- if (this.cursor) {
- success = this.setCursor(this.cursor - 1);
- }
- break;
- case Button.RIGHT:
- if (this.cursor < pages.length - 1) {
- success = this.setCursor(this.cursor + 1);
- if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.cursor === Page.MOVES) {
- this.moveSelect = true;
- }
- }
+ if (!fromPartyMode) {
break;
+ }
+ const isDown = button === Button.DOWN;
+ const party = globalScene.getPlayerParty();
+ const partyMemberIndex = this.pokemon ? party.indexOf(this.pokemon) : -1;
+ if ((isDown && partyMemberIndex < party.length - 1) || (!isDown && partyMemberIndex)) {
+ const page = this.cursor;
+ this.clear();
+ this.show([party[partyMemberIndex + (isDown ? 1 : -1)], this.summaryUiMode, page]);
+ }
+ break;
}
+ case Button.LEFT:
+ if (this.cursor) {
+ success = this.setCursor(this.cursor - 1);
+ }
+ break;
+ case Button.RIGHT:
+ if (this.cursor < pages.length - 1) {
+ success = this.setCursor(this.cursor + 1);
+ if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.cursor === Page.MOVES) {
+ this.moveSelect = true;
+ }
+ }
+ break;
}
}
@@ -737,7 +750,7 @@ export class SummaryUiHandler extends UiHandler {
const forward = this.cursor < cursor;
this.cursor = cursor;
- this.tabSprite.setTexture(`summary_tabs_${this.cursor + 1}`);
+ this.tabSprite.setTexture(getLocalizedSpriteKey(`summary_tabs_${this.cursor + 1}`)); // Pixel text 'STATUS' and "MOVES" tabs
this.getUi().hideTooltip();
@@ -811,6 +824,14 @@ export class SummaryUiHandler extends UiHandler {
? i18next.t("trainerNames:playerF")
: i18next.t("trainerNames:playerM");
+ const profileContainerProfilTitle = globalScene.add.image(
+ 7,
+ 4,
+ getLocalizedSpriteKey("summary_profile_profile_title"), // Pixel text 'PROFIL'
+ );
+ profileContainerProfilTitle.setOrigin(0, 0.5);
+ profileContainer.add(profileContainerProfilTitle);
+
// TODO: should add field for original trainer name to Pokemon object, to support gift/traded Pokemon from MEs
const trainerText = addBBCodeTextObject(
7,
@@ -875,8 +896,8 @@ export class SummaryUiHandler extends UiHandler {
}
if (
- globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) &&
- !isNullOrUndefined(this.pokemon)
+ globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id)
+ && !isNullOrUndefined(this.pokemon)
) {
const teraIcon = globalScene.add.sprite(123, 26, "button_tera");
teraIcon.setName("terastallize-icon");
@@ -885,7 +906,7 @@ export class SummaryUiHandler extends UiHandler {
}
this.abilityContainer = {
- labelImage: globalScene.add.image(0, 0, "summary_profile_ability"),
+ labelImage: globalScene.add.image(0, 0, getLocalizedSpriteKey("summary_profile_ability")), // Pixel text 'ABILITY'
ability: this.pokemon?.getAbility(true)!, // TODO: is this bang correct?
nameText: null,
descriptionText: null,
@@ -895,7 +916,7 @@ export class SummaryUiHandler extends UiHandler {
// Only add to the array and set up displaying a passive if it's unlocked
if (this.pokemon?.hasPassive()) {
this.passiveContainer = {
- labelImage: globalScene.add.image(0, 0, "summary_profile_passive"),
+ labelImage: globalScene.add.image(0, 0, getLocalizedSpriteKey("summary_profile_passive")), // Pixel text 'PASSIVE'
ability: this.pokemon.getPassiveAbility(),
nameText: null,
descriptionText: null,
@@ -915,9 +936,9 @@ export class SummaryUiHandler extends UiHandler {
}
allAbilityInfo.forEach(abilityInfo => {
- abilityInfo.labelImage.setPosition(17, 43);
+ abilityInfo.labelImage.setPosition(17, 47);
abilityInfo.labelImage.setVisible(true);
- abilityInfo.labelImage.setOrigin(0, 0);
+ abilityInfo.labelImage.setOrigin(0, 0.5);
profileContainer.add(abilityInfo.labelImage);
abilityInfo.nameText = addTextObject(7, 68, abilityInfo.ability?.name!, TextStyle.SUMMARY_ALT); // TODO: is this bang correct?
@@ -965,6 +986,14 @@ export class SummaryUiHandler extends UiHandler {
const rawNature = toCamelCase(Nature[this.pokemon?.getNature()!]); // TODO: is this bang correct?
const nature = `${getBBCodeFrag(toTitleCase(getNatureName(this.pokemon?.getNature()!)), TextStyle.SUMMARY_RED)}${closeFragment}`; // TODO: is this bang correct?
+ const profileContainerMemoTitle = globalScene.add.image(
+ 7,
+ 107,
+ getLocalizedSpriteKey("summary_profile_memo_title"), // Pixel text 'TRAINER MEMO'
+ );
+ profileContainerMemoTitle.setOrigin(0, 0.5);
+ profileContainer.add(profileContainerMemoTitle);
+
const memoString = i18next.t("pokemonSummary:memoString", {
metFragment: i18next.t(
`pokemonSummary:metFragment.${this.pokemon?.metBiome === -1 ? "apparently" : "normal"}`,
@@ -974,7 +1003,7 @@ export class SummaryUiHandler extends UiHandler {
wave: `${getBBCodeFrag(this.pokemon?.metWave ? this.pokemon.metWave.toString()! : i18next.t("pokemonSummary:unknownTrainer"), TextStyle.SUMMARY_RED)}${closeFragment}`,
},
),
- natureFragment: i18next.t(`pokemonSummary:natureFragment.${rawNature}`, { nature: nature }),
+ natureFragment: i18next.t(`pokemonSummary:natureFragment.${rawNature}`, { nature }),
});
const memoText = addBBCodeTextObject(7, 113, String(memoString), TextStyle.WINDOW_ALT);
@@ -991,6 +1020,30 @@ export class SummaryUiHandler extends UiHandler {
this.statsContainer.add(this.ivContainer);
this.statsContainer.setVisible(true);
+ this.statsContainerItemTitle = globalScene.add.image(7, 4, getLocalizedSpriteKey("summary_stats_item_title")); // Pixel text 'ITEM'
+ this.statsContainerItemTitle.setOrigin(0, 0.5);
+ this.statsContainer.add(this.statsContainerItemTitle);
+
+ this.statsContainerStatsTitle = globalScene.add.image(
+ 16,
+ 51,
+ getLocalizedSpriteKey("summary_stats_stats_title"), // Pixel text 'STATS'
+ );
+ this.statsContainerStatsTitle.setOrigin(0, 0.5);
+ this.statsContainer.add(this.statsContainerStatsTitle);
+
+ this.statsContainerExpTitle = globalScene.add.image(7, 107, getLocalizedSpriteKey("summary_stats_exp_title")); // Pixel text 'EXP.'
+ this.statsContainerExpTitle.setOrigin(0, 0.5);
+ this.statsContainer.add(this.statsContainerExpTitle);
+
+ this.statsContainerExpBarTitle = globalScene.add.image(
+ 126,
+ 144,
+ getLocalizedSpriteKey("summary_stats_expbar_title"), // Pixel mini text 'EXP'
+ );
+ this.statsContainerExpBarTitle.setOrigin(0, 0);
+ this.statsContainer.add(this.statsContainerExpBarTitle);
+
PERMANENT_STATS.forEach((stat, s) => {
const statName = i18next.t(getStatKey(stat));
const rowIndex = s % 3;
@@ -1106,6 +1159,22 @@ export class SummaryUiHandler extends UiHandler {
this.movesContainer = globalScene.add.container(5, -pageBg.height + 26);
pageContainer.add(this.movesContainer);
+ this.movesContainerMovesTitle = globalScene.add.image(
+ 2,
+ -22,
+ getLocalizedSpriteKey("summary_moves_moves_title"),
+ ); // Pixel text 'MOVES'
+ this.movesContainerMovesTitle.setOrigin(0, 0.5);
+ this.movesContainer.add(this.movesContainerMovesTitle);
+
+ this.movesContainerDescriptionsTitle = globalScene.add.image(
+ 2,
+ 78,
+ getLocalizedSpriteKey("summary_moves_descriptions_title"),
+ ); // Pixel text 'DESCRIPTIONS'
+ this.movesContainerDescriptionsTitle.setOrigin(0, 0.5);
+ this.movesContainer.add(this.movesContainerDescriptionsTitle);
+
this.extraMoveRowContainer = globalScene.add.container(0, 64);
this.extraMoveRowContainer.setVisible(false);
this.movesContainer.add(this.extraMoveRowContainer);
@@ -1135,8 +1204,8 @@ export class SummaryUiHandler extends UiHandler {
newMoveTypeIcon.setOrigin(0, 1);
this.extraMoveRowContainer.add(newMoveTypeIcon);
}
- const ppOverlay = globalScene.add.image(163, -1, "summary_moves_overlay_pp");
- ppOverlay.setOrigin(0, 1);
+ const ppOverlay = globalScene.add.image(172, -5, getLocalizedSpriteKey("summary_moves_overlay_pp")); // Pixel text 'PP'
+ ppOverlay.setOrigin(1, 0.5);
this.extraMoveRowContainer.add(ppOverlay);
const pp = padInt(this.newMove?.pp!, 2, " "); // TODO: is this bang correct?
@@ -1166,8 +1235,8 @@ export class SummaryUiHandler extends UiHandler {
moveText.setOrigin(0, 1);
moveRowContainer.add(moveText);
- const ppOverlay = globalScene.add.image(163, -1, "summary_moves_overlay_pp");
- ppOverlay.setOrigin(0, 1);
+ const ppOverlay = globalScene.add.image(172, -5, getLocalizedSpriteKey("summary_moves_overlay_pp")); // Pixel text 'PP'
+ ppOverlay.setOrigin(1, 0.5);
moveRowContainer.add(ppOverlay);
const ppText = addTextObject(173, 1, "--/--", TextStyle.WINDOW);
diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/handlers/target-select-ui-handler.ts
similarity index 90%
rename from src/ui/target-select-ui-handler.ts
rename to src/ui/handlers/target-select-ui-handler.ts
index 5ea89929cda..777a6734383 100644
--- a/src/ui/target-select-ui-handler.ts
+++ b/src/ui/handlers/target-select-ui-handler.ts
@@ -7,7 +7,7 @@ import { UiMode } from "#enums/ui-mode";
import type { Pokemon } from "#field/pokemon";
import type { ModifierBar } from "#modifiers/modifier";
import { getMoveTargets } from "#moves/move-utils";
-import { UiHandler } from "#ui/ui-handler";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { fixedInt, isNullOrUndefined } from "#utils/common";
export type TargetSelectCallback = (targets: BattlerIndex[]) => void;
@@ -50,7 +50,7 @@ export class TargetSelectUiHandler extends UiHandler {
this.targets = moveTargets.targets;
this.isMultipleTargets = moveTargets.multiple ?? false;
- if (!this.targets.length) {
+ if (this.targets.length === 0) {
return false;
}
@@ -70,11 +70,12 @@ export class TargetSelectUiHandler extends UiHandler {
* @param user the Pokemon using the move
*/
resetCursor(cursorN: number, user: Pokemon): void {
- if (!isNullOrUndefined(cursorN)) {
- if ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.tempSummonData.waveTurnCount === 1) {
- // Reset cursor on the first turn of a fight or if an ally was targeted last turn
- cursorN = -1;
- }
+ if (
+ !isNullOrUndefined(cursorN)
+ && ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.tempSummonData.waveTurnCount === 1)
+ ) {
+ // Reset cursor on the first turn of a fight or if an ally was targeted last turn
+ cursorN = -1;
}
this.setCursor(this.targets.includes(cursorN) ? cursorN : this.targets[0]);
}
@@ -92,10 +93,11 @@ export class TargetSelectUiHandler extends UiHandler {
if (isNullOrUndefined(this.cursor0) || this.cursor0 !== this.cursor) {
this.cursor0 = this.cursor;
}
- } else if (this.fieldIndex === BattlerIndex.PLAYER_2) {
- if (isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor) {
- this.cursor1 = this.cursor;
- }
+ } else if (
+ this.fieldIndex === BattlerIndex.PLAYER_2
+ && (isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor)
+ ) {
+ this.cursor1 = this.cursor;
}
} else if (this.isMultipleTargets) {
success = false;
@@ -163,7 +165,7 @@ export class TargetSelectUiHandler extends UiHandler {
},
});
- if (this.targetBattleInfoMoveTween.length >= 1) {
+ if (this.targetBattleInfoMoveTween.length > 0) {
this.targetBattleInfoMoveTween.filter(t => t !== undefined).forEach(tween => tween.stop());
for (const pokemon of multipleTargets) {
pokemon.getBattleInfo().resetY();
@@ -198,7 +200,7 @@ export class TargetSelectUiHandler extends UiHandler {
this.highlightItems(pokemon.id, 1);
}
- if (this.targetBattleInfoMoveTween.length >= 1) {
+ if (this.targetBattleInfoMoveTween.length > 0) {
this.targetBattleInfoMoveTween.filter(t => t !== undefined).forEach(tween => tween.stop());
this.targetBattleInfoMoveTween = [];
}
diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/handlers/test-dialogue-ui-handler.ts
similarity index 91%
rename from src/ui/test-dialogue-ui-handler.ts
rename to src/ui/handlers/test-dialogue-ui-handler.ts
index 6f7c79a151b..d72de64ef70 100644
--- a/src/ui/test-dialogue-ui-handler.ts
+++ b/src/ui/handlers/test-dialogue-ui-handler.ts
@@ -1,9 +1,9 @@
import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
-import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
-import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
-import type { ModalConfig } from "#ui/modal-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { InputFieldConfig } from "#ui/handlers/form-modal-ui-handler";
+import { FormModalUiHandler } from "#ui/handlers/form-modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
import { isNullOrUndefined } from "#utils/common";
import i18next from "i18next";
@@ -83,8 +83,8 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
input.on("keydown", (inputObject, evt: KeyboardEvent) => {
if (
- ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) &&
- ui.getMode() === UiMode.AUTO_COMPLETE
+ ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase())
+ && ui.getMode() === UiMode.AUTO_COMPLETE
) {
// Delete autocomplete list and recovery focus.
inputObject.on("blur", () => inputObject.node.focus(), { once: true });
@@ -100,9 +100,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
let options: OptionSelectItem[] = [];
const splitArr = inputObject.text.split(" ");
- const filteredKeys = this.keys.filter(command =>
- command.toLowerCase().includes(splitArr[splitArr.length - 1].toLowerCase()),
- );
+ const filteredKeys = this.keys.filter(command => command.toLowerCase().includes(splitArr.at(-1)!.toLowerCase()));
if (inputObject.text !== "" && filteredKeys.length > 0) {
// if performance is required, you could reduce the number of total results by changing the slice below to not have all ~8000 inputs going
options = filteredKeys.slice(0).map(value => {
@@ -125,7 +123,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
if (options.length > 0) {
const modalOpts = {
- options: options,
+ options,
maxOptions: 5,
modalContainer: this.modalContainer,
};
diff --git a/src/ui/title-ui-handler.ts b/src/ui/handlers/title-ui-handler.ts
similarity index 94%
rename from src/ui/title-ui-handler.ts
rename to src/ui/handlers/title-ui-handler.ts
index 5ae195231e5..28165a33db7 100644
--- a/src/ui/title-ui-handler.ts
+++ b/src/ui/handlers/title-ui-handler.ts
@@ -109,10 +109,10 @@ export class TitleUiHandler extends OptionSelectUiHandler {
const rand = randInt(1025, 1);
const pokemon = getPokemonSpecies(rand as SpeciesId);
if (
- this.splashMessage === "splashMessages:underratedPokemon" ||
- this.splashMessage === "splashMessages:dontTalkAboutThePokemonIncident" ||
- this.splashMessage === "splashMessages:aWildPokemonAppeared" ||
- this.splashMessage === "splashMessages:aprilFools.removedPokemon"
+ this.splashMessage === "splashMessages:underratedPokemon"
+ || this.splashMessage === "splashMessages:dontTalkAboutThePokemonIncident"
+ || this.splashMessage === "splashMessages:aWildPokemonAppeared"
+ || this.splashMessage === "splashMessages:aprilFools.removedPokemon"
) {
this.splashMessageText.setText(i18next.t(this.splashMessage, { pokemonName: pokemon.name }));
}
@@ -141,7 +141,8 @@ export class TitleUiHandler extends OptionSelectUiHandler {
}),
);
- this.appVersionText.setText("v" + version);
+ const betaText = import.meta.env.DEV ? " (Beta)" : "";
+ this.appVersionText.setText("v" + version + betaText);
const ui = this.getUi();
diff --git a/src/ui/ui-handler.ts b/src/ui/handlers/ui-handler.ts
similarity index 86%
rename from src/ui/ui-handler.ts
rename to src/ui/handlers/ui-handler.ts
index 7dde6b22dcd..558cb08f252 100644
--- a/src/ui/ui-handler.ts
+++ b/src/ui/handlers/ui-handler.ts
@@ -1,8 +1,6 @@
import { globalScene } from "#app/global-scene";
import type { Button } from "#enums/buttons";
-import type { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
-import { getTextColor } from "#ui/text";
/**
* A basic abstract class to act as a holder and processor for UI elements.
@@ -33,10 +31,6 @@ export abstract class UiHandler {
return globalScene.ui;
}
- getTextColor(style: TextStyle, shadow = false): string {
- return getTextColor(style, shadow, globalScene.uiTheme);
- }
-
getCursor(): number {
return this.cursor;
}
diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/handlers/unavailable-modal-ui-handler.ts
similarity index 85%
rename from src/ui/unavailable-modal-ui-handler.ts
rename to src/ui/handlers/unavailable-modal-ui-handler.ts
index 5c3dc513473..5b885bfff77 100644
--- a/src/ui/unavailable-modal-ui-handler.ts
+++ b/src/ui/handlers/unavailable-modal-ui-handler.ts
@@ -2,15 +2,14 @@ import { updateUserInfo } from "#app/account";
import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
-import type { ModalConfig } from "#ui/modal-ui-handler";
-import { ModalUiHandler } from "#ui/modal-ui-handler";
+import type { ModalConfig } from "#ui/handlers/modal-ui-handler";
+import { ModalUiHandler } from "#ui/handlers/modal-ui-handler";
import { addTextObject } from "#ui/text";
import { sessionIdKey } from "#utils/common";
import { removeCookie } from "#utils/cookies";
import i18next from "i18next";
export class UnavailableModalUiHandler extends ModalUiHandler {
- private reconnectTimer: NodeJS.Timeout | null;
private reconnectDuration: number;
private reconnectCallback: () => void;
@@ -62,7 +61,6 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
tryReconnect(): void {
updateUserInfo().then(response => {
if (response[0] || [200, 400].includes(response[1])) {
- this.reconnectTimer = null;
this.reconnectDuration = this.minTime;
globalScene.playSound("se/pb_bounce_1");
this.reconnectCallback();
@@ -71,7 +69,7 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
globalScene.reset(true, true);
} else {
this.reconnectDuration = Math.min(this.reconnectDuration * 2, this.maxTime); // Set a max delay so it isn't infinite
- this.reconnectTimer = setTimeout(
+ setTimeout(
() => this.tryReconnect(),
// Adds a random factor to avoid pendulum effect during long total breakdown
this.reconnectDuration + Math.random() * this.randVarianceTime,
@@ -81,14 +79,14 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
}
show(args: any[]): boolean {
- if (args.length >= 1 && args[0] instanceof Function) {
+ if (args.length > 0 && args[0] instanceof Function) {
const config: ModalConfig = {
buttonActions: [],
};
this.reconnectCallback = args[0];
this.reconnectDuration = this.minTime;
- this.reconnectTimer = setTimeout(() => this.tryReconnect(), this.reconnectDuration);
+ setTimeout(() => this.tryReconnect(), this.reconnectDuration);
return super.show([config]);
}
diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts
index b40676fc97c..2d9f3e6a6bd 100644
--- a/src/ui/settings/abstract-control-settings-ui-handler.ts
+++ b/src/ui/settings/abstract-control-settings-ui-handler.ts
@@ -5,10 +5,10 @@ import type { Device } from "#enums/devices";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
import { getIconWithSettingName } from "#inputs/config-handler";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { UiHandler } from "#ui/handlers/ui-handler";
import { NavigationManager, NavigationMenu } from "#ui/navigation-menu";
-import { ScrollBar } from "#ui/scroll-bar";
-import { addTextObject } from "#ui/text";
-import { UiHandler } from "#ui/ui-handler";
+import { addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next";
@@ -544,8 +544,13 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler {
}
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
success = false;
- } else if (this.optionCursors[cursor]) {
- success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
+ } else {
+ // Cycle to the rightmost position when at the leftmost, otherwise move left
+ success = this.setOptionCursor(
+ cursor,
+ Phaser.Math.Wrap(this.optionCursors[cursor] - 1, 0, this.optionValueLabels[cursor].length),
+ true,
+ );
}
break;
case Button.RIGHT: // Move selection right within the current option set.
@@ -554,8 +559,13 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler {
}
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
success = false;
- } else if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
- success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
+ } else {
+ // Cycle to the leftmost position when at the rightmost, otherwise move right
+ success = this.setOptionCursor(
+ cursor,
+ Phaser.Math.Wrap(this.optionCursors[cursor] + 1, 0, this.optionValueLabels[cursor].length),
+ true,
+ );
}
break;
case Button.CYCLE_FORM:
@@ -653,16 +663,16 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler {
if (!this.bindingSettings.includes(setting) && !setting.includes("BUTTON_")) {
// Get the label of the last selected option and revert its color to the default.
const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor];
- lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW));
- lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
+ lastValueLabel.setColor(getTextColor(TextStyle.WINDOW));
+ lastValueLabel.setShadowColor(getTextColor(TextStyle.WINDOW, true));
// Update the cursor for the setting to the new position.
this.optionCursors[settingIndex] = cursor;
// Change the color of the new selected option to indicate it's selected.
const newValueLabel = this.optionValueLabels[settingIndex][cursor];
- newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
- newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
+ newValueLabel.setColor(getTextColor(TextStyle.SETTINGS_SELECTED));
+ newValueLabel.setShadowColor(getTextColor(TextStyle.SETTINGS_SELECTED, true));
}
// If the save flag is set, save the setting to local storage
diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts
index 91d5aec984a..876c18eb697 100644
--- a/src/ui/settings/abstract-settings-ui-handler.ts
+++ b/src/ui/settings/abstract-settings-ui-handler.ts
@@ -4,11 +4,12 @@ import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
import type { SettingType } from "#system/settings";
import { Setting, SettingKeys } from "#system/settings";
+import type { AnyFn } from "#types/type-helpers";
import type { InputsIcons } from "#ui/abstract-control-settings-ui-handler";
-import { MessageUiHandler } from "#ui/message-ui-handler";
+import { ScrollBar } from "#ui/containers/scroll-bar";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
import { NavigationManager, NavigationMenu } from "#ui/navigation-menu";
-import { ScrollBar } from "#ui/scroll-bar";
-import { addTextObject } from "#ui/text";
+import { addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import i18next from "i18next";
@@ -34,8 +35,6 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
protected navigationIcons: InputsIcons;
private cursorObj: Phaser.GameObjects.NineSlice | null;
-
- private reloadSettings: Array;
private reloadRequired: boolean;
protected rowsToDisplay: number;
@@ -55,60 +54,51 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
*/
setup() {
const ui = this.getUi();
+ const canvasWidth = globalScene.scaledCanvas.width;
+ const canvasHeight = globalScene.scaledCanvas.height;
- this.settingsContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1);
- this.settingsContainer.setName(`settings-${this.title}`);
- this.settingsContainer.setInteractive(
- new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height - 20),
- Phaser.Geom.Rectangle.Contains,
- );
+ this.settingsContainer = globalScene.add
+ .container(1, -canvasHeight + 1)
+ .setName(`settings-${this.title}`)
+ .setInteractive(new Phaser.Geom.Rectangle(0, 0, canvasWidth, canvasHeight - 20), Phaser.Geom.Rectangle.Contains);
this.navigationIcons = {};
this.navigationContainer = new NavigationMenu(0, 0);
+ const navWidth = this.navigationContainer.width;
+ const navHeight = this.navigationContainer.height;
- this.optionsBg = addWindow(
- 0,
- this.navigationContainer.height,
- globalScene.scaledCanvas.width - 2,
- globalScene.scaledCanvas.height - 16 - this.navigationContainer.height - 2,
- );
- this.optionsBg.setName("window-options-bg");
- this.optionsBg.setOrigin(0, 0);
+ this.optionsBg = addWindow(0, navHeight, canvasWidth - 2, canvasHeight - 16 - navHeight - 2)
+ .setName("window-options-bg")
+ .setOrigin(0);
- const actionsBg = addWindow(
- 0,
- globalScene.scaledCanvas.height - this.navigationContainer.height,
- globalScene.scaledCanvas.width - 2,
- 22,
- );
- actionsBg.setOrigin(0, 0);
+ const actionsBg = addWindow(0, canvasHeight - navHeight, canvasWidth - 2, 22) // formatting
+ .setOrigin(0);
- const iconAction = globalScene.add.sprite(0, 0, "keyboard");
- iconAction.setOrigin(0, -0.1);
- iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4);
+ const iconAction = globalScene.add
+ .sprite(0, 0, "keyboard")
+ .setOrigin(0, -0.1)
+ .setPositionRelative(actionsBg, navWidth - 32, 4);
this.navigationIcons["BUTTON_ACTION"] = iconAction;
- const actionText = addTextObject(0, 0, i18next.t("settings:action"), TextStyle.SETTINGS_LABEL);
- actionText.setOrigin(0, 0.15);
+ const actionText = addTextObject(0, 0, i18next.t("settings:action"), TextStyle.SETTINGS_LABEL).setOrigin(0, 0.15);
actionText.setPositionRelative(iconAction, -actionText.width / 6 - 2, 0);
- const iconCancel = globalScene.add.sprite(0, 0, "keyboard");
- iconCancel.setOrigin(0, -0.1);
- iconCancel.setPositionRelative(actionsBg, actionText.x - 28, 4);
+ const iconCancel = globalScene.add
+ .sprite(0, 0, "keyboard")
+ .setOrigin(0, -0.1)
+ .setPositionRelative(actionsBg, actionText.x - 28, 4);
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
- const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL);
- cancelText.setOrigin(0, 0.15);
+ const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL) // formatting
+ .setOrigin(0, 0.15);
cancelText.setPositionRelative(iconCancel, -cancelText.width / 6 - 2, 0);
- this.optionsContainer = globalScene.add.container(0, 0);
+ this.optionsContainer = globalScene.add.container();
this.settingLabels = [];
this.optionValueLabels = [];
- this.reloadSettings = this.settings.filter(s => s?.requireReload);
-
let anyReloadRequired = false;
this.settings.forEach((setting, s) => {
let settingName = setting.label;
@@ -117,8 +107,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
anyReloadRequired = true;
}
- this.settingLabels[s] = addTextObject(8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL);
- this.settingLabels[s].setOrigin(0, 0);
+ this.settingLabels[s] = addTextObject(8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL).setOrigin(0);
this.optionsContainer.add(this.settingLabels[s]);
this.optionValueLabels.push(
@@ -129,7 +118,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
option.label,
setting.default === o ? TextStyle.SETTINGS_SELECTED : TextStyle.SETTINGS_VALUE,
);
- valueLabel.setOrigin(0, 0);
+ valueLabel.setOrigin(0);
this.optionsContainer.add(valueLabel);
@@ -164,32 +153,33 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
this.scrollBar.setTotalRows(this.settings.length);
// Two-lines message box
- this.messageBoxContainer = globalScene.add.container(0, globalScene.scaledCanvas.height);
- this.messageBoxContainer.setName("settings-message-box");
- this.messageBoxContainer.setVisible(false);
+ this.messageBoxContainer = globalScene.add
+ .container(0, globalScene.scaledCanvas.height)
+ .setName("settings-message-box")
+ .setVisible(false);
const settingsMessageBox = addWindow(0, -1, globalScene.scaledCanvas.width - 2, 48);
settingsMessageBox.setOrigin(0, 1);
this.messageBoxContainer.add(settingsMessageBox);
- const messageText = addTextObject(8, -40, "", TextStyle.WINDOW, {
- maxLines: 2,
- });
- messageText.setWordWrapWidth(globalScene.game.canvas.width - 60);
- messageText.setName("settings-message");
- messageText.setOrigin(0, 0);
+ const messageText = addTextObject(8, -40, "", TextStyle.WINDOW, { maxLines: 2 })
+ .setWordWrapWidth(globalScene.game.canvas.width - 60)
+ .setName("settings-message")
+ .setOrigin(0);
this.messageBoxContainer.add(messageText);
this.message = messageText;
- this.settingsContainer.add(this.optionsBg);
- this.settingsContainer.add(this.scrollBar);
- this.settingsContainer.add(this.navigationContainer);
- this.settingsContainer.add(actionsBg);
- this.settingsContainer.add(this.optionsContainer);
- this.settingsContainer.add(iconAction);
- this.settingsContainer.add(iconCancel);
- this.settingsContainer.add(actionText);
+ this.settingsContainer.add([
+ this.optionsBg,
+ this.scrollBar,
+ this.navigationContainer,
+ actionsBg,
+ this.optionsContainer,
+ iconAction,
+ iconCancel,
+ actionText,
+ ]);
// Only add the ReloadRequired text on pages that have settings that require a reload.
if (anyReloadRequired) {
const reloadRequired = addTextObject(0, 0, `*${i18next.t("settings:requireReload")}`, TextStyle.SETTINGS_LABEL)
@@ -198,8 +188,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
.setY(actionText.y);
this.settingsContainer.add(reloadRequired);
}
- this.settingsContainer.add(cancelText);
- this.settingsContainer.add(this.messageBoxContainer);
+ this.settingsContainer.add([cancelText, this.messageBoxContainer]);
ui.add(this.settingsContainer);
@@ -214,17 +203,13 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
updateBindings(): void {
for (const settingName of Object.keys(this.navigationIcons)) {
if (settingName === "BUTTON_HOME") {
- this.navigationIcons[settingName].setTexture("keyboard");
- this.navigationIcons[settingName].setFrame("HOME.png");
- this.navigationIcons[settingName].alpha = 1;
+ this.navigationIcons[settingName].setTexture("keyboard").setFrame("HOME.png").alpha = 1;
continue;
}
const icon = globalScene.inputController?.getIconForLatestInputRecorded(settingName);
if (icon) {
const type = globalScene.inputController?.getLastSourceType();
- this.navigationIcons[settingName].setTexture(type);
- this.navigationIcons[settingName].setFrame(icon);
- this.navigationIcons[settingName].alpha = 1;
+ this.navigationIcons[settingName].setTexture(type).setFrame(icon).alpha = 1;
} else {
this.navigationIcons[settingName].alpha = 0;
}
@@ -246,21 +231,43 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
? JSON.parse(localStorage.getItem(this.localStorageKey)!)
: {}; // TODO: is this bang correct?
- this.settings.forEach((setting, s) =>
- this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default),
- );
+ this.settings.forEach((setting, s) => {
+ this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default);
+ });
this.settingsContainer.setVisible(true);
this.setCursor(0);
this.setScrollCursor(0);
- this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
+ const ui = this.getUi();
- this.getUi().hideTooltip();
+ ui.moveTo(this.settingsContainer, ui.length - 1);
+
+ ui.hideTooltip();
return true;
}
+ /**
+ * Submethod of {@linkcode processInput} to handle left/right input for changing option values
+ *
+ * @remarks
+ * If the cursor is positioned on a boundary option, will apply clamping / wrapping as appropriate
+ * @param cursor - Current cursor position in the settings menu
+ * @param dir - Direction to pan when scrolling, -1 for left, 1 for right
+ * @returns `true` if the action associated with the button was successfully processed, `false` otherwise.
+ */
+ private processLeftRightInput(cursor: number, dir: -1 | 1): boolean {
+ let boundaryAction = Phaser.Math.Wrap;
+ let upperBound = this.optionValueLabels[cursor].length;
+ if (this.settings[cursor]?.clamp) {
+ boundaryAction = Phaser.Math.Clamp;
+ // clamping is right inclusive; wrapping isn't
+ upperBound -= 1;
+ }
+ return this.setOptionCursor(cursor, boundaryAction(this.optionCursors[cursor] + dir, 0, upperBound), true);
+ }
+
/**
* Processes input from a specified button.
* This method handles navigation through a UI menu, including movement through menu items
@@ -318,16 +325,10 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
}
break;
case Button.LEFT:
- if (this.optionCursors[cursor]) {
- // Moves the option cursor left, if possible.
- success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
- }
+ success = this.processLeftRightInput(cursor, -1);
break;
case Button.RIGHT:
- // Moves the option cursor right, if possible.
- if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
- success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
- }
+ success = this.processLeftRightInput(cursor, 1);
break;
case Button.CYCLE_FORM:
case Button.CYCLE_SHINY:
@@ -376,8 +377,9 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
if (!this.cursorObj) {
const cursorWidth = globalScene.scaledCanvas.width - (this.scrollBar.visible ? 16 : 10);
- this.cursorObj = globalScene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1);
- this.cursorObj.setOrigin(0, 0);
+ this.cursorObj = globalScene.add
+ .nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1)
+ .setOrigin(0);
this.optionsContainer.add(this.cursorObj);
}
@@ -399,18 +401,21 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
settingIndex = this.cursor + this.scrollCursor;
}
const setting = this.settings[settingIndex];
-
const lastCursor = this.optionCursors[settingIndex];
+ // do nothing if the option isn't changing
+ if (cursor === lastCursor) {
+ return false;
+ }
- const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor];
- lastValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_VALUE));
- lastValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_VALUE, true));
+ this.optionValueLabels[settingIndex][lastCursor]
+ .setColor(getTextColor(TextStyle.SETTINGS_VALUE))
+ .setShadowColor(getTextColor(TextStyle.SETTINGS_VALUE, true));
this.optionCursors[settingIndex] = cursor;
- const newValueLabel = this.optionValueLabels[settingIndex][cursor];
- newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
- newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
+ this.optionValueLabels[settingIndex][cursor]
+ .setColor(getTextColor(TextStyle.SETTINGS_SELECTED))
+ .setShadowColor(getTextColor(TextStyle.SETTINGS_SELECTED, true));
if (save) {
const saveSetting = () => {
@@ -511,12 +516,12 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
override showText(
text: string,
delay?: number,
- callback?: Function,
+ callback?: AnyFn,
callbackDelay?: number,
prompt?: boolean,
promptDelay?: number,
) {
- this.messageBoxContainer.setVisible(!!text?.length);
+ this.messageBoxContainer.setVisible(text?.length > 0);
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
}
}
diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts
index 53d606b6f84..4fdc4abdbfe 100644
--- a/src/ui/settings/gamepad-binding-ui-handler.ts
+++ b/src/ui/settings/gamepad-binding-ui-handler.ts
@@ -3,7 +3,7 @@ import { Device } from "#enums/devices";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
import { getIconWithSettingName, getKeyWithKeycode } from "#inputs/config-handler";
-import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler";
+import { AbstractBindingUiHandler } from "#ui/handlers/abstract-binding-ui-handler";
import { addTextObject } from "#ui/text";
import i18next from "i18next";
@@ -53,10 +53,10 @@ export class GamepadBindingUiHandler extends AbstractBindingUiHandler {
const blacklist = [12, 13, 14, 15]; // d-pad buttons are blacklisted.
// Check conditions before processing the button press.
if (
- !this.listening ||
- pad.id.toLowerCase() !== this.getSelectedDevice() ||
- blacklist.includes(button.index) ||
- this.buttonPressed !== null
+ !this.listening
+ || pad.id.toLowerCase() !== this.getSelectedDevice()
+ || blacklist.includes(button.index)
+ || this.buttonPressed !== null
) {
return;
}
diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts
index b339ac16188..b1fd153461f 100644
--- a/src/ui/settings/keyboard-binding-ui-handler.ts
+++ b/src/ui/settings/keyboard-binding-ui-handler.ts
@@ -3,7 +3,7 @@ import { Device } from "#enums/devices";
import { TextStyle } from "#enums/text-style";
import type { UiMode } from "#enums/ui-mode";
import { getKeyWithKeycode } from "#inputs/config-handler";
-import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler";
+import { AbstractBindingUiHandler } from "#ui/handlers/abstract-binding-ui-handler";
import { addTextObject } from "#ui/text";
import i18next from "i18next";
diff --git a/src/ui/settings/move-touch-controls-handler.ts b/src/ui/settings/move-touch-controls-handler.ts
index 60572529c89..dd7bceec55e 100644
--- a/src/ui/settings/move-touch-controls-handler.ts
+++ b/src/ui/settings/move-touch-controls-handler.ts
@@ -9,9 +9,9 @@ export const TOUCH_CONTROL_POSITIONS_PORTRAIT = "touchControlPositionsPortrait";
type ControlPosition = { id: string; x: number; y: number };
type ConfigurationEventListeners = {
- touchstart: EventListener[];
- touchmove: EventListener[];
- touchend: EventListener[];
+ pointerdown: EventListener[];
+ pointermove: EventListener[];
+ pointerup: EventListener[];
};
type ToolbarRefs = {
@@ -39,9 +39,9 @@ export class MoveTouchControlsHandler {
* These are used to remove the event listeners when the configuration mode is disabled.
*/
private configurationEventListeners: ConfigurationEventListeners = {
- touchstart: [],
- touchmove: [],
- touchend: [],
+ pointerdown: [],
+ pointermove: [],
+ pointerup: [],
};
private overlay: Phaser.GameObjects.Container;
@@ -165,40 +165,39 @@ export class MoveTouchControlsHandler {
/**
* Start dragging the given button.
* @param controlGroup The button that is being dragged.
- * @param touch The touch event that started the drag.
+ * @param event The pointer event that started the drag.
*/
private startDrag = (controlGroup: HTMLElement): void => {
this.draggingElement = controlGroup;
};
/**
- * Drags the currently dragged element to the given touch position.
- * @param touch The touch event that is currently happening.
- * @param isLeft Whether the dragged element is a left button.
+ * Drags the currently dragged element to the given pointer position.
+ * @param event The pointer event that is currently happening.
*/
- private drag = (touch: Touch): void => {
+ private drag = (event: PointerEvent): void => {
if (!this.draggingElement) {
return;
}
const rect = this.draggingElement.getBoundingClientRect();
- // Map the touch position to the center of the dragged element.
+ // Map the pointer position to the center of the dragged element.
const xOffset = this.isLeft(this.draggingElement)
- ? touch.clientX - rect.width / 2
- : window.innerWidth - touch.clientX - rect.width / 2;
- const yOffset = window.innerHeight - touch.clientY - rect.height / 2;
+ ? event.clientX - rect.width / 2
+ : window.innerWidth - event.clientX - rect.width / 2;
+ const yOffset = window.innerHeight - event.clientY - rect.height / 2;
this.setPosition(this.draggingElement, xOffset, yOffset);
};
/**
* Stops dragging the currently dragged element.
*/
- private stopDrag = () => {
+ private stopDrag = (): void => {
this.draggingElement = null;
};
/**
* Returns the current positions of all touch controls that have moved from their default positions of this orientation.
- * @returns {ControlPosition[]} The current positions of all touch controls that have moved from their default positions of this orientation
+ * @returns The current positions of all touch controls that have moved from their default positions of this orientation
*/
private getModifiedCurrentPositions(): ControlPosition[] {
return this.getControlGroupElements()
@@ -303,19 +302,19 @@ export class MoveTouchControlsHandler {
*/
private createConfigurationEventListeners(controlGroups: HTMLDivElement[]): ConfigurationEventListeners {
return {
- touchstart: controlGroups.map((element: HTMLDivElement) => {
+ pointerdown: controlGroups.map((element: HTMLDivElement) => {
const startDrag = () => this.startDrag(element);
- element.addEventListener("touchstart", startDrag, { passive: true });
+ element.addEventListener("pointerdown", startDrag, { passive: true });
return startDrag;
}),
- touchmove: controlGroups.map(() => {
- const drag = event => this.drag(event.touches[0]);
- window.addEventListener("touchmove", drag, { passive: true });
+ pointermove: controlGroups.map(() => {
+ const drag = (event: PointerEvent) => this.drag(event);
+ window.addEventListener("pointermove", drag, { passive: true });
return drag;
}),
- touchend: controlGroups.map(() => {
+ pointerup: controlGroups.map(() => {
const stopDrag = () => this.stopDrag();
- window.addEventListener("touchend", stopDrag, { passive: true });
+ window.addEventListener("pointerup", stopDrag, { passive: true });
return stopDrag;
}),
};
@@ -373,12 +372,12 @@ export class MoveTouchControlsHandler {
this.draggingElement = null;
// Remove event listeners
- const { touchstart, touchmove, touchend } = this.configurationEventListeners;
+ const { pointerdown, pointermove, pointerup } = this.configurationEventListeners;
this.getControlGroupElements().forEach((element, index) =>
- element.removeEventListener("touchstart", touchstart[index]),
+ element.removeEventListener("pointerdown", pointerdown[index]),
);
- touchmove.forEach(listener => window.removeEventListener("touchmove", listener));
- touchend.forEach(listener => window.removeEventListener("touchend", listener));
+ pointermove.forEach(listener => window.removeEventListener("pointermove", listener));
+ pointerup.forEach(listener => window.removeEventListener("pointerup", listener));
// Remove configuration toolbar
const toolbar = document.querySelector("#touchControls #configToolbar");
diff --git a/src/ui/settings/option-select-ui-handler.ts b/src/ui/settings/option-select-ui-handler.ts
index c989c768244..235f16e7f09 100644
--- a/src/ui/settings/option-select-ui-handler.ts
+++ b/src/ui/settings/option-select-ui-handler.ts
@@ -1,5 +1,5 @@
import { UiMode } from "#enums/ui-mode";
-import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
+import { AbstractOptionSelectUiHandler } from "#ui/handlers/abstract-option-select-ui-handler";
export class OptionSelectUiHandler extends AbstractOptionSelectUiHandler {
constructor(mode: UiMode = UiMode.OPTION_SELECT) {
diff --git a/src/ui/settings/settings-gamepad-ui-handler.ts b/src/ui/settings/settings-gamepad-ui-handler.ts
index 57a70411f4c..5c8f187f4f8 100644
--- a/src/ui/settings/settings-gamepad-ui-handler.ts
+++ b/src/ui/settings/settings-gamepad-ui-handler.ts
@@ -18,11 +18,7 @@ import { addTextObject } from "#ui/text";
import { truncateString } from "#utils/common";
import i18next from "i18next";
-/**
- * Class representing the settings UI handler for gamepads.
- *
- * @extends AbstractControlSettingsUiHandler
- */
+/** Class representing the settings UI handler for gamepads */
export class SettingsGamepadUiHandler extends AbstractControlSettingsUiHandler {
/**
diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts
index 295a71abe36..f802a4fa9cc 100644
--- a/src/ui/settings/settings-keyboard-ui-handler.ts
+++ b/src/ui/settings/settings-keyboard-ui-handler.ts
@@ -19,11 +19,7 @@ import { truncateString } from "#utils/common";
import { toPascalSnakeCase } from "#utils/strings";
import i18next from "i18next";
-/**
- * Class representing the settings UI handler for keyboards.
- *
- * @extends AbstractControlSettingsUiHandler
- */
+/** Class representing the settings UI handler for keyboards */
export class SettingsKeyboardUiHandler extends AbstractControlSettingsUiHandler {
/**
* Creates an instance of SettingsKeyboardUiHandler.
diff --git a/src/ui/text.ts b/src/ui/text.ts
index 8aa50983874..4338a73179d 100644
--- a/src/ui/text.ts
+++ b/src/ui/text.ts
@@ -16,11 +16,7 @@ export function addTextObject(
style: TextStyle,
extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle,
): Phaser.GameObjects.Text {
- const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(
- style,
- globalScene.uiTheme,
- extraStyleOptions,
- );
+ const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, extraStyleOptions);
const ret = globalScene.add
.text(x, y, content, styleOptions)
@@ -38,11 +34,7 @@ export function setTextStyle(
style: TextStyle,
extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle,
) {
- const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(
- style,
- globalScene.uiTheme,
- extraStyleOptions,
- );
+ const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, extraStyleOptions);
obj.setScale(scale).setShadow(shadowXpos, shadowYpos, shadowColor);
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) {
obj.setLineSpacing(scale * 30);
@@ -60,11 +52,7 @@ export function addBBCodeTextObject(
style: TextStyle,
extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle,
): BBCodeText {
- const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(
- style,
- globalScene.uiTheme,
- extraStyleOptions,
- );
+ const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, extraStyleOptions);
const ret = new BBCodeText(globalScene, x, y, content, styleOptions as BBCodeText.TextStyle);
globalScene.add.existing(ret);
@@ -84,7 +72,7 @@ export function addTextInputObject(
style: TextStyle,
extraStyleOptions?: InputText.IConfig,
): InputText {
- const { scale, styleOptions } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions);
+ const { scale, styleOptions } = getTextStyleOptions(style, extraStyleOptions);
const ret = globalScene.add.rexInputText(x, y, width, height, styleOptions as InputText.IConfig);
ret.setScale(scale);
@@ -94,7 +82,6 @@ export function addTextInputObject(
export function getTextStyleOptions(
style: TextStyle,
- uiTheme: UiTheme,
extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle,
): TextStyleOptions {
const lang = i18next.resolvedLanguage;
@@ -106,7 +93,7 @@ export function getTextStyleOptions(
let styleOptions: Phaser.Types.GameObjects.Text.TextStyle = {
fontFamily: "emerald",
fontSize: 96,
- color: getTextColor(style, false, uiTheme),
+ color: getTextColor(style, false),
padding: {
bottom: 6,
},
@@ -349,6 +336,15 @@ export function getTextStyleOptions(
styleOptions.fontSize = defaultFontSize - 42;
styleOptions.padding = { top: 4 };
break;
+ case "ko":
+ styleOptions.fontSize = defaultFontSize - 38;
+ styleOptions.padding = { top: 4, left: 6 };
+ break;
+ case "zh-CN":
+ case "zh-TW":
+ styleOptions.fontSize = defaultFontSize - 42;
+ styleOptions.padding = { top: 5, left: 14 };
+ break;
default:
styleOptions.fontSize = defaultFontSize - 30;
styleOptions.padding = { left: 12 };
@@ -456,13 +452,13 @@ export function getTextStyleOptions(
break;
}
- const shadowColor = getTextColor(style, true, uiTheme);
+ const shadowColor = getTextColor(style, true);
if (extraStyleOptions) {
if (extraStyleOptions.fontSize) {
const sizeRatio =
- Number.parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) /
- Number.parseInt(styleOptions.fontSize?.toString().slice(0, -2) ?? "1");
+ Number.parseInt(extraStyleOptions.fontSize.toString().slice(0, -2))
+ / Number.parseInt(styleOptions.fontSize?.toString().slice(0, -2) ?? "1");
shadowXpos *= sizeRatio;
}
styleOptions = Object.assign(styleOptions, extraStyleOptions);
@@ -471,8 +467,8 @@ export function getTextStyleOptions(
return { scale, styleOptions, shadowColor, shadowXpos, shadowYpos };
}
-export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string {
- return `[color=${getTextColor(textStyle, false, uiTheme)}][shadow=${getTextColor(textStyle, true, uiTheme)}]${content}`;
+export function getBBCodeFrag(content: string, textStyle: TextStyle): string {
+ return `[color=${getTextColor(textStyle, false)}][shadow=${getTextColor(textStyle, true)}]${content}`;
}
/**
@@ -491,14 +487,9 @@ export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: Ui
* @param forWindow set to `true` if the text is to be displayed in a window ({@linkcode BattleScene.addWindow})
* it will replace all instances of the default MONEY TextStyle by {@linkcode TextStyle.MONEY_WINDOW}
*/
-export function getTextWithColors(
- content: string,
- primaryStyle: TextStyle,
- uiTheme: UiTheme,
- forWindow?: boolean,
-): string {
+export function getTextWithColors(content: string, primaryStyle: TextStyle, forWindow?: boolean): string {
// Apply primary styling before anything else
- let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]";
+ let text = getBBCodeFrag(content, primaryStyle) + "[/color][/shadow]";
const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0];
/* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY}
@@ -511,10 +502,7 @@ export function getTextWithColors(
// Set custom colors
text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (_substring, textStyle: string, textToColor: string) => {
return (
- "[/color][/shadow]" +
- getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) +
- "[/color][/shadow]" +
- primaryStyleString
+ "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle]) + "[/color][/shadow]" + primaryStyleString
);
});
@@ -523,8 +511,8 @@ export function getTextWithColors(
}
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a giant switch which is the best option.
-export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: UiTheme = UiTheme.DEFAULT): string {
- const isLegacyTheme = uiTheme === UiTheme.LEGACY;
+export function getTextColor(textStyle: TextStyle, shadow?: boolean): string {
+ const isLegacyTheme = globalScene.uiTheme === UiTheme.LEGACY;
switch (textStyle) {
case TextStyle.MESSAGE:
return !shadow ? "#f8f8f8" : "#6b5a73";
diff --git a/src/ui/ui-theme.ts b/src/ui/ui-theme.ts
index f2d434c024a..3520447ed1f 100644
--- a/src/ui/ui-theme.ts
+++ b/src/ui/ui-theme.ts
@@ -51,7 +51,7 @@ export function addWindow(
windowVariant = WindowVariant.NORMAL;
}
- const borderSize = globalScene.uiTheme ? 6 : 8;
+ const borderSize = globalScene.uiTheme === UiTheme.LEGACY ? 6 : 8;
const window = globalScene.add.nineslice(
x,
@@ -117,10 +117,8 @@ export function updateWindowType(windowTypeIndex: number): void {
} else if (object.texture?.key === "namebox") {
themedObjects.push(object);
}
- } else if (object instanceof Phaser.GameObjects.Sprite) {
- if (object.texture?.key === "bg") {
- themedObjects.push(object);
- }
+ } else if (object instanceof Phaser.GameObjects.Sprite && object.texture?.key === "bg") {
+ themedObjects.push(object);
}
};
@@ -153,7 +151,11 @@ export function addUiThemeOverrides(): void {
frame?: string | number,
): Phaser.GameObjects.Image {
let legacy = false;
- if (typeof texture === "string" && globalScene.uiTheme && legacyCompatibleImages.includes(texture)) {
+ if (
+ typeof texture === "string"
+ && globalScene.uiTheme === UiTheme.LEGACY
+ && legacyCompatibleImages.includes(texture)
+ ) {
legacy = true;
texture += "_legacy";
}
@@ -176,7 +178,11 @@ export function addUiThemeOverrides(): void {
frame?: string | number,
): Phaser.GameObjects.Sprite {
let legacy = false;
- if (typeof texture === "string" && globalScene.uiTheme && legacyCompatibleImages.includes(texture)) {
+ if (
+ typeof texture === "string"
+ && globalScene.uiTheme === UiTheme.LEGACY
+ && legacyCompatibleImages.includes(texture)
+ ) {
legacy = true;
texture += "_legacy";
}
@@ -205,7 +211,11 @@ export function addUiThemeOverrides(): void {
bottomHeight?: number,
): Phaser.GameObjects.NineSlice {
let legacy = false;
- if (typeof texture === "string" && globalScene.uiTheme && legacyCompatibleImages.includes(texture)) {
+ if (
+ typeof texture === "string"
+ && globalScene.uiTheme === UiTheme.LEGACY
+ && legacyCompatibleImages.includes(texture)
+ ) {
legacy = true;
texture += "_legacy";
}
diff --git a/src/ui/ui.ts b/src/ui/ui.ts
index e381d205b78..c7d33f66605 100644
--- a/src/ui/ui.ts
+++ b/src/ui/ui.ts
@@ -4,63 +4,63 @@ import { Device } from "#enums/devices";
import { PlayerGender } from "#enums/player-gender";
import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode";
-import { AchvBar } from "#ui/achv-bar";
-import { AchvsUiHandler } from "#ui/achvs-ui-handler";
-import { AdminUiHandler } from "#ui/admin-ui-handler";
-import { AutoCompleteUiHandler } from "#ui/autocomplete-ui-handler";
-import { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
-import { BallUiHandler } from "#ui/ball-ui-handler";
-import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
-import type { BgmBar } from "#ui/bgm-bar";
-import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler";
-import { ChangePasswordFormUiHandler } from "#ui/change-password-form-ui-handler";
-import { CommandUiHandler } from "#ui/command-ui-handler";
-import { ConfirmUiHandler } from "#ui/confirm-ui-handler";
-import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler";
-import { EggHatchSceneHandler } from "#ui/egg-hatch-scene-handler";
-import { EggListUiHandler } from "#ui/egg-list-ui-handler";
-import { EggSummaryUiHandler } from "#ui/egg-summary-ui-handler";
-import { EvolutionSceneHandler } from "#ui/evolution-scene-handler";
-import { FightUiHandler } from "#ui/fight-ui-handler";
-import { GameStatsUiHandler } from "#ui/game-stats-ui-handler";
+import { AchvBar } from "#ui/containers/achv-bar";
+import type { BgmBar } from "#ui/containers/bgm-bar";
+import { SavingIconContainer } from "#ui/containers/saving-icon-handler";
import { GamepadBindingUiHandler } from "#ui/gamepad-binding-ui-handler";
+import { AchvsUiHandler } from "#ui/handlers/achvs-ui-handler";
+import { AutoCompleteUiHandler } from "#ui/handlers/autocomplete-ui-handler";
+import { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
+import { BallUiHandler } from "#ui/handlers/ball-ui-handler";
+import { BattleMessageUiHandler } from "#ui/handlers/battle-message-ui-handler";
+import { GameChallengesUiHandler } from "#ui/handlers/challenges-select-ui-handler";
+import { ChangePasswordFormUiHandler } from "#ui/handlers/change-password-form-ui-handler";
+import { CommandUiHandler } from "#ui/handlers/command-ui-handler";
+import { ConfirmUiHandler } from "#ui/handlers/confirm-ui-handler";
+import { EggGachaUiHandler } from "#ui/handlers/egg-gacha-ui-handler";
+import { EggHatchSceneUiHandler } from "#ui/handlers/egg-hatch-scene-ui-handler";
+import { EggListUiHandler } from "#ui/handlers/egg-list-ui-handler";
+import { EggSummaryUiHandler } from "#ui/handlers/egg-summary-ui-handler";
+import { EvolutionSceneUiHandler } from "#ui/handlers/evolution-scene-ui-handler";
+import { FightUiHandler } from "#ui/handlers/fight-ui-handler";
+import { GameStatsUiHandler } from "#ui/handlers/game-stats-ui-handler";
+import { LoadingModalUiHandler } from "#ui/handlers/loading-modal-ui-handler";
+import { LoginFormUiHandler } from "#ui/handlers/login-form-ui-handler";
+import { MenuUiHandler } from "#ui/handlers/menu-ui-handler";
+import { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
+import { MysteryEncounterUiHandler } from "#ui/handlers/mystery-encounter-ui-handler";
+import { PartyUiHandler } from "#ui/handlers/party-ui-handler";
+import { PokedexPageUiHandler } from "#ui/handlers/pokedex-page-ui-handler";
+import { PokedexScanUiHandler } from "#ui/handlers/pokedex-scan-ui-handler";
+import { PokedexUiHandler } from "#ui/handlers/pokedex-ui-handler";
+import { RegistrationFormUiHandler } from "#ui/handlers/registration-form-ui-handler";
+import { RenameFormUiHandler } from "#ui/handlers/rename-form-ui-handler";
+import { RunHistoryUiHandler } from "#ui/handlers/run-history-ui-handler";
+import { RunInfoUiHandler } from "#ui/handlers/run-info-ui-handler";
+import { SaveSlotSelectUiHandler } from "#ui/handlers/save-slot-select-ui-handler";
+import { SessionReloadModalUiHandler } from "#ui/handlers/session-reload-modal-ui-handler";
+import { StarterSelectUiHandler } from "#ui/handlers/starter-select-ui-handler";
+import { SummaryUiHandler } from "#ui/handlers/summary-ui-handler";
+import { TargetSelectUiHandler } from "#ui/handlers/target-select-ui-handler";
+import { TestDialogueUiHandler } from "#ui/handlers/test-dialogue-ui-handler";
+import { TitleUiHandler } from "#ui/handlers/title-ui-handler";
+import type { UiHandler } from "#ui/handlers/ui-handler";
+import { UnavailableModalUiHandler } from "#ui/handlers/unavailable-modal-ui-handler";
import { KeyboardBindingUiHandler } from "#ui/keyboard-binding-ui-handler";
-import { LoadingModalUiHandler } from "#ui/loading-modal-ui-handler";
-import { LoginFormUiHandler } from "#ui/login-form-ui-handler";
-import { MenuUiHandler } from "#ui/menu-ui-handler";
-import { MessageUiHandler } from "#ui/message-ui-handler";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
-import { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler";
import { NavigationManager } from "#ui/navigation-menu";
import { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
-import { PartyUiHandler } from "#ui/party-ui-handler";
-import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler";
-import { PokedexScanUiHandler } from "#ui/pokedex-scan-ui-handler";
-import { PokedexUiHandler } from "#ui/pokedex-ui-handler";
-import { RegistrationFormUiHandler } from "#ui/registration-form-ui-handler";
-import { RenameFormUiHandler } from "#ui/rename-form-ui-handler";
-import { RunHistoryUiHandler } from "#ui/run-history-ui-handler";
-import { RunInfoUiHandler } from "#ui/run-info-ui-handler";
-import { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler";
-import { SavingIconHandler } from "#ui/saving-icon-handler";
-import { SessionReloadModalUiHandler } from "#ui/session-reload-modal-ui-handler";
import { SettingsAudioUiHandler } from "#ui/settings-audio-ui-handler";
import { SettingsDisplayUiHandler } from "#ui/settings-display-ui-handler";
import { SettingsGamepadUiHandler } from "#ui/settings-gamepad-ui-handler";
import { SettingsKeyboardUiHandler } from "#ui/settings-keyboard-ui-handler";
import { SettingsUiHandler } from "#ui/settings-ui-handler";
-import { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";
-import { SummaryUiHandler } from "#ui/summary-ui-handler";
-import { TargetSelectUiHandler } from "#ui/target-select-ui-handler";
-import { TestDialogueUiHandler } from "#ui/test-dialogue-ui-handler";
import { addTextObject } from "#ui/text";
-import { TitleUiHandler } from "#ui/title-ui-handler";
-import type { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";
-import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
import { executeIf } from "#utils/common";
import i18next from "i18next";
-import { RenameRunFormUiHandler } from "./rename-run-ui-handler";
+import { AdminUiHandler } from "./handlers/admin-ui-handler";
+import { RenameRunFormUiHandler } from "./handlers/rename-run-ui-handler";
const transitionModes = [
UiMode.SAVE_SLOT,
@@ -115,7 +115,7 @@ export class UI extends Phaser.GameObjects.Container {
private overlay: Phaser.GameObjects.Rectangle;
public achvBar: AchvBar;
public bgmBar: BgmBar;
- public savingIcon: SavingIconHandler;
+ public savingIcon: SavingIconContainer;
private tooltipContainer: Phaser.GameObjects.Container;
private tooltipBg: Phaser.GameObjects.NineSlice;
@@ -141,8 +141,8 @@ export class UI extends Phaser.GameObjects.Container {
new PartyUiHandler(),
new SummaryUiHandler(),
new StarterSelectUiHandler(),
- new EvolutionSceneHandler(),
- new EggHatchSceneHandler(),
+ new EvolutionSceneUiHandler(),
+ new EggHatchSceneUiHandler(),
new EggSummaryUiHandler(),
new ConfirmUiHandler(),
new OptionSelectUiHandler(),
@@ -198,7 +198,7 @@ export class UI extends Phaser.GameObjects.Container {
globalScene.uiContainer.add(this.achvBar);
- this.savingIcon = new SavingIconHandler();
+ this.savingIcon = new SavingIconContainer();
this.savingIcon.setup();
globalScene.uiContainer.add(this.savingIcon);
@@ -377,10 +377,12 @@ export class UI extends Phaser.GameObjects.Container {
}
shouldSkipDialogue(i18nKey: string): boolean {
- if (i18next.exists(i18nKey)) {
- if (globalScene.skipSeenDialogues && globalScene.gameData.getSeenDialogues()[i18nKey] === true) {
- return true;
- }
+ if (
+ i18next.exists(i18nKey)
+ && globalScene.skipSeenDialogues
+ && globalScene.gameData.getSeenDialogues()[i18nKey] === true
+ ) {
+ return true;
}
return false;
}
@@ -491,7 +493,7 @@ export class UI extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: this.overlay,
alpha: 1,
- duration: duration,
+ duration,
ease: "Sine.easeOut",
onComplete: () => resolve(),
});
@@ -506,7 +508,7 @@ export class UI extends Phaser.GameObjects.Container {
globalScene.tweens.add({
targets: this.overlay,
alpha: 0,
- duration: duration,
+ duration,
ease: "Sine.easeIn",
onComplete: () => {
this.overlay.setVisible(false);
@@ -548,11 +550,11 @@ export class UI extends Phaser.GameObjects.Container {
resolve();
};
if (
- (!chainMode &&
- (transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1) &&
- noTransitionModes.indexOf(this.mode) === -1 &&
- noTransitionModes.indexOf(mode) === -1) ||
- (chainMode && noTransitionModes.indexOf(mode) === -1)
+ (!chainMode
+ && (transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1)
+ && noTransitionModes.indexOf(this.mode) === -1
+ && noTransitionModes.indexOf(mode) === -1)
+ || (chainMode && noTransitionModes.indexOf(mode) === -1)
) {
this.fadeOut(250).then(() => {
globalScene.time.delayedCall(100, () => {
@@ -593,7 +595,7 @@ export class UI extends Phaser.GameObjects.Container {
revertMode(): Promise {
return new Promise(resolve => {
- if (!this?.modeChain?.length) {
+ if (this?.modeChain?.length === 0) {
return resolve(false);
}
@@ -625,7 +627,7 @@ export class UI extends Phaser.GameObjects.Container {
revertModes(): Promise {
return new Promise(resolve => {
- if (!this?.modeChain?.length) {
+ if (this?.modeChain?.length === 0) {
return resolve();
}
this.revertMode().then(success => executeIf(success, this.revertModes).then(() => resolve()));
diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/utils/pokemon-icon-anim-helper.ts
similarity index 98%
rename from src/ui/pokemon-icon-anim-handler.ts
rename to src/ui/utils/pokemon-icon-anim-helper.ts
index 408e0ebc9d3..0d8de7ce1ca 100644
--- a/src/ui/pokemon-icon-anim-handler.ts
+++ b/src/ui/utils/pokemon-icon-anim-helper.ts
@@ -9,7 +9,7 @@ export enum PokemonIconAnimMode {
type PokemonIcon = Phaser.GameObjects.Container | Phaser.GameObjects.Sprite;
-export class PokemonIconAnimHandler {
+export class PokemonIconAnimHelper {
private icons: Map;
private toggled: boolean;
diff --git a/src/ui/scrollable-grid-handler.ts b/src/ui/utils/scrollable-grid-helper.ts
similarity index 96%
rename from src/ui/scrollable-grid-handler.ts
rename to src/ui/utils/scrollable-grid-helper.ts
index 3c6458f5083..675352ff6fb 100644
--- a/src/ui/scrollable-grid-handler.ts
+++ b/src/ui/utils/scrollable-grid-helper.ts
@@ -1,6 +1,6 @@
import { Button } from "#enums/buttons";
-import type { ScrollBar } from "#ui/scroll-bar";
-import type { UiHandler } from "#ui/ui-handler";
+import type { ScrollBar } from "#ui/containers/scroll-bar";
+import type { UiHandler } from "#ui/handlers/ui-handler";
type UpdateGridCallbackFunction = () => void;
type UpdateDetailsCallbackFunction = (index: number) => void;
@@ -16,7 +16,7 @@ type UpdateDetailsCallbackFunction = (index: number) => void;
* - in `UiHandler.processInput`: call `processNavigationInput` to have it handle the cursor updates while calling the defined callbacks
* - in `UiHandler.clear`: call `reset`
*/
-export class ScrollableGridUiHandler {
+export class ScrollableGridHelper {
private readonly ROWS: number;
private readonly COLUMNS: number;
private handler: UiHandler;
@@ -47,7 +47,7 @@ export class ScrollableGridUiHandler {
* @param scrollBar {@linkcode ScrollBar}
* @returns this
*/
- withScrollBar(scrollBar: ScrollBar): ScrollableGridUiHandler {
+ withScrollBar(scrollBar: ScrollBar): ScrollableGridHelper {
this.scrollBar = scrollBar;
this.scrollBar.setTotalRows(Math.ceil(this.totalElements / this.COLUMNS));
return this;
@@ -58,7 +58,7 @@ export class ScrollableGridUiHandler {
* @param callback {@linkcode UpdateGridCallbackFunction}
* @returns this
*/
- withUpdateGridCallBack(callback: UpdateGridCallbackFunction): ScrollableGridUiHandler {
+ withUpdateGridCallBack(callback: UpdateGridCallbackFunction): ScrollableGridHelper {
this.updateGridCallback = callback;
return this;
}
@@ -68,7 +68,7 @@ export class ScrollableGridUiHandler {
* @param callback {@linkcode UpdateDetailsCallbackFunction}
* @returns this
*/
- withUpdateSingleElementCallback(callback: UpdateDetailsCallbackFunction): ScrollableGridUiHandler {
+ withUpdateSingleElementCallback(callback: UpdateDetailsCallbackFunction): ScrollableGridHelper {
this.updateDetailsCallback = callback;
return this;
}
diff --git a/src/utils/challenge-utils.ts b/src/utils/challenge-utils.ts
index b0c162a74ed..f79f48f30d2 100644
--- a/src/utils/challenge-utils.ts
+++ b/src/utils/challenge-utils.ts
@@ -1,3 +1,5 @@
+// biome-ignore-all lint/style/useUnifiedTypeSignatures: Rule does not allow stuff with JSDoc comments
+
import type { FixedBattleConfig } from "#app/battle";
import { globalScene } from "#app/global-scene";
import { pokemonEvolutions } from "#balance/pokemon-evolutions";
@@ -10,8 +12,8 @@ import type { MoveSourceType } from "#enums/move-source-type";
import type { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import type { ModifierTypeOption } from "#modifiers/modifier-type";
-import type { DexAttrProps, StarterDataEntry } from "#system/game-data";
import type { DexEntry } from "#types/dex-data";
+import type { DexAttrProps, StarterDataEntry } from "#types/save-data";
import { BooleanHolder, type NumberHolder } from "./common";
import { getPokemonSpecies } from "./pokemon-utils";
@@ -202,7 +204,7 @@ export function applyChallenges(challengeType: ChallengeType.SHOP, status: Boole
* @param challengeType - {@linkcode ChallengeType.POKEMON_ADD_TO_PARTY}
* @param pokemon - The pokemon being caught
* @param status - Whether the pokemon can be added to the party or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_ADD_TO_PARTY,
@@ -215,7 +217,7 @@ export function applyChallenges(
* @param challengeType - {@linkcode ChallengeType.POKEMON_FUSION}
* @param pokemon - The pokemon being checked
* @param status - Whether the selected pokemon is allowed to fuse or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_FUSION,
@@ -228,7 +230,7 @@ export function applyChallenges(
* @param challengeType - {@linkcode ChallengeType.POKEMON_MOVE}
* @param moveId - The move being checked
* @param status - Whether the move can be used or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_MOVE,
@@ -241,7 +243,7 @@ export function applyChallenges(
* @param challengeType - {@linkcode ChallengeType.SHOP_ITEM}
* @param shopItem - The item being checked
* @param status - Whether the item should be added to the shop or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.SHOP_ITEM,
@@ -254,7 +256,7 @@ export function applyChallenges(
* @param challengeType - {@linkcode ChallengeType.WAVE_REWARD}
* @param reward - The reward being checked
* @param status - Whether the reward should be added to the reward options or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.WAVE_REWARD,
@@ -266,7 +268,7 @@ export function applyChallenges(
* Apply all challenges that prevent recovery from fainting
* @param challengeType - {@linkcode ChallengeType.PREVENT_REVIVE}
* @param status - Whether fainting is a permanent status or not
- * @return `true` if any challenge was sucessfully applied, `false` otherwise
+ * @returns `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(challengeType: ChallengeType.PREVENT_REVIVE, status: BooleanHolder): boolean;
@@ -366,7 +368,7 @@ export function checkStarterValidForChallenge(species: PokemonSpecies, props: De
}
// We check the validity of every evolution and form change, and require that at least one is valid
const speciesToCheck = [species.speciesId];
- while (speciesToCheck.length) {
+ while (speciesToCheck.length > 0) {
const checking = speciesToCheck.pop();
// Linter complains if we don't handle this
if (!checking) {
diff --git a/src/utils/common.ts b/src/utils/common.ts
index aac1ef359e6..2734b075a53 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -133,14 +133,10 @@ export function randSeedItem(items: T[]): T {
return items.length === 1 ? items[0] : Phaser.Math.RND.pick(items);
}
-export function randSeedWeightedItem(items: T[]): T {
- return items.length === 1 ? items[0] : Phaser.Math.RND.weightedPick(items);
-}
-
/**
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
- * @param {Array} items An array of items.
- * @returns {Array} A new shuffled array of items.
+ * @param items An array of items.
+ * @returns A new shuffled array of items.
*/
export function randSeedShuffle(items: T[]): T[] {
if (items.length <= 1) {
@@ -276,25 +272,11 @@ export function executeIf(condition: boolean, promiseFunc: () => Promise):
}
export const sessionIdKey = "pokerogue_sessionId";
-// Check if the current hostname is 'localhost' or an IP address, and ensure a port is specified
-export const isLocal =
- ((window.location.hostname === "localhost" || /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/.test(window.location.hostname)) &&
- window.location.port !== "") ||
- window.location.hostname === "";
-/**
- * @deprecated Refer to [pokerogue-api.ts](./plugins/api/pokerogue-api.ts) instead
- */
-export const localServerUrl =
- import.meta.env.VITE_SERVER_URL ?? `http://${window.location.hostname}:${window.location.port + 1}`;
+/** `true` when run via `pnpm start:dev` (which runs `vite --mode development`) */
+export const isLocal = import.meta.env.MODE === "development";
-/**
- * Set the server URL based on whether it's local or not
- *
- * @deprecated Refer to [pokerogue-api.ts](./plugins/api/pokerogue-api.ts) instead
- */
-export const apiUrl = localServerUrl ?? "https://api.pokerogue.net";
-// used to disable api calls when isLocal is true and a server is not found
+/** Used to disable api calls when isLocal is true and a server is not found */
export let isLocalServerConnected = true;
/**
@@ -302,7 +284,7 @@ export let isLocalServerConnected = true;
* with a GET request to verify if a server is running,
* sets isLocalServerConnected based on results
*/
-export async function localPing() {
+export async function localPing(): Promise {
if (isLocal) {
const titleStats = await pokerogueApi.getGameTitleStats();
isLocalServerConnected = !!titleStats;
@@ -358,8 +340,8 @@ export function rgbToHsv(r: number, g: number, b: number) {
/**
* Compare color difference in RGB
- * @param {Array} rgb1 First RGB color in array
- * @param {Array} rgb2 Second RGB color in array
+ * @param rgb1 First RGB color in array
+ * @param rgb2 Second RGB color in array
*/
export function deltaRgb(rgb1: number[], rgb2: number[]): number {
const [r1, g1, b1] = rgb1;
diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts
index 06391e6f4c7..e82895d1fac 100644
--- a/src/utils/cookies.ts
+++ b/src/utils/cookies.ts
@@ -23,13 +23,11 @@ export function getCookie(cName: string): string {
}
const name = `${cName}=`;
const ca = document.cookie.split(";");
- for (let i = 0; i < ca.length; i++) {
- let c = ca[i];
- while (c.charAt(0) === " ") {
- c = c.substring(1);
- }
- if (c.indexOf(name) === 0) {
- return c.substring(name.length, c.length);
+ // Check all cookies in the document and see if any of them match, grabbing the first one whose value lines up
+ for (const c of ca) {
+ const cTrimmed = c.trim();
+ if (cTrimmed.startsWith(name)) {
+ return c.slice(name.length, c.length);
}
}
return "";
diff --git a/src/utils/data.ts b/src/utils/data.ts
index 6580ecf2ee9..1383d8e6ff2 100644
--- a/src/utils/data.ts
+++ b/src/utils/data.ts
@@ -1,6 +1,6 @@
import { loggedInUser } from "#app/account";
import { saveKey } from "#app/constants";
-import type { StarterAttributes } from "#system/game-data";
+import type { StarterAttributes } from "#types/save-data";
import { AES, enc } from "crypto-js";
/**
@@ -8,7 +8,7 @@ import { AES, enc } from "crypto-js";
* @param values - The object to be deep copied.
* @returns A new object that is a deep copy of the input.
*/
-export function deepCopy(values: object): object {
+export function deepCopy(values: T): T {
// Convert the object to a JSON string and parse it back to an object to perform a deep copy
return JSON.parse(JSON.stringify(values));
}
@@ -28,9 +28,9 @@ export function deepCopy(values: object): object {
export function deepMergeSpriteData(dest: object, source: object) {
for (const key of Object.keys(source)) {
if (
- !(key in dest) ||
- typeof source[key] !== typeof dest[key] ||
- Array.isArray(source[key]) !== Array.isArray(dest[key])
+ !(key in dest)
+ || typeof source[key] !== typeof dest[key]
+ || Array.isArray(source[key]) !== Array.isArray(dest[key])
) {
continue;
}
@@ -58,13 +58,28 @@ export function decrypt(data: string, bypassLogin: boolean): string {
return AES.decrypt(data, saveKey).toString(enc.Utf8);
}
+/**
+ * Check if an object has no properties of its own (its shape is `{}`). An empty array is considered a bare object.
+ * @param obj - Object to check
+ * @returns - Whether the object is bare
+ */
+export function isBareObject(obj: any): boolean {
+ if (typeof obj !== "object") {
+ return false;
+ }
+ for (const _ in obj) {
+ return false;
+ }
+ return true;
+}
+
// the latest data saved/loaded for the Starter Preferences. Required to reduce read/writes. Initialize as "{}", since this is the default value and no data needs to be stored if present.
// if they ever add private static variables, move this into StarterPrefs
const StarterPrefers_DEFAULT: string = "{}";
let StarterPrefers_private_latest: string = StarterPrefers_DEFAULT;
export interface StarterPreferences {
- [key: number]: StarterAttributes;
+ [key: number]: StarterAttributes | undefined;
}
// called on starter selection show once
@@ -74,11 +89,17 @@ export function loadStarterPreferences(): StarterPreferences {
localStorage.getItem(`starterPrefs_${loggedInUser?.username}`) || StarterPrefers_DEFAULT),
);
}
-// called on starter selection clear, always
export function saveStarterPreferences(prefs: StarterPreferences): void {
- const pStr: string = JSON.stringify(prefs);
+ // Fastest way to check if an object has any properties (does no allocation)
+ if (isBareObject(prefs)) {
+ console.warn("Refusing to save empty starter preferences");
+ return;
+ }
+ // no reason to store `{}` (for starters not customized)
+ const pStr: string = JSON.stringify(prefs, (_, value) => (isBareObject(value) ? undefined : value));
if (pStr !== StarterPrefers_private_latest) {
+ console.log("%cSaving starter preferences", "color: blue");
// something changed, store the update
localStorage.setItem(`starterPrefs_${loggedInUser?.username}`, pStr);
// update the latest prefs
diff --git a/src/utils/pokemon-utils.ts b/src/utils/pokemon-utils.ts
index 8de0a3bfcf1..60a4d9e0ef7 100644
--- a/src/utils/pokemon-utils.ts
+++ b/src/utils/pokemon-utils.ts
@@ -80,7 +80,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string):
let fragA: string;
let fragB: string;
- fragA = splitNameA.length === 1 ? (fragAMatch ? fragAMatch[1] : speciesAName) : splitNameA[splitNameA.length - 1];
+ fragA = splitNameA.length === 1 ? (fragAMatch ? fragAMatch[1] : speciesAName) : splitNameA.at(-1)!;
if (splitNameB.length === 1) {
if (fragBMatch) {
@@ -101,7 +101,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string):
fragB = speciesBName;
}
} else {
- fragB = splitNameB[splitNameB.length - 1];
+ fragB = splitNameB.at(-1)!;
}
if (splitNameA.length > 1) {
diff --git a/src/utils/strings.ts b/src/utils/strings.ts
index bf5e5c6473f..b4b2498fe9d 100644
--- a/src/utils/strings.ts
+++ b/src/utils/strings.ts
@@ -62,7 +62,7 @@ function trimFromStartAndEnd(str: string, charToTrim: string): string {
/**
* Capitalize the first letter of a string.
* @param str - The string whose first letter is to be capitalized
- * @return The original string with its first letter capitalized.
+ * @returns The original string with its first letter capitalized.
* @example
* ```ts
* console.log(capitalizeFirstLetter("consectetur adipiscing elit")); // returns "Consectetur adipiscing elit"
diff --git a/src/utils/utility-vars.ts b/src/utils/utility-vars.ts
index 081f70164c8..1f8eca00650 100644
--- a/src/utils/utility-vars.ts
+++ b/src/utils/utility-vars.ts
@@ -1 +1,4 @@
-export const isBeta = import.meta.env.MODE === "beta"; // this checks to see if the env mode is development. Technically this gives the same value for beta AND for dev envs
+// TODO: move this (and other global constants) to `src/constants/*-constants.ts`
+
+/** `true` if running on `beta.pokerogue.net` or via `pnpm start:beta` (which runs `vite --mode beta`) */
+export const isBeta = import.meta.env.MODE === "beta";
diff --git a/test/@types/vitest.d.ts b/test/@types/vitest.d.ts
index 2ed0512538a..9a6f07b4afb 100644
--- a/test/@types/vitest.d.ts
+++ b/test/@types/vitest.d.ts
@@ -1,9 +1,9 @@
import "vitest";
-import type { TerrainType } from "#app/data/terrain";
+import type { Phase } from "#app/phase";
import type Overrides from "#app/overrides";
import type { ArenaTag } from "#data/arena-tag";
-import type { PositionalTag } from "#data/positional-tags/positional-tag";
+import type { TerrainType } from "#data/terrain";
import type { AbilityId } from "#enums/ability-id";
import type { ArenaTagSide } from "#enums/arena-tag-side";
import type { ArenaTagType } from "#enums/arena-tag-type";
@@ -11,24 +11,24 @@ import type { BattlerTagType } from "#enums/battler-tag-type";
import type { MoveId } from "#enums/move-id";
import type { PokemonType } from "#enums/pokemon-type";
import type { PositionalTagType } from "#enums/positional-tag-type";
-import type { BattleStat, EffectiveStat, Stat } from "#enums/stat";
-import type { StatusEffect } from "#enums/status-effect";
+import type { BattleStat, EffectiveStat } from "#enums/stat";
import type { WeatherType } from "#enums/weather-type";
-import type { Arena } from "#field/arena";
-import type { Pokemon } from "#field/pokemon";
-import type { PokemonMove } from "#moves/pokemon-move";
import type { toHaveArenaTagOptions } from "#test/test-utils/matchers/to-have-arena-tag";
import type { toHaveEffectiveStatOptions } from "#test/test-utils/matchers/to-have-effective-stat";
import type { toHavePositionalTagOptions } from "#test/test-utils/matchers/to-have-positional-tag";
import type { expectedStatusType } from "#test/test-utils/matchers/to-have-status-effect";
import type { toHaveTypesOptions } from "#test/test-utils/matchers/to-have-types";
+import type { PhaseString } from "#types/phase-types";
import type { TurnMove } from "#types/turn-move";
import type { AtLeastOne } from "#types/type-helpers";
-import type { toDmgValue } from "utils/common";
+import type { toDmgValue } from "#utils/common";
import type { expect } from "vitest";
+import type { toHaveBattlerTagOptions } from "#test/test-utils/matchers/to-have-battler-tag";
declare module "vitest" {
interface Assertion {
+ // #region Generic Matchers
+
/**
* Check whether an array contains EXACTLY the given items (in any order).
*
@@ -40,6 +40,21 @@ declare module "vitest" {
*/
toEqualArrayUnsorted(expected: T[]): void;
+ // #endregion Generic Matchers
+
+ // #region GameManager Matchers
+
+ /**
+ * Check if the {@linkcode GameManager} has shown the given message at least once in the current battle.
+ * @param expectedMessage - The expected message
+ */
+ toHaveShownMessage(expectedMessage: string): void;
+ /**
+ * @param expectedPhase - The expected {@linkcode PhaseString}
+ */
+ toBeAtPhase(expectedPhase: PhaseString): void;
+ // #endregion GameManager Matchers
+
// #region Arena Matchers
/**
@@ -125,10 +140,15 @@ declare module "vitest" {
toHaveStatStage(stat: BattleStat, expectedStage: number): void;
/**
- * Check whether a {@linkcode Pokemon} has a specific {@linkcode BattlerTagType}.
- * @param expectedBattlerTagType - The expected {@linkcode BattlerTagType}
+ * Check whether a {@linkcode Pokemon} has the given {@linkcode BattlerTag}.
+ * @param expectedTag - A partially-filled {@linkcode BattlerTag} containing the desired properties
*/
- toHaveBattlerTag(expectedBattlerTagType: BattlerTagType): void;
+ toHaveBattlerTag(expectedTag: toHaveBattlerTagOptions): void;
+ /**
+ * Check whether a {@linkcode Pokemon} has the given {@linkcode BattlerTag}.
+ * @param expectedType - The expected {@linkcode BattlerTagType}
+ */
+ toHaveBattlerTag(expectedType: BattlerTagType): void;
/**
* Check whether a {@linkcode Pokemon} has applied a specific {@linkcode AbilityId}.
diff --git a/test/abilities/analytic.test.ts b/test/abilities/analytic.test.ts
index cf5a501bbc5..aa9eb3135db 100644
--- a/test/abilities/analytic.test.ts
+++ b/test/abilities/analytic.test.ts
@@ -56,7 +56,7 @@ describe("Abilities - Analytic", () => {
game.override.battleStyle("double");
await game.classicMode.startBattle([SpeciesId.GENGAR, SpeciesId.SHUCKLE]);
- const [enemy] = game.scene.getEnemyField();
+ const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.TACKLE, 0, BattlerIndex.ENEMY);
game.move.select(MoveId.SPLASH, 1);
diff --git a/test/abilities/arena-trap.test.ts b/test/abilities/arena-trap.test.ts
index 0090487f49c..5b426fd4f47 100644
--- a/test/abilities/arena-trap.test.ts
+++ b/test/abilities/arena-trap.test.ts
@@ -6,7 +6,7 @@ import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
+import type { PartyUiHandler } from "#ui/handlers/party-ui-handler";
import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -57,7 +57,7 @@ describe("Abilities - Arena Trap", () => {
await game.phaseInterceptor.to("CommandPhase");
- expect(game.textInterceptor.logs).toContain(
+ expect(game).toHaveShownMessage(
i18next.t("abilityTriggers:arenaTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
abilityName: allAbilities[AbilityId.ARENA_TRAP].name,
diff --git a/test/abilities/cud-chew.test.ts b/test/abilities/cud-chew.test.ts
index f68141096eb..8d80ba119ca 100644
--- a/test/abilities/cud-chew.test.ts
+++ b/test/abilities/cud-chew.test.ts
@@ -99,7 +99,7 @@ describe("Abilities - Cud Chew", () => {
expect(abDisplaySpy.mock.calls[1][2]).toBe(false);
// should display messgae
- expect(game.textInterceptor.getLatestMessage()).toBe(
+ expect(game).toHaveShownMessage(
i18next.t("battle:hpIsFull", {
pokemonName: getPokemonNameWithAffix(farigiraf),
}),
diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts
index f36501cb647..8a7e9a05ddb 100644
--- a/test/abilities/disguise.test.ts
+++ b/test/abilities/disguise.test.ts
@@ -196,7 +196,7 @@ describe("Abilities - Disguise", () => {
game.move.select(MoveId.SHADOW_SNEAK);
await game.toNextWave();
- expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
});
diff --git a/test/abilities/flower-gift.test.ts b/test/abilities/flower-gift.test.ts
index 01459cd4e1e..6d8641917aa 100644
--- a/test/abilities/flower-gift.test.ts
+++ b/test/abilities/flower-gift.test.ts
@@ -18,8 +18,8 @@ describe("Abilities - Flower Gift", () => {
/**
* Tests reverting to normal form when Cloud Nine/Air Lock is active on the field
- * @param {GameManager} game The game manager instance
- * @param {AbilityId} ability The ability that is active on the field
+ * @param game The game manager instance
+ * @param ability The ability that is active on the field
*/
const testRevertFormAgainstAbility = async (game: GameManager, ability: AbilityId) => {
game.override.starterForms({ [SpeciesId.CASTFORM]: SUNSHINE_FORM }).enemyAbility(ability);
diff --git a/test/abilities/friend-guard.test.ts b/test/abilities/friend-guard.test.ts
index 32f4fe06df4..10a9a9c6ee5 100644
--- a/test/abilities/friend-guard.test.ts
+++ b/test/abilities/friend-guard.test.ts
@@ -38,7 +38,12 @@ describe("Moves - Friend Guard", () => {
const [player1, player2] = game.scene.getPlayerField();
const spy = vi.spyOn(player1, "getAttackDamage");
- const enemy1 = game.scene.getEnemyField()[0];
+ const enemy1 = game.field.getEnemyPokemon();
+ const baseDmg = player1.getBaseDamage({
+ source: enemy1,
+ move: allMoves[MoveId.TACKLE],
+ moveCategory: MoveCategory.PHYSICAL,
+ });
game.move.select(MoveId.SPLASH);
game.move.select(MoveId.SPLASH, 1);
@@ -47,12 +52,11 @@ describe("Moves - Friend Guard", () => {
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
- const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
// Making sure the test is controlled; turn 1 damage is equal to base damage (after rounding)
- expect(turn1Damage).toBe(
- Math.floor(
- player1.getBaseDamage({ source: enemy1, move: allMoves[MoveId.TACKLE], moveCategory: MoveCategory.PHYSICAL }),
- ),
+ expect(spy).toHaveLastReturnedWith(
+ expect.objectContaining({
+ damage: Math.floor(baseDmg),
+ }),
);
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[AbilityId.FRIEND_GUARD]);
@@ -64,14 +68,8 @@ describe("Moves - Friend Guard", () => {
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
- const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
// With the ally's Friend Guard, damage should have been reduced from base damage by 25%
- expect(turn2Damage).toBe(
- Math.floor(
- player1.getBaseDamage({ source: enemy1, move: allMoves[MoveId.TACKLE], moveCategory: MoveCategory.PHYSICAL }) *
- 0.75,
- ),
- );
+ expect(spy).toHaveLastReturnedWith(expect.objectContaining({ damage: Math.floor(baseDmg * 0.75) }));
});
it("should NOT reduce damage to pokemon with friend guard", async () => {
@@ -86,7 +84,7 @@ describe("Moves - Friend Guard", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
- const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
+ const turn1Damage = spy.mock.results.at(-1)!.value.damage;
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[AbilityId.FRIEND_GUARD]);
@@ -96,7 +94,7 @@ describe("Moves - Friend Guard", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
- const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
+ const turn2Damage = spy.mock.results.at(-1)!.value.damage;
expect(turn2Damage).toBe(turn1Damage);
});
@@ -112,7 +110,7 @@ describe("Moves - Friend Guard", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
- const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
+ const turn1Damage = spy.mock.results.at(-1)!.value.damage;
expect(turn1Damage).toBe(40);
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[AbilityId.FRIEND_GUARD]);
@@ -123,7 +121,7 @@ describe("Moves - Friend Guard", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
- const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
+ const turn2Damage = spy.mock.results.at(-1)!.value.damage;
expect(turn2Damage).toBe(40);
});
});
diff --git a/test/abilities/gulp-missile.test.ts b/test/abilities/gulp-missile.test.ts
index 865a319251f..82f446623f8 100644
--- a/test/abilities/gulp-missile.test.ts
+++ b/test/abilities/gulp-missile.test.ts
@@ -21,7 +21,7 @@ describe("Abilities - Gulp Missile", () => {
/**
* Gets the effect damage of Gulp Missile
* See Gulp Missile {@link https://bulbapedia.bulbagarden.net/wiki/Gulp_Missile_(Ability)}
- * @param {Pokemon} pokemon The pokemon taking the effect damage.
+ * @param pokemon The pokemon taking the effect damage.
* @returns The effect damage of Gulp Missile
*/
const getEffectDamage = (pokemon: Pokemon): number => {
diff --git a/test/abilities/intimidate.test.ts b/test/abilities/intimidate.test.ts
index 3c283e0392b..8064f1e62aa 100644
--- a/test/abilities/intimidate.test.ts
+++ b/test/abilities/intimidate.test.ts
@@ -35,13 +35,43 @@ describe("Abilities - Intimidate", () => {
it("should lower all opponents' ATK by 1 stage on entry and switch", async () => {
await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]);
+ const [mightyena, poochyena] = game.scene.getPlayerParty();
+
const enemy = game.field.getEnemyPokemon();
expect(enemy.getStatStage(Stat.ATK)).toBe(-1);
+ expect(mightyena).toHaveAbilityApplied(AbilityId.INTIMIDATE);
game.doSwitchPokemon(1);
await game.toNextTurn();
+ expect(poochyena.isActive()).toBe(true);
expect(enemy.getStatStage(Stat.ATK)).toBe(-2);
+ expect(poochyena).toHaveAbilityApplied(AbilityId.INTIMIDATE);
+ });
+
+ it("should trigger once on initial switch prompt without cancelling opposing abilities", async () => {
+ await game.classicMode.runToSummon([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]);
+ await game.classicMode.startBattleWithSwitch(1);
+
+ const [poochyena, mightyena] = game.scene.getPlayerParty();
+ expect(poochyena.species.speciesId).toBe(SpeciesId.POOCHYENA);
+
+ const enemy = game.field.getEnemyPokemon();
+ expect(enemy).toHaveStatStage(Stat.ATK, -1);
+ expect(poochyena).toHaveStatStage(Stat.ATK, -1);
+
+ expect(poochyena).toHaveAbilityApplied(AbilityId.INTIMIDATE);
+ expect(mightyena).not.toHaveAbilityApplied(AbilityId.INTIMIDATE);
+ });
+
+ it("should activate on reload with single party", async () => {
+ await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
+
+ expect(game.field.getEnemyPokemon()).toHaveStatStage(Stat.ATK, -1);
+
+ await game.reload.reloadSession();
+
+ expect(game.field.getEnemyPokemon()).toHaveStatStage(Stat.ATK, -1);
});
it("should lower ATK of all opponents in a double battle", async () => {
diff --git a/test/abilities/moody.test.ts b/test/abilities/moody.test.ts
index d1f8aa2e351..b7882c15ced 100644
--- a/test/abilities/moody.test.ts
+++ b/test/abilities/moody.test.ts
@@ -84,4 +84,40 @@ describe("Abilities - Moody", () => {
expect(decreasedStat).toBeTruthy();
expect(decreasedStat.length).toBe(1);
});
+
+ it("should only try to increase a stat stage by 1 if the stat stage is not at 6", async () => {
+ await game.classicMode.startBattle();
+
+ const playerPokemon = game.field.getPlayerPokemon();
+
+ // Set all stat stages to 6
+ vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6));
+
+ // Set one of the stat stages to -6
+ const raisedStat = EFFECTIVE_STATS[playerPokemon.randBattleSeedInt(EFFECTIVE_STATS.length)];
+ playerPokemon.setStatStage(raisedStat, -6);
+
+ game.move.select(MoveId.SPLASH);
+ await game.toNextTurn();
+
+ expect(playerPokemon.getStatStage(raisedStat), "should increase only the stat that is not at stage 6").toBe(-4);
+ });
+
+ it("should only try to decrease a stat stage by 1 if the stat stage is not at -6", async () => {
+ await game.classicMode.startBattle();
+
+ const playerPokemon = game.field.getPlayerPokemon();
+
+ // Set all stat stages to -6
+ vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(-6));
+
+ // Set one of the stat stages to 6
+ const raisedStat = EFFECTIVE_STATS[playerPokemon.randBattleSeedInt(EFFECTIVE_STATS.length)];
+ playerPokemon.setStatStage(raisedStat, 6);
+
+ game.move.select(MoveId.SPLASH);
+ await game.toNextTurn();
+
+ expect(playerPokemon.getStatStage(raisedStat), "should decrease only the stat that is not at stage -6").toBe(5);
+ });
});
diff --git a/test/abilities/pastel-veil.test.ts b/test/abilities/pastel-veil.test.ts
index c4b368c94d0..cc414fa6f87 100644
--- a/test/abilities/pastel-veil.test.ts
+++ b/test/abilities/pastel-veil.test.ts
@@ -3,7 +3,6 @@ import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect";
-import { TurnEndPhase } from "#phases/turn-end-phase";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -24,48 +23,35 @@ describe("Abilities - Pastel Veil", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
- game.override
- .battleStyle("double")
- .moveset([MoveId.TOXIC_THREAD, MoveId.SPLASH])
- .enemyAbility(AbilityId.BALL_FETCH)
- .enemySpecies(SpeciesId.SUNKERN)
- .enemyMoveset(MoveId.SPLASH);
+ game.override.battleStyle("double").enemyAbility(AbilityId.BALL_FETCH).enemySpecies(SpeciesId.TOXAPEX);
});
- it("prevents the user and its allies from being afflicted by poison", async () => {
+ it("should prevent the user and its allies from being poisoned", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.GALAR_PONYTA]);
- const ponyta = game.scene.getPlayerField()[1];
- const magikarp = game.scene.getPlayerField()[0];
- ponyta.abilityIndex = 1;
+ const [magikarp, ponyta] = game.scene.getPlayerField();
+ game.field.mockAbility(ponyta, AbilityId.PASTEL_VEIL);
- expect(ponyta.hasAbility(AbilityId.PASTEL_VEIL)).toBe(true);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.move.forceEnemyMove(MoveId.TOXIC, BattlerIndex.PLAYER);
+ await game.move.forceEnemyMove(MoveId.TOXIC, BattlerIndex.PLAYER_2);
+ await game.toEndOfTurn();
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.TOXIC_THREAD, 1, BattlerIndex.PLAYER);
-
- await game.phaseInterceptor.to(TurnEndPhase);
-
- expect(magikarp.status?.effect).toBeUndefined();
+ expect(magikarp).toHaveStatusEffect(StatusEffect.NONE);
+ expect(ponyta).toHaveStatusEffect(StatusEffect.NONE);
});
- it("it heals the poisoned status condition of allies if user is sent out into battle", async () => {
+ it("should cure allies' poison if user is sent out into battle", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS, SpeciesId.GALAR_PONYTA]);
- const ponyta = game.scene.getPlayerParty()[2];
- const magikarp = game.scene.getPlayerField()[0];
- ponyta.abilityIndex = 1;
+ const [magikarp, , ponyta] = game.scene.getPlayerParty();
+ game.field.mockAbility(ponyta, AbilityId.PASTEL_VEIL);
- expect(ponyta.hasAbility(AbilityId.PASTEL_VEIL)).toBe(true);
+ magikarp.doSetStatus(StatusEffect.POISON);
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.TOXIC_THREAD, 1, BattlerIndex.PLAYER);
-
- await game.phaseInterceptor.to(TurnEndPhase);
- expect(magikarp.status?.effect).toBe(StatusEffect.POISON);
-
- game.move.select(MoveId.SPLASH);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
game.doSwitchPokemon(2);
- await game.phaseInterceptor.to(TurnEndPhase);
+ await game.toEndOfTurn();
- expect(magikarp.status?.effect).toBeUndefined();
+ expect(magikarp).toHaveStatusEffect(StatusEffect.NONE);
});
});
diff --git a/test/abilities/power-construct.test.ts b/test/abilities/power-construct.test.ts
index 34e02851daf..6c504359370 100644
--- a/test/abilities/power-construct.test.ts
+++ b/test/abilities/power-construct.test.ts
@@ -33,8 +33,8 @@ describe("Abilities - POWER CONSTRUCT", () => {
});
test("check if fainted 50% Power Construct Pokemon switches to base form on arena reset", async () => {
- const baseForm = 2,
- completeForm = 4;
+ const baseForm = 2;
+ const completeForm = 4;
game.override.startingWave(4).starterForms({
[SpeciesId.ZYGARDE]: completeForm,
});
@@ -59,8 +59,8 @@ describe("Abilities - POWER CONSTRUCT", () => {
});
test("check if fainted 10% Power Construct Pokemon switches to base form on arena reset", async () => {
- const baseForm = 3,
- completeForm = 5;
+ const baseForm = 3;
+ const completeForm = 5;
game.override.startingWave(4).starterForms({
[SpeciesId.ZYGARDE]: completeForm,
});
diff --git a/test/abilities/schooling.test.ts b/test/abilities/schooling.test.ts
index fea919c7c5a..5c908d39866 100644
--- a/test/abilities/schooling.test.ts
+++ b/test/abilities/schooling.test.ts
@@ -29,8 +29,8 @@ describe("Abilities - SCHOOLING", () => {
});
test("check if fainted pokemon switches to base form on arena reset", async () => {
- const soloForm = 0,
- schoolForm = 1;
+ const soloForm = 0;
+ const schoolForm = 1;
game.override.startingWave(4).starterForms({
[SpeciesId.WISHIWASHI]: schoolForm,
});
diff --git a/test/abilities/sweet-veil.test.ts b/test/abilities/sweet-veil.test.ts
index 12eeae9f9c5..d08c3eb04f8 100644
--- a/test/abilities/sweet-veil.test.ts
+++ b/test/abilities/sweet-veil.test.ts
@@ -2,8 +2,9 @@ import { AbilityId } from "#enums/ability-id";
import { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id";
+import { MoveResult } from "#enums/move-result";
import { SpeciesId } from "#enums/species-id";
-import { TurnEndPhase } from "#phases/turn-end-phase";
+import { StatusEffect } from "#enums/status-effect";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -26,62 +27,108 @@ describe("Abilities - Sweet Veil", () => {
game = new GameManager(phaserGame);
game.override
.battleStyle("double")
- .moveset([MoveId.SPLASH, MoveId.REST, MoveId.YAWN])
+ .ability(AbilityId.BALL_FETCH)
.enemySpecies(SpeciesId.MAGIKARP)
.enemyAbility(AbilityId.BALL_FETCH)
- .enemyMoveset(MoveId.POWDER);
+ .enemyMoveset(MoveId.SPLASH);
});
- it("prevents the user and its allies from falling asleep", async () => {
+ function expectNoStatus() {
+ game.scene.getPlayerField().forEach(p => {
+ expect.soft(p).toHaveStatusEffect(StatusEffect.NONE);
+ });
+ }
+
+ it("should prevent the user and its allies from falling asleep", async () => {
await game.classicMode.startBattle([SpeciesId.SWIRLIX, SpeciesId.MAGIKARP]);
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.SPLASH, 1);
+ game.field.mockAbility(game.field.getPlayerPokemon(), AbilityId.SWEET_VEIL);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.move.forceEnemyMove(MoveId.SPORE, BattlerIndex.PLAYER);
+ await game.move.forceEnemyMove(MoveId.SPORE, BattlerIndex.PLAYER_2);
+ await game.toEndOfTurn();
- await game.phaseInterceptor.to(TurnEndPhase);
-
- expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false);
+ expectNoStatus();
});
- it("causes Rest to fail when used by the user or its allies", async () => {
- game.override.enemyMoveset(MoveId.SPLASH);
+ it("should cause Rest to fail when used by the user or its allies", async () => {
await game.classicMode.startBattle([SpeciesId.SWIRLIX, SpeciesId.MAGIKARP]);
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.REST, 1);
+ const [swirlix, magikarp] = game.scene.getPlayerField();
+ game.field.mockAbility(swirlix, AbilityId.SWEET_VEIL);
+ swirlix.hp = 1;
+ magikarp.hp = 1;
- await game.phaseInterceptor.to(TurnEndPhase);
+ game.move.use(MoveId.REST, BattlerIndex.PLAYER);
+ game.move.use(MoveId.REST, BattlerIndex.PLAYER_2);
+ await game.toEndOfTurn();
- expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false);
+ expectNoStatus();
+ expect(swirlix).toHaveUsedMove({ move: MoveId.REST, result: MoveResult.FAIL });
+ expect(magikarp).toHaveUsedMove({ move: MoveId.REST, result: MoveResult.FAIL });
});
- it("causes Yawn to fail if used on the user or its allies", async () => {
- game.override.enemyMoveset(MoveId.YAWN);
+ it("should cause Yawn to fail if used on the user or its allies", async () => {
await game.classicMode.startBattle([SpeciesId.SWIRLIX, SpeciesId.MAGIKARP]);
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.SPLASH, 1);
+ const [shuckle, swirlix] = game.scene.getPlayerField();
+ game.field.mockAbility(swirlix, AbilityId.SWEET_VEIL);
- await game.phaseInterceptor.to(TurnEndPhase);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.move.forceEnemyMove(MoveId.YAWN, BattlerIndex.PLAYER);
+ await game.move.forceEnemyMove(MoveId.YAWN, BattlerIndex.PLAYER_2);
+ await game.toEndOfTurn();
- expect(game.scene.getPlayerField().every(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(false);
+ expect(shuckle).not.toHaveBattlerTag(BattlerTagType.DROWSY);
+ expect(swirlix).not.toHaveBattlerTag(BattlerTagType.DROWSY);
+ // TODO: This dooesn't work ATM
+ /*
+ const [karp1, karp2] = game.scene.getEnemyField();
+ expect(karp1).toHaveUsedMove({move: MoveId.YAWN, result: MoveResult.FAIL});
+ expect(karp2).toHaveUsedMove({move: MoveId.YAWN, result: MoveResult.FAIL});
+ */
});
- it("prevents the user and its allies already drowsy due to Yawn from falling asleep.", async () => {
- game.override.enemySpecies(SpeciesId.PIKACHU).enemyLevel(5).startingLevel(5).enemyMoveset(MoveId.SPLASH);
+ it("should NOT cure allies' sleep status if user is sent out into battle", async () => {
+ await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS, SpeciesId.SWIRLIX]);
- await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE, SpeciesId.SWIRLIX]);
+ const [magikarp, , swirlix] = game.scene.getPlayerParty();
+ game.field.mockAbility(swirlix, AbilityId.PASTEL_VEIL);
- game.move.select(MoveId.SPLASH);
- game.move.select(MoveId.YAWN, 1, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH);
+ game.move.use(MoveId.SPORE, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER);
+ await game.toNextTurn();
- await game.phaseInterceptor.to("BerryPhase");
+ expect(magikarp).toHaveStatusEffect(StatusEffect.SLEEP);
- expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true);
-
- game.move.select(MoveId.SPLASH);
+ game.move.use(MoveId.SPLASH);
game.doSwitchPokemon(2);
+ await game.toEndOfTurn();
- expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false);
+ expect(magikarp).toHaveStatusEffect(StatusEffect.SLEEP);
+ });
+
+ it("should prevent an already-drowsy user or ally from falling asleep", async () => {
+ await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SWIRLIX]);
+
+ // Add yawn before granting ability
+ const [shuckle, swirlix] = game.scene.getPlayerField();
+ shuckle.addTag(BattlerTagType.DROWSY, 1);
+ swirlix.addTag(BattlerTagType.DROWSY, 1);
+
+ game.field.mockAbility(shuckle, AbilityId.SWEET_VEIL);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.toNextTurn();
+
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.toNextTurn();
+
+ expect(shuckle).not.toHaveBattlerTag(BattlerTagType.DROWSY);
+ expect(swirlix).not.toHaveBattlerTag(BattlerTagType.DROWSY);
+ expectNoStatus();
});
});
diff --git a/test/abilities/truant.test.ts b/test/abilities/truant.test.ts
index 0d71cd393b0..31098fa1a85 100644
--- a/test/abilities/truant.test.ts
+++ b/test/abilities/truant.test.ts
@@ -54,7 +54,7 @@ describe("Ability - Truant", () => {
expect(player.getLastXMoves(1)[0]).toEqual(expect.objectContaining({ move: MoveId.NONE, result: MoveResult.FAIL }));
expect(enemy.hp).toBe(enemy.getMaxHp());
- expect(game.textInterceptor.logs).toContain(
+ expect(game).toHaveShownMessage(
i18next.t("battlerTags:truantLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(player),
}),
diff --git a/test/battle/special-battle.test.ts b/test/battle/special-battle.test.ts
index d22931bfea5..4fb859a2a40 100644
--- a/test/battle/special-battle.test.ts
+++ b/test/battle/special-battle.test.ts
@@ -36,62 +36,62 @@ describe("Test Battle Phase", () => {
game.override.battleStyle("single").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 boss", async () => {
game.override.battleStyle("double").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs1 rival", async () => {
game.override.battleStyle("single").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 rival", async () => {
game.override.battleStyle("double").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 1vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 4vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
});
});
diff --git a/test/challenges/hardcore.test.ts b/test/challenges/hardcore.test.ts
index 0f4ab1b9f02..a52d7102868 100644
--- a/test/challenges/hardcore.test.ts
+++ b/test/challenges/hardcore.test.ts
@@ -8,7 +8,7 @@ import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
diff --git a/test/challenges/limited-support.test.ts b/test/challenges/limited-support.test.ts
index 35413220550..ba8930943dd 100644
--- a/test/challenges/limited-support.test.ts
+++ b/test/challenges/limited-support.test.ts
@@ -5,7 +5,7 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { ExpBoosterModifier } from "#modifiers/modifier";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
diff --git a/test/daily-mode.test.ts b/test/daily-mode.test.ts
index 34a8da80478..fae12a0b5d7 100644
--- a/test/daily-mode.test.ts
+++ b/test/daily-mode.test.ts
@@ -5,7 +5,7 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { MapModifier } from "#modifiers/modifier";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Daily Mode", () => {
diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts
index f1bb955582a..fb677e81a45 100644
--- a/test/escape-calculations.test.ts
+++ b/test/escape-calculations.test.ts
@@ -76,9 +76,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 80 },
];
- for (let i = 0; i < escapeChances.length; i++) {
- // sets the number of escape attempts to the required amount
- game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
+ for (const check of escapeChances) {
+ // set the number of escape attempts to the required amount
+ game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@@ -86,10 +86,10 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- escapeChances[i].pokemonSpeedRatio * enemySpeed,
+ check.pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
- expect(chance).toBe(escapeChances[i].expectedEscapeChance);
+ expect(chance).toBe(check.expectedEscapeChance);
}
});
@@ -146,9 +146,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 95 },
];
- for (let i = 0; i < escapeChances.length; i++) {
+ for (const check of escapeChances) {
// sets the number of escape attempts to the required amount
- game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
+ game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@@ -156,7 +156,7 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
+ Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
]);
// set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
@@ -165,15 +165,13 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
+ check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same
- expect(chance).toBe(escapeChances[i].expectedEscapeChance);
+ expect(chance).toBe(check.expectedEscapeChance);
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
- expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(
- escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
- );
+ expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(check.pokemonSpeedRatio * totalEnemySpeed);
}
});
@@ -238,9 +236,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 6.1, escapeAttempts: 3, expectedEscapeChance: 25 },
];
- for (let i = 0; i < escapeChances.length; i++) {
+ for (const check of escapeChances) {
// sets the number of escape attempts to the required amount
- game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
+ game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@@ -248,10 +246,10 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- escapeChances[i].pokemonSpeedRatio * enemySpeed,
+ check.pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
- expect(chance).toBe(escapeChances[i].expectedEscapeChance);
+ expect(chance).toBe(check.expectedEscapeChance);
}
});
@@ -321,9 +319,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 5.2, escapeAttempts: 2, expectedEscapeChance: 25 },
];
- for (let i = 0; i < escapeChances.length; i++) {
+ for (const check of escapeChances) {
// sets the number of escape attempts to the required amount
- game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
+ game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@@ -331,7 +329,7 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
+ Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
]);
// set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
@@ -340,15 +338,13 @@ describe("Escape chance calculations", () => {
20,
20,
20,
- escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
+ check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same
- expect(chance).toBe(escapeChances[i].expectedEscapeChance);
+ expect(chance).toBe(check.expectedEscapeChance);
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
- expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(
- escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
- );
+ expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(check.pokemonSpeedRatio * totalEnemySpeed);
}
});
});
diff --git a/test/evolution.test.ts b/test/evolution.test.ts
index 3fb763e9190..7079404bdec 100644
--- a/test/evolution.test.ts
+++ b/test/evolution.test.ts
@@ -175,4 +175,27 @@ describe("Evolution", () => {
expect(fourForm.evoFormKey).toBe("four"); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is "four"
}
});
+
+ it("tyrogue should evolve if move is not in first slot", async () => {
+ game.override
+ .moveset([MoveId.TACKLE, MoveId.RAPID_SPIN, MoveId.LOW_KICK])
+ .enemySpecies(SpeciesId.GOLEM)
+ .enemyMoveset(MoveId.SPLASH)
+ .startingWave(41)
+ .startingLevel(19)
+ .enemyLevel(30);
+
+ await game.classicMode.startBattle([SpeciesId.TYROGUE]);
+
+ const tyrogue = game.field.getPlayerPokemon();
+
+ const golem = game.field.getEnemyPokemon();
+ golem.hp = 1;
+ expect(golem.hp).toBe(1);
+
+ game.move.select(MoveId.TACKLE);
+ await game.phaseInterceptor.to("EndEvolutionPhase");
+
+ expect(tyrogue.species.speciesId).toBe(SpeciesId.HITMONTOP);
+ });
});
diff --git a/test/imports.test.ts b/test/imports.test.ts
index 04e9462d8d0..aeaa763c05e 100644
--- a/test/imports.test.ts
+++ b/test/imports.test.ts
@@ -1,4 +1,4 @@
-import { initStatsKeys } from "#ui/game-stats-ui-handler";
+import { initStatsKeys } from "#ui/handlers/game-stats-ui-handler";
import { describe, expect, it } from "vitest";
async function importModule() {
diff --git a/test/items/dire-hit.test.ts b/test/items/dire-hit.test.ts
index 6d4bc7524eb..d704a94f3a8 100644
--- a/test/items/dire-hit.test.ts
+++ b/test/items/dire-hit.test.ts
@@ -10,7 +10,7 @@ import { NewBattlePhase } from "#phases/new-battle-phase";
import { TurnEndPhase } from "#phases/turn-end-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManager } from "#test/test-utils/game-manager";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/items/double-battle-chance-booster.test.ts b/test/items/double-battle-chance-booster.test.ts
index 2c12b34eba3..9985d4b3a55 100644
--- a/test/items/double-battle-chance-booster.test.ts
+++ b/test/items/double-battle-chance-booster.test.ts
@@ -5,7 +5,7 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { DoubleBattleChanceBoosterModifier } from "#modifiers/modifier";
import { GameManager } from "#test/test-utils/game-manager";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
diff --git a/test/items/leftovers.test.ts b/test/items/leftovers.test.ts
index 6ae4094799b..484843b81ff 100644
--- a/test/items/leftovers.test.ts
+++ b/test/items/leftovers.test.ts
@@ -2,7 +2,6 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { DamageAnimPhase } from "#phases/damage-anim-phase";
-import { TurnEndPhase } from "#phases/turn-end-phase";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -54,7 +53,7 @@ describe("Items - Leftovers", () => {
const leadHpAfterDamage = leadPokemon.hp;
// Check if leftovers heal us
- await game.phaseInterceptor.to(TurnEndPhase);
+ await game.phaseInterceptor.to("PokemonHealPhase");
expect(leadPokemon.hp).toBeGreaterThan(leadHpAfterDamage);
});
});
diff --git a/test/items/temp-stat-stage-booster.test.ts b/test/items/temp-stat-stage-booster.test.ts
index 05ea5a03eae..499f1d630b0 100644
--- a/test/items/temp-stat-stage-booster.test.ts
+++ b/test/items/temp-stat-stage-booster.test.ts
@@ -7,7 +7,7 @@ import { BATTLE_STATS, Stat } from "#enums/stat";
import { UiMode } from "#enums/ui-mode";
import { TempStatStageBoosterModifier } from "#modifiers/modifier";
import { GameManager } from "#test/test-utils/game-manager";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/moves/aurora-veil.test.ts b/test/moves/aurora-veil.test.ts
index 6edc214d580..15aa72d039a 100644
--- a/test/moves/aurora-veil.test.ts
+++ b/test/moves/aurora-veil.test.ts
@@ -139,17 +139,15 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
- if (globalScene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side)) {
- if (move.getAttrs("CritOnlyAttr").length === 0) {
- globalScene.arena.applyTagsForSide(
- ArenaTagType.AURORA_VEIL,
- side,
- false,
- attacker,
- move.category,
- multiplierHolder,
- );
- }
+ if (globalScene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side) && move.getAttrs("CritOnlyAttr").length === 0) {
+ globalScene.arena.applyTagsForSide(
+ ArenaTagType.AURORA_VEIL,
+ side,
+ false,
+ attacker,
+ move.category,
+ multiplierHolder,
+ );
}
return move.power * multiplierHolder.value;
diff --git a/test/moves/chilly-reception.test.ts b/test/moves/chilly-reception.test.ts
index 096454132f3..f9caea3d560 100644
--- a/test/moves/chilly-reception.test.ts
+++ b/test/moves/chilly-reception.test.ts
@@ -47,7 +47,7 @@ describe("Moves - Chilly Reception", () => {
expect(game.field.getPlayerPokemon()).toBe(meowth);
expect(slowking.isOnField()).toBe(false);
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
- expect(game.textInterceptor.logs).toContain(
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
);
});
@@ -110,7 +110,7 @@ describe("Moves - Chilly Reception", () => {
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
expect(game.field.getPlayerPokemon()).toBe(slowking);
expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
- expect(game.textInterceptor.logs).toContain(
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
);
});
@@ -129,7 +129,7 @@ describe("Moves - Chilly Reception", () => {
expect(game.field.getPlayerPokemon()).toBe(meowth);
expect(slowking.isOnField()).toBe(false);
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
- expect(game.textInterceptor.logs).not.toContain(
+ expect(game).not.toHaveShownMessage(
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
);
});
diff --git a/test/moves/delayed-attack.test.ts b/test/moves/delayed-attack.test.ts
index e8cf2871626..6817c7fd17a 100644
--- a/test/moves/delayed-attack.test.ts
+++ b/test/moves/delayed-attack.test.ts
@@ -4,12 +4,15 @@ import { allMoves } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { BattleType } from "#enums/battle-type";
import { BattlerIndex } from "#enums/battler-index";
+import { Button } from "#enums/buttons";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
+import { PokeballType } from "#enums/pokeball";
import { PokemonType } from "#enums/pokemon-type";
import { PositionalTagType } from "#enums/positional-tag-type";
import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat";
+import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager";
import i18next from "i18next";
import Phaser from "phaser";
@@ -95,8 +98,8 @@ describe("Moves - Delayed Attacks", () => {
expectFutureSightActive(0);
const enemy = game.field.getEnemyPokemon();
- expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
- expect(game.textInterceptor.logs).toContain(
+ expect(enemy).not.toHaveFullHp();
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(enemy),
moveName: allMoves[move].name,
@@ -130,12 +133,12 @@ describe("Moves - Delayed Attacks", () => {
expectFutureSightActive();
const enemy = game.field.getEnemyPokemon();
- expect(enemy.hp).toBe(enemy.getMaxHp());
+ expect(enemy).toHaveFullHp();
await passTurns(2);
expectFutureSightActive(0);
- expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
+ expect(enemy).not.toHaveFullHp();
});
it("should work when used against different targets in doubles", async () => {
@@ -149,15 +152,15 @@ describe("Moves - Delayed Attacks", () => {
await game.toEndOfTurn();
expectFutureSightActive(2);
- expect(enemy1.hp).toBe(enemy1.getMaxHp());
- expect(enemy2.hp).toBe(enemy2.getMaxHp());
+ expect(enemy1).toHaveFullHp();
+ expect(enemy2).toHaveFullHp();
expect(karp.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
expect(feebas.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
await passTurns(2);
- expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp());
- expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
+ expect(enemy1).not.toHaveFullHp();
+ expect(enemy2).not.toHaveFullHp();
});
it("should trigger multiple pending attacks in order of creation, even if that order changes later on", async () => {
@@ -222,9 +225,9 @@ describe("Moves - Delayed Attacks", () => {
expect(game.scene.getPlayerParty()).toEqual([milotic, karp, feebas]);
- expect(karp.hp).toBe(karp.getMaxHp());
- expect(feebas.hp).toBe(feebas.getMaxHp());
- expect(game.textInterceptor.logs).not.toContain(
+ expect(karp).toHaveFullHp();
+ expect(feebas).toHaveFullHp();
+ expect(game).not.toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(karp),
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
@@ -245,16 +248,15 @@ describe("Moves - Delayed Attacks", () => {
expect(enemy2.isFainted()).toBe(true);
expectFutureSightActive();
- const attack = game.scene.arena.positionalTagManager.tags.find(
- t => t.tagType === PositionalTagType.DELAYED_ATTACK,
- )!;
- expect(attack).toBeDefined();
- expect(attack.targetIndex).toBe(enemy1.getBattlerIndex());
+ expect(game).toHavePositionalTag({
+ tagType: PositionalTagType.DELAYED_ATTACK,
+ targetIndex: enemy1.getBattlerIndex(),
+ });
await passTurns(2);
- expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp());
- expect(game.textInterceptor.logs).toContain(
+ expect(enemy1).not.toHaveFullHp();
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(enemy1),
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
@@ -281,8 +283,8 @@ describe("Moves - Delayed Attacks", () => {
await game.toNextTurn();
expectFutureSightActive(0);
- expect(enemy1.hp).toBe(enemy1.getMaxHp());
- expect(game.textInterceptor.logs).not.toContain(
+ expect(enemy1).toHaveFullHp();
+ expect(game).not.toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(enemy1),
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
@@ -317,9 +319,9 @@ describe("Moves - Delayed Attacks", () => {
await game.toEndOfTurn();
- expect(enemy1.hp).toBe(enemy1.getMaxHp());
- expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
- expect(game.textInterceptor.logs).toContain(
+ expect(enemy1).toHaveFullHp();
+ expect(enemy2).not.toHaveFullHp();
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(enemy2),
moveName: allMoves[MoveId.FUTURE_SIGHT].name,
@@ -351,8 +353,8 @@ describe("Moves - Delayed Attacks", () => {
// Player Normalize was not applied due to being off field
const enemy = game.field.getEnemyPokemon();
- expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
- expect(game.textInterceptor.logs).toContain(
+ expect(enemy).not.toHaveFullHp();
+ expect(game).toHaveShownMessage(
i18next.t("moveTriggers:tookMoveAttack", {
pokemonName: getPokemonNameWithAffix(enemy),
moveName: allMoves[MoveId.DOOM_DESIRE].name,
@@ -384,6 +386,35 @@ describe("Moves - Delayed Attacks", () => {
expect(typeBoostSpy).not.toHaveBeenCalled();
});
+ it("should not crash when catching & releasing a Pokemon on the same turn its delayed attack expires", async () => {
+ game.override.startingModifier([{ name: "MASTER_BALL", count: 1 }]);
+ await game.classicMode.startBattle([
+ SpeciesId.FEEBAS,
+ SpeciesId.FEEBAS,
+ SpeciesId.FEEBAS,
+ SpeciesId.FEEBAS,
+ SpeciesId.FEEBAS,
+ SpeciesId.FEEBAS,
+ ]);
+
+ game.move.use(MoveId.SPLASH);
+ await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT);
+ await game.toNextTurn();
+
+ expectFutureSightActive(1);
+
+ await passTurns(1);
+
+ // Throw master ball and release the enemy
+ game.doThrowPokeball(PokeballType.MASTER_BALL);
+ game.onNextPrompt("AttemptCapturePhase", UiMode.CONFIRM, () => {
+ game.scene.ui.processInput(Button.CANCEL);
+ });
+ await game.toEndOfTurn();
+
+ expectFutureSightActive(0);
+ });
+
// TODO: Implement and move to a power spot's test file
it.todo("Should activate ally's power spot when switched in during single battles");
});
diff --git a/test/moves/dragon-cheer.test.ts b/test/moves/dragon-cheer.test.ts
index 614dd9ab6ab..50880e067d9 100644
--- a/test/moves/dragon-cheer.test.ts
+++ b/test/moves/dragon-cheer.test.ts
@@ -1,15 +1,18 @@
import { AbilityId } from "#enums/ability-id";
import { BattlerIndex } from "#enums/battler-index";
+import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id";
+import { MoveResult } from "#enums/move-result";
import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
-describe("Moves - Dragon Cheer", () => {
+describe("Move - Dragon Cheer", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
+
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
@@ -24,75 +27,81 @@ describe("Moves - Dragon Cheer", () => {
game = new GameManager(phaserGame);
game.override
.battleStyle("double")
+ .ability(AbilityId.BALL_FETCH)
.enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset(MoveId.SPLASH)
- .enemyLevel(20)
- .moveset([MoveId.DRAGON_CHEER, MoveId.TACKLE, MoveId.SPLASH]);
+ .enemyLevel(20);
});
- it("increases the user's allies' critical hit ratio by one stage", async () => {
+ it("should increase non-Dragon type allies' crit ratios by 1 stage", async () => {
await game.classicMode.startBattle([SpeciesId.DRAGONAIR, SpeciesId.MAGIKARP]);
- const enemy = game.scene.getEnemyField()[0];
-
+ const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "getCritStage");
- game.move.select(MoveId.DRAGON_CHEER, 0);
- game.move.select(MoveId.TACKLE, 1, BattlerIndex.ENEMY);
-
+ game.move.use(MoveId.DRAGON_CHEER, BattlerIndex.PLAYER);
+ game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
+ await game.toEndOfTurn();
- // After Tackle
- await game.phaseInterceptor.to("TurnEndPhase");
+ const [dragonair, magikarp] = game.scene.getPlayerField();
+ expect(dragonair).not.toHaveBattlerTag(BattlerTagType.DRAGON_CHEER);
+ expect(magikarp).toHaveBattlerTag({ tagType: BattlerTagType.DRAGON_CHEER, critStages: 1 });
expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
});
- it("increases the user's Dragon-type allies' critical hit ratio by two stages", async () => {
+ it("should increase Dragon-type allies' crit ratios by 2 stages", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.DRAGONAIR]);
- const enemy = game.scene.getEnemyField()[0];
-
+ const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "getCritStage");
- game.move.select(MoveId.DRAGON_CHEER, 0);
- game.move.select(MoveId.TACKLE, 1, BattlerIndex.ENEMY);
-
+ game.move.use(MoveId.DRAGON_CHEER, BattlerIndex.PLAYER);
+ game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
+ await game.toEndOfTurn();
- // After Tackle
- await game.phaseInterceptor.to("TurnEndPhase");
+ const [magikarp, dragonair] = game.scene.getPlayerField();
+ expect(magikarp).not.toHaveBattlerTag(BattlerTagType.DRAGON_CHEER);
+ expect(dragonair).toHaveBattlerTag({ tagType: BattlerTagType.DRAGON_CHEER, critStages: 2 });
expect(enemy.getCritStage).toHaveReturnedWith(2); // getCritStage is called on defender
});
- it("applies the effect based on the allies' type upon use of the move, and do not change if the allies' type changes later in battle", async () => {
+ it("should maintain crit boost amount even if user's type is changed", async () => {
await game.classicMode.startBattle([SpeciesId.DRAGONAIR, SpeciesId.MAGIKARP]);
- const magikarp = game.scene.getPlayerField()[1];
- const enemy = game.scene.getEnemyField()[0];
-
- vi.spyOn(enemy, "getCritStage");
-
- game.move.select(MoveId.DRAGON_CHEER, 0);
- game.move.select(MoveId.TACKLE, 1, BattlerIndex.ENEMY);
-
+ // Use Reflect Type to become Dragon-type mid-turn
+ game.move.use(MoveId.DRAGON_CHEER, BattlerIndex.PLAYER);
+ game.move.use(MoveId.REFLECT_TYPE, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
-
- // After Tackle
- await game.phaseInterceptor.to("TurnEndPhase");
- expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
-
- await game.toNextTurn();
-
- // Change Magikarp's type to Dragon
- vi.spyOn(magikarp, "getTypes").mockReturnValue([PokemonType.DRAGON]);
- expect(magikarp.getTypes()).toEqual([PokemonType.DRAGON]);
-
- game.move.select(MoveId.SPLASH, 0);
- game.move.select(MoveId.TACKLE, 1, BattlerIndex.ENEMY);
-
- await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
-
await game.phaseInterceptor.to("MoveEndPhase");
- expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
+
+ // Dragon cheer added +1 stages
+ const magikarp = game.scene.getPlayerField()[1];
+ expect(magikarp).toHaveBattlerTag({ tagType: BattlerTagType.DRAGON_CHEER, critStages: 1 });
+ expect(magikarp).toHaveTypes([PokemonType.WATER]);
+
+ await game.toEndOfTurn();
+
+ // Should be dragon type, but still with a +1 stage boost
+ expect(magikarp).toHaveTypes([PokemonType.DRAGON]);
+ expect(magikarp).toHaveBattlerTag({ tagType: BattlerTagType.DRAGON_CHEER, critStages: 1 });
+ });
+
+ it.each([
+ { name: "Focus Energy", tagType: BattlerTagType.CRIT_BOOST },
+ { name: "Dragon Cheer", tagType: BattlerTagType.DRAGON_CHEER },
+ ])("should fail if $name is already present", async ({ tagType }) => {
+ await game.classicMode.startBattle([SpeciesId.DRAGONAIR, SpeciesId.MAGIKARP]);
+
+ const [dragonair, magikarp] = game.scene.getPlayerField();
+ magikarp.addTag(tagType);
+
+ game.move.use(MoveId.DRAGON_CHEER, BattlerIndex.PLAYER);
+ game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
+ await game.toEndOfTurn();
+
+ expect(dragonair).toHaveUsedMove({ move: MoveId.DRAGON_CHEER, result: MoveResult.FAIL });
+ expect(magikarp).toHaveBattlerTag(tagType);
});
});
diff --git a/test/moves/entry-hazards.test.ts b/test/moves/entry-hazards.test.ts
index c4dead1bb67..af8145183a3 100644
--- a/test/moves/entry-hazards.test.ts
+++ b/test/moves/entry-hazards.test.ts
@@ -200,7 +200,7 @@ describe("Moves - Entry Hazards", () => {
expect(enemy).toHaveTakenDamage(enemy.getMaxHp() * 0.125 * multi);
expect(game.textInterceptor.logs).toContain(
i18next.t("arenaTag:stealthRockActivateTrap", {
- pokemonName: getPokemonNameWithAffix(enemy),
+ pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
}),
);
});
diff --git a/test/moves/flame-burst.test.ts b/test/moves/flame-burst.test.ts
index ce82b46d0fc..e340936f94c 100644
--- a/test/moves/flame-burst.test.ts
+++ b/test/moves/flame-burst.test.ts
@@ -16,7 +16,7 @@ describe("Moves - Flame Burst", () => {
* Calculates the effect damage of Flame Burst which is 1/16 of the target ally's max HP
* See Flame Burst {@link https://bulbapedia.bulbagarden.net/wiki/Flame_Burst_(move)}
* See Flame Burst's move attribute {@linkcode FlameBurstAttr}
- * @param pokemon {@linkcode Pokemon} - The ally of the move's target
+ * @param pokemon - The ally of the move's target
* @returns Effect damage of Flame Burst
*/
const getEffectDamage = (pokemon: Pokemon): number => {
diff --git a/test/moves/focus-energy.test.ts b/test/moves/focus-energy.test.ts
new file mode 100644
index 00000000000..3c2882f5bf3
--- /dev/null
+++ b/test/moves/focus-energy.test.ts
@@ -0,0 +1,69 @@
+import { AbilityId } from "#enums/ability-id";
+import { BattlerTagType } from "#enums/battler-tag-type";
+import { MoveId } from "#enums/move-id";
+import { MoveResult } from "#enums/move-result";
+import { SpeciesId } from "#enums/species-id";
+import { GameManager } from "#test/test-utils/game-manager";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+
+describe("Move - Focus Energy", () => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(() => {
+ game = new GameManager(phaserGame);
+ game.override
+ .ability(AbilityId.BALL_FETCH)
+ .battleStyle("single")
+ .criticalHits(false)
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyAbility(AbilityId.BALL_FETCH)
+ .enemyMoveset(MoveId.SPLASH)
+ .startingLevel(100)
+ .enemyLevel(100);
+ });
+
+ it("should increase the user's crit ratio by 2 stages", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ game.move.use(MoveId.FOCUS_ENERGY);
+ await game.toNextTurn();
+
+ const feebas = game.field.getPlayerPokemon();
+ expect(feebas).toHaveBattlerTag({ tagType: BattlerTagType.CRIT_BOOST, critStages: 2 });
+
+ const enemy = game.field.getEnemyPokemon();
+ vi.spyOn(enemy, "getCritStage");
+
+ game.move.use(MoveId.TACKLE);
+ await game.toEndOfTurn();
+
+ expect(enemy.getCritStage).toHaveReturnedWith(2);
+ });
+
+ it.each([
+ { name: "Focus Energy", tagType: BattlerTagType.CRIT_BOOST },
+ { name: "Dragon Cheer", tagType: BattlerTagType.DRAGON_CHEER },
+ ])("should fail if $name is already present", async ({ tagType }) => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ const feebas = game.field.getPlayerPokemon();
+ feebas.addTag(tagType);
+
+ game.move.use(MoveId.FOCUS_ENERGY);
+ await game.toEndOfTurn();
+
+ expect(feebas).toHaveUsedMove({ move: MoveId.FOCUS_ENERGY, result: MoveResult.FAIL });
+ });
+});
diff --git a/test/moves/focus-punch.test.ts b/test/moves/focus-punch.test.ts
index 9a76dbec0db..d7b40569aaa 100644
--- a/test/moves/focus-punch.test.ts
+++ b/test/moves/focus-punch.test.ts
@@ -9,7 +9,7 @@ import { TurnStartPhase } from "#phases/turn-start-phase";
import { GameManager } from "#test/test-utils/game-manager";
import i18next from "i18next";
import Phaser from "phaser";
-import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Focus Punch", () => {
let phaserGame: Phaser.Game;
@@ -125,8 +125,8 @@ describe("Moves - Focus Punch", () => {
game.move.select(MoveId.FOCUS_PUNCH);
await game.phaseInterceptor.to("MoveEndPhase", true);
await game.phaseInterceptor.to("MessagePhase", false);
- const consoleSpy = vi.spyOn(console, "log");
await game.phaseInterceptor.to("MoveEndPhase", true);
- expect(consoleSpy).nthCalledWith(1, i18next.t("moveTriggers:lostFocus", { pokemonName: "Charizard" }));
+ expect(game.textInterceptor.logs).toContain(i18next.t("moveTriggers:lostFocus", { pokemonName: "Charizard" }));
+ expect(game.textInterceptor.logs).not.toContain(i18next.t("battle:attackFailed"));
});
});
diff --git a/test/moves/laser-focus.test.ts b/test/moves/laser-focus.test.ts
new file mode 100644
index 00000000000..7496e3ed87f
--- /dev/null
+++ b/test/moves/laser-focus.test.ts
@@ -0,0 +1,104 @@
+import { getPokemonNameWithAffix } from "#app/messages";
+import { AbilityId } from "#enums/ability-id";
+import { BattlerIndex } from "#enums/battler-index";
+import { BattlerTagType } from "#enums/battler-tag-type";
+import { MoveId } from "#enums/move-id";
+import { SpeciesId } from "#enums/species-id";
+import { GameManager } from "#test/test-utils/game-manager";
+import i18next from "i18next";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+
+describe("Move - Laser Focus", () => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(() => {
+ game = new GameManager(phaserGame);
+ game.override
+ .ability(AbilityId.BALL_FETCH)
+ .battleStyle("single")
+ .criticalHits(false)
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyAbility(AbilityId.BALL_FETCH)
+ .enemyMoveset(MoveId.SPLASH)
+ .startingLevel(100)
+ .enemyLevel(100);
+ });
+
+ it("should make the user's next attack a guaranteed critical hit", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ game.move.use(MoveId.LASER_FOCUS);
+ await game.toNextTurn();
+
+ const feebas = game.field.getPlayerPokemon();
+ expect(feebas).toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
+ expect(game).toHaveShownMessage(
+ i18next.t("battlerTags:laserFocusOnAdd", {
+ pokemonNameWithAffix: getPokemonNameWithAffix(feebas),
+ }),
+ );
+
+ const enemy = game.field.getEnemyPokemon();
+ const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
+
+ game.move.use(MoveId.TACKLE);
+ await game.toEndOfTurn();
+
+ expect(critSpy).toHaveLastReturnedWith(true);
+ });
+
+ it("should disappear at the end of the next turn", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ const feebas = game.field.getPlayerPokemon();
+
+ game.move.use(MoveId.LASER_FOCUS);
+ await game.toNextTurn();
+
+ expect(feebas).toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
+
+ game.move.use(MoveId.SPLASH);
+ await game.toNextTurn();
+
+ expect(feebas).not.toHaveBattlerTag(BattlerTagType.ALWAYS_CRIT);
+
+ const enemy = game.field.getEnemyPokemon();
+ const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
+
+ game.move.use(MoveId.TACKLE);
+ await game.toEndOfTurn();
+
+ expect(critSpy).toHaveLastReturnedWith(false);
+ });
+
+ it("should boost all attacks until the end of the next turn", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ game.move.use(MoveId.LASER_FOCUS);
+ await game.toNextTurn();
+
+ const enemy = game.field.getEnemyPokemon();
+ const critSpy = vi.spyOn(enemy, "getCriticalHitResult");
+
+ game.move.use(MoveId.TACKLE);
+ await game.move.forceEnemyMove(MoveId.INSTRUCT);
+ await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
+ await game.toEndOfTurn();
+
+ expect(critSpy).toHaveReturnedTimes(2);
+ expect(critSpy).toHaveNthReturnedWith(1, true);
+ expect(critSpy).toHaveNthReturnedWith(2, true);
+ });
+});
diff --git a/test/moves/light-screen.test.ts b/test/moves/light-screen.test.ts
index b6eb2725346..c715229d4a6 100644
--- a/test/moves/light-screen.test.ts
+++ b/test/moves/light-screen.test.ts
@@ -127,17 +127,15 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
- if (globalScene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side)) {
- if (move.getAttrs("CritOnlyAttr").length === 0) {
- globalScene.arena.applyTagsForSide(
- ArenaTagType.LIGHT_SCREEN,
- side,
- false,
- attacker,
- move.category,
- multiplierHolder,
- );
- }
+ if (globalScene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side) && move.getAttrs("CritOnlyAttr").length === 0) {
+ globalScene.arena.applyTagsForSide(
+ ArenaTagType.LIGHT_SCREEN,
+ side,
+ false,
+ attacker,
+ move.category,
+ multiplierHolder,
+ );
}
return move.power * multiplierHolder.value;
diff --git a/test/moves/pledge-moves.test.ts b/test/moves/pledge-moves.test.ts
index 7a9c4112d7d..34058829d07 100644
--- a/test/moves/pledge-moves.test.ts
+++ b/test/moves/pledge-moves.test.ts
@@ -126,7 +126,7 @@ describe("Moves - Pledge Moves", () => {
expect(playerPokemon[1].getMoveType).toHaveLastReturnedWith(PokemonType.FIRE);
expect(grassPledge.calculateBattlePower).toHaveLastReturnedWith(150);
- const baseDmg = baseDmgMock.mock.results[baseDmgMock.mock.results.length - 1].value;
+ const baseDmg = baseDmgMock.mock.results.at(-1)!.value;
expect(enemyPokemon[0].getMaxHp() - enemyPokemon[0].hp).toBe(toDmgValue(baseDmg * 1.5));
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); // PLAYER should not have attacked
expect(game.scene.arena.getTagOnSide(ArenaTagType.FIRE_GRASS_PLEDGE, ArenaTagSide.ENEMY)).toBeDefined();
diff --git a/test/moves/pollen-puff.test.ts b/test/moves/pollen-puff.test.ts
index 76732a39c43..02772055f1f 100644
--- a/test/moves/pollen-puff.test.ts
+++ b/test/moves/pollen-puff.test.ts
@@ -61,4 +61,16 @@ describe("Moves - Pollen Puff", () => {
expect(target.battleData.hitCount).toBe(2);
});
+
+ // Regression test for pollen puff healing an enemy after dealing damage
+ it("should not heal an enemy after dealing damage", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+ const target = game.field.getEnemyPokemon();
+ game.move.use(MoveId.POLLEN_PUFF);
+
+ await game.phaseInterceptor.to("BerryPhase", false);
+
+ expect(target.hp).not.toBe(target.getMaxHp());
+ expect(game.phaseInterceptor.log).not.toContain("PokemonHealPhase");
+ });
});
diff --git a/test/moves/poltergeist.test.ts b/test/moves/poltergeist.test.ts
new file mode 100644
index 00000000000..3e603702416
--- /dev/null
+++ b/test/moves/poltergeist.test.ts
@@ -0,0 +1,50 @@
+import { AbilityId } from "#enums/ability-id";
+import { BattlerIndex } from "#enums/battler-index";
+import { MoveId } from "#enums/move-id";
+import { MoveResult } from "#enums/move-result";
+import { SpeciesId } from "#enums/species-id";
+import { GameManager } from "#test/test-utils/game-manager";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
+
+describe("Move - Poltergeist", () => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(() => {
+ game = new GameManager(phaserGame);
+ game.override
+ .ability(AbilityId.BALL_FETCH)
+ .battleStyle("single")
+ .criticalHits(false)
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyAbility(AbilityId.BALL_FETCH)
+ .enemyMoveset(MoveId.SPLASH)
+ .startingLevel(100)
+ .enemyLevel(100);
+ });
+
+ it("should not crash when used after both opponents have fainted", async () => {
+ game.override.battleStyle("double").enemyLevel(5);
+ await game.classicMode.startBattle([SpeciesId.STARYU, SpeciesId.SLOWPOKE]);
+
+ game.move.use(MoveId.DAZZLING_GLEAM);
+ game.move.use(MoveId.POLTERGEIST, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY);
+ const [_, poltergeistUser] = game.scene.getPlayerField();
+ await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
+ await game.toEndOfTurn();
+ // Expect poltergeist to have failed
+ expect(poltergeistUser).toHaveUsedMove({ move: MoveId.POLTERGEIST, result: MoveResult.FAIL });
+ // If the test makes it to the end of turn, no crash occurred. Nothing to assert
+ });
+});
diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts
index 426027d3f31..2d1593fb935 100644
--- a/test/moves/reflect.test.ts
+++ b/test/moves/reflect.test.ts
@@ -143,10 +143,8 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
- if (globalScene.arena.getTagOnSide(ArenaTagType.REFLECT, side)) {
- if (move.getAttrs("CritOnlyAttr").length === 0) {
- globalScene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, attacker, move.category, multiplierHolder);
- }
+ if (globalScene.arena.getTagOnSide(ArenaTagType.REFLECT, side) && move.getAttrs("CritOnlyAttr").length === 0) {
+ globalScene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, attacker, move.category, multiplierHolder);
}
return move.power * multiplierHolder.value;
diff --git a/test/moves/splash-celebrate.test.ts b/test/moves/splash-celebrate.test.ts
new file mode 100644
index 00000000000..346ffedd12c
--- /dev/null
+++ b/test/moves/splash-celebrate.test.ts
@@ -0,0 +1,52 @@
+import { loggedInUser } from "#app/account";
+import { AbilityId } from "#enums/ability-id";
+import { MoveId } from "#enums/move-id";
+import { SpeciesId } from "#enums/species-id";
+import { GameManager } from "#test/test-utils/game-manager";
+import i18next from "i18next";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
+
+describe.each<{ name: string; move: MoveId; message: () => string }>([
+ { name: "Splash", move: MoveId.SPLASH, message: () => i18next.t("moveTriggers:splash") },
+ {
+ name: "Celebrate",
+ move: MoveId.CELEBRATE,
+ message: () => i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username }),
+ },
+])("Move - $name", ({ move, message }) => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(() => {
+ game = new GameManager(phaserGame);
+ game.override
+ .ability(AbilityId.BALL_FETCH)
+ .battleStyle("single")
+ .criticalHits(false)
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyAbility(AbilityId.BALL_FETCH)
+ .enemyMoveset(MoveId.TACKLE)
+ .startingLevel(100)
+ .enemyLevel(100);
+ });
+
+ it("should show a message on use", async () => {
+ await game.classicMode.startBattle([SpeciesId.FEEBAS]);
+
+ game.move.use(move);
+ await game.toEndOfTurn();
+
+ expect(game).toHaveShownMessage(message());
+ });
+});
diff --git a/test/moves/transform-imposter.test.ts b/test/moves/transform-imposter.test.ts
index b1631130154..1b38a4bce9c 100644
--- a/test/moves/transform-imposter.test.ts
+++ b/test/moves/transform-imposter.test.ts
@@ -212,7 +212,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
@@ -242,7 +242,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
- expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
+ expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
diff --git a/test/moves/wish.test.ts b/test/moves/wish.test.ts
index 55877edbfd4..1c1f3f3b8ba 100644
--- a/test/moves/wish.test.ts
+++ b/test/moves/wish.test.ts
@@ -55,7 +55,7 @@ describe("Move - Wish", () => {
await game.toEndOfTurn();
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
- expect(game.textInterceptor.logs).toContain(
+ expect(game).toHaveShownMessage(
i18next.t("arenaTag:wishTagOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(alomomola),
}),
@@ -165,7 +165,7 @@ describe("Move - Wish", () => {
// Wish went away without doing anything
expect(game).toHavePositionalTag(PositionalTagType.WISH, 0);
- expect(game.textInterceptor.logs).not.toContain(
+ expect(game).not.toHaveShownMessage(
i18next.t("arenaTag:wishTagOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(blissey),
}),
diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts
index 7b2dbfc9aeb..fcf27b2c6fb 100644
--- a/test/mystery-encounter/encounter-test-utils.ts
+++ b/test/mystery-encounter/encounter-test-utils.ts
@@ -13,10 +13,10 @@ import {
} from "#phases/mystery-encounter-phases";
import { VictoryPhase } from "#phases/victory-phase";
import type { GameManager } from "#test/test-utils/game-manager";
-import type { MessageUiHandler } from "#ui/message-ui-handler";
-import type { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler";
+import type { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import type { MysteryEncounterUiHandler } from "#ui/handlers/mystery-encounter-ui-handler";
+import type { PartyUiHandler } from "#ui/handlers/party-ui-handler";
import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
import { isNullOrUndefined } from "#utils/common";
import { expect, vi } from "vitest";
diff --git a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts
index 93cf4537c53..8dc4348adae 100644
--- a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts
+++ b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts
@@ -9,7 +9,6 @@ import { ATrainersTestEncounter } from "#mystery-encounters/a-trainers-test-enco
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { PartyHealPhase } from "#phases/party-heal-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@@ -106,7 +105,7 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(scene.currentBattle.trainer).toBeDefined();
expect(
@@ -131,13 +130,13 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();
expect(eggsBeforeLength + 1).toBe(eggsAfter.length);
- const eggTier = eggsAfter[eggsAfter.length - 1].tier;
- expect(eggTier === EggTier.EPIC || eggTier === EggTier.LEGENDARY).toBeTruthy();
+ const eggTier = eggsAfter.at(-1)?.tier;
+ expect(eggTier).toBeOneOf([EggTier.EPIC, EggTier.LEGENDARY]);
});
});
@@ -179,12 +178,12 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();
expect(eggsBeforeLength + 1).toBe(eggsAfter.length);
- const eggTier = eggsAfter[eggsAfter.length - 1].tier;
+ const eggTier = eggsAfter.at(-1)?.tier;
expect(eggTier).toBe(EggTier.RARE);
});
diff --git a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts
index 562482dd520..d269e40db0f 100644
--- a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts
+++ b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts
@@ -10,7 +10,6 @@ import { BerryModifier, PokemonHeldItemModifier } from "#modifiers/modifier";
import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@@ -132,7 +131,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GREEDENT);
const moveset = enemyField[0].moveset.map(m => m.moveId);
@@ -148,7 +147,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
for (const partyPokemon of scene.getPlayerParty()) {
const pokemonId = partyPokemon.id;
diff --git a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts
index 12c5a6515bc..44585d4d795 100644
--- a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts
+++ b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts
@@ -11,15 +11,13 @@ import { BerriesAboundEncounter } from "#mystery-encounters/berries-abound-encou
import * as EncounterDialogueUtils from "#mystery-encounters/encounter-dialogue-utils";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/berriesAbound";
@@ -114,7 +112,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
});
@@ -135,7 +133,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0);
@@ -186,7 +184,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@@ -210,7 +208,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@@ -230,8 +228,6 @@ describe("Berries Abound - Mystery Encounter", () => {
});
await runMysteryEncounterToEnd(game, 2);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts
index 13d3c030c63..a3357b00b89 100644
--- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts
+++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts
@@ -12,9 +12,7 @@ import { PokemonMove } from "#moves/pokemon-move";
import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter";
import * as encounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
-import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
+import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import {
runMysteryEncounterToEnd,
runSelectMysteryEncounterOption,
@@ -22,7 +20,7 @@ import {
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/bugTypeSuperfan";
@@ -231,7 +229,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(2);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -244,7 +242,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(3);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -258,7 +256,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(4);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -273,7 +271,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -289,7 +287,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -307,7 +305,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -325,7 +323,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -343,7 +341,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@@ -365,7 +363,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterRewardsPhase");
game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers
game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
game.endPhase();
@@ -406,7 +404,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -416,7 +414,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -435,7 +433,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -457,7 +455,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -481,7 +479,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -542,7 +540,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -557,7 +555,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts
index f02a5c623af..d199a331943 100644
--- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts
+++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts
@@ -22,18 +22,15 @@ import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-enc
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
-import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
+import type { PartyUiHandler } from "#ui/handlers/party-ui-handler";
import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -171,7 +168,7 @@ describe("Clowning Around - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.MR_MIME);
expect(enemyField[0].moveset).toEqual([
@@ -199,9 +196,6 @@ describe("Clowning Around - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
- await game.phaseInterceptor.to("SelectModifierPhase");
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => {
@@ -215,7 +209,7 @@ describe("Clowning Around - Mystery Encounter", () => {
vi.spyOn(partyUiHandler, "show");
game.endPhase();
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("PostMysteryEncounterPhase");
// Wait for Yes/No confirmation to appear
await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled());
diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts
index de47b074089..bf42e6d4df3 100644
--- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts
+++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts
@@ -9,18 +9,16 @@ import { UiMode } from "#enums/ui-mode";
import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { LearnMovePhase } from "#phases/learn-move-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
runSelectMysteryEncounterOption,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/dancingLessons";
@@ -105,7 +103,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.ORICORIO);
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]);
@@ -126,7 +124,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -226,7 +224,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
const partyCountAfter = scene.getPlayerParty().length;
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts
index 7398b639f1c..fe17f091d0e 100644
--- a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts
+++ b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts
@@ -161,7 +161,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -316,7 +316,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -449,7 +449,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts
index 3c19d458049..d6c566c4de6 100644
--- a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts
+++ b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts
@@ -9,10 +9,9 @@ import { DepartmentStoreSaleEncounter } from "#mystery-encounters/department-sto
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/departmentStoreSale";
@@ -93,7 +92,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only TMs", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -130,7 +129,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Vitamins", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -140,8 +139,8 @@ describe("Department Store Sale - Mystery Encounter", () => {
expect(modifierSelectHandler.options.length).toEqual(3);
for (const option of modifierSelectHandler.options) {
expect(
- option.modifierTypeOption.type.id.includes("PP_UP") ||
- option.modifierTypeOption.type.id.includes("BASE_STAT_BOOSTER"),
+ option.modifierTypeOption.type.id.includes("PP_UP")
+ || option.modifierTypeOption.type.id.includes("BASE_STAT_BOOSTER"),
).toBeTruthy();
}
});
@@ -170,7 +169,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only X Items", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -180,8 +179,8 @@ describe("Department Store Sale - Mystery Encounter", () => {
expect(modifierSelectHandler.options.length).toEqual(5);
for (const option of modifierSelectHandler.options) {
expect(
- option.modifierTypeOption.type.id.includes("DIRE_HIT") ||
- option.modifierTypeOption.type.id.includes("TEMP_STAT_STAGE_BOOSTER"),
+ option.modifierTypeOption.type.id.includes("DIRE_HIT")
+ || option.modifierTypeOption.type.id.includes("TEMP_STAT_STAGE_BOOSTER"),
).toBeTruthy();
}
});
@@ -210,7 +209,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Pokeballs", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 4);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/test/mystery-encounter/encounters/field-trip-encounter.test.ts
index fd3e20012b1..b8e6e36cf17 100644
--- a/test/mystery-encounter/encounters/field-trip-encounter.test.ts
+++ b/test/mystery-encounter/encounters/field-trip-encounter.test.ts
@@ -12,7 +12,7 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import i18next from "i18next";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts
index 54f790ca207..91a32c025d5 100644
--- a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts
+++ b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts
@@ -16,7 +16,6 @@ import { AttackTypeBoosterModifier, PokemonHeldItemModifier } from "#modifiers/m
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
@@ -161,7 +160,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.VOLCARONA);
expect(enemyField[1].species.speciesId).toBe(SpeciesId.VOLCARONA);
@@ -177,7 +176,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonId = scene.getPlayerParty()?.[0].id;
const leadPokemonItems = scene.findModifiers(
@@ -266,7 +265,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
await runMysteryEncounterToEnd(game, 3);
await game.phaseInterceptor.to(SelectModifierPhase, false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonItems = scene.getPlayerParty()[0].getHeldItems() as PokemonHeldItemModifier[];
const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier);
@@ -292,7 +291,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(continueEncounterSpy).not.toHaveBeenCalled();
});
});
diff --git a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts
index 8650b42ce4d..34c0f635000 100644
--- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts
+++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts
@@ -9,9 +9,7 @@ import { UiMode } from "#enums/ui-mode";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
runSelectMysteryEncounterOption,
@@ -19,7 +17,7 @@ import {
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/fightOrFlight";
@@ -109,7 +107,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
});
@@ -122,8 +120,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
+
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find(
@@ -165,7 +164,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -182,7 +181,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts
index 7bfaaac1141..8a058bad5fe 100644
--- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts
+++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts
@@ -14,16 +14,15 @@ import { FunAndGamesEncounter } from "#mystery-encounters/fun-and-games-encounte
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
+import type { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
runSelectMysteryEncounterOption,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/funAndGames";
@@ -131,7 +130,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -143,7 +142,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET);
expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]);
expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD);
@@ -165,7 +164,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
});
it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => {
@@ -173,7 +172,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@@ -184,7 +183,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -200,7 +199,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@@ -213,7 +212,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -230,7 +229,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@@ -243,7 +242,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -260,7 +259,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@@ -273,7 +272,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts
index bb56505ac48..90bc41fde2b 100644
--- a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts
+++ b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts
@@ -13,10 +13,9 @@ import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"
import { GlobalTradeSystemEncounter } from "#mystery-encounters/global-trade-system-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import * as Utils from "#utils/common";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -226,7 +225,7 @@ describe("Global Trade System - Mystery Encounter", () => {
await scene.updateModifiers(true);
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts
index 73134381553..ed0ca02720c 100644
--- a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts
+++ b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts
@@ -147,7 +147,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -212,7 +212,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts
index 0c4e3044bbd..ed8b6bffbe9 100644
--- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts
+++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts
@@ -12,8 +12,6 @@ import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-c
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
-import { CommandPhase } from "#phases/command-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
@@ -22,7 +20,7 @@ import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
import { TrainerConfig } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/mysteriousChallengers";
@@ -152,7 +150,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@@ -162,7 +160,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -196,7 +194,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@@ -206,7 +204,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -215,20 +213,20 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.GREAT);
expect(
- modifierSelectHandler.options[3].modifierTypeOption.type.tier -
- modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[3].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.GREAT);
});
});
@@ -253,7 +251,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 3, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@@ -262,8 +260,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -272,20 +269,20 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[3].modifierTypeOption.type.tier -
- modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[3].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
).toBe(ModifierTier.GREAT);
});
});
diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts
index 36a92b2b6bf..1826c75381a 100644
--- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts
+++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts
@@ -246,7 +246,7 @@ describe("Part-Timer - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/safari-zone.test.ts b/test/mystery-encounter/encounters/safari-zone.test.ts
index ec43dcfb69b..dd41d08df38 100644
--- a/test/mystery-encounter/encounters/safari-zone.test.ts
+++ b/test/mystery-encounter/encounters/safari-zone.test.ts
@@ -119,7 +119,7 @@ describe("Safari Zone - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts
index 4d006abc636..a096ea5ff6e 100644
--- a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts
+++ b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts
@@ -8,9 +8,7 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter";
-import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
runSelectMysteryEncounterOption,
@@ -18,7 +16,7 @@ import {
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import i18next from "i18next";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -157,7 +155,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -167,7 +165,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
});
it("should transport to a new area", async () => {
@@ -229,7 +227,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -239,7 +237,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.METAGROSS]);
await runMysteryEncounterToEnd(game, 2, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
});
it("should transport to a new area", async () => {
@@ -300,7 +298,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts
index ade98bfa99f..ae4eb0647ce 100644
--- a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts
+++ b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts
@@ -12,8 +12,6 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
-import { CommandPhase } from "#phases/command-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
@@ -157,7 +155,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@@ -175,8 +173,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
+ await game.phaseInterceptor.to("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs;
@@ -242,7 +240,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@@ -260,8 +258,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
+ await game.phaseInterceptor.to("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs;
@@ -324,7 +322,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@@ -342,8 +340,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
+ await game.phaseInterceptor.to("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs;
diff --git a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts
index 3880c07c312..4011a850a08 100644
--- a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts
+++ b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts
@@ -182,7 +182,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts
index 3592e2dc774..5df2d2ab358 100644
--- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts
+++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts
@@ -17,16 +17,14 @@ import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -192,7 +190,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.SHUCKLE);
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]);
@@ -230,7 +228,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts
index cf0ff7a94bd..9b009879522 100644
--- a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts
+++ b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts
@@ -15,14 +15,12 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
-import { CommandPhase } from "#phases/command-phase";
import { PartyHealPhase } from "#phases/party-heal-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { VictoryPhase } from "#phases/victory-phase";
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -262,7 +260,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR);
expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(4);
@@ -295,7 +293,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
// Should have Macho Brace in the rewards
await skipBattleToNextBattle(game, true);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -337,7 +335,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
it("should have a Rarer Candy in the rewards", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
await runMysteryEncounterToEnd(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
diff --git a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts
index 7cc9a69b32d..6f6c01c7322 100644
--- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts
+++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts
@@ -20,16 +20,14 @@ import {
} from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import * as Utils from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -172,8 +170,8 @@ describe("Trash to Treasure - Mystery Encounter", () => {
it("should give 1 Leftovers, 1 Shell Bell, and Black Sludge", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
await runMysteryEncounterToEnd(game, 1);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
+ await game.phaseInterceptor.to("SelectModifierPhase");
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
expect(leftovers).toBeDefined();
@@ -221,7 +219,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GARBODOR);
expect(enemyField[0].moveset).toEqual([
@@ -243,7 +241,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -252,20 +250,20 @@ describe("Trash to Treasure - Mystery Encounter", () => {
) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[3].modifierTypeOption.type.tier -
- modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[3].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.GREAT);
});
});
diff --git a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts
index 5aadaf5c29a..e5b086ceba9 100644
--- a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts
+++ b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts
@@ -15,7 +15,6 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter";
-import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { StatStageChangePhase } from "#phases/stat-stage-change-phase";
@@ -120,7 +119,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@@ -147,7 +146,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@@ -199,7 +198,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@@ -259,7 +258,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts
index e2ec7ae514a..3640ed3809f 100644
--- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts
+++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts
@@ -10,15 +10,13 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import * as EncounterTransformationSequence from "#mystery-encounters/encounter-transformation-sequence";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter";
-import { CommandPhase } from "#phases/command-phase";
-import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
skipBattleRunMysteryEncounterRewardsPhase,
} from "#test/mystery-encounter/encounter-test-utils";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const namespace = "mysteryEncounters/weirdDream";
@@ -116,8 +114,8 @@ describe("Weird Dream - Mystery Encounter", () => {
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
await runMysteryEncounterToEnd(game, 1);
- await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
+ await game.phaseInterceptor.to("SelectModifierPhase");
const pokemonAfter = scene.getPlayerParty();
const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal());
@@ -140,7 +138,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -187,7 +185,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
+ expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length);
});
@@ -197,7 +195,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to("SelectModifierPhase", false);
- expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
+ expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@@ -206,28 +204,28 @@ describe("Weird Dream - Mystery Encounter", () => {
) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(6);
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[3].modifierTypeOption.type.tier -
- modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[3].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[4].modifierTypeOption.type.tier -
- modifierSelectHandler.options[4].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[4].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[4].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.GREAT);
expect(
- modifierSelectHandler.options[5].modifierTypeOption.type.tier -
- modifierSelectHandler.options[5].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[5].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[5].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.GREAT);
});
});
diff --git a/test/mystery-encounter/mystery-encounter.test.ts b/test/mystery-encounter/mystery-encounter.test.ts
index ec27f7c6a48..d44e3dd2905 100644
--- a/test/mystery-encounter/mystery-encounter.test.ts
+++ b/test/mystery-encounter/mystery-encounter.test.ts
@@ -34,7 +34,7 @@ describe("Mystery Encounters", () => {
]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
- expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
});
it("Encounters should not run on X1 waves", async () => {
diff --git a/test/phases/mystery-encounter-phase.test.ts b/test/phases/mystery-encounter-phase.test.ts
index a3dc779b02c..8dfbd509a05 100644
--- a/test/phases/mystery-encounter-phase.test.ts
+++ b/test/phases/mystery-encounter-phase.test.ts
@@ -3,10 +3,10 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
-import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
+import { MysteryEncounterOptionSelectedPhase } from "#phases/mystery-encounter-phases";
import { GameManager } from "#test/test-utils/game-manager";
-import type { MessageUiHandler } from "#ui/message-ui-handler";
-import type { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler";
+import type { MessageUiHandler } from "#ui/handlers/message-ui-handler";
+import type { MysteryEncounterUiHandler } from "#ui/handlers/mystery-encounter-ui-handler";
import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -38,7 +38,7 @@ describe("Mystery Encounter Phases", () => {
]);
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
- expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
+ expect(game).toBeAtPhase("MysteryEncounterPhase");
});
it("Runs MysteryEncounterPhase", async () => {
diff --git a/test/phases/select-modifier-phase.test.ts b/test/phases/select-modifier-phase.test.ts
index b77e31e931f..f6446c6e7be 100644
--- a/test/phases/select-modifier-phase.test.ts
+++ b/test/phases/select-modifier-phase.test.ts
@@ -12,7 +12,7 @@ import { ModifierTypeOption } from "#modifiers/modifier-type";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { GameManager } from "#test/test-utils/game-manager";
import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
import { shiftCharCodes } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import Phaser from "phaser";
@@ -137,16 +137,16 @@ describe("SelectModifierPhase", () => {
expect(modifierSelectHandler.options.length).toEqual(3);
// Reroll with lock can still upgrade
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toEqual(firstRollTiers[0]);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toEqual(firstRollTiers[1]);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toEqual(firstRollTiers[2]);
});
@@ -210,24 +210,24 @@ describe("SelectModifierPhase", () => {
) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(5);
expect(
- modifierSelectHandler.options[0].modifierTypeOption.type.tier -
- modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[0].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.COMMON);
expect(
- modifierSelectHandler.options[1].modifierTypeOption.type.tier -
- modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[1].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.GREAT);
expect(
- modifierSelectHandler.options[2].modifierTypeOption.type.tier -
- modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[2].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ULTRA);
expect(
- modifierSelectHandler.options[3].modifierTypeOption.type.tier -
- modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[3].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.ROGUE);
expect(
- modifierSelectHandler.options[4].modifierTypeOption.type.tier -
- modifierSelectHandler.options[4].modifierTypeOption.upgradeCount,
+ modifierSelectHandler.options[4].modifierTypeOption.type.tier
+ - modifierSelectHandler.options[4].modifierTypeOption.upgradeCount,
).toEqual(ModifierTier.MASTER);
});
diff --git a/test/plugins/api/pokerogue-daily-api.test.ts b/test/plugins/api/pokerogue-daily-api.test.ts
index ef5dfddada5..b45896e6a2c 100644
--- a/test/plugins/api/pokerogue-daily-api.test.ts
+++ b/test/plugins/api/pokerogue-daily-api.test.ts
@@ -2,7 +2,7 @@ import { PokerogueDailyApi } from "#api/pokerogue-daily-api";
import { initServerForApiTests } from "#test/test-utils/test-file-initialization";
import { getApiBaseUrl } from "#test/test-utils/test-utils";
import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/api/pokerogue-daily-api";
-import { type RankingEntry, ScoreboardCategory } from "#ui/daily-run-scoreboard";
+import { type RankingEntry, ScoreboardCategory } from "#ui/containers/daily-run-scoreboard";
import { HttpResponse, http } from "msw";
import type { SetupServerApi } from "msw/node";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/plugins/api/pokerogue-session-savedata-api.test.ts b/test/plugins/api/pokerogue-session-savedata-api.test.ts
index d7ee2703405..d91db4425cb 100644
--- a/test/plugins/api/pokerogue-session-savedata-api.test.ts
+++ b/test/plugins/api/pokerogue-session-savedata-api.test.ts
@@ -1,5 +1,4 @@
import { PokerogueSessionSavedataApi } from "#api/pokerogue-session-savedata-api";
-import type { SessionSaveData } from "#system/game-data";
import { initServerForApiTests } from "#test/test-utils/test-file-initialization";
import { getApiBaseUrl } from "#test/test-utils/test-utils";
import type {
@@ -10,6 +9,7 @@ import type {
NewClearSessionSavedataRequest,
UpdateSessionSavedataRequest,
} from "#types/api/pokerogue-session-save-data-api";
+import type { SessionSaveData } from "#types/save-data";
import { HttpResponse, http } from "msw";
import type { SetupServerApi } from "msw/node";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/plugins/api/pokerogue-system-savedata-api.test.ts b/test/plugins/api/pokerogue-system-savedata-api.test.ts
index d6e4fe18eed..3480b00b206 100644
--- a/test/plugins/api/pokerogue-system-savedata-api.test.ts
+++ b/test/plugins/api/pokerogue-system-savedata-api.test.ts
@@ -1,5 +1,4 @@
import { PokerogueSystemSavedataApi } from "#api/pokerogue-system-savedata-api";
-import type { SystemSaveData } from "#system/game-data";
import { initServerForApiTests } from "#test/test-utils/test-file-initialization";
import { getApiBaseUrl } from "#test/test-utils/test-utils";
import type {
@@ -8,6 +7,7 @@ import type {
VerifySystemSavedataRequest,
VerifySystemSavedataResponse,
} from "#types/api/pokerogue-system-save-data-api";
+import type { SystemSaveData } from "#types/save-data";
import { HttpResponse, http } from "msw";
import type { SetupServerApi } from "msw/node";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/setting-menu/helpers/menu-manip.ts b/test/setting-menu/helpers/menu-manip.ts
index 276fef2f973..392425ea6f2 100644
--- a/test/setting-menu/helpers/menu-manip.ts
+++ b/test/setting-menu/helpers/menu-manip.ts
@@ -17,16 +17,12 @@ export class MenuManip {
private config;
private settingName;
private keycode;
- private icon;
private iconDisplayed;
- private specialCaseIcon;
constructor(config) {
this.config = config;
this.settingName = null;
- this.icon = null;
this.iconDisplayed = null;
- this.specialCaseIcon = null;
}
// TODO: Review this
@@ -87,8 +83,8 @@ export class MenuManip {
const icon = getIconWithKeycode(this.config, this.keycode);
const key = getKeyWithKeycode(this.config, this.keycode)!; // TODO: is this bang correct?
const _keys = key.toLowerCase().split("_");
- const iconIdentifier = _keys[_keys.length - 1];
- expect(icon.toLowerCase().includes(iconIdentifier)).toEqual(true);
+ const iconIdentifier = _keys.at(-1);
+ expect(icon.toLowerCase()).toContain(iconIdentifier);
return this;
}
diff --git a/test/font-face.setup.ts b/test/setup/font-face.setup.ts
similarity index 100%
rename from test/font-face.setup.ts
rename to test/setup/font-face.setup.ts
diff --git a/test/matchers.setup.ts b/test/setup/matchers.setup.ts
similarity index 86%
rename from test/matchers.setup.ts
rename to test/setup/matchers.setup.ts
index f76a9423ab3..88ca0a5c6bc 100644
--- a/test/matchers.setup.ts
+++ b/test/setup/matchers.setup.ts
@@ -1,3 +1,4 @@
+import { toBeAtPhase } from "#test/test-utils/matchers/to-be-at-phase";
import { toEqualArrayUnsorted } from "#test/test-utils/matchers/to-equal-array-unsorted";
import { toHaveAbilityApplied } from "#test/test-utils/matchers/to-have-ability-applied";
import { toHaveArenaTag } from "#test/test-utils/matchers/to-have-arena-tag";
@@ -7,6 +8,7 @@ import { toHaveFainted } from "#test/test-utils/matchers/to-have-fainted";
import { toHaveFullHp } from "#test/test-utils/matchers/to-have-full-hp";
import { toHaveHp } from "#test/test-utils/matchers/to-have-hp";
import { toHavePositionalTag } from "#test/test-utils/matchers/to-have-positional-tag";
+import { toHaveShownMessage } from "#test/test-utils/matchers/to-have-shown-message";
import { toHaveStatStage } from "#test/test-utils/matchers/to-have-stat-stage";
import { toHaveStatusEffect } from "#test/test-utils/matchers/to-have-status-effect";
import { toHaveTakenDamage } from "#test/test-utils/matchers/to-have-taken-damage";
@@ -19,11 +21,13 @@ import { expect } from "vitest";
/*
* Setup file for custom matchers.
- * Make sure to define the call signatures in `test/@types/vitest.d.ts` too!
+ * Make sure to define the call signatures in `#test/@types/vitest.d.ts` too!
*/
expect.extend({
toEqualArrayUnsorted,
+ toHaveShownMessage,
+ toBeAtPhase,
toHaveWeather,
toHaveTerrain,
toHaveArenaTag,
diff --git a/test/vitest.setup.ts b/test/setup/vitest.setup.ts
similarity index 51%
rename from test/vitest.setup.ts
rename to test/setup/vitest.setup.ts
index be35e18e2e9..3f506d73228 100644
--- a/test/vitest.setup.ts
+++ b/test/setup/vitest.setup.ts
@@ -1,16 +1,21 @@
import "vitest-canvas-mock";
+import { MockConsole } from "#test/test-utils/mocks/mock-console/mock-console";
+import { logTestEnd, logTestStart } from "#test/test-utils/setup/test-end-log";
import { initTests } from "#test/test-utils/test-file-initialization";
-import { afterAll, beforeAll, vi } from "vitest";
+import chalk from "chalk";
+import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
-/** Set the timezone to UTC for tests. */
+//#region Mocking
-/** Mock the override import to always return default values, ignoring any custom overrides. */
-vi.mock("#app/overrides", async importOriginal => {
- const { defaultOverrides } = await importOriginal();
+// Mock the override import to always return default values, ignoring any custom overrides.
+vi.mock(import("#app/overrides"), async importOriginal => {
+ const { defaultOverrides } = await importOriginal();
return {
default: defaultOverrides,
- defaultOverrides,
+ // Export `defaultOverrides` as a *copy*.
+ // This ensures we can easily reset `overrides` back to its default values after modifying it.
+ defaultOverrides: { ...defaultOverrides },
} satisfies typeof import("#app/overrides");
});
@@ -20,7 +25,7 @@ vi.mock("#app/overrides", async importOriginal => {
* This is necessary because how our code is structured.
* Do NOT try to put any of this code into external functions, it won't work as it's elevated during runtime.
*/
-vi.mock("i18next", async importOriginal => {
+vi.mock(import("i18next"), async importOriginal => {
console.log("Mocking i18next");
const { setupServer } = await import("msw/node");
const { http, HttpResponse } = await import("msw");
@@ -30,8 +35,11 @@ vi.mock("i18next", async importOriginal => {
const filename = req.params[0];
try {
- const json = await import(`../public/locales/en/${req.params[0]}`);
- console.log("Loaded locale", filename);
+ const localeFiles = import.meta.glob("../../public/locales/en/**/*.json", { eager: true });
+ const json = localeFiles[`../../public/locales/en/${filename}`] || {};
+ if (import.meta.env.VITE_I18N_DEBUG === "1") {
+ console.log("Loaded locale", filename);
+ }
return HttpResponse.json(json);
} catch (err) {
console.log(`Failed to load locale ${filename}!`, err);
@@ -54,7 +62,15 @@ beforeAll(() => {
initTests();
});
+beforeEach(context => {
+ logTestStart(context.task);
+});
+afterEach(context => {
+ logTestEnd(context.task);
+});
+
afterAll(() => {
global.server.close();
- console.log("Closing i18n MSW server!");
+ MockConsole.printPostTestWarnings();
+ console.log(chalk.hex("#dfb8d8")("Closing i18n MSW server!"));
});
diff --git a/test/sprites/pokemon-sprite.test.ts b/test/sprites/pokemon-sprite.test.ts
index bf945636a71..f3c118921fd 100644
--- a/test/sprites/pokemon-sprite.test.ts
+++ b/test/sprites/pokemon-sprite.test.ts
@@ -148,7 +148,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("check root variant files", () => {
const dirPath = rootDir;
const errors = getMissingFiles(masterlist, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -157,7 +157,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("check female variant files", () => {
const dirPath = `${rootDir}female${path.sep}`;
const errors = getMissingFiles(femaleVariant, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -166,7 +166,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("check back female variant files", () => {
const dirPath = `${rootDir}back${path.sep}female${path.sep}`;
const errors = getMissingFiles(backVariant.female, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -177,7 +177,7 @@ describe("check if every variant's sprite are correctly set", () => {
const backMaleVariant = deepCopy(backVariant);
delete backMaleVariant.female;
const errors = getMissingFiles(backMaleVariant, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -186,7 +186,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("check exp back female variant files", () => {
const dirPath = `${rootDir}exp${path.sep}back${path.sep}female${path.sep}`;
const errors = getMissingFiles(expVariant.back.female, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors.length).toBe(0);
@@ -197,7 +197,7 @@ describe("check if every variant's sprite are correctly set", () => {
const backMaleVariant = deepCopy(expVariant.back);
delete backMaleVariant.female;
const errors = getMissingFiles(backMaleVariant, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -206,7 +206,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("check exp female variant files", () => {
const dirPath = `${rootDir}exp${path.sep}female${path.sep}`;
const errors = getMissingFiles(expVariant.female, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -218,7 +218,7 @@ describe("check if every variant's sprite are correctly set", () => {
delete expMaleVariant.female;
delete expMaleVariant.back;
const errors = getMissingFiles(expMaleVariant, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors", errors);
}
expect(errors).toEqual([]);
@@ -229,7 +229,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant female and check if present in masterlist", () => {
const dirPath = `${rootDir}female${path.sep}`;
const errors = getMissingMasterlist(femaleVariant, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -238,7 +238,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant back female and check if present in masterlist", () => {
const dirPath = `${rootDir}back${path.sep}female${path.sep}`;
const errors = getMissingMasterlist(backVariant.female, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -248,7 +248,7 @@ describe("check if every variant's sprite are correctly set", () => {
const dirPath = `${rootDir}back${path.sep}`;
const backMaleVariant = deepCopy(backVariant);
const errors = getMissingMasterlist(backMaleVariant, dirPath, ["female"]);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -257,7 +257,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant exp back female and check if present in masterlist", () => {
const dirPath = `${rootDir}exp${path.sep}back${path.sep}female${path.sep}`;
const errors = getMissingMasterlist(expVariant.back, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -266,7 +266,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant exp back male and check if present in masterlist", () => {
const dirPath = `${rootDir}exp${path.sep}back${path.sep}`;
const errors = getMissingMasterlist(expVariant.back, dirPath, ["female"]);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors.length).toBe(0);
@@ -275,7 +275,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant exp female and check if present in masterlist", () => {
const dirPath = `${rootDir}exp${path.sep}female${path.sep}`;
const errors = getMissingMasterlist(expVariant.female, dirPath);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -284,7 +284,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant exp male and check if present in masterlist", () => {
const dirPath = `${rootDir}exp${path.sep}`;
const errors = getMissingMasterlist(expVariant, dirPath, ["back", "female"]);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
@@ -293,7 +293,7 @@ describe("check if every variant's sprite are correctly set", () => {
it("look over every file in variant root and check if present in masterlist", () => {
const dirPath = `${rootDir}`;
const errors = getMissingMasterlist(masterlist, dirPath, ["back", "female", "exp", "icons"]);
- if (errors.length) {
+ if (errors.length > 0) {
console.log("errors for ", dirPath, errors);
}
expect(errors).toEqual([]);
diff --git a/test/status-effects/general-status-effect.test.ts b/test/status-effects/general-status-effect.test.ts
new file mode 100644
index 00000000000..db73265181b
--- /dev/null
+++ b/test/status-effects/general-status-effect.test.ts
@@ -0,0 +1,60 @@
+import { allAbilities } from "#data/data-lists";
+import { AbilityId } from "#enums/ability-id";
+import { MoveId } from "#enums/move-id";
+import { SpeciesId } from "#enums/species-id";
+import { StatusEffect } from "#enums/status-effect";
+import { ObtainStatusEffectPhase } from "#phases/obtain-status-effect-phase";
+import { GameManager } from "#test/test-utils/game-manager";
+import type { PostAttackContactApplyStatusEffectAbAttr } from "#types/ability-types";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
+
+describe("Status Effects - General", () => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(() => {
+ game = new GameManager(phaserGame);
+ game.override
+ .battleStyle("single")
+ .enemyLevel(5)
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyAbility(AbilityId.BALL_FETCH)
+ .enemyMoveset(MoveId.SPLASH)
+ .ability(AbilityId.BALL_FETCH);
+ });
+
+ test("multiple status effects from the same interaction should not overwrite each other", async () => {
+ game.override.ability(AbilityId.POISON_TOUCH).moveset([MoveId.NUZZLE]);
+ await game.classicMode.startBattle([SpeciesId.PIKACHU]);
+
+ // Force poison touch to always apply
+ vi.spyOn(
+ allAbilities[AbilityId.POISON_TOUCH].getAttrs(
+ "PostAttackContactApplyStatusEffectAbAttr",
+ // expose chance, which is private, for testing purpose, but keep type safety otherwise
+ )[0] as unknown as Omit & { chance: number },
+ "chance",
+ "get",
+ ).mockReturnValue(100);
+ const statusEffectPhaseSpy = vi.spyOn(ObtainStatusEffectPhase.prototype, "start");
+
+ game.move.select(MoveId.NUZZLE);
+ await game.toEndOfTurn();
+
+ expect(statusEffectPhaseSpy).toHaveBeenCalledOnce();
+ const enemy = game.field.getEnemyPokemon();
+ // This test does not care which status effect is applied, as long as one is.
+ expect(enemy.status?.effect).toBeOneOf([StatusEffect.POISON, StatusEffect.PARALYSIS]);
+ });
+});
diff --git a/test/system/game-data.test.ts b/test/system/game-data.test.ts
index 18775f310b7..42a3ac339ce 100644
--- a/test/system/game-data.test.ts
+++ b/test/system/game-data.test.ts
@@ -3,8 +3,8 @@ import * as account from "#app/account";
import * as bypassLoginModule from "#app/global-vars/bypass-login";
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
-import type { SessionSaveData } from "#system/game-data";
import { GameManager } from "#test/test-utils/game-manager";
+import type { SessionSaveData } from "#types/save-data";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/system/rename-run.test.ts b/test/system/rename-run.test.ts
index 5031d84245f..038303c3254 100644
--- a/test/system/rename-run.test.ts
+++ b/test/system/rename-run.test.ts
@@ -1,10 +1,10 @@
import * as account from "#app/account";
import * as bypassLoginModule from "#app/global-vars/bypass-login";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
-import type { SessionSaveData } from "#app/system/game-data";
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { GameManager } from "#test/test-utils/game-manager";
+import type { SessionSaveData } from "#types/save-data";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
diff --git a/test/test-utils/game-manager-utils.ts b/test/test-utils/game-manager-utils.ts
index 89e352cdbdc..7b12251819f 100644
--- a/test/test-utils/game-manager-utils.ts
+++ b/test/test-utils/game-manager-utils.ts
@@ -8,8 +8,8 @@ import { GameModes } from "#enums/game-modes";
import type { MoveId } from "#enums/move-id";
import type { SpeciesId } from "#enums/species-id";
import { PlayerPokemon } from "#field/pokemon";
-import type { StarterMoveset } from "#system/game-data";
-import type { Starter } from "#ui/starter-select-ui-handler";
+import type { StarterMoveset } from "#types/save-data";
+import type { Starter } from "#ui/handlers/starter-select-ui-handler";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
/** Function to convert Blob to string */
diff --git a/test/test-utils/game-manager.ts b/test/test-utils/game-manager.ts
index 57badd866b1..6b7c805df74 100644
--- a/test/test-utils/game-manager.ts
+++ b/test/test-utils/game-manager.ts
@@ -44,13 +44,14 @@ import type { InputsHandler } from "#test/test-utils/inputs-handler";
import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
import { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
import { TextInterceptor } from "#test/test-utils/text-interceptor";
-import type { BallUiHandler } from "#ui/ball-ui-handler";
-import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
-import type { CommandUiHandler } from "#ui/command-ui-handler";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
-import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";
-import type { TargetSelectUiHandler } from "#ui/target-select-ui-handler";
+import type { PhaseClass, PhaseString } from "#types/phase-types";
+import type { BallUiHandler } from "#ui/handlers/ball-ui-handler";
+import type { BattleMessageUiHandler } from "#ui/handlers/battle-message-ui-handler";
+import type { CommandUiHandler } from "#ui/handlers/command-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
+import type { PartyUiHandler } from "#ui/handlers/party-ui-handler";
+import type { StarterSelectUiHandler } from "#ui/handlers/starter-select-ui-handler";
+import type { TargetSelectUiHandler } from "#ui/handlers/target-select-ui-handler";
import { isNullOrUndefined } from "#utils/common";
import fs from "node:fs";
import { AES, enc } from "crypto-js";
@@ -160,7 +161,7 @@ export class GameManager {
* End the currently running phase immediately.
*/
endPhase() {
- this.scene.phaseManager.getCurrentPhase()?.end();
+ this.scene.phaseManager.getCurrentPhase().end();
}
/**
@@ -305,10 +306,10 @@ export class GameManager {
handler.processInput(Button.ACTION);
},
() =>
- this.isCurrentPhase(CommandPhase) ||
- this.isCurrentPhase(MovePhase) ||
- this.isCurrentPhase(TurnStartPhase) ||
- this.isCurrentPhase(TurnEndPhase),
+ this.isCurrentPhase(CommandPhase)
+ || this.isCurrentPhase(MovePhase)
+ || this.isCurrentPhase(TurnStartPhase)
+ || this.isCurrentPhase(TurnEndPhase),
);
}
@@ -330,9 +331,9 @@ export class GameManager {
handler.processInput(Button.CANCEL);
},
() =>
- this.isCurrentPhase(CommandPhase) ||
- this.isCurrentPhase(NewBattlePhase) ||
- this.isCurrentPhase(CheckSwitchPhase),
+ this.isCurrentPhase(CommandPhase)
+ || this.isCurrentPhase(NewBattlePhase)
+ || this.isCurrentPhase(CheckSwitchPhase),
true,
);
@@ -344,9 +345,9 @@ export class GameManager {
handler.processInput(Button.ACTION);
},
() =>
- this.isCurrentPhase(CommandPhase) ||
- this.isCurrentPhase(NewBattlePhase) ||
- this.isCurrentPhase(CheckSwitchPhase),
+ this.isCurrentPhase(CommandPhase)
+ || this.isCurrentPhase(NewBattlePhase)
+ || this.isCurrentPhase(CheckSwitchPhase),
);
}
@@ -412,10 +413,11 @@ export class GameManager {
* Checks if the current phase matches the target phase.
* @param phaseTarget - The target phase.
* @returns Whether the current phase matches the target phase
+ * @todo Remove `phaseClass` from signature
*/
- isCurrentPhase(phaseTarget) {
+ isCurrentPhase(phaseTarget: PhaseClass | PhaseString) {
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
- return this.scene.phaseManager.getCurrentPhase()?.constructor.name === targetName;
+ return this.scene.phaseManager.getCurrentPhase().phaseName === targetName;
}
/**
diff --git a/test/test-utils/game-wrapper.ts b/test/test-utils/game-wrapper.ts
index 1a906bf8492..166f4b8de7a 100644
--- a/test/test-utils/game-wrapper.ts
+++ b/test/test-utils/game-wrapper.ts
@@ -6,17 +6,13 @@ import * as bypassLoginModule from "#app/global-vars/bypass-login";
import { MoveAnim } from "#data/battle-anims";
import { Pokemon } from "#field/pokemon";
import { version } from "#package.json";
-import { blobToString } from "#test/test-utils/game-manager-utils";
import { MockClock } from "#test/test-utils/mocks/mock-clock";
-import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
import { MockGameObjectCreator } from "#test/test-utils/mocks/mock-game-object-creator";
import { MockLoader } from "#test/test-utils/mocks/mock-loader";
import { MockTextureManager } from "#test/test-utils/mocks/mock-texture-manager";
import { MockTimedEventManager } from "#test/test-utils/mocks/mock-timed-event-manager";
import { MockContainer } from "#test/test-utils/mocks/mocks-container/mock-container";
-import { PokedexMonContainer } from "#ui/pokedex-mon-container";
-import { sessionIdKey } from "#utils/common";
-import { setCookie } from "#utils/cookies";
+import { PokedexMonContainer } from "#ui/containers/pokedex-mon-container";
import fs from "node:fs";
import Phaser from "phaser";
import { vi } from "vitest";
@@ -28,20 +24,6 @@ const GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin;
const EventEmitter = Phaser.Events.EventEmitter;
const UpdateList = Phaser.GameObjects.UpdateList;
-window.URL.createObjectURL = (blob: Blob) => {
- blobToString(blob).then((data: string) => {
- localStorage.setItem("toExport", data);
- });
- return null;
-};
-navigator.getGamepads = () => [];
-global.fetch = vi.fn(MockFetch);
-setCookie(sessionIdKey, "fake_token");
-
-window.matchMedia = () => ({
- matches: false,
-});
-
export class GameWrapper {
public game: Phaser.Game;
public scene: BattleScene;
@@ -99,6 +81,7 @@ export class GameWrapper {
removeAll: () => null,
};
+ // TODO: Can't we just turn on `noAudio` in audio config?
this.scene.sound = {
play: () => null,
pause: () => null,
diff --git a/test/test-utils/helpers/challenge-mode-helper.ts b/test/test-utils/helpers/challenge-mode-helper.ts
index a6d4b5c3b5a..7bc40aec035 100644
--- a/test/test-utils/helpers/challenge-mode-helper.ts
+++ b/test/test-utils/helpers/challenge-mode-helper.ts
@@ -1,5 +1,6 @@
import overrides from "#app/overrides";
import type { Challenge } from "#data/challenge";
+import { copyChallenge } from "#data/challenge";
import { BattleStyle } from "#enums/battle-style";
import type { Challenges } from "#enums/challenges";
import type { SpeciesId } from "#enums/species-id";
@@ -10,7 +11,6 @@ import { SelectStarterPhase } from "#phases/select-starter-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { generateStarter } from "#test/test-utils/game-manager-utils";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
-import { copyChallenge } from "data/challenge";
/**
* Helper to handle Challenge mode specifics
diff --git a/test/test-utils/helpers/classic-mode-helper.ts b/test/test-utils/helpers/classic-mode-helper.ts
index 008648fcd0d..f813a8f797e 100644
--- a/test/test-utils/helpers/classic-mode-helper.ts
+++ b/test/test-utils/helpers/classic-mode-helper.ts
@@ -1,6 +1,7 @@
import { getGameMode } from "#app/game-mode";
import overrides from "#app/overrides";
import { BattleStyle } from "#enums/battle-style";
+import { Button } from "#enums/buttons";
import { GameModes } from "#enums/game-modes";
import { Nature } from "#enums/nature";
import type { SpeciesId } from "#enums/species-id";
@@ -18,8 +19,10 @@ import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"
export class ClassicModeHelper extends GameManagerHelper {
/**
* Runs the classic game to the summon phase.
- * @param species - An array of {@linkcode Species} to summon.
+ * @param species - An array of {@linkcode SpeciesId} to summon.
* @returns A promise that resolves when the summon phase is reached.
+ * @remarks
+ * Do not use this when {@linkcode startBattle} can be used!
*/
async runToSummon(species: SpeciesId[]): Promise;
/**
@@ -29,6 +32,7 @@ export class ClassicModeHelper extends GameManagerHelper {
* @returns A promise that resolves when the summon phase is reached.
* @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes.
*/
+ // biome-ignore lint/style/useUnifiedTypeSignatures: Marks the overload for deprecation
async runToSummon(): Promise;
async runToSummon(species: SpeciesId[] | undefined): Promise;
async runToSummon(species?: SpeciesId[]): Promise {
@@ -60,7 +64,7 @@ export class ClassicModeHelper extends GameManagerHelper {
/**
* Transitions to the start of a battle.
- * @param species - An array of {@linkcode Species} to start the battle with.
+ * @param species - An array of {@linkcode SpeciesId} to start the battle with.
* @returns A promise that resolves when the battle is started.
*/
async startBattle(species: SpeciesId[]): Promise;
@@ -71,6 +75,7 @@ export class ClassicModeHelper extends GameManagerHelper {
* @returns A promise that resolves when the battle is started.
* @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes.
*/
+ // biome-ignore lint/style/useUnifiedTypeSignatures: Marks the overload for deprecation
async startBattle(): Promise;
async startBattle(species?: SpeciesId[]): Promise {
await this.runToSummon(species);
@@ -100,4 +105,33 @@ export class ClassicModeHelper extends GameManagerHelper {
await this.game.phaseInterceptor.to(CommandPhase);
console.log("==================[New Turn]==================");
}
+
+ /**
+ * Queue inputs to switch at the start of the next battle, and then start it.
+ * @param pokemonIndex - The 0-indexed position of the party pokemon to switch to.
+ * Should never be called with 0 as that will select the currently active pokemon and freeze
+ * @returns A Promise that resolves once the battle has been started and the switch prompt resolved
+ * @todo Make this work for double battles
+ * @example
+ * ```ts
+ * await game.classicMode.runToSummon([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA])
+ * await game.startBattleWithSwitch(1);
+ * ```
+ */
+ public async startBattleWithSwitch(pokemonIndex: number): Promise {
+ this.game.scene.battleStyle = BattleStyle.SWITCH;
+ this.game.onNextPrompt(
+ "CheckSwitchPhase",
+ UiMode.CONFIRM,
+ () => {
+ this.game.scene.ui.getHandler().setCursor(0);
+ this.game.scene.ui.getHandler().processInput(Button.ACTION);
+ },
+ () => this.game.isCurrentPhase("CommandPhase") || this.game.isCurrentPhase("TurnInitPhase"),
+ );
+ this.game.doSelectPartyPokemon(pokemonIndex);
+
+ await this.game.phaseInterceptor.to("CommandPhase");
+ console.log("==================[New Battle (Initial Switch)]==================");
+ }
}
diff --git a/test/test-utils/helpers/daily-mode-helper.ts b/test/test-utils/helpers/daily-mode-helper.ts
index ca882eaf548..365c5df3627 100644
--- a/test/test-utils/helpers/daily-mode-helper.ts
+++ b/test/test-utils/helpers/daily-mode-helper.ts
@@ -7,7 +7,7 @@ import { EncounterPhase } from "#phases/encounter-phase";
import { TitlePhase } from "#phases/title-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
-import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler";
+import type { SaveSlotSelectUiHandler } from "#ui/handlers/save-slot-select-ui-handler";
/**
* Helper to handle daily mode specifics
diff --git a/test/test-utils/helpers/move-helper.ts b/test/test-utils/helpers/move-helper.ts
index 3d5e9ae6af9..747a8ad576e 100644
--- a/test/test-utils/helpers/move-helper.ts
+++ b/test/test-utils/helpers/move-helper.ts
@@ -67,9 +67,10 @@ export class MoveHelper extends GameManagerHelper {
const movePosition = this.getMovePosition(pkmIndex, move);
if (movePosition === -1) {
expect.fail(
- `MoveHelper.select called with move '${toTitleCase(MoveId[move])}' not in moveset!` +
- `\nBattler Index: ${toTitleCase(BattlerIndex[pkmIndex])}` +
- `\nMoveset: [${this.game.scene
+ // biome-ignore lint/complexity/noUselessStringConcat: Biome does not currently detect this as multiline (BUG)
+ `MoveHelper.select called with move '${toTitleCase(MoveId[move])}' not in moveset!`
+ + `\nBattler Index: ${toTitleCase(BattlerIndex[pkmIndex])}`
+ + `\nMoveset: [${this.game.scene
.getPlayerParty()
[pkmIndex].getMoveset()
.map(pm => toTitleCase(MoveId[pm.moveId]))
@@ -111,9 +112,10 @@ export class MoveHelper extends GameManagerHelper {
const movePosition = this.getMovePosition(pkmIndex, move);
if (movePosition === -1) {
expect.fail(
- `MoveHelper.selectWithTera called with move '${toTitleCase(MoveId[move])}' not in moveset!` +
- `\nBattler Index: ${toTitleCase(BattlerIndex[pkmIndex])}` +
- `\nMoveset: [${this.game.scene
+ // biome-ignore lint/complexity/noUselessStringConcat: Biome does not currently detect this as multiline (BUG)
+ `MoveHelper.selectWithTera called with move '${toTitleCase(MoveId[move])}' not in moveset!`
+ + `\nBattler Index: ${toTitleCase(BattlerIndex[pkmIndex])}`
+ + `\nMoveset: [${this.game.scene
.getPlayerParty()
[pkmIndex].getMoveset()
.map(pm => toTitleCase(MoveId[pm.moveId]))
@@ -227,11 +229,9 @@ export class MoveHelper extends GameManagerHelper {
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]);
console.warn("Player moveset override disabled due to use of `game.move.changeMoveset`!");
}
- } else {
- if (coerceArray(Overrides.ENEMY_MOVESET_OVERRIDE).length > 0) {
- vi.spyOn(Overrides, "ENEMY_MOVESET_OVERRIDE", "get").mockReturnValue([]);
- console.warn("Enemy moveset override disabled due to use of `game.move.changeMoveset`!");
- }
+ } else if (coerceArray(Overrides.ENEMY_MOVESET_OVERRIDE).length > 0) {
+ vi.spyOn(Overrides, "ENEMY_MOVESET_OVERRIDE", "get").mockReturnValue([]);
+ console.warn("Enemy moveset override disabled due to use of `game.move.changeMoveset`!");
}
moveset = coerceArray(moveset);
expect(moveset.length, "Cannot assign more than 4 moves to a moveset!").toBeLessThanOrEqual(4);
diff --git a/test/test-utils/helpers/overrides-helper.ts b/test/test-utils/helpers/overrides-helper.ts
index 93b89688935..da0d75bf564 100644
--- a/test/test-utils/helpers/overrides-helper.ts
+++ b/test/test-utils/helpers/overrides-helper.ts
@@ -1,7 +1,9 @@
/** biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */
import type { NewArenaEvent } from "#events/battle-scene";
+
/** biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */
+import { OVERRIDES_COLOR } from "#app/constants/colors";
import type { BattleStyle, RandomTrainerOverride } from "#app/overrides";
import Overrides from "#app/overrides";
import { AbilityId } from "#enums/ability-id";
@@ -19,6 +21,7 @@ import type { ModifierOverride } from "#modifiers/modifier-type";
import type { Variant } from "#sprites/variant";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
import { coerceArray, shiftCharCodes } from "#utils/common";
+import chalk from "chalk";
import { vi } from "vitest";
/**
@@ -48,7 +51,8 @@ export class OverridesHelper extends GameManagerHelper {
/**
* Override the starting biome
- * @warning Any event listeners that are attached to {@linkcode NewArenaEvent} may need to be handled down the line
+ *
+ * ⚠️ Any event listeners that are attached to {@linkcode NewArenaEvent} may need to be handled down the line
* @param biome - The biome to set
*/
public startingBiome(biome: BiomeId): this {
@@ -338,7 +342,11 @@ export class OverridesHelper extends GameManagerHelper {
/**
* Force random critical hit rolls to always or never suceed.
* @param crits - `true` to guarantee crits on eligible moves, `false` to force rolls to fail, `null` to disable override
- * @remarks This does not bypass effects that guarantee or block critical hits; it merely mocks the chance-based rolls.
+ * @remarks
+ * This does not change any effects that guarantee or block critical hits;
+ * it merely mocks any chance-based rolls not already at 100%. \
+ * For instance, a Pokemon at +3 crit stages will still critically hit with the override set to `false`,
+ * whereas one at +2 crit stages (a 50% chance) will not.
* @returns `this`
*/
public criticalHits(crits: boolean | null): this {
@@ -665,6 +673,6 @@ export class OverridesHelper extends GameManagerHelper {
}
private log(...params: any[]) {
- console.log("Overrides:", ...params);
+ console.log(chalk.hex(OVERRIDES_COLOR)(...params));
}
}
diff --git a/test/test-utils/helpers/reload-helper.ts b/test/test-utils/helpers/reload-helper.ts
index 7166f1b6cf9..e46096f3fab 100644
--- a/test/test-utils/helpers/reload-helper.ts
+++ b/test/test-utils/helpers/reload-helper.ts
@@ -3,9 +3,9 @@ import { UiMode } from "#enums/ui-mode";
import { CommandPhase } from "#phases/command-phase";
import { TitlePhase } from "#phases/title-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
-import type { SessionSaveData } from "#system/game-data";
import type { GameManager } from "#test/test-utils/game-manager";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
+import type { SessionSaveData } from "#types/save-data";
import { vi } from "vitest";
/**
@@ -48,7 +48,7 @@ export class ReloadHelper extends GameManagerHelper {
// remove all persistent mods before loading
// TODO: Look into why these aren't removed before load
- if (this.game.scene.modifiers.length) {
+ if (this.game.scene.modifiers.length > 0) {
console.log(
"Removing %d modifiers from scene on load...",
this.game.scene.modifiers.length,
diff --git a/test/test-utils/helpers/settings-helper.ts b/test/test-utils/helpers/settings-helper.ts
index a26aa2de33c..46ac74b83dc 100644
--- a/test/test-utils/helpers/settings-helper.ts
+++ b/test/test-utils/helpers/settings-helper.ts
@@ -1,7 +1,9 @@
+import { SETTINGS_COLOR } from "#app/constants/colors";
import { BattleStyle } from "#enums/battle-style";
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { PlayerGender } from "#enums/player-gender";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
+import chalk from "chalk";
/**
* Helper to handle settings for tests
@@ -49,6 +51,6 @@ export class SettingsHelper extends GameManagerHelper {
}
private log(...params: any[]) {
- console.log("Settings:", ...params);
+ console.log(chalk.hex(SETTINGS_COLOR)(...params));
}
}
diff --git a/test/test-utils/inputs-handler.ts b/test/test-utils/inputs-handler.ts
index b8b3224c31d..b5638f3694c 100644
--- a/test/test-utils/inputs-handler.ts
+++ b/test/test-utils/inputs-handler.ts
@@ -111,7 +111,7 @@ class FakeMobile {
if (!node) {
return;
}
- const event = new Event("touchstart");
+ const event = new Event("pointerdown");
node.dispatchEvent(event);
}
@@ -120,7 +120,7 @@ class FakeMobile {
if (!node) {
return;
}
- const event = new Event("touchend");
+ const event = new Event("pointerup");
node.dispatchEvent(event);
}
}
diff --git a/test/test-utils/matchers/to-be-at-phase.ts b/test/test-utils/matchers/to-be-at-phase.ts
new file mode 100644
index 00000000000..7ff76fa0365
--- /dev/null
+++ b/test/test-utils/matchers/to-be-at-phase.ts
@@ -0,0 +1,45 @@
+/** biome-ignore-start lint/correctness/noUnusedImports: TSDoc imports */
+import type { Phase } from "#app/phase";
+import type { GameManager } from "#test/test-utils/game-manager";
+// biome-ignore-end lint/correctness/noUnusedImports: TSDoc
+
+import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
+import type { PhaseString } from "#types/phase-types";
+import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
+
+/**
+ * Matcher that checks if the current {@linkcode Phase} is of the given type.
+ * @param received - The object to check. Should be the current {@linkcode GameManager}
+ * @param expectedPhase - The expected {@linkcode PhaseString}
+ * @returns The result of the matching
+ */
+export function toBeAtPhase(this: MatcherState, received: unknown, expectedPhase: PhaseString): SyncExpectationResult {
+ if (!isGameManagerInstance(received)) {
+ return {
+ pass: this.isNot,
+ message: () => `Expected to receive a GameManager, but got ${receivedStr(received)}!`,
+ };
+ }
+
+ if (!received.scene?.phaseManager) {
+ return {
+ pass: this.isNot,
+ message: () => `Expected GameManager.${received.scene ? "scene.phaseManager" : "scene"} to be defined!`,
+ };
+ }
+
+ const currPhase = received.scene.phaseManager.getCurrentPhase();
+ const pass = currPhase.is(expectedPhase);
+
+ const actual = currPhase.phaseName;
+
+ return {
+ pass,
+ message: () =>
+ pass
+ ? `Expected the current phase to NOT be ${expectedPhase}, but it was!`
+ : `Expected the current phase to be ${expectedPhase}, but got ${actual} instead!`,
+ expected: expectedPhase,
+ actual,
+ };
+}
diff --git a/test/test-utils/matchers/to-have-arena-tag.ts b/test/test-utils/matchers/to-have-arena-tag.ts
index e2a4a71ffd5..a9d619686d1 100644
--- a/test/test-utils/matchers/to-have-arena-tag.ts
+++ b/test/test-utils/matchers/to-have-arena-tag.ts
@@ -6,11 +6,21 @@ import type { OneOther } from "#test/@types/test-helpers";
import type { GameManager } from "#test/test-utils/game-manager";
import { getOnelineDiffStr } from "#test/test-utils/string-utils";
import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
+import type { ArenaTagDataMap, SerializableArenaTagType } from "#types/arena-tags";
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
-// intersection required to preserve T for inferences
-export type toHaveArenaTagOptions = OneOther & {
- tagType: T;
+/**
+ * Options type for {@linkcode toHaveArenaTag}.
+ * @typeParam A - The {@linkcode ArenaTagType} being checked
+ * @remarks
+ * If A corresponds to a serializable `ArenaTag`, only properties allowed to be serialized
+ * (i.e. can change across instances) will be present and able to be checked.
+ */
+export type toHaveArenaTagOptions = OneOther<
+ A extends SerializableArenaTagType ? ArenaTagDataMap[A] : ArenaTagTypeMap[A],
+ "tagType" | "side"
+> & {
+ tagType: A;
};
/**
@@ -22,10 +32,10 @@ export type toHaveArenaTagOptions = OneOther(
+export function toHaveArenaTag(
this: MatcherState,
received: unknown,
- expectedTag: T | toHaveArenaTagOptions,
+ expectedTag: A | toHaveArenaTagOptions,
side: ArenaTagSide = ArenaTagSide.BOTH,
): SyncExpectationResult {
if (!isGameManagerInstance(received)) {
diff --git a/test/test-utils/matchers/to-have-battler-tag.ts b/test/test-utils/matchers/to-have-battler-tag.ts
index af405d7da39..ba6679b2af4 100644
--- a/test/test-utils/matchers/to-have-battler-tag.ts
+++ b/test/test-utils/matchers/to-have-battler-tag.ts
@@ -3,21 +3,39 @@ import type { Pokemon } from "#field/pokemon";
/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */
import { getPokemonNameWithAffix } from "#app/messages";
+import type { BattlerTagTypeMap } from "#data/battler-tags";
import { BattlerTagType } from "#enums/battler-tag-type";
-import { getEnumStr } from "#test/test-utils/string-utils";
+import type { OneOther } from "#test/@types/test-helpers";
+import { getEnumStr, getOnelineDiffStr } from "#test/test-utils/string-utils";
import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils";
+import type { BattlerTagDataMap, SerializableBattlerTagType } from "#types/battler-tags";
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
+// intersection required to preserve T for inferences
/**
- * Matcher that checks if a {@linkcode Pokemon} has a specific {@linkcode BattlerTagType}.
+ * Options type for {@linkcode toHaveBattlerTag}.
+ * @typeParam B - The {@linkcode BattlerTagType} being checked
+ * @remarks
+ * If B corresponds to a serializable `BattlerTag`, only properties allowed to be serialized
+ * (i.e. can change across instances) will be present and able to be checked.
+ */
+export type toHaveBattlerTagOptions = (B extends SerializableBattlerTagType
+ ? OneOther
+ : OneOther) & {
+ tagType: B;
+};
+
+/**
+ * Matcher that checks if a {@linkcode Pokemon} has a specific {@linkcode BattlerTag}.
* @param received - The object to check. Should be a {@linkcode Pokemon}
- * @param expectedBattlerTagType - The {@linkcode BattlerTagType} to check for
+ * @param expectedTag - The `BattlerTagType` of the desired tag, or a partially-filled object
+ * containing the desired properties
* @returns Whether the matcher passed
*/
-export function toHaveBattlerTag(
+export function toHaveBattlerTag(
this: MatcherState,
received: unknown,
- expectedBattlerTagType: BattlerTagType,
+ expectedTag: B | toHaveBattlerTagOptions,
): SyncExpectationResult {
if (!isPokemonInstance(received)) {
return {
@@ -26,18 +44,44 @@ export function toHaveBattlerTag(
};
}
- const pass = !!received.getTag(expectedBattlerTagType);
const pkmName = getPokemonNameWithAffix(received);
- // "BattlerTagType.SEEDED (=1)"
- const expectedTagStr = getEnumStr(BattlerTagType, expectedBattlerTagType, { prefix: "BattlerTagType." });
+ // Coerce lone `tagType`s into objects
+ const etag = typeof expectedTag === "object" ? expectedTag : { tagType: expectedTag };
+ const gotTag = received.getTag(etag.tagType);
+
+ // If checking exclusively tag type OR no tags were found, break out early.
+ if (typeof expectedTag !== "object" || !gotTag) {
+ const pass = !!gotTag;
+ // "BattlerTagType.SEEDED (=1)"
+ const expectedTagStr = getEnumStr(BattlerTagType, etag.tagType, { prefix: "BattlerTagType." });
+
+ return {
+ pass,
+ message: () =>
+ pass
+ ? `Expected ${pkmName} to NOT have a tag of type ${expectedTagStr}, but it did!`
+ : `Expected ${pkmName} to have a tag of type ${expectedTagStr}, but it didn't!`,
+ expected: expectedTag,
+ actual: received.summonData.tags.map(t => t.tagType),
+ };
+ }
+
+ // Check for equality with the provided tag
+ const pass = this.equals(gotTag, etag, [
+ ...this.customTesters,
+ this.utils.subsetEquality,
+ this.utils.iterableEquality,
+ ]);
+
+ const expectedStr = getOnelineDiffStr.call(this, expectedTag);
return {
pass,
message: () =>
pass
- ? `Expected ${pkmName} to NOT have ${expectedTagStr}, but it did!`
- : `Expected ${pkmName} to have ${expectedTagStr}, but it didn't!`,
- expected: expectedBattlerTagType,
- actual: received.summonData.tags.map(t => t.tagType),
+ ? `Expected ${pkmName} to NOT have a tag matching ${expectedStr}, but it did!`
+ : `Expected ${pkmName} to have a tag matching ${expectedStr}, but it didn't!`,
+ expected: expectedTag,
+ actual: gotTag,
};
}
diff --git a/test/test-utils/matchers/to-have-shown-message.ts b/test/test-utils/matchers/to-have-shown-message.ts
new file mode 100644
index 00000000000..bf5576ee630
--- /dev/null
+++ b/test/test-utils/matchers/to-have-shown-message.ts
@@ -0,0 +1,43 @@
+// biome-ignore lint/correctness/noUnusedImports: TSDoc
+import type { GameManager } from "#test/test-utils/game-manager";
+import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
+import { truncateString } from "#utils/common";
+import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
+
+/**
+ * Matcher to check if the {@linkcode GameManager} has shown the given message at least once.
+ * @param received - The object to check. Should be the current {@linkcode GameManager}.
+ * @param expectedMessage - The expected message
+ * @returns The result of the matching
+ */
+export function toHaveShownMessage(
+ this: MatcherState,
+ received: unknown,
+ expectedMessage: string,
+): SyncExpectationResult {
+ if (!isGameManagerInstance(received)) {
+ return {
+ pass: this.isNot,
+ message: () => `Expected to receive a GameManager, but got ${receivedStr(received)}!`,
+ };
+ }
+
+ if (!received.textInterceptor) {
+ return {
+ pass: this.isNot,
+ message: () => "Expected GameManager.TextInterceptor to be defined!",
+ };
+ }
+
+ // Pass if any of the matching tags meet our criteria
+ const pass = received.textInterceptor.logs.includes(expectedMessage);
+ return {
+ pass,
+ message: () =>
+ pass
+ ? `Expected the GameManager to NOT have shown the message ${truncateString(expectedMessage, 30)}, but it did!`
+ : `Expected the GameManager to have shown the message ${truncateString(expectedMessage, 30)}, but it didn't!`,
+ expected: expectedMessage,
+ actual: received.textInterceptor.logs,
+ };
+}
diff --git a/test/test-utils/mocks/mock-console-log.ts b/test/test-utils/mocks/mock-console-log.ts
deleted file mode 100644
index f54d41fea3e..00000000000
--- a/test/test-utils/mocks/mock-console-log.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-const originalLog = console.log;
-const originalError = console.error;
-const originalDebug = console.debug;
-const originalWarn = console.warn;
-
-const blacklist = ["Phaser", "variant icon does not exist", 'Texture "%s" not found'];
-const whitelist = ["Phase"];
-
-export class MockConsoleLog {
- constructor(
- private logDisabled = false,
- private phaseText = false,
- ) {}
- private logs: any[] = [];
- private notified: any[] = [];
-
- public log(...args) {
- const argsStr = this.getStr(args);
- this.logs.push(argsStr);
- if (this.logDisabled && !this.phaseText) {
- return;
- }
- if ((this.phaseText && !whitelist.some(b => argsStr.includes(b))) || blacklist.some(b => argsStr.includes(b))) {
- return;
- }
- originalLog(args);
- }
- public error(...args) {
- const argsStr = this.getStr(args);
- this.logs.push(argsStr);
- originalError(args); // Appelle le console.error originel
- }
- public debug(...args) {
- const argsStr = this.getStr(args);
- this.logs.push(argsStr);
- if (this.logDisabled && !this.phaseText) {
- return;
- }
- if (!whitelist.some(b => argsStr.includes(b)) || blacklist.some(b => argsStr.includes(b))) {
- return;
- }
- originalDebug(args);
- }
- warn(...args) {
- const argsStr = this.getStr(args);
- this.logs.push(args);
- if (this.logDisabled && !this.phaseText) {
- return;
- }
- if (!whitelist.some(b => argsStr.includes(b)) || blacklist.some(b => argsStr.includes(b))) {
- return;
- }
- originalWarn(args);
- }
- notify(msg) {
- originalLog(msg);
- this.notified.push(msg);
- }
- getLogs() {
- return this.logs;
- }
- clearLogs() {
- this.logs = [];
- }
- getStr(...args) {
- return args
- .map(arg => {
- if (typeof arg === "object" && arg !== null) {
- // Handle objects including arrays
- return JSON.stringify(arg, (_key, value) => (typeof value === "bigint" ? value.toString() : value));
- }
- if (typeof arg === "bigint") {
- // Handle BigInt values
- return arg.toString();
- }
- return arg.toString();
- })
- .join(";");
- }
-}
diff --git a/test/test-utils/mocks/mock-console/color-map.json b/test/test-utils/mocks/mock-console/color-map.json
new file mode 100644
index 00000000000..ded83e889b0
--- /dev/null
+++ b/test/test-utils/mocks/mock-console/color-map.json
@@ -0,0 +1,150 @@
+{
+ "AliceBlue": "f0f8ff",
+ "AntiqueWhite": "faebd7",
+ "Aqua": "00ffff",
+ "Aquamarine": "7fffd4",
+ "Azure": "f0ffff",
+ "Beige": "f5f5dc",
+ "Bisque": "ffe4c4",
+ "Black": "000000",
+ "BlanchedAlmond": "ffebcd",
+ "Blue": "0000ff",
+ "BlueViolet": "8a2be2",
+ "Brown": "a52a2a",
+ "BurlyWood": "deb887",
+ "CadetBlue": "5f9ea0",
+ "Chartreuse": "7fff00",
+ "Chocolate": "d2691e",
+ "Coral": "ff7f50",
+ "CornflowerBlue": "6495ed",
+ "Cornsilk": "fff8dc",
+ "Crimson": "dc143c",
+ "Cyan": "00ffff",
+ "DarkBlue": "00008b",
+ "DarkCyan": "008b8b",
+ "DarkGoldenRod": "b8860b",
+ "DarkGray": "a9a9a9",
+ "DarkGrey": "a9a9a9",
+ "DarkGreen": "006400",
+ "DarkKhaki": "bdb76b",
+ "DarkMagenta": "8b008b",
+ "DarkOliveGreen": "556b2f",
+ "DarkOrange": "ff8c00",
+ "DarkOrchid": "9932cc",
+ "DarkRed": "8b0000",
+ "DarkSalmon": "e9967a",
+ "DarkSeaGreen": "8fbc8f",
+ "DarkSlateBlue": "483d8b",
+ "DarkSlateGray": "2f4f4f",
+ "DarkSlateGrey": "2f4f4f",
+ "DarkTurquoise": "00ced1",
+ "DarkViolet": "9400d3",
+ "DeepPink": "ff1493",
+ "DeepSkyBlue": "00bfff",
+ "DimGray": "696969",
+ "DimGrey": "696969",
+ "DodgerBlue": "1e90ff",
+ "FireBrick": "b22222",
+ "FloralWhite": "fffaf0",
+ "ForestGreen": "228b22",
+ "Fuchsia": "ff00ff",
+ "Gainsboro": "dcdcdc",
+ "GhostWhite": "f8f8ff",
+ "Gold": "ffd700",
+ "GoldenRod": "daa520",
+ "Gray": "808080",
+ "Grey": "808080",
+ "Green": "008000",
+ "GreenYellow": "adff2f",
+ "HoneyDew": "f0fff0",
+ "HotPink": "ff69b4",
+ "IndianRed": "cd5c5c",
+ "Indigo": "4b0082",
+ "Ivory": "fffff0",
+ "Khaki": "f0e68c",
+ "Lavender": "e6e6fa",
+ "LavenderBlush": "fff0f5",
+ "LawnGreen": "7cfc00",
+ "LemonChiffon": "fffacd",
+ "LightBlue": "add8e6",
+ "LightCoral": "f08080",
+ "LightCyan": "e0ffff",
+ "LightGoldenRodYellow": "fafad2",
+ "LightGray": "d3d3d3",
+ "LightGrey": "d3d3d3",
+ "LightGreen": "90ee90",
+ "LightPink": "ffb6c1",
+ "LightSalmon": "ffa07a",
+ "LightSeaGreen": "20b2aa",
+ "LightSkyBlue": "87cefa",
+ "LightSlateGray": "778899",
+ "LightSlateGrey": "778899",
+ "LightSteelBlue": "b0c4de",
+ "LightYellow": "ffffe0",
+ "Lime": "00ff00",
+ "LimeGreen": "32cd32",
+ "Linen": "faf0e6",
+ "Magenta": "ff00ff",
+ "Maroon": "800000",
+ "MediumAquaMarine": "66cdaa",
+ "MediumBlue": "0000cd",
+ "MediumOrchid": "ba55d3",
+ "MediumPurple": "9370db",
+ "MediumSeaGreen": "3cb371",
+ "MediumSlateBlue": "7b68ee",
+ "MediumSpringGreen": "00fa9a",
+ "MediumTurquoise": "48d1cc",
+ "MediumVioletRed": "c71585",
+ "MidnightBlue": "191970",
+ "MintCream": "f5fffa",
+ "MistyRose": "ffe4e1",
+ "Moccasin": "ffe4b5",
+ "NavajoWhite": "ffdead",
+ "Navy": "000080",
+ "OldLace": "fdf5e6",
+ "Olive": "808000",
+ "OliveDrab": "6b8e23",
+ "Orange": "ffa500",
+ "OrangeRed": "ff4500",
+ "Orchid": "da70d6",
+ "PaleGoldenRod": "eee8aa",
+ "PaleGreen": "98fb98",
+ "PaleTurquoise": "afeeee",
+ "PaleVioletRed": "db7093",
+ "PapayaWhip": "ffefd5",
+ "PeachPuff": "ffdab9",
+ "Peru": "cd853f",
+ "Pink": "ffc0cb",
+ "Plum": "dda0dd",
+ "PowderBlue": "b0e0e6",
+ "Purple": "800080",
+ "RebeccaPurple": "663399",
+ "Red": "ff0000",
+ "RosyBrown": "bc8f8f",
+ "RoyalBlue": "4169e1",
+ "SaddleBrown": "8b4513",
+ "Salmon": "fa8072",
+ "SandyBrown": "f4a460",
+ "SeaGreen": "2e8b57",
+ "SeaShell": "fff5ee",
+ "Sienna": "a0522d",
+ "Silver": "c0c0c0",
+ "SkyBlue": "87ceeb",
+ "SlateBlue": "6a5acd",
+ "SlateGray": "708090",
+ "SlateGrey": "708090",
+ "Snow": "fffafa",
+ "SpringGreen": "00ff7f",
+ "SteelBlue": "4682b4",
+ "Tan": "d2b48c",
+ "Teal": "008080",
+ "Thistle": "d8bfd8",
+ "Tomato": "ff6347",
+ "Turquoise": "40e0d0",
+ "Violet": "ee82ee",
+ "Wheat": "f5deb3",
+ "White": "ffffff",
+ "WhiteSmoke": "f5f5f5",
+ "Yellow": "ffff00",
+ "YellowGreen": "9acd32"
+}
diff --git a/test/test-utils/mocks/mock-console/infer-color.ts b/test/test-utils/mocks/mock-console/infer-color.ts
new file mode 100644
index 00000000000..e01adbc4ad4
--- /dev/null
+++ b/test/test-utils/mocks/mock-console/infer-color.ts
@@ -0,0 +1,61 @@
+import { hslToHex } from "#utils/common";
+import chalk, { type ChalkInstance, type ForegroundColorName, foregroundColorNames } from "chalk";
+import colorMap from "./color-map.json";
+
+export function inferColorFormat(data: [string, ...unknown[]]): ChalkInstance {
+ // Remove all CSS format strings and find the first one containing something vaguely resembling a color
+ data[0] = data[0].replaceAll("%c", "");
+ const args = data.slice(1).filter(t => typeof t === "string");
+ const color = findColorPrefix(args);
+
+ // If the color is within Chalk's native roster, use it directly.
+ if ((foregroundColorNames as string[]).includes(color)) {
+ return chalk[color as ForegroundColorName];
+ }
+
+ // Otherwise, coerce it to hex before feeding it in.
+ return getColor(color);
+}
+
+/**
+ * Find the first string with a "color:" CSS directive in an argument list.
+ * @param args - The arguments containing the color directive
+ * @returns The found color, or `"green"` if none were found
+ */
+function findColorPrefix(args: string[]): string {
+ for (const arg of args) {
+ const match = /color:\s*(.+?)(?:;|$)/g.exec(arg);
+ if (match === null) {
+ continue;
+ }
+
+ return match[1];
+ }
+ return "green";
+}
+
+/**
+ * Coerce an arbitrary CSS color string to a Chalk instance.
+ * @param color - The color to coerce
+ * @returns The Chalk color equivalent.
+ */
+function getColor(color: string): ChalkInstance {
+ if (/^#([a-z0-9]{3,4}|[a-z0-9]{6}|[a-z0-9]{8})$/i.test(color)) {
+ // already in hex
+ return chalk.hex(color);
+ }
+
+ const rgbMatch = /^rgba?\((\d{1,3})%?,\s*(\d{1,3})%?,?\s*(\d{1,3})%?,\s*/i.exec(color);
+ if (rgbMatch) {
+ const [red, green, blue] = rgbMatch;
+ return chalk.rgb(+red, +green, +blue);
+ }
+
+ const hslMatch = /^hslv?\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)$/i.exec(color);
+ if (hslMatch) {
+ const [hue, saturation, light] = hslMatch;
+ return chalk.hex(hslToHex(+hue, +saturation / 100, +light / 100));
+ }
+
+ return chalk.hex(colorMap[color] ?? "#00ff95ff");
+}
diff --git a/test/test-utils/mocks/mock-console/mock-console.ts b/test/test-utils/mocks/mock-console/mock-console.ts
new file mode 100644
index 00000000000..52ed0af6aa7
--- /dev/null
+++ b/test/test-utils/mocks/mock-console/mock-console.ts
@@ -0,0 +1,211 @@
+import { DEBUG_COLOR, NEW_TURN_COLOR, TRACE_COLOR, UI_MSG_COLOR } from "#app/constants/colors";
+import { inferColorFormat } from "#test/test-utils/mocks/mock-console/infer-color";
+import { coerceArray } from "#utils/common";
+import { type InspectOptions, inspect } from "node:util";
+import chalk, { type ChalkInstance } from "chalk";
+
+// Tell chalk we support truecolor
+chalk.level = 3;
+
+// TODO: Review this
+const blacklist = [
+ "variant icon does not exist", // Repetitive warnings about icons not found
+ 'Texture "%s" not found', // Repetitive warnings about textures not found
+ "type: 'Pokemon',", // Large Pokemon objects
+ "gameVersion: ", // Large session-data and system-data objects
+ "Phaser v", // Phaser version text
+ "Seed:", // Stuff about wave seed (we should really stop logging this shit)
+ "Wave Seed:", // Stuff about wave seed (we should really stop logging this shit)
+] as const;
+const whitelist = ["Start Phase"] as const;
+
+const inspectOptions: InspectOptions = { sorted: true, breakLength: 120, numericSeparator: true };
+
+/**
+ * The {@linkcode MockConsole} is a wrapper around the global {@linkcode console} object.
+ * It automatically colors text and such.
+ */
+export class MockConsole implements Omit {
+ /**
+ * A list of warnings that are queued to be displayed after all tests in the same file are finished.
+ */
+ private static readonly queuedWarnings: unknown[][] = [];
+ /**
+ * The original `Console` object, preserved to avoid overwriting
+ * Vitest's native `console.log` wrapping.
+ */
+ private console = console;
+
+ //#region Static Properties
+
+ /**
+ * Queue a warning to be printed after all tests in the same file are finished.
+ */
+ // TODO: Add some warnings
+ public static queuePostTestWarning(...data: unknown[]): void {
+ MockConsole.queuedWarnings.push(data);
+ }
+
+ /**
+ * Print and reset all post-test warnings.
+ */
+ public static printPostTestWarnings(): void {
+ for (const data of MockConsole.queuedWarnings) {
+ console.warn(...data);
+ }
+ MockConsole.queuedWarnings.splice(0);
+ }
+
+ //#endregion Private Properties
+
+ //#region Utilities
+
+ /**
+ * Check whether a given set of data is in the blacklist to be barred from logging.
+ * @param data - The data being logged
+ * @returns Whether `data` is blacklisted from console logging
+ */
+ private checkBlacklist(data: unknown[]): boolean {
+ const dataStr = this.getStr(data);
+ return !whitelist.some(b => dataStr.includes(b)) && blacklist.some(b => dataStr.includes(b));
+ }
+
+ /**
+ * Returns a human-readable string representation of `data`.
+ */
+ private getStr(data: unknown): string {
+ return inspect(data, inspectOptions);
+ }
+
+ /**
+ * Stringify the given data in a manner fit for logging.
+ * @param color - A Chalk instance or other transformation function used to transform the output,
+ * or `undefined` to not transform it at all.
+ * @param data - The data that the format should be applied to.
+ * @returns A stringified copy of `data` with {@linkcode color} applied to each individual argument.
+ * @todo Do we need to apply color to each entry or just run it through `util.format`?
+ */
+ private format(color: ((s: unknown) => unknown) | undefined, data: unknown | unknown[]): unknown[] {
+ data = coerceArray(data);
+ color ??= a => a;
+ return (data as unknown[]).map(a => color(typeof a === "function" || typeof a === "object" ? this.getStr(a) : a));
+ }
+
+ //#endregion Utilities
+
+ //#region Custom wrappers
+ public info(...data: unknown[]) {
+ return this.log(...data);
+ }
+
+ public trace(...data: unknown[]) {
+ if (this.checkBlacklist(data)) {
+ return;
+ }
+
+ // TODO: Figure out how to add color to the full trace text
+ this.console.trace(...this.format(chalk.hex(TRACE_COLOR), data));
+ }
+
+ public debug(...data: unknown[]) {
+ if (this.checkBlacklist(data)) {
+ return;
+ }
+
+ this.console.debug(...this.format(chalk.hex(DEBUG_COLOR), data));
+ }
+
+ public log(...data: unknown[]): void {
+ if (this.checkBlacklist(data)) {
+ return;
+ }
+
+ let formatter: ChalkInstance | undefined;
+
+ if (data.some(d => typeof d === "string" && d.includes("color:"))) {
+ // Infer the color format from the arguments, then remove everything but the message.
+ formatter = inferColorFormat(data as [string, ...unknown[]]);
+ data.splice(1);
+ } else if (data[0] === "[UI]") {
+ // Cyan for UI debug messages
+ formatter = chalk.hex(UI_MSG_COLOR);
+ } else if (typeof data[0] === "string" && data[0].startsWith("=====")) {
+ // Orange logging for "New Turn"/etc messages
+ formatter = chalk.hex(NEW_TURN_COLOR);
+ }
+
+ this.console.log(...this.format(formatter, data));
+ }
+
+ public warn(...data: unknown[]) {
+ if (this.checkBlacklist(data)) {
+ return;
+ }
+
+ this.console.warn(...this.format(chalk.yellow, data));
+ }
+
+ public error(...data: unknown[]) {
+ if (this.checkBlacklist(data)) {
+ return;
+ }
+
+ this.console.error(...this.format(chalk.redBright, data));
+ }
+
+ //#endregion Custom Wrappers
+
+ //#region Copy-pasted Console code
+ // TODO: Progressively add proper coloration and support for all these methods
+ public dir(...args: Parameters<(typeof console)["dir"]>): ReturnType<(typeof console)["dir"]> {
+ return this.console.dir(...args);
+ }
+ public dirxml(...args: Parameters<(typeof console)["dirxml"]>): ReturnType<(typeof console)["dirxml"]> {
+ return this.console.dirxml(...args);
+ }
+ public table(...args: Parameters<(typeof console)["table"]>): ReturnType<(typeof console)["table"]> {
+ return this.console.table(...args);
+ }
+ public group(...args: Parameters<(typeof console)["group"]>): ReturnType<(typeof console)["group"]> {
+ return this.console.group(...args);
+ }
+ public groupCollapsed(
+ ...args: Parameters<(typeof console)["groupCollapsed"]>
+ ): ReturnType<(typeof console)["groupCollapsed"]> {
+ return this.console.groupCollapsed(...args);
+ }
+ public groupEnd(...args: Parameters<(typeof console)["groupEnd"]>): ReturnType<(typeof console)["groupEnd"]> {
+ return this.console.groupEnd(...args);
+ }
+ public clear(...args: Parameters<(typeof console)["clear"]>): ReturnType<(typeof console)["clear"]> {
+ return this.console.clear(...args);
+ }
+ public count(...args: Parameters<(typeof console)["count"]>): ReturnType<(typeof console)["count"]> {
+ return this.console.count(...args);
+ }
+ public countReset(...args: Parameters<(typeof console)["countReset"]>): ReturnType<(typeof console)["countReset"]> {
+ return this.console.countReset(...args);
+ }
+ public assert(...args: Parameters<(typeof console)["assert"]>): ReturnType<(typeof console)["assert"]> {
+ return this.console.assert(...args);
+ }
+ public profile(...args: Parameters<(typeof console)["profile"]>): ReturnType<(typeof console)["profile"]> {
+ return this.console.profile(...args);
+ }
+ public profileEnd(...args: Parameters<(typeof console)["profileEnd"]>): ReturnType<(typeof console)["profileEnd"]> {
+ return this.console.profileEnd(...args);
+ }
+ public time(...args: Parameters<(typeof console)["time"]>): ReturnType<(typeof console)["time"]> {
+ return this.console.time(...args);
+ }
+ public timeLog(...args: Parameters<(typeof console)["timeLog"]>): ReturnType<(typeof console)["timeLog"]> {
+ return this.console.timeLog(...args);
+ }
+ public timeEnd(...args: Parameters<(typeof console)["timeEnd"]>): ReturnType<(typeof console)["timeEnd"]> {
+ return this.console.timeEnd(...args);
+ }
+ public timeStamp(...args: Parameters<(typeof console)["timeStamp"]>): ReturnType<(typeof console)["timeStamp"]> {
+ return this.console.timeStamp(...args);
+ }
+ //#endregion Copy-pasted Console code
+}
diff --git a/test/test-utils/mocks/mock-timed-event-manager.ts b/test/test-utils/mocks/mock-timed-event-manager.ts
index b6c84876783..4f3de90d9a0 100644
--- a/test/test-utils/mocks/mock-timed-event-manager.ts
+++ b/test/test-utils/mocks/mock-timed-event-manager.ts
@@ -3,8 +3,9 @@ import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "#balance/starters";
/** Mock TimedEventManager so that ongoing events don't impact tests */
export class MockTimedEventManager extends TimedEventManager {
- override activeEvent() {
- return undefined;
+ // biome-ignore lint/nursery/noUselessUndefined: Changes return type to void instead of undefined
+ override activeEvent(): undefined {
+ return;
}
override isEventActive(): boolean {
return false;
diff --git a/test/test-utils/mocks/mocks-container/mock-text.ts b/test/test-utils/mocks/mocks-container/mock-text.ts
index ad2fce80972..1216597fbe0 100644
--- a/test/test-utils/mocks/mocks-container/mock-text.ts
+++ b/test/test-utils/mocks/mocks-container/mock-text.ts
@@ -1,4 +1,5 @@
import type { MockGameObject } from "#test/test-utils/mocks/mock-game-object";
+import type { TextInterceptor } from "#test/test-utils/text-interceptor";
import { UI } from "#ui/ui";
export class MockText implements MockGameObject {
@@ -53,13 +54,11 @@ export class MockText implements MockGameObject {
wordWidthWithSpace += whiteSpaceWidth;
}
- if (wordWidthWithSpace > spaceLeft) {
- // Skip printing the newline if it's the first word of the line that is greater
- // than the word wrap width.
- if (j > 0) {
- result += "\n";
- spaceLeft = this.wordWrapWidth;
- }
+ // Skip printing the newline if it's the first word of the line that is greater
+ // than the word wrap width.
+ if (wordWidthWithSpace > spaceLeft && j > 0) {
+ result += "\n";
+ spaceLeft = this.wordWrapWidth;
}
result += word;
@@ -82,13 +81,14 @@ export class MockText implements MockGameObject {
showText(
text: string,
- delay?: number | null,
+ _delay?: number | null,
callback?: Function | null,
- callbackDelay?: number | null,
- prompt?: boolean | null,
- promptDelay?: number | null,
+ _callbackDelay?: number | null,
+ _prompt?: boolean | null,
+ _promptDelay?: number | null,
) {
- this.scene.messageWrapper.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
+ // TODO: this is a very bad way to pass calls around
+ (this.scene.messageWrapper as TextInterceptor).showText(text);
if (callback) {
callback();
}
@@ -96,13 +96,13 @@ export class MockText implements MockGameObject {
showDialogue(
keyOrText: string,
- name: string | undefined,
- delay: number | null = 0,
+ name: string,
+ _delay: number | null,
callback: Function,
- callbackDelay?: number,
- promptDelay?: number,
+ _callbackDelay?: number,
+ _promptDelay?: number,
) {
- this.scene.messageWrapper.showDialogue(keyOrText, name, delay, callback, callbackDelay, promptDelay);
+ (this.scene.messageWrapper as TextInterceptor).showDialogue(keyOrText, name);
if (callback) {
callback();
}
diff --git a/test/test-utils/phase-interceptor.ts b/test/test-utils/phase-interceptor.ts
index 0d357a75557..4ac5e1150e5 100644
--- a/test/test-utils/phase-interceptor.ts
+++ b/test/test-utils/phase-interceptor.ts
@@ -1,6 +1,7 @@
import type { BattleScene } from "#app/battle-scene";
import { Phase } from "#app/phase";
import { UiMode } from "#enums/ui-mode";
+import { AttemptCapturePhase } from "#phases/attempt-capture-phase";
import { AttemptRunPhase } from "#phases/attempt-run-phase";
import { BattleEndPhase } from "#phases/battle-end-phase";
import { BerryPhase } from "#phases/berry-phase";
@@ -37,6 +38,7 @@ import { NewBiomeEncounterPhase } from "#phases/new-biome-encounter-phase";
import { NextEncounterPhase } from "#phases/next-encounter-phase";
import { PartyExpPhase } from "#phases/party-exp-phase";
import { PartyHealPhase } from "#phases/party-heal-phase";
+import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { PokemonTransformPhase } from "#phases/pokemon-transform-phase";
import { PositionalTagPhase } from "#phases/positional-tag-phase";
import { PostGameOverPhase } from "#phases/post-game-over-phase";
@@ -65,7 +67,7 @@ import { UnlockPhase } from "#phases/unlock-phase";
import { VictoryPhase } from "#phases/victory-phase";
import { ErrorInterceptor } from "#test/test-utils/error-interceptor";
import type { PhaseClass, PhaseString } from "#types/phase-types";
-import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
+import type { AwaitableUiHandler } from "#ui/handlers/awaitable-ui-handler";
import { UI } from "#ui/ui";
export interface PromptHandler {
@@ -181,6 +183,8 @@ export class PhaseInterceptor {
UnlockPhase,
PostGameOverPhase,
RevivalBlessingPhase,
+ PokemonHealPhase,
+ AttemptCapturePhase,
];
private endBySetMode = [
@@ -233,7 +237,7 @@ export class PhaseInterceptor {
ErrorInterceptor.getInstance().add(this);
const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name;
this.intervalRun = setInterval(async () => {
- const currentPhase = this.onHold?.length && this.onHold[0];
+ const currentPhase = this.onHold?.length > 0 && this.onHold[0];
if (!currentPhase) {
// No current phase means the manager either hasn't started yet
// or we were interrupted by prompt; wait for phase to finish
@@ -378,20 +382,21 @@ export class PhaseInterceptor {
*/
startPromptHandler() {
this.promptInterval = setInterval(() => {
- if (this.prompts.length) {
+ if (this.prompts.length > 0) {
const actionForNextPrompt = this.prompts[0];
const expireFn = actionForNextPrompt.expireFn?.();
const currentMode = this.scene.ui.getMode();
- const currentPhase = this.scene.phaseManager.getCurrentPhase()?.constructor.name;
+ const currentPhase = this.scene.phaseManager.getCurrentPhase().phaseName;
const currentHandler = this.scene.ui.getHandler();
if (expireFn) {
this.prompts.shift();
} else if (
- currentMode === actionForNextPrompt.mode &&
- currentPhase === actionForNextPrompt.phaseTarget &&
- currentHandler.active &&
- (!actionForNextPrompt.awaitingActionInput ||
- (actionForNextPrompt.awaitingActionInput && (currentHandler as AwaitableUiHandler)["awaitingActionInput"]))
+ currentMode === actionForNextPrompt.mode
+ && currentPhase === actionForNextPrompt.phaseTarget
+ && currentHandler.active
+ && (!actionForNextPrompt.awaitingActionInput
+ || (actionForNextPrompt.awaitingActionInput
+ && (currentHandler as AwaitableUiHandler)["awaitingActionInput"]))
) {
const prompt = this.prompts.shift();
if (prompt?.callback) {
diff --git a/test/test-utils/reporters/custom-default-reporter.ts b/test/test-utils/reporters/custom-default-reporter.ts
new file mode 100644
index 00000000000..15c4881b83c
--- /dev/null
+++ b/test/test-utils/reporters/custom-default-reporter.ts
@@ -0,0 +1,62 @@
+import { relative } from "node:path";
+import { parseStacktrace } from "@vitest/utils/source-map";
+import chalk from "chalk";
+import type { UserConsoleLog } from "vitest";
+import type { TestState } from "vitest/node";
+import { DefaultReporter } from "vitest/reporters";
+
+/**
+ * Custom Vitest reporter to strip the current file names from the output.
+ */
+export default class CustomDefaultReporter extends DefaultReporter {
+ public override onUserConsoleLog(log: UserConsoleLog, taskState?: TestState): void {
+ // This code is more or less copied verbatim from `vitest/reporters` source, with minor tweaks to use
+ // dependencies we actually _have_ (i.e. chalk) rather than ones we don't (i.e. tinyrainbow).
+
+ // SPDX-SnippetBegin
+ // SPDX-SnippetCopyrightText: 2021 VoidZero Inc. and Vitest contributors
+ // SPDX-License-Identifier: MIT
+
+ if (!super.shouldLog(log, taskState)) {
+ return;
+ }
+
+ const output = log.type === "stdout" ? this.ctx.logger.outputStream : this.ctx.logger.errorStream;
+
+ const write = (msg: string) => output.write(msg);
+
+ const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : undefined;
+
+ write(log.content); // this is about the only changed line (that and us skipping a newline)
+
+ if (!log.origin) {
+ return;
+ }
+
+ // Code for stack trace, ripped directly out of Vitest source code.
+ // I wish they had a helper function to do this so we didn't have to import `@vitest/utils`, but oh well...
+ // browser logs don't have an extra end of line at the end like Node.js does
+ if (log.browser) {
+ write("\n");
+ }
+
+ const project = task ? this.ctx.getProjectByName(task.file.projectName ?? "") : this.ctx.getRootProject();
+
+ const stack = log.browser ? (project.browser?.parseStacktrace(log.origin) ?? []) : parseStacktrace(log.origin);
+
+ const highlight = task && stack.find(i => i.file === task.file.filepath);
+
+ for (const frame of stack) {
+ const color = frame === highlight ? chalk.cyan : chalk.gray;
+ const path = relative(project.config.root, frame.file);
+
+ const positions = [frame.method, `${path}:${chalk.dim(`${frame.line}:${frame.column}`)}`]
+ .filter(Boolean)
+ .join(" ");
+
+ write(color(` ${chalk.dim(">")} ${positions}\n`));
+ }
+
+ // SPDX-SnippetEnd
+ }
+}
diff --git a/test/test-utils/setup/test-end-log.ts b/test/test-utils/setup/test-end-log.ts
new file mode 100644
index 00000000000..9814ba8a45c
--- /dev/null
+++ b/test/test-utils/setup/test-end-log.ts
@@ -0,0 +1,113 @@
+// biome-ignore lint/correctness/noUnusedImports: TSDoc
+import type CustomDefaultReporter from "#test/test-utils/reporters/custom-default-reporter";
+import { basename, join, relative } from "path";
+import chalk from "chalk";
+import type { RunnerTask, RunnerTaskResult, RunnerTestCase } from "vitest";
+
+/**
+ * @module
+ * Code to add markers to the beginning and end of tests.
+ * Intended for use with {@linkcode CustomDefaultReporter}, and placed inside test hooks
+ * (rather than as part of the reporter) to ensure Vitest waits for the log messages to be printed.
+ */
+
+/** A long string of "="s to partition off each test from one another. */
+const TEST_END_BARRIER = chalk.bold.hex("#ff7c7cff")("==================");
+
+// Colors used for Vitest-related test utils
+const TEST_NAME_COLOR = "#008886ff" as const;
+const VITEST_PINK_COLOR = "#c162de" as const;
+
+const testRoot = join(import.meta.dirname, "..", "..", "..");
+
+/**
+ * Log the testfile name and path upon a case starting. \
+ * Used to compensate for us overridding the global Console object and removing Vitest's
+ * test name annotations.
+ * @param test - The {@linkcode RunnerTask} passed from the context
+ */
+export function logTestStart(test: RunnerTask): void {
+ console.log(TEST_END_BARRIER);
+ console.log(
+ `${chalk.dim("> ")}${chalk.hex(VITEST_PINK_COLOR)("Starting test: ")}${chalk.hex(TEST_NAME_COLOR)(getTestName(test))}`,
+ );
+}
+
+/**
+ * Log the testfile name, path and result upon a case ending. \
+ * Used to compensate for us overridding the global Console object and removing Vitest's
+ * test name annotations.
+ * @param task - The {@linkcode RunnerTestCase} passed from the hook
+ */
+export function logTestEnd(task: RunnerTestCase): void {
+ const durationStr = getDurationPrefix(task.result);
+ const resultStr = getResultStr(task.result);
+ console.log(`${chalk.dim("> ")}${chalk.black.bgHex(VITEST_PINK_COLOR)(" Test finished! ")}
+ Name: ${chalk.hex(TEST_NAME_COLOR)(getTestName(task))}
+ Result: ${resultStr}${durationStr}
+ File: ${chalk.hex("#d29b0eff")(
+ getPathFromTest(task.file.filepath) + (task.location ? `:${task.location.line}:${task.location.column}` : ""),
+ )}`);
+}
+
+/**
+ * Get the path of the current test file relative to the `test` directory.
+ * @param abs - The absolute path to the file
+ * @returns The relative path with `test/` appended to it.
+ */
+function getPathFromTest(abs: string): string {
+ return join(basename(testRoot), relative(testRoot, abs));
+}
+
+function getResultStr(result: RunnerTaskResult | undefined): string {
+ if (result?.state !== "pass" && result?.state !== "fail") {
+ return "Unknown";
+ }
+
+ const resultStr =
+ result.state === "pass"
+ ? chalk.green.bold("✔ Passed")
+ : (result?.duration ?? 0) > 2
+ ? chalk.cyan.bold("◴ Timed out")
+ : chalk.red.bold("✗ Failed");
+
+ return resultStr;
+}
+
+/**
+ * Get the text to be displayed for a test's duration.
+ * @param result - The {@linkcode RunnerTaskResult} of the finished test
+ * @returns An appropriately colored suffix for the start time.
+ * Will return an empty string if `result.startTime` is `undefined`
+ */
+function getDurationPrefix(result?: RunnerTaskResult): string {
+ const startTime = result?.startTime;
+ if (!startTime) {
+ return "";
+ }
+ const duration = Math.round(Date.now() - startTime);
+
+ // TODO: Figure out a way to access the current vitest config from a hook
+ const color = duration > 10_000 ? chalk.yellow : chalk.green;
+ return ` ${chalk.dim("in")} ${color(duration)}${chalk.dim("ms")}`;
+}
+
+// Function copied from vitest source to avoid having to import `@vitest/runner/utils` for 1 function
+
+// SPDX-SnippetBegin
+// SPDX-SnippetCopyrightText: 2021 VoidZero Inc. and Vitest contributors
+// SPDX-License-Identifier: MIT
+function getTestName(task: RunnerTask, separator = " > "): string {
+ const names: string[] = [task.name];
+ let current: RunnerTask | undefined = task;
+
+ while ((current = current?.suite)) {
+ if (current?.name) {
+ names.unshift(current.name);
+ }
+ }
+
+ return names.join(separator);
+}
+
+// SPDX-SnippetEnd
diff --git a/test/test-utils/string-utils.ts b/test/test-utils/string-utils.ts
index 6c29c04c107..e19224f4571 100644
--- a/test/test-utils/string-utils.ts
+++ b/test/test-utils/string-utils.ts
@@ -183,5 +183,5 @@ export function getOnelineDiffStr(this: MatcherState, obj: unknown): string {
return this.utils
.stringify(obj, undefined, { maxLength: 35, indent: 0, printBasicPrototype: false })
.replace(/\n/g, " ") // Replace newlines with spaces
- .replace(/,(\s*)}$/g, "$1}"); // Trim trailing commas
+ .replace(/,(\s*)\}$/g, "$1}"); // Trim trailing commas
}
diff --git a/test/test-utils/test-file-initialization.ts b/test/test-utils/test-file-initialization.ts
index 631d3f9146b..c172e2d1da8 100644
--- a/test/test-utils/test-file-initialization.ts
+++ b/test/test-utils/test-file-initialization.ts
@@ -3,7 +3,7 @@ import { initializeGame } from "#app/init/init";
import { initI18n } from "#plugins/i18n";
import { blobToString } from "#test/test-utils/game-manager-utils";
import { manageListeners } from "#test/test-utils/listeners-manager";
-import { MockConsoleLog } from "#test/test-utils/mocks/mock-console-log";
+import { MockConsole } from "#test/test-utils/mocks/mock-console/mock-console";
import { mockContext } from "#test/test-utils/mocks/mock-context-canvas";
import { mockLocalStorage } from "#test/test-utils/mocks/mock-local-storage";
import { MockImage } from "#test/test-utils/mocks/mocks-container/mock-image";
@@ -38,14 +38,22 @@ function initTestFile(): void {
/**
* Setup various stubs for testing.
* @todo Move this into a dedicated stub file instead of running it once per test instance
+ * @todo review these to see which are actually necessary
* @todo Investigate why this resets on new test suite start
*/
function setupStubs(): void {
- Object.defineProperty(window, "localStorage", {
- value: mockLocalStorage(),
- });
- Object.defineProperty(window, "console", {
- value: new MockConsoleLog(false),
+ Object.defineProperties(global, {
+ localStorage: {
+ value: mockLocalStorage(),
+ },
+ console: {
+ value: new MockConsole(),
+ },
+ matchMedia: {
+ value: () => ({
+ matches: false,
+ }),
+ },
});
Object.defineProperty(document, "fonts", {
writable: true,
@@ -69,11 +77,6 @@ function setupStubs(): void {
navigator.getGamepads = () => [];
setCookie(SESSION_ID_COOKIE_NAME, "fake_token");
- window.matchMedia = () =>
- ({
- matches: false,
- }) as any;
-
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject - The {@linkcode Phaser.GameObjects.GameObject} to base the position off of
diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts
index b9e73c3e9da..fe8a8f3cf8a 100644
--- a/test/test-utils/test-utils.ts
+++ b/test/test-utils/test-utils.ts
@@ -68,7 +68,7 @@ function isObject(received: unknown): received is object {
/**
* Helper function to check if a given object is a {@linkcode Pokemon}.
* @param received - The object to check
- * @return Whether `received` is a {@linkcode Pokemon} instance.
+ * @returns Whether `received` is a {@linkcode Pokemon} instance.
*/
export function isPokemonInstance(received: unknown): received is Pokemon {
return isObject(received) && received instanceof Pokemon;
diff --git a/test/test-utils/text-interceptor.ts b/test/test-utils/text-interceptor.ts
index 36a5db4c78d..dfbaf2ff11c 100644
--- a/test/test-utils/text-interceptor.ts
+++ b/test/test-utils/text-interceptor.ts
@@ -1,39 +1,49 @@
+import type { BattleScene } from "#app/battle-scene";
+import chalk from "chalk";
+
/**
- * Class will intercept any text or dialogue message calls and log them for test purposes
+ * The {@linkcode TextInterceptor} is a wrapper class that intercepts and logs any messages
+ * that would be displayed on-screen.
*/
export class TextInterceptor {
- private scene;
- public logs: string[] = [];
- constructor(scene) {
- this.scene = scene;
+ /** A log containing messages having been displayed on screen, sorted in FIFO order. */
+ public readonly logs: string[] = [];
+
+ constructor(scene: BattleScene) {
+ // @ts-expect-error: Find another more sanitary way of doing this
scene.messageWrapper = this;
}
- showText(
- text: string,
- _delay?: number,
- _callback?: Function,
- _callbackDelay?: number,
- _prompt?: boolean,
- _promptDelay?: number,
- ): void {
- console.log(text);
+ /** Clear the current content of the TextInterceptor. */
+ public clearLogs(): void {
+ this.logs.splice(0);
+ }
+
+ showText(text: string): void {
+ // NB: We do not format the raw _logs_ themselves as tests will be actively checking it.
+ console.log(this.formatText(text));
this.logs.push(text);
}
- showDialogue(
- text: string,
- name: string,
- _delay?: number,
- _callback?: Function,
- _callbackDelay?: number,
- _promptDelay?: number,
- ): void {
- console.log(name, text);
+ showDialogue(text: string, name: string): void {
+ console.log(`${name}: \n${this.formatText(text)}`);
this.logs.push(name, text);
}
- getLatestMessage(): string {
- return this.logs.pop() ?? "";
+ /**
+ * Format text to be displayed to the test console, as follows:
+ * 1. Replaces new lines and new text boxes (marked by `$`) with indented new lines.
+ * 2. Removes all `@c{}`, `@d{}`, `@s{}`, and `@f{}` flags from the text.
+ * 3. Makes text blue
+ * @param text - The unformatted text
+ * @returns The formatted text
+ */
+ private formatText(text: string): string {
+ return chalk.blue(
+ text
+ .replace(/\n/g, " ")
+ .replace(/\$/g, "\n ")
+ .replace(/@\w{.*?}/g, ""),
+ );
}
}
diff --git a/test/ui/item-manage-button.test.ts b/test/ui/item-manage-button.test.ts
index a7ea76918a5..b5c24776e7b 100644
--- a/test/ui/item-manage-button.test.ts
+++ b/test/ui/item-manage-button.test.ts
@@ -5,8 +5,8 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import type { Pokemon } from "#field/pokemon";
import { GameManager } from "#test/test-utils/game-manager";
-import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
-import type { PartyUiHandler } from "#ui/party-ui-handler";
+import type { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
+import { type PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -169,4 +169,51 @@ describe("UI - Transfer Items", () => {
expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([2, 2]);
}
});
+
+ // TODO: This test breaks when running all tests on github. Fix this once hotfix period is over.
+ it.todo("should not allow changing to discard mode when transfering items", async () => {
+ let handler: PartyUiHandler | undefined;
+
+ const { resolve, promise } = Promise.withResolvers();
+
+ game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const modifierHandler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
+
+ modifierHandler.processInput(Button.DOWN);
+ modifierHandler.setCursor(1);
+ modifierHandler.processInput(Button.ACTION);
+ });
+
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ handler.setCursor(0);
+ handler.processInput(Button.ACTION);
+
+ await new Promise(r => setTimeout(r, 100));
+ handler.processInput(Button.ACTION);
+
+ resolve();
+ });
+
+ await promise;
+ expect(handler).toBeDefined();
+ if (handler) {
+ const partyMode = handler["partyUiMode"];
+ expect(partyMode).toBe(PartyUiMode.MODIFIER_TRANSFER);
+
+ handler.setCursor(7);
+ handler.processInput(Button.ACTION);
+ // Should not change mode to discard
+ expect(handler["partyUiMode"]).toBe(PartyUiMode.MODIFIER_TRANSFER);
+
+ handler.processInput(Button.CANCEL);
+ handler.setCursor(7);
+ handler.processInput(Button.ACTION);
+ // Should change mode to discard
+ expect(handler["partyUiMode"]).toBe(PartyUiMode.DISCARD);
+ }
+ });
});
diff --git a/test/ui/pokedex.test.ts b/test/ui/pokedex.test.ts
index edd9fa879d0..34539094232 100644
--- a/test/ui/pokedex.test.ts
+++ b/test/ui/pokedex.test.ts
@@ -6,11 +6,11 @@ import { DropDownColumn } from "#enums/drop-down-column";
import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
-import type { StarterAttributes } from "#system/game-data";
import { GameManager } from "#test/test-utils/game-manager";
-import { FilterTextRow } from "#ui/filter-text";
-import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler";
-import { PokedexUiHandler } from "#ui/pokedex-ui-handler";
+import type { StarterAttributes } from "#types/save-data";
+import { FilterTextRow } from "#ui/containers/filter-text";
+import { PokedexPageUiHandler } from "#ui/handlers/pokedex-page-ui-handler";
+import { PokedexUiHandler } from "#ui/handlers/pokedex-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -106,8 +106,8 @@ describe("UI - Pokedex", () => {
const speciesSet = new Set();
for (const pkmn of allSpecies) {
if (
- [pkmn.ability1, pkmn.ability2, pkmn.getPassiveAbility(), pkmn.abilityHidden].includes(ability) ||
- pkmn.forms.some(form =>
+ [pkmn.ability1, pkmn.ability2, pkmn.getPassiveAbility(), pkmn.abilityHidden].includes(ability)
+ || pkmn.forms.some(form =>
[form.ability1, form.ability2, form.abilityHidden, form.getPassiveAbility()].includes(ability),
)
) {
diff --git a/test/ui/starter-select.test.ts b/test/ui/starter-select.test.ts
index 397f3d6086f..2f575b72a5c 100644
--- a/test/ui/starter-select.test.ts
+++ b/test/ui/starter-select.test.ts
@@ -8,10 +8,10 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import type { TitlePhase } from "#phases/title-phase";
import { GameManager } from "#test/test-utils/game-manager";
-import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
+import type { OptionSelectItem } from "#ui/handlers/abstract-option-select-ui-handler";
+import type { SaveSlotSelectUiHandler } from "#ui/handlers/save-slot-select-ui-handler";
+import type { StarterSelectUiHandler } from "#ui/handlers/starter-select-ui-handler";
import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
-import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler";
-import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";
import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
diff --git a/test/ui/transfer-item-options.test.ts b/test/ui/transfer-item-options.test.ts
new file mode 100644
index 00000000000..7e9c1b5e36b
--- /dev/null
+++ b/test/ui/transfer-item-options.test.ts
@@ -0,0 +1,235 @@
+import { BerryType } from "#enums/berry-type";
+import { Button } from "#enums/buttons";
+import { MoveId } from "#enums/move-id";
+import { SpeciesId } from "#enums/species-id";
+import { UiMode } from "#enums/ui-mode";
+import { GameManager } from "#test/test-utils/game-manager";
+import { type PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
+import type { RenameFormUiHandler } from "#ui/handlers/rename-form-ui-handler";
+import Phaser from "phaser";
+import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
+
+// todo: Some tests fail when running all tests at once, but pass when running individually. Seams like it's always the 2nd and 4th (non todo) tests that fail.
+describe("UI - Transfer Item Options", () => {
+ let phaserGame: Phaser.Game;
+ let game: GameManager;
+
+ beforeAll(() => {
+ phaserGame = new Phaser.Game({
+ type: Phaser.HEADLESS,
+ });
+ });
+
+ afterEach(() => {
+ game.phaseInterceptor.restoreOg();
+ });
+
+ beforeEach(async () => {
+ game = new GameManager(phaserGame);
+ game.override
+ .battleStyle("single")
+ .startingLevel(100)
+ .startingHeldItems([
+ { name: "BERRY", count: 1, type: BerryType.SITRUS },
+ { name: "BERRY", count: 2, type: BerryType.APICOT },
+ { name: "BERRY", count: 2, type: BerryType.LUM },
+ ])
+ .enemySpecies(SpeciesId.MAGIKARP)
+ .enemyMoveset(MoveId.SPLASH);
+
+ await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.SQUIRTLE, SpeciesId.CHARMANDER]);
+
+ game.move.use(MoveId.DRAGON_CLAW);
+
+ await game.phaseInterceptor.to("SelectModifierPhase");
+ await game.scene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.MODIFIER_TRANSFER);
+ });
+
+ it.todo("should open the summary screen while transfering an item", async () => {
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // Select first party member
+ handler.setCursor(0);
+ handler.processInput(Button.ACTION);
+
+ resolve();
+ });
+ });
+
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+ // select item to transfer
+ handler.processInput(Button.ACTION);
+ resolve();
+ });
+ });
+
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // move to second pokemon
+ handler.setCursor(1);
+ handler.processInput(Button.ACTION);
+
+ // select summary
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.ACTION);
+
+ await new Promise(r => setTimeout(r, 100));
+ expect(game.scene.ui.getMode()).toBe(UiMode.SUMMARY);
+ });
+
+ it.todo("should open the pokèdex screen while transfering an item", async () => {
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // Select first party member
+ handler.setCursor(0);
+ handler.processInput(Button.ACTION);
+
+ resolve();
+ });
+ });
+
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+ // select item to transfer
+ handler.processInput(Button.ACTION);
+ resolve();
+ });
+ });
+
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+ // move to second pokemon
+ handler.setCursor(1);
+ handler.processInput(Button.ACTION);
+
+ // select pokèdex
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.ACTION);
+
+ await new Promise(r => setTimeout(r, 100));
+ expect(game.scene.ui.getMode()).toBe(UiMode.POKEDEX_PAGE);
+ });
+
+ it.todo("should open the rename screen and rename the pokemon while transfering an item", async () => {
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // Select first party member
+ handler.setCursor(0);
+ handler.processInput(Button.ACTION);
+
+ resolve();
+ });
+ });
+
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+ // select item to transfer
+ handler.processInput(Button.ACTION);
+ resolve();
+ });
+ });
+
+ await new Promise(r => setTimeout(r, 100));
+ let handler: PartyUiHandler | RenameFormUiHandler | undefined;
+ handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // move to second pokemon
+ handler.setCursor(1);
+ handler.processInput(Button.ACTION);
+
+ // select rename
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.ACTION);
+
+ const pokemon = game.scene.getPlayerParty()[1];
+ if (!pokemon) {
+ expect.fail("Pokemon is undefined");
+ }
+ const nickname = pokemon.nickname;
+
+ expect(nickname).toBe(undefined);
+
+ await new Promise(r => setTimeout(r, 100));
+ expect(game.scene.ui.getMode()).toBe(UiMode.RENAME_POKEMON);
+ await new Promise(r => setTimeout(r, 100));
+ handler = game.scene.ui.getHandler() as RenameFormUiHandler;
+ handler["inputs"][0].setText("New nickname");
+ handler.processInput(Button.SUBMIT);
+ await new Promise(r => setTimeout(r, 100));
+ // get the sanitized name
+ const sanitizedName = btoa(unescape(encodeURIComponent("New nickname")));
+ expect(pokemon.nickname).toBe(sanitizedName);
+ });
+
+ it.todo("should pause the evolution while transfering an item", async () => {
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // Select first party member
+ handler.setCursor(0);
+ handler.processInput(Button.ACTION);
+
+ resolve();
+ });
+ });
+
+ await new Promise(resolve => {
+ game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+ // select item to transfer
+ handler.processInput(Button.ACTION);
+ resolve();
+ });
+ });
+
+ await new Promise(r => setTimeout(r, 100));
+ const handler = game.scene.ui.getHandler() as PartyUiHandler;
+
+ // move to second pokemon
+ handler.setCursor(1);
+ handler.processInput(Button.ACTION);
+
+ const pokemon = game.scene.getPlayerParty()[1];
+
+ if (!pokemon) {
+ expect.fail("Pokemon is undefined");
+ }
+ if (pokemon.pauseEvolutions !== undefined) {
+ expect(pokemon.pauseEvolutions).toBe(false);
+ }
+
+ // select pause evolution
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.DOWN);
+ handler.processInput(Button.ACTION);
+
+ await new Promise(r => setTimeout(r, 100));
+ expect(game.scene.ui.getMode()).toBe(UiMode.PARTY);
+ expect(pokemon.pauseEvolutions).toBe(true);
+ });
+});
diff --git a/test/ui/transfer-item.test.ts b/test/ui/transfer-item.test.ts
index 8e42149acc3..67a21b0656d 100644
--- a/test/ui/transfer-item.test.ts
+++ b/test/ui/transfer-item.test.ts
@@ -4,8 +4,8 @@ import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager";
-import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
-import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
+import { ModifierSelectUiHandler } from "#ui/handlers/modifier-select-ui-handler";
+import { PartyUiHandler, PartyUiMode } from "#ui/handlers/party-ui-handler";
import Phaser from "phaser";
import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@@ -70,7 +70,9 @@ describe("UI - Transfer Items", () => {
handler.optionsContainer.list.some(option => (option as BBCodeText).text?.includes("Apicot Berry (2)")),
).toBe(true);
expect(
- handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)),
+ handler.optionsContainer.list.some(option =>
+ new RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text),
+ ),
).toBe(true);
});
diff --git a/test/ui/type-hints.test.ts b/test/ui/type-hints.test.ts
index b5fe0d9585a..56891e22c2a 100644
--- a/test/ui/type-hints.test.ts
+++ b/test/ui/type-hints.test.ts
@@ -4,7 +4,7 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager";
import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text";
-import { FightUiHandler } from "#ui/fight-ui-handler";
+import { FightUiHandler } from "#ui/handlers/fight-ui-handler";
import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
diff --git a/test/utils/data.test.ts b/test/utils/data.test.ts
new file mode 100644
index 00000000000..c0b853e2643
--- /dev/null
+++ b/test/utils/data.test.ts
@@ -0,0 +1,39 @@
+import { deepCopy, isBareObject } from "#utils/data";
+import { describe, expect, it } from "vitest";
+
+describe("Utils - Data", () => {
+ describe("deepCopy", () => {
+ it("should create a deep copy of an object", () => {
+ const original = { a: 1, b: { c: 2 } };
+ const copy = deepCopy(original);
+ // ensure the references are different
+ expect(copy === original, "copied object should not compare equal").not;
+ expect(copy).toEqual(original);
+ // update copy's `a` to a different value and ensure original is unaffected
+ copy.a = 42;
+ expect(original.a, "adjusting property of copy should not affect original").toBe(1);
+ // update copy's nested `b.c` to a different value and ensure original is unaffected
+ copy.b.c = 99;
+ expect(original.b.c, "adjusting nested property of copy should not affect original").toBe(2);
+ });
+ });
+
+ describe("isBareObject", () => {
+ it("should properly identify bare objects", () => {
+ expect(isBareObject({}), "{} should be considered bare");
+ expect(isBareObject(new Object()), "new Object() should be considered bare");
+ expect(isBareObject(Object.create(null)));
+ expect(isBareObject([]), "an empty array should be considered bare");
+ });
+
+ it("should properly reject non-objects", () => {
+ expect(isBareObject(new Date())).not;
+ expect(isBareObject(null)).not;
+ expect(isBareObject(42)).not;
+ expect(isBareObject("")).not;
+ expect(isBareObject(undefined)).not;
+ expect(isBareObject(() => {})).not;
+ expect(isBareObject(new (class A {})())).not;
+ });
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index dcbf7456df8..7bf82eaaca0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -18,46 +18,53 @@
"esModuleInterop": true,
"strictNullChecks": true,
"sourceMap": false,
- "checkJs": true,
"strict": false, // TODO: Enable this eventually
"rootDir": ".",
- "baseUrl": "./src",
- "paths": {
- "#abilities/*": ["./data/abilities/*.ts"],
- "#api/*": ["./plugins/api/*.ts"],
- "#balance/*": ["./data/balance/*.ts"],
- "#enums/*": ["./enums/*.ts"],
- "#events/*": ["./events/*.ts"],
- "#field/*": ["./field/*.ts"],
- "#inputs/*": ["./configs/inputs/*.ts"],
- "#modifiers/*": ["./modifier/*.ts"],
- "#moves/*": ["./data/moves/*.ts"],
- "#mystery-encounters/*": [
- "./data/mystery-encounters/utils/*.ts",
- "./data/mystery-encounters/encounters/*.ts",
- "./data/mystery-encounters/requirements/*.ts",
- "./data/mystery-encounters/*.ts"
- ],
- "#package.json": ["../package.json"],
- "#phases/*": ["./phases/*.ts"],
- "#plugins/*": ["./plugins/vite/*.ts", "./plugins/*.ts"],
- "#sprites/*": ["./sprites/*.ts"],
- "#system/*": [
- "./system/settings/*.ts",
- "./system/version-migration/versions/*.ts",
- "./system/version-migration/*.ts",
- "./system/*.ts"
- ],
- "#trainers/*": ["./data/trainers/*.ts"],
- "#types/*": ["./@types/helpers/*.ts", "./@types/*.ts", "./typings/phaser/*.ts"],
- "#ui/*": ["./ui/battle-info/*.ts", "./ui/settings/*.ts", "./ui/*.ts"],
- "#utils/*": ["./utils/*.ts"],
- "#data/*": ["./data/pokemon-forms/*.ts", "./data/pokemon/*.ts", "./data/*.ts"],
- "#test/*": ["../test/*.ts"],
- "#app/*": ["*.ts"]
- },
"outDir": "./build",
- "noEmit": true
+ "noEmit": true,
+ "paths": {
+ "#abilities/*": ["./src/data/abilities/*.ts"],
+ "#api/*": ["./src/plugins/api/*.ts"],
+ "#balance/*": ["./src/data/balance/*.ts"],
+ "#enums/*": ["./src/enums/*.ts"],
+ "#events/*": ["./src/events/*.ts"],
+ "#field/*": ["./src/field/*.ts"],
+ "#inputs/*": ["./src/configs/inputs/*.ts"],
+ "#modifiers/*": ["./src/modifier/*.ts"],
+ "#moves/*": ["./src/data/moves/*.ts"],
+ "#mystery-encounters/*": [
+ "./src/data/mystery-encounters/utils/*.ts",
+ "./src/data/mystery-encounters/encounters/*.ts",
+ "./src/data/mystery-encounters/requirements/*.ts",
+ "./src/data/mystery-encounters/*.ts"
+ ],
+ "#package.json": ["./package.json"],
+ "#phases/*": ["./src/phases/*.ts"],
+ "#plugins/*": ["./src/plugins/vite/*.ts", "./src/plugins/*.ts"],
+ "#sprites/*": ["./src/sprites/*.ts"],
+ "#system/*": [
+ "./src/system/settings/*.ts",
+ "./src/system/version-migration/versions/*.ts",
+ "./src/system/version-migration/*.ts",
+ "./src/system/*.ts"
+ ],
+ "#trainers/*": ["./src/data/trainers/*.ts"],
+ "#types/*": ["./src/@types/helpers/*.ts", "./src/@types/*.ts", "./src/typings/phaser/*.ts"],
+ "#ui/*": ["./src/ui/battle-info/*.ts", "./src/ui/settings/*.ts", "./src/ui/*.ts"],
+ "#utils/*": ["./src/utils/*.ts"],
+ "#data/*": ["./src/data/pokemon-forms/*.ts", "./src/data/pokemon/*.ts", "./src/data/*.ts"],
+ "#test/*": ["./test/*.ts"],
+ "#app/*": ["./src/*.ts"]
+ }
},
- "exclude": ["node_modules", "dist", "vite.config.ts", "vitest.config.ts", "vitest.workspace.ts"]
+ // Exclude checking for script JS files as those are covered by the folder's `jsconfig.json`
+ "include": ["**/*.ts", "**/*.d.ts"],
+ "exclude": [
+ "node_modules",
+ "dist",
+ "vite.config.ts",
+ "vitest.config.ts",
+ "vitest.workspace.ts",
+ "public/service-worker.js"
+ ]
}
diff --git a/tsdoc.json b/tsdoc.json
index 689f7a96c5c..c17030cdbe7 100644
--- a/tsdoc.json
+++ b/tsdoc.json
@@ -13,6 +13,10 @@
{
"tagName": "@module",
"syntaxKind": "modifier"
+ },
+ {
+ "tagName": "@interface",
+ "syntaxKind": "modifier"
}
]
}
diff --git a/typedoc-plugins/typedoc-plugin-rename-svg.js b/typedoc-plugins/typedoc-plugin-rename-svg.js
new file mode 100644
index 00000000000..307206d6006
--- /dev/null
+++ b/typedoc-plugins/typedoc-plugin-rename-svg.js
@@ -0,0 +1,29 @@
+// @ts-check
+
+import { PageKind, Renderer } from "typedoc";
+
+/**
+ * @module
+ * Typedoc plugin to run post-processing on the `index.html` file and replace the coverage SVG
+ * for Beta with the newly generated file for the current branch.
+ */
+
+/**
+ * @param {import('typedoc').Application} app
+ */
+export function load(app) {
+ // Don't do anything if no REF_NAME was specified (likely indicating a local docs run)
+ if (!process.env.REF_NAME) {
+ return;
+ }
+ app.renderer.on(Renderer.EVENT_END_PAGE, page => {
+ if (page.pageKind === PageKind.Index && page.contents) {
+ page.contents = page.contents
+ // Replace the SVG to the beta documentation site with the current ref name
+ .replace(
+ /^ } -->
+ */
+const config = {
+ entryPoints: ["./src", "./test/test-utils"],
+ entryPointStrategy: "expand",
+ exclude: [
+ "src/polyfills.ts",
+ "src/vite.env.d.ts",
+ "**/*+.test.ts",
+ "test/test-utils/setup",
+ "test/test-utils/reporters",
+ ],
+ excludePrivate: false, // Private members are useful in the docs for contributors
+ excludeReferences: true, // prevent documenting re-exports
+ requiredToBeDocumented: [
+ "Enum",
+ "EnumMember",
+ "Variable",
+ "Function",
+ "Class",
+ "Interface",
+ "Property",
+ "Method",
+ "Accessor",
+ "TypeAlias",
+ ],
+ highlightLanguages: ["javascript", "json", "jsonc", "json5", "tsx", "typescript", "markdown"],
+ plugin: [
+ "typedoc-github-theme",
+ "typedoc-plugin-coverage",
+ "typedoc-plugin-mdn-links",
+ ...globSync("./typedoc-plugins/**/*.js").map(plugin => "./" + plugin),
+ ],
+ // Avoid emitting docs for branches other than main/beta
+ emit: dryRun ? "none" : "docs",
+ out: process.env.CI ? "/tmp/docs" : "./typedoc",
+ name: "PokéRogue",
+ readme: "./README.md",
+ coverageLabel: "Documented",
+ coverageSvgWidth: 120, // Increased from 104 baseline due to adding 2 extra letters
+ favicon: "./public/images/logo.png",
+ theme: "typedoc-github-theme",
+ customFooterHtml: "Copyright Pagefault Games 2025
",
+ customFooterHtmlDisableWrapper: true,
+ navigationLinks: {
+ GitHub: "https://github.com/pagefaultgames/pokerogue",
+ },
+};
+
+// If generating docs for main/beta, check the ref name and add an appropriate navigation header
+if (!dryRun && process.env.REF_NAME) {
+ const otherRefName = process.env.REF_NAME === "main" ? "beta" : "main";
+ config.navigationLinks = {
+ ...config.navigationLinks,
+ // This will be "Switch to Beta" when on main, and vice versa
+ [`Switch to ${otherRefName.charAt(0).toUpperCase() + otherRefName.slice(1).toLowerCase()}`]: `https://pagefaultgames.github.io/pokerogue/${otherRefName}`,
+ };
+}
+
+// biome-ignore lint/style/noDefaultExport: required by TypeDoc
+export default config;
diff --git a/typedoc.json b/typedoc.json
deleted file mode 100644
index c34e6190c1a..00000000000
--- a/typedoc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "entryPoints": ["./src"],
- "entryPointStrategy": "expand",
- "exclude": ["**/*+.test.ts"],
- "out": "typedoc",
- "highlightLanguages": ["javascript", "json", "jsonc", "json5", "tsx", "typescript", "markdown"]
-}
diff --git a/vitest.config.ts b/vitest.config.ts
index 65c5427e591..682b8052878 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -1,18 +1,25 @@
-import { defineProject } from "vitest/config";
+import { defineConfig } from "vitest/config";
import { BaseSequencer, type TestSpecification } from "vitest/node";
import { defaultConfig } from "./vite.config";
-export default defineProject(({ mode }) => ({
+export default defineConfig(({ mode }) => ({
...defaultConfig,
test: {
+ reporters: process.env.GITHUB_ACTIONS
+ ? ["github-actions", "./test/test-utils/reporters/custom-default-reporter.ts"]
+ : ["./test/test-utils/reporters/custom-default-reporter.ts"],
env: {
TZ: "UTC",
},
- testTimeout: 20000,
- setupFiles: ["./test/font-face.setup.ts", "./test/vitest.setup.ts", "./test/matchers.setup.ts"],
+ testTimeout: 20_000,
+ slowTestThreshold: 10_000,
+ // TODO: Consider enabling
+ // expect: {requireAssertions: true},
+ setupFiles: ["./test/setup/font-face.setup.ts", "./test/setup/vitest.setup.ts", "./test/setup/matchers.setup.ts"],
sequence: {
sequencer: MySequencer,
},
+ includeTaskLocation: true,
environment: "jsdom" as const,
environmentOptions: {
jsdom: {