Merge branch 'beta' into bulkgacha

This commit is contained in:
David Yang 2025-05-04 22:33:23 +08:00 committed by GitHub
commit 5554c491ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1268 changed files with 52336 additions and 38888 deletions

View File

@ -65,7 +65,7 @@ Do the reviewers need to do something special in order to test your changes?
- [ ] The PR is self-contained and cannot be split into smaller PRs?
- [ ] Have I provided a clear explanation of the changes?
- [ ] Have I tested the changes manually?
- [ ] Are all unit tests still passing? (`npm run test`)
- [ ] Are all unit tests still passing? (`npm run test:silent`)
- [ ] Have I created new automated tests (`npm run create-test`) or updated existing tests related to the PR's changes?
- [ ] Have I provided screenshots/videos of the changes (if applicable)?
- [ ] Have I made sure that any UI change works for both UI themes (default and legacy)?

73
.github/workflows/create-release.yml vendored Normal file
View File

@ -0,0 +1,73 @@
name: Create Release Branch
on:
workflow_dispatch:
inputs:
versionName:
description: "Name of version (i.e. 1.9.0)"
type: string
required: true
confirmVersion:
type: string
required: true
description: "Confirm version name"
# explicitly specify the necessary scopes
permissions:
pull-requests: write
actions: write
contents: write
jobs:
create-release:
if: github.repository == 'pagefaultgames/pokerogue' && (vars.BETA_DEPLOY_BRANCH == '' || ! startsWith(vars.BETA_DEPLOY_BRANCH, 'release'))
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for github cli commands
runs-on: ubuntu-latest
steps:
- name: Validate provided version
# Ensure version matches confirmation and conforms to expected pattern.
run: |
if [[ "${{ github.event.inputs.versionName }}" != "${{ github.event.inputs.confirmVersion }}" ]]; then
echo "Version name does not match confirmation. Exiting."
exit 1
fi
if [[ ! "${{ github.event.inputs.versionName }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version name must follow the format X.Y.Z where X, Y, and Z are all numbers. Exiting..."
exit 1
fi
shell: bash
- name: Check out code
uses: actions/checkout@v4
with:
submodules: "recursive"
# Always base off of beta branch, regardless of the branch the workflow was triggered from.
ref: beta
- 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
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 }}"
- 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="${{ secrets.RW_VARS_PAT }}" gh variable set BETA_DEPLOY_BRANCH --body "release"
- name: Create pull request to main
run: |
gh pr create --base main \
--head release \
--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 \
--head release \
--title "Release v${{ github.event.inputs.versionName }} to beta" \
--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

View File

@ -4,18 +4,23 @@ on:
push:
branches:
- beta
- release
workflow_run:
types: completed
workflows: ["Post Release Deleted"]
jobs:
deploy:
if: github.repository == 'pagefaultgames/pokerogue'
if: github.repository == 'pagefaultgames/pokerogue' && github.ref_name == ${{ vars.BETA_DEPLOY_BRANCH || 'beta' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
submodules: "recursive"
ref: ${{ vars.BETA_DEPLOY_BRANCH || 'beta'}}
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: ".nvmrc"
- name: Install dependencies
run: npm ci
- name: Build
@ -30,5 +35,5 @@ jobs:
chmod 600 ~/.ssh/*
ssh-keyscan -H ${{ secrets.BETA_SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy build on server
run: |
rsync --del --no-times --checksum -vrm dist/* ${{ secrets.BETA_SSH_USER }}@${{ secrets.BETA_SSH_HOST }}:${{ secrets.BETA_DESTINATION_DIR }}
run: |
rsync --del --no-times --checksum -vrm dist/* ${{ secrets.BETA_SSH_USER }}@${{ secrets.BETA_SSH_HOST }}:${{ secrets.BETA_DESTINATION_DIR }}

View File

@ -18,7 +18,7 @@ jobs:
submodules: 'recursive'
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: '.nvmrc'
- name: Install dependencies
run: npm ci
- name: Build

View File

@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout repository for Typedoc
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: 'recursive'
path: pokerogue_docs
@ -34,14 +34,14 @@ jobs:
sudo apt update
sudo apt install -y git openssh-client
- name: Setup Node 20.13.1
uses: actions/setup-node@v1
- name: Setup Node 22.14.1
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: "pokerogue_docs/.nvmrc"
- name: Checkout repository for Github Pages
if: github.event_name == 'push'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: pokerogue_gh
ref: gh-pages

View File

@ -0,0 +1,12 @@
name: Post Release Deleted
on:
delete:
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'
runs-on: ubuntu-latest
steps:
- name: Set BETA_DEPLOY_BRANCH to beta
run: GITHUB_TOKEN="${{ secrets.RW_VARS_PAT }}" gh variable set BETA_DEPLOY_BRANCH --body "beta" --repo "pagefaultgames/pokerogue"

View File

@ -29,6 +29,7 @@ jobs:
uses: actions/setup-node@v4 # Use the setup-node action version 4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install Node.js dependencies # Step to install Node.js dependencies
run: npm ci # Use 'npm ci' to install dependencies

View File

@ -19,13 +19,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install Node.js dependencies
run: npm ci
- name: Run tests

View File

@ -5,18 +5,60 @@ on:
# but only for the main branch
push:
branches:
- main # Trigger on push events to the main branch
- main # Trigger on push events to the main branch
- beta # Trigger on push events to the beta branch
- release # Trigger on push events to the release branch
# go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft until they fix this or ditch yml for workflows
paths:
# src and test files
- "src/**"
- "test/**"
- "public/**"
# Workflows that can impact tests
- ".github/workflows/test*.yml"
# top-level files
- "package*.json"
- ".nvrmc" # Updates to node version can break tests
- "vite.*.ts" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts
- "tsconfig*.json" # tsconfig.json tweaking can impact compilation
- "global.d.ts"
- ".env.*"
# Blanket negations for files that cannot impact tests
- "!**/*.py" # No .py files
- "!**/*.sh" # No .sh files
- "!**/*.md" # No .md files
- "!**/.git*" # .gitkeep and family
pull_request:
branches:
- main # Trigger on pull request events targeting the main branch
- main # Trigger on pull request events targeting the main branch
- beta # Trigger on pull request events targeting the beta branch
- release # Trigger on pull request events targeting the release branch
paths: # go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft because until then we have to duplicate this
# src and test files
- "src/**"
- "test/**"
- "public/**"
# Workflows that can impact tests
- ".github/workflows/test*.yml"
# top-level files
- "package*.json"
- ".nvrmc" # Updates to node version can break tests
- "vite*" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts
- "tsconfig*.json" # tsconfig.json tweaking can impact compilation
- "global.d.ts"
- ".env.*"
# Blanket negations for files that cannot impact tests
- "!**/*.py" # No .py files
- "!**/*.sh" # No .sh files
- "!**/*.md" # No .md files
- "!**/.git*" # .gitkeep and family
merge_group:
types: [checks_requested]
jobs:
run-tests:
name: Run Tests
name: Run Tests
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@ -24,4 +66,4 @@ jobs:
with:
project: main
shard: ${{ matrix.shard }}
totalShards: 10
totalShards: 10

3
.gitignore vendored
View File

@ -13,7 +13,8 @@ dist-ssr
*.local
# Editor directories and files
.vscode/*
.vscode
*.code-workspace
.idea
.DS_Store
*.suo

2
.nvmrc
View File

@ -1 +1 @@
v20.13.1
v22.14.0

View File

@ -12,7 +12,7 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
#### Prerequisites
- node: 20.13.1
- node: 22.14.0
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
#### Running Locally

View File

@ -31,13 +31,16 @@
"src/overrides.ts",
// TODO: these files are too big and complex, ignore them until their respective refactors
"src/data/moves/move.ts",
"src/data/ability.ts",
"src/data/abilities/ability.ts",
"src/field/pokemon.ts",
// this file is just too big:
"src/data/balance/tms.ts"
]
},
// While it'd be nice to enable consistent sorting, enabling this causes issues due to circular import resolution order
// TODO: Remove if we ever get down to 0 circular imports
"organizeImports": { "enabled": false },
"linter": {
"ignore": [
@ -55,31 +58,35 @@
},
"style": {
"noVar": "error",
"useEnumInitializers": "off",
"useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
"useBlockStatements": "error",
"useConst": "error",
"useImportType": "error",
"noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions
"noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files
"noParameterAssign": "off",
"useExponentiationOperator": "off",
"useExponentiationOperator": "off", // Too typo-prone and easy to mixup with standard multiplication (* vs **)
"useDefaultParameterLast": "off", // TODO: Fix spots in the codebase where this flag would be triggered, and then enable
"useSingleVarDeclarator": "off",
"useNodejsImportProtocol": "off",
"useTemplate": "off" // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
"noNamespaceImport": "error"
},
"suspicious": {
"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",
"noFallthroughSwitchClause": "off",
"noImplicitAnyLet": "info", // TODO: Refactor and make this an error
"noRedeclare": "off", // TODO: Refactor and make this an error
"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",
"noAsyncPromiseExecutor": "warn" // TODO: Refactor and make this an error
},
"complexity": {
"noExcessiveCognitiveComplexity": "warn",
"noExcessiveCognitiveComplexity": "warn", // TODO: Refactor and make this an error
"useLiteralKeys": "off",
"noForEach": "off", // Foreach vs for of is not that simple.
"noUselessSwitchCase": "off", // Explicit > Implicit
@ -98,7 +105,10 @@
"linter": {
"rules": {
"performance": {
"noDelete": "off"
"noDelete": "off" // TODO: evaluate if this is necessary for the test(s) to function
},
"style": {
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests
}
}
}

View File

@ -1,64 +1,107 @@
## How do I comment my code?
# Commenting code
### While we're not enforcing a strict standard, there are some things to keep in mind:
People spend more time reading code than writing it (sometimes substantially more so). As such, comments and documentation are **vital** for any large codebase like this.
## General Guidelines
While we're not enforcing a strict standard, here are some things to keep in mind:
- Make comments meaningful
- Comments should be explaining why a line or block of code exists and what the reason behind it is
- Comments should not be repeating chunks of code or explaining what 'true' and 'false' means in typescript
- Comments should **NOT** repeat _what_ code _does_[^1] or explain concepts obvious to someone with a basic understanding of the language at hand. Instead, focus on explaining _why_ a line or block of code exists.
- Anyone with basic reading comprehension and a good IDE can figure out what code does; gaining a _post hoc_ understanding of the _reasons_ behind its existence takes a lot more digging, effort and bloodshed.
- Keep comments readable
- A comment's verbosity should roughly scale with the complexity of its subject matter. Some people naturally write shorter or longer comments as a personal style, but summarizing a 300 line function with "does a thing" is about as good as writing nothing. Conversely, writing a paragraph-level response where a basic one-liner would suffice is no less undesirable.
- Long comments should ideally be broken into multiple lines at around the 100-120 character mark. This isn't _mandatory_, but avoids unnecessary scrolling in terminals and IDEs.
- Make sure comments exist on Functions, Classes, Methods, and Properties
- This may be the most important things to comment. When someone goes to use a function/class/method/etc., having a comment reduces the need to flip back and forth between files to figure out how something works. Peek Definition is great until you're three nested functions deep.
- The best example of this is JSDoc-style comments as seen below:
- When formatted this way, the comment gets shown by intellisense in VS Code or similar IDEs just by hovering over the text!
- Functions also show each the comment for parameter as you type them, making keeping track of what each one does in lengthy functions much more clear
```js
/**
* Changes the type-based weather modifier if this move's power would be reduced by it
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @param args [0] {@linkcode Utils.NumberHolder} for arenaAttackTypeMultiplier
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
}
- These may be the most important things to comment. When someone goes to use a function/class/method/etc., having a comment reduces the need to flip back and forth between files to figure out what XYZ does. Peek Definition is great until you're three nested levels deep.
/** Set to true when experimental animated sprites from Gen6+ are used */
public experimentalSprites: boolean = false;
[^1]: With exceptions for extremely long, convoluted or unintuitive methods (though an over-dependency on said comments is likely a symptom of poorly structured code).
# TSDoc
The codebase makes extensive use of [TSDoc](https://tsdoc.org), which is a TypeScript-specific version of [JSDoc](https://jsdoc.app/about-getting-started)
that uses similar syntax and attaches to functions, classes, etc.
When formatted correctly, these comments are shown within VS Code or similar IDEs just by hovering over the function or object.
- Functions also show the comment for each parameter as you type them, making keeping track of arguments inside lengthy functions much more clear.
They can also be used to generate a commentated overview of the codebase. There is a GitHub action that automatically updates [this docs site](https://pagefaultgames.github.io/pokerogue/main/index.html)
and you can generate it locally as well via `npm run docs` which will generate into the `typedoc/` directory.
## Syntax
For an example of how TSDoc comments work, here are some TSDoc comments taken from `src/data/moves/move.ts`:
```ts
/**
* Cures the user's party of non-volatile status conditions, ie. Heal Bell, Aromatherapy
* @extends MoveAttr
* @see {@linkcode apply}
* Attribute to put in a {@link https://bulbapedia.bulbagarden.net/wiki/Substitute_(doll) | Substitute Doll} for the user.
*/
export class DontHealThePartyPlsAttr extends MoveAttr {
export class AddSubstituteAttr extends MoveEffectAttr {
/** The ratio of the user's max HP that is required to apply this effect */
private hpCost: number;
/** Whether the damage taken should be rounded up (Shed Tail rounds up) */
private roundUp: boolean;
constructor(hpCost: number, roundUp: boolean) {
// code removed
}
/**
* Removes 1/4 of the user's maximum HP (rounded down) to create a substitute for the user
* @param user - The {@linkcode Pokemon} that used the move.
* @param target - n/a
* @param move - The {@linkcode Move} with this attribute.
* @param args - n/a
* @returns `true` if the attribute successfully applies, `false` otherwise
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// code removed
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
// code removed
}
getCondition(): MoveConditionFunc {
// code removed
}
/**
* Get the substitute-specific failure message if one should be displayed.
* @param user - The pokemon using the move.
* @returns The substitute-specific failure message if the conditions apply, otherwise `undefined`
*/
getFailedText(user: Pokemon, _target: Pokemon, _move: Move): string | undefined {
// code removed
}
}
```
You'll notice this contains an `{@linkcode Object}` tag for each parameter. This provides an easy type denomination and hyperlink to that type using VS Code's Intellisense. `@linkcode` is used instead of `@link` so that the text appears in monospace which is more obviously a `type` rather than a random hyperlink.
If you're interested in going more in depth, you can find a reference guide for how comments like these work [here](https://jsdoc.app)
Looking at the example given, you'll notice this contains an `{@linkcode XYZ}` tag in some of the parameters. This provides a clickable hyperlink to that type or object in most modern IDEs. (`@linkcode` is used here instead of `@link` so that the text appears in monospace which is more obviously a `type` rather than a random hyperlink.) \
Also note the dashes (` - `) between the parameter names and descriptions - these are **mandatory** under the TSDoc spec[^2].
If you're interested in going more in depth, you can find a reference guide for how comments like these work [on the TSDoc website](https://tsdoc.org).
The [playground page](https://tsdoc.org/play/) there can also be used for live testing of examples.
[^2]: Incidentally, this is also the only place dashes are explicitly _required_.
### What not to do:
- Don't leave comments for code you don't understand
- Incorrect information is worse than no information. If you aren't sure how something works, don't make something up to explain it. Ask for help instead.
- Incorrect information is worse than no information. If you aren't sure how something works, don't make something up to explain it - ask for help and/or mark it as TODO.
- Don't over-comment
- Not everything needs an explanation. Try to summarize blocks of code instead of singular lines where possible. Single line comments should call out specific oddities.
- Not everything needs a comment. Try to summarize blocks of code instead of singular lines where possible, always preferring giving a reason over stating a fact. Single line comments should call out specific oddities or features.
## How do Abilities and Moves differ from other classes?
While other classes should be fully documented, Abilities and Moves heavily incoperate inheritance (i.e. the `extends` keyword). Because of this, much of the functionality in these classes is duplicated or only slightly changed between classes.
### With this in mind, there's a few more things to keep in mind for these:
- Do not document any parameters if the function mirrors the one they extend.
- Keep this in mind for functions that are not the `apply` function as they are usually sparce and mostly reused
- The class itself must be documented
- This must include the `@extends BaseClass` and `@see {@linkcode apply}` tags
- Keep this in mind for functions that are not the `apply` function as they are usually sparse and mostly reused
- Class member variables must be documented
- You can use a single line documentation comment for these `/** i.e. a comment like this */`
- `args` parameters must be documented if used
- This should look something like this when there are multiple:
- This should look something vaguely like this when there are multiple:
```ts
/**
...
* @param args [0] {@linkcode Utils.NumberHolder} of arenaAttackTypeMultiplier
* [1] {@linkcode Utils.BooleanHolder} of cancelled
* [2] {@linkcode Utils.BooleanHolder} of rWeDoneYet
* @param args -
* `[0]` The {@linkcode Move} being used
* `[1]` A {@linkcode BooleanHolder} used to track XYZ
* `[2]` {@linkcode BooleanHolder} `paramC` - paramC description here
...
*/
```
```

View File

@ -37,12 +37,12 @@ The `EnemyCommandPhase` follows this process to determine whether or not an enem
1. If the Pokémon has a move already queued (e.g. they are recharging after using Hyper Beam), or they are trapped (e.g. by Bind or Arena Trap), skip to resolving a `FIGHT` command (see next section).
2. For each Pokémon in the enemy's party, [compute their matchup scores](#calculating-matchup-scores) against the active player Pokémon. If there are two active player Pokémon in the battle, add their matchup scores together.
3. Take the party member with the highest matchup score and apply a multiplier to the score that reduces the score based on how frequently the enemy trainer has switched Pokémon in the current battle.
- The multiplier scales off of a counter that increments when the enemy trainer chooses to switch a Pokémon and decrements when they choose to use a move.
- The multiplier scales off of a counter that increments when the enemy trainer chooses to switch a Pokémon and decrements when they choose to use a move.
4. Compare the result of Step 3 with the active enemy Pokémon's matchup score. If the party member's matchup score is at least three times that of the active Pokémon, switch to that party member.
- "Boss" trainers only require the party member's matchup score to be at least two times that of the active Pokémon, so they are more likely to switch than other trainers. The full list of boss trainers in the game is as follows:
- All gym leaders, Elite 4 members, and Champions
- All Evil Team leaders
- The last three Rival Fights (on waves 95, 145, and 195)
- "Boss" trainers only require the party member's matchup score to be at least two times that of the active Pokémon, so they are more likely to switch than other trainers. The full list of boss trainers in the game is as follows:
- All gym leaders, Elite 4 members, and Champions
- All Evil Team leaders
- The last three Rival Fights (on waves 95, 145, and 195)
5. If the enemy decided to switch, send a switch `turnCommand` and end this `EnemyCommandPhase`; otherwise, move on to resolving a `FIGHT` enemy command.
## Step 2: Selecting a Move
@ -54,28 +54,35 @@ At this point, the enemy (a wild or trainer Pokémon) has decided against switch
In `getNextMove()`, the enemy Pokémon chooses a move to use in the following steps:
1. If the Pokémon has a move in its Move Queue (e.g. the second turn of a charging move), and the queued move is still usable, use that move against the given target.
2. Filter out any moves it can't use within its moveset. The remaining moves make up the enemy's **move pool** for the turn.
1. A move can be unusable if it has no PP left or it has been disabled by another move or effect
2. If the enemy's move pool is empty, use Struggle.
1. A move can be unusable if it has no PP left or it has been disabled by another move or effect.
2. If the enemy's move pool is empty, use Struggle.
3. Calculate the **move score** of each move in the enemy's move pool.
1. A move's move score is equivalent to the move's maximum **target score** among all of the move's possible targets on the field ([more on this later](#calculating-move-and-target-scores)).
2. A move's move score is set to -20 if at least one of these conditions are met:
- The move is unimplemented (or, more precisely, the move's name ends with " (N)").
- Conditions for the move to succeed are not met (unless the move is Sucker Punch, Upper Hand, or Thunderclap, as those moves' conditions can't be resolved before the turn starts).
- The move's target scores are 0 or `NaN` for each target. In this case, the game assumes the target score calculation for that move is unimplemented.
1. A move's move score is equivalent to the move's maximum **target score** among all of the move's possible targets on the field ([more on this later](#calculating-move-and-target-scores)).
2. A move's move score is set to -20 if at least one of these conditions are met:
- The move is unimplemented (or, more precisely, the move's name ends with "(N)").
- Conditions for the move to succeed are not met (unless the move is Sucker Punch, Upper Hand or Thunderclap, as those moves' conditions can't be resolved until after the turn starts).
- The move's target scores are 0 or `NaN` for each target. In this case, the game assumes the target score calculation for that move is unimplemented.
4. Sort the move pool in descending order of move scores.
5. From here, the enemy's move selection varies based on its `aiType`. If the enemy is a Boss Pokémon or has a Trainer, it uses the `SMART` AI type; otherwise, it uses the `SMART_RANDOM` AI type.
1. Let $m_i$ be the *i*-th move in the sorted move pool $M$:
- If `aiType === SMART_RANDOM`, the enemy has a 5/8 chance of selecting $m_0$ and a 3/8 chance of advancing to the next best move $m_1$, where it then repeats this roll. This process stops when a move is selected or the last move in the move pool is reached.
- If `aiType === SMART`, a similar loop is used to decide between selecting the move $m_i$ and advancing to the next iteration with the move $m_{i+1}$. However, instead of using a flat probability, the following conditions need to be met to advance from selecting $m_i$ to $m_{i+1}$:
- $\text{sign}(s_i) = \text{sign}(s_{i+1})$, where $s_i$ is the move score of $m_i$.
- $\text{randInt}(0, 100) < \text{round}(\frac{s_{i+1}}{s_i}\times 50)$. In other words: if the scores of $m_i$ and $m_{i+1}$ have the same sign, the chance to advance to the next iteration with $m_{i+1}$ is proportional to how close the scores are to each other. The probability to advance to the next iteration is at most 50 percent (when $s_i$ and $s_{i+1}$ are equal).
1. Let $m_i$ be the *i*-th move in the sorted move pool $M$:
- If `aiType === SMART_RANDOM`, the enemy has a 5/8 chance of selecting $m_0$ and a 3/8 chance of advancing to the next best move $m_1$, where it then repeats this roll. This process stops when a move is selected or the last move in the move pool is reached.
- If `aiType === SMART`, a similar loop is used to decide between selecting the move $m_i$ and advancing to the next iteration with the move $m_{i+1}$. However, instead of using a flat probability, the following conditions need to be met to advance from selecting $m_i$ to $m_{i+1}$:
- $\text{sign}(s_i) = \text{sign}(s_{i+1})$, where $s_i$ is the move score of $m_i$.
- $\text{randInt}(0, 100) < \text{round}(\frac{s_{i+1}}{s_i}\times 50)$. In other words: if the scores of $m_i$ and $m_{i+1}$ have the same sign, the chance to advance to the next iteration with $m_{i+1}$ is proportional to how close the scores are to each other. The probability to advance to the next iteration is at most 50 percent (when $s_i$ and $s_{i+1}$ are equal).
6. The enemy will use the move selected in Step 5 against the target(s) with the highest [**target selection score (TSS)**](#choosing-targets-with-getnexttargets)
### Calculating Move and Target Scores
As part of the move selection process, the enemy Pokémon must compute a **target score (TS)** for each legal target for each move in its move pool. The base target score for all moves is a combination of the move's **user benefit score (UBS)** and **target benefit score (TBS)**.
As part of the move selection process, the enemy Pokémon must compute a **target score (TS)** for each legal target for each move in its move pool. The base target score is a combination of the move's **user benefit score (UBS)** and **target benefit score (TBS)**, representing how much the move helps or hinders the user and/or its target(s).
![equation](https://latex.codecogs.com/png.image?%5Cinline%20%5Cdpi%7B100%7D%5Cbg%7Bwhite%7D%5Ctext%7BTS%7D=%5Ctext%7BUBS%7D&plus;%5Ctext%7BTBS%7D%5Ctimes%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D-1&%5Ctext%7Bif%20target%20is%20an%20opponent%7D%5C%5C1&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
$$
\text{TS} = \text{UBS} + \left( \text{TBS} \times
\begin{cases}
-1 & \text{if target is an opponent} \\
1 & \text{otherwise}
\end{cases}
\right)
$$
A move's UBS and TBS are computed with the respective functions in the `Move` class:
@ -96,19 +103,38 @@ In addition to the base score from `Move.getTargetBenefitScore()`, attack moves
- The move's category (Physical/Special), and whether the user has a higher Attack or Special Attack stat.
More specifically, the following steps are taken to compute the move's `attackScore`:
1. Compute a multiplier based on the move's type effectiveness:
1. Compute a multiplier based on the move's type effectiveness:
![typeMultEqn](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D%5Cbg%7Bwhite%7D%5Ctext%7BtypeMult%7D=%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D2&&%5Ctext%7Bif%20move%20is%20super%20effective(or%20better)%7D%5C%5C-2&&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
$$
\text{typeMult} =
\begin{cases}
2 & \text{if move is super effective (or better)} \\
-2 & \text{otherwise}
\end{cases}
$$
2. Compute a multiplier based on the move's category and the user's offensive stats:
1. Compute the user's offensive stat ratio:
![statRatioEqn](https://latex.codecogs.com/png.image?%5Cinline%20%5Cdpi%7B100%7D%5Cbg%7Bwhite%7D%5Ctext%7BstatRatio%7D=%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D%5Cfrac%7B%5Ctext%7BuserSpAtk%7D%7D%7B%5Ctext%7BuserAtk%7D%7D&%5Ctext%7Bif%20move%20is%20physical%7D%5C%5C%5Cfrac%7B%5Ctext%7BuserAtk%7D%7D%7B%5Ctext%7BuserSpAtk%7D%7D&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
2. Compute the stat-based multiplier:
1. Compute the user's offensive stat ratio:
![statMultEqn](https://latex.codecogs.com/png.image?%5Cinline%20%5Cdpi%7B100%7D%5Cbg%7Bwhite%7D%5Ctext%7BstatMult%7D=%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D2&%5Ctext%7Bif%20statRatio%7D%5Cle%200.75%5C%5C1.5&%5Ctext%7Bif%5C;%7D0.75%5Cle%5Ctext%7BstatRatio%7D%5Cle%200.875%5C%5C1&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
$$
\text{statRatio} =
\begin{cases}
\frac{\text{userSpAtk}}{\text{userAtk}} & \text{if move is physical} \\
\frac{\text{userAtk}}{\text{userSpAtk}} & \text{otherwise}
\end{cases}
$$
2. Compute the stat-based multiplier:
$$
\text{statMult} =
\begin{cases}
2 & \text{if statRatio} \leq 0.75 \\
1.5 & \text{if } 0.75 \leq \text{statRatio} \leq 0.875 \\
1 & \text{otherwise}
\end{cases}
$$
3. Calculate the move's `attackScore`:
$\text{attackScore} = (\text{typeMult}\times \text{statMult})+\lfloor \frac{\text{power}}{5} \rfloor$
$\text{attackScore} = (\text{typeMult}\times \text{statMult})+\lfloor \frac{\text{power}}{5} \rfloor$
The maximum total multiplier in `attackScore` ($\text{typeMult}\times \text{statMult}$) is 4, which occurs for attacks that are super effective against the target and are categorically aligned with the user's offensive stats (e.g. the move is physical, and the user has much higher Attack than Sp. Atk). The minimum total multiplier of -4 occurs (somewhat confusingly) for attacks that are not super effective but are categorically aligned with the user's offensive stats.
@ -125,18 +151,31 @@ The final step to calculate an attack move's target score (TS) is to multiply th
The enemy's target selection for single-target moves works in a very similar way to its move selection. Each potential target is given a **target selection score (TSS)** which is based on the move's [target benefit score](#calculating-move-and-target-scores) for that target:
![TSSEqn](https://latex.codecogs.com/png.image?%5Cinline%20%5Cdpi%7B100%7D%5Cbg%7Bwhite%7D%5Ctext%7BTSS%7D=%5Ctext%7BTBS%7D%5Ctimes%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D-1&%5Ctext%7Bif%20target%20is%20an%20opponent%7D%5C%5C1&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
$$
\text{TSS} = \text{TBS} \times
\begin{cases}
-1 & \text{if target is an opponent} \\
1 & \text{otherwise}
\end{cases}
$$
Once the TSS is calculated for each target, the target is selected as follows:
1. Sort the targets (indexes) in decreasing order of their target selection scores (or weights). Let $t_i$ be the index of the *i*-th target in the sorted list, and let $w_i$ be that target's corresponding TSS.
2. Normalize the weights. Let $w_n$ be the lowest-weighted target in the sorted list, then:
![normWeightEqn](https://latex.codecogs.com/png.image?%5Cinline%20%5Cdpi%7B100%7D%5Cbg%7Bwhite%7DW_i=%5Cleft%5C%7B%5Cbegin%7Bmatrix%7Dw_i&plus;%7Cw_n%7C&%5Ctext%7Bif%5C;%7Dw_n%5C;%5Ctext%7Bis%20negative%7D%5C%5Cw_i&%5Ctext%7Botherwise%7D%5C%5C%5Cend%7Bmatrix%7D%5Cright.)
$$
W_i =
\begin{cases}
w_i + |w_n| & \text{if } w_n \text{ is negative} \\
w_i & \text{otherwise}
\end{cases}
$$
3. Remove all weights from the list such that $W_i < \frac{W_0}{2}$
4. Generate a random integer $R=\text{rand}(0, W_{\text{total}})$ where $W_{\text{total}}$ is the sum of all the remaining weights after Step 3.
5. For each target $(t_i, W_i)$,
1. if $R \le \sum_{j=0}^{i} W_i$, or if $t_i$ is the last target in the list, **return** $t_i$
2. otherwise, advance to the next target $t_{i+1}$ and repeat this check.
1. if $R \le \sum_{j=0}^{i} W_i$, or if $t_i$ is the last target in the list, **return** $t_i$
2. otherwise, advance to the next target $t_{i+1}$ and repeat this check.
Once the target is selected, the enemy has successfully determined its next action for the turn, and its corresponding `EnemyCommandPhase` ends. From here, the `TurnStartPhase` processes the enemy's commands alongside the player's commands and begins to resolve the turn.
@ -145,15 +184,15 @@ Once the target is selected, the enemy has successfully determined its next acti
Suppose you enter a single battle against an enemy trainer with the following Pokémon in their party:
1. An [Excadrill](https://bulbapedia.bulbagarden.net/wiki/Excadrill_(Pok%C3%A9mon)) with the Ability Sand Force and the following moveset
1. Earthquake
2. Iron Head
3. Crush Claw
4. Swords Dance
1. Earthquake
2. Iron Head
3. Crush Claw
4. Swords Dance
2. A [Heatmor](https://bulbapedia.bulbagarden.net/wiki/Heatmor_(Pok%C3%A9mon)) with the Ability Flash Fire and the following moveset
1. Fire Lash
2. Inferno
3. Hone Claws
4. Shadow Claw
1. Fire Lash
2. Inferno
3. Hone Claws
4. Shadow Claw
The enemy trainer leads with their Heatmor, and you lead with a [Dachsbun](https://bulbapedia.bulbagarden.net/wiki/Dachsbun_(Pok%C3%A9mon)) with the Ability Well-Baked Body. We'll cover the enemy's behavior over the next two turns.
@ -172,13 +211,13 @@ Based on the enemy party's matchup scores, whether or not the trainer switches o
Now that the enemy Pokémon with the best matchup score is on the field (assuming it survives Dachsbun's attack on the last turn), the enemy will now decide to have Excadrill use one of its moves. Assuming all of its moves are usable, we'll go through the target score calculations for each move:
- **Earthquake**: In a single battle, this move is just a 100-power Ground-type physical attack with no additional effects. With no additional benefit score from attributes, the move's base target score against the player's Dachsbun is just the `attackScore` from `AttackMove.getTargetBenefitScore()`. In this case, Earthquake's `attackScore` is given by
$\text{attackScore}=(\text{typeMult}\times \text{statMult}) + \lfloor \frac{\text{power}}{5} \rfloor = -2\times 2 + 20 = 16$
Here, `typeMult` is -2 because the move is not super effective, and `statMult` is 2 because Excadrill's Attack is significantly higher than its Sp. Atk. Accounting for STAB thanks to Excadrill's typing, the final target score for this move is **24**
- **Iron Head**: This move is an 80-power Steel-type physical attack with an additional chance to cause the target to flinch. With these properties, Iron Head has a user benefit score of 0 and a target benefit score given by
$\text{TBS}=\text{getTargetBenefitScore(FlinchAttr)}-\text{attackScore}$
Under its current implementation, the target benefit score of `FlinchAttr` is -5. Calculating the move's `attackScore`, we get:
@ -198,7 +237,7 @@ Now that the enemy Pokémon with the best matchup score is on the field (assumin
where `levels` is the number of stat stages added by the attribute (in this case, +2). The final score for this move is **6** (Note: because this move is self-targeted, we don't flip the sign of TBS when computing the target score).
- **Crush Claw**: This move is a 75-power Normal-type physical attack with a 50 percent chance to lower the target's Defense by one stage. The additional effect is implemented by the same `StatStageChangeAttr` as Swords Dance, so we can use the same formulas from before to compute the total TBS and base target score.
$\text{TBS}=\text{getTargetBenefitScore(StatStageChangeAttr)}-\text{attackScore}$
$\text{TBS}=(-4 + 2)-(-2\times 2 + \lfloor \frac{75}{5} \rfloor)=-2-11=-13$

View File

@ -1,40 +1,65 @@
# ESLint
## Key Features
# Linting & Formatting
1. **Automation**:
- A pre-commit hook has been added to automatically run ESLint on the added or modified files, ensuring code quality before commits.
> "Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
>
> — Martin Fowler
2. **Manual Usage**:
- If you prefer not to use the pre-commit hook, you can manually run ESLint to automatically fix issues using the command:
```sh
npx eslint --fix . or npm run eslint
```
- Running this command will lint all files in the repository.
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.
3. **GitHub Action**:
- A GitHub Action has been added to automatically run ESLint on every push and pull request, ensuring code quality in the CI/CD pipeline.
### Installation
You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `npm install` after cloning the repo (assuming you followed proper instructions, that is). If you haven't done that yet, go do it.
## Summary of ESLint Rules
# Using Biome
1. **General Rules**:
- **Equality**: Use `===` and `!==` instead of `==` and `!=` (`eqeqeq`).
- **Indentation**: Enforce 2-space indentation (`indent`).
- **Quotes**: Use doublequotes for strings (`quotes`).
- **Variable Declarations**:
- Disallow `var`; use `let` or `const` (`no-var`).
- Prefer `const` for variables that are never reassigned (`prefer-const`).
- **Unused Variables**: Allow unused function parameters but enforce error for other unused variables (`@typescript-eslint/no-unused-vars`).
- **End of Line**: Ensure at least one newline at the end of files (`eol-last`).
- **Curly Braces**: Enforce the use of curly braces for all control statements (`curly`).
- **Brace Style**: Use one true brace style (`1tbs`) for TypeScript-specific syntax (`@typescript-eslint/brace-style`).
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.
2. **TypeScript-Specific Rules**:
- **Semicolons**:
- Enforce semicolons for TypeScript-specific syntax (`@typescript-eslint/semi`).
- Disallow unnecessary semicolons (`@typescript-eslint/no-extra-semi`).
## 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.
## Benefits
## 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.
- **Consistency**: Ensures consistent coding style across the project.
- **Code Quality**: Helps catch potential errors and improve overall code quality.
- **Readability**: Makes the codebase easier to read and maintain.
We also have a [Github Action](../.github/workflows/quality.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream.
### Why am I getting errors for code I didn't write?
<!-- TODO: Remove this if/when we perform a project wide linting spree -->
To save time and minimize friction with existing code, both the pre-commit hook and workflow run will only check files **directly changed** by a given PR or commit.
As a result, changes to files not updated since Biome's introduction can cause any _prior_ linting errors in them to resurface and get flagged.
This should occur less and less often as time passes and more files are updated to the new standard.
## Running Biome via CLI
If you want Biome to check your files manually, you can run it from the command line like so:
```sh
npx biome check --[flags]
```
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).
- `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].
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.**
- 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.
[^1]: A complete list of rules can be found in the `biome.jsonc` file in the project root.
## What about ESLint?
<!-- Remove if/when we finally ditch eslint for good -->
Our project migrated away from ESLint around March 2025 due to it simply not scaling well enough with the codebase's ever-growing size. The [existing eslint rules](../eslint.config.js) are considered _deprecated_, only kept due to Biome lacking the corresponding rules in its current ruleset.
No additional ESLint rules should be added under any circumstances - even the few currently in circulation take longer to run than the entire Biome formatting/linting suite combined.

View File

@ -1,9 +1,10 @@
import tseslint from "@typescript-eslint/eslint-plugin";
/** @ts-check */
import tseslint from "typescript-eslint";
import stylisticTs from "@stylistic/eslint-plugin-ts";
import parser from "@typescript-eslint/parser";
import importX from "eslint-plugin-import-x";
export default [
export default tseslint.config(
{
name: "eslint-config",
files: ["src/**/*.{ts,tsx,js,jsx}", "test/**/*.{ts,tsx,js,jsx}"],
@ -14,12 +15,11 @@ export default [
plugins: {
"import-x": importX,
"@stylistic/ts": stylisticTs,
"@typescript-eslint": tseslint,
"@typescript-eslint": tseslint.plugin,
},
rules: {
"prefer-const": "error", // Enforces the use of `const` for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"no-extra-semi": "error", // Disallows unnecessary semicolons for TypeScript-specific syntax
"import-x/extensions": ["error", "never", { json: "always" }], // Enforces no extension for imports unless json
},
},
@ -33,11 +33,11 @@ export default [
},
},
plugins: {
"@typescript-eslint": tseslint,
"@typescript-eslint": tseslint.plugin,
},
rules: {
"@typescript-eslint/no-floating-promises": "error", // Require Promise-like statements to be handled appropriately. - https://typescript-eslint.io/rules/no-floating-promises/
"@typescript-eslint/no-misused-promises": "error", // Disallow Promises in places not designed to handle them. - https://typescript-eslint.io/rules/no-misused-promises/
},
},
];
);

View File

@ -2,13 +2,12 @@ pre-commit:
parallel: true
commands:
biome-lint:
glob: "*.{js,jsx,ts,tsx}"
run: npx @biomejs/biome check --write --reporter=summary {staged_files} --no-errors-on-unmatched
run: npx biome check --write --reporter=summary --staged --no-errors-on-unmatched
stage_fixed: true
skip:
- merge
- rebase
post-merge:
commands:
update-submodules:

258
package-lock.json generated
View File

@ -1,15 +1,16 @@
{
"name": "pokemon-rogue-battle",
"version": "1.8.4",
"version": "1.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pokemon-rogue-battle",
"version": "1.8.4",
"version": "1.9.0",
"hasInstallScript": true,
"dependencies": {
"@material/material-color-utilities": "^0.2.7",
"compare-versions": "^6.1.1",
"crypto-js": "^4.2.0",
"i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.4",
@ -17,8 +18,8 @@
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.2.0",
"jszip": "^3.10.1",
"phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.80.14"
"phaser": "^3.88.2",
"phaser3-rex-plugins": "^1.80.15"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
@ -26,7 +27,7 @@
"@hpcc-js/wasm": "^2.22.4",
"@stylistic/eslint-plugin-ts": "^4.1.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.12.13",
"@types/node": "^22.13.14",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@vitest/coverage-istanbul": "^3.0.9",
@ -38,16 +39,17 @@
"lefthook": "^1.11.5",
"msw": "^2.7.3",
"phaser3spectorjs": "^0.0.8",
"rollup": "^4.40.1",
"typedoc": "^0.28.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.28.0",
"vite": "^6.2.0",
"vite": "^6.3.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.9",
"vitest-canvas-mock": "^0.3.3"
},
"engines": {
"node": ">=20.0.0"
"node": ">=22.0.0"
}
},
"node_modules/@ampproject/remapping": {
@ -2160,9 +2162,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz",
"integrity": "sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
"integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==",
"cpu": [
"arm"
],
@ -2174,9 +2176,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz",
"integrity": "sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz",
"integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==",
"cpu": [
"arm64"
],
@ -2188,9 +2190,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz",
"integrity": "sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz",
"integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==",
"cpu": [
"arm64"
],
@ -2202,9 +2204,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz",
"integrity": "sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz",
"integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==",
"cpu": [
"x64"
],
@ -2216,9 +2218,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz",
"integrity": "sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz",
"integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==",
"cpu": [
"arm64"
],
@ -2230,9 +2232,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz",
"integrity": "sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz",
"integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==",
"cpu": [
"x64"
],
@ -2244,9 +2246,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz",
"integrity": "sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz",
"integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==",
"cpu": [
"arm"
],
@ -2258,9 +2260,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz",
"integrity": "sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz",
"integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==",
"cpu": [
"arm"
],
@ -2272,9 +2274,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz",
"integrity": "sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz",
"integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==",
"cpu": [
"arm64"
],
@ -2286,9 +2288,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz",
"integrity": "sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz",
"integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==",
"cpu": [
"arm64"
],
@ -2300,9 +2302,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz",
"integrity": "sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz",
"integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==",
"cpu": [
"loong64"
],
@ -2314,9 +2316,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz",
"integrity": "sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz",
"integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==",
"cpu": [
"ppc64"
],
@ -2328,9 +2330,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz",
"integrity": "sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz",
"integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==",
"cpu": [
"riscv64"
],
@ -2342,9 +2344,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz",
"integrity": "sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz",
"integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==",
"cpu": [
"riscv64"
],
@ -2356,9 +2358,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz",
"integrity": "sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz",
"integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==",
"cpu": [
"s390x"
],
@ -2370,9 +2372,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz",
"integrity": "sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz",
"integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==",
"cpu": [
"x64"
],
@ -2384,9 +2386,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz",
"integrity": "sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz",
"integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==",
"cpu": [
"x64"
],
@ -2398,9 +2400,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz",
"integrity": "sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz",
"integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==",
"cpu": [
"arm64"
],
@ -2412,9 +2414,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz",
"integrity": "sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz",
"integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==",
"cpu": [
"ia32"
],
@ -2426,9 +2428,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz",
"integrity": "sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz",
"integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==",
"cpu": [
"x64"
],
@ -2581,12 +2583,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.14.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
"integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
"version": "22.13.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
"undici-types": "~6.20.0"
}
},
"node_modules/@types/statuses": {
@ -3605,6 +3608,12 @@
"node": ">=18"
}
},
"node_modules/compare-versions": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz",
"integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==",
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -4419,6 +4428,21 @@
"reusify": "^1.0.4"
}
},
"node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@ -6219,18 +6243,18 @@
}
},
"node_modules/phaser": {
"version": "3.80.1",
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.80.1.tgz",
"integrity": "sha512-VQGAWoDOkEpAWYkI+PUADv5Ql+SM0xpLuAMBJHz9tBcOLqjJ2wd8bUhxJgOqclQlLTg97NmMd9MhS75w16x1Cw==",
"version": "3.88.2",
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.88.2.tgz",
"integrity": "sha512-UBgd2sAFuRJbF2xKaQ5jpMWB8oETncChLnymLGHcrnT53vaqiGrQWbUKUDBawKLm24sghjKo4Bf+/xfv8espZQ==",
"license": "MIT",
"dependencies": {
"eventemitter3": "^5.0.1"
}
},
"node_modules/phaser3-rex-plugins": {
"version": "1.80.14",
"resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.14.tgz",
"integrity": "sha512-eHi3VgryO9umNu6D1yQU5IS6tH4TyC2Y6RgJ495nNp37X2fdYnmYpBfgFg+YaumvtaoOvCkUVyi/YqWNPf2X2A==",
"version": "1.80.15",
"resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.15.tgz",
"integrity": "sha512-Ur973N1W5st6XEYBcJko8eTcEbdDHMM+m7VqvT3j/EJeJwYyJ3bVb33JJDsFgefk3A2iAz2itP/UY7CzxJOJVA==",
"license": "MIT",
"dependencies": {
"dagre": "^0.8.5",
@ -6551,9 +6575,9 @@
}
},
"node_modules/rollup": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz",
"integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==",
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz",
"integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6567,26 +6591,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.38.0",
"@rollup/rollup-android-arm64": "4.38.0",
"@rollup/rollup-darwin-arm64": "4.38.0",
"@rollup/rollup-darwin-x64": "4.38.0",
"@rollup/rollup-freebsd-arm64": "4.38.0",
"@rollup/rollup-freebsd-x64": "4.38.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.38.0",
"@rollup/rollup-linux-arm-musleabihf": "4.38.0",
"@rollup/rollup-linux-arm64-gnu": "4.38.0",
"@rollup/rollup-linux-arm64-musl": "4.38.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.38.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.38.0",
"@rollup/rollup-linux-riscv64-gnu": "4.38.0",
"@rollup/rollup-linux-riscv64-musl": "4.38.0",
"@rollup/rollup-linux-s390x-gnu": "4.38.0",
"@rollup/rollup-linux-x64-gnu": "4.38.0",
"@rollup/rollup-linux-x64-musl": "4.38.0",
"@rollup/rollup-win32-arm64-msvc": "4.38.0",
"@rollup/rollup-win32-ia32-msvc": "4.38.0",
"@rollup/rollup-win32-x64-msvc": "4.38.0",
"@rollup/rollup-android-arm-eabi": "4.40.1",
"@rollup/rollup-android-arm64": "4.40.1",
"@rollup/rollup-darwin-arm64": "4.40.1",
"@rollup/rollup-darwin-x64": "4.40.1",
"@rollup/rollup-freebsd-arm64": "4.40.1",
"@rollup/rollup-freebsd-x64": "4.40.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.40.1",
"@rollup/rollup-linux-arm-musleabihf": "4.40.1",
"@rollup/rollup-linux-arm64-gnu": "4.40.1",
"@rollup/rollup-linux-arm64-musl": "4.40.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.40.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.40.1",
"@rollup/rollup-linux-riscv64-gnu": "4.40.1",
"@rollup/rollup-linux-riscv64-musl": "4.40.1",
"@rollup/rollup-linux-s390x-gnu": "4.40.1",
"@rollup/rollup-linux-x64-gnu": "4.40.1",
"@rollup/rollup-linux-x64-musl": "4.40.1",
"@rollup/rollup-win32-arm64-msvc": "4.40.1",
"@rollup/rollup-win32-ia32-msvc": "4.40.1",
"@rollup/rollup-win32-x64-msvc": "4.40.1",
"fsevents": "~2.3.2"
}
},
@ -7035,6 +7059,23 @@
"dev": true,
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinypool": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
@ -7305,9 +7346,9 @@
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"dev": true,
"license": "MIT"
},
@ -7405,15 +7446,18 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/vite": {
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz",
"integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==",
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz",
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.30.1"
"rollup": "^4.34.9",
"tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"

View File

@ -1,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.8.4",
"version": "1.9.0",
"type": "module",
"scripts": {
"start": "vite",
@ -9,7 +9,7 @@
"build": "vite build",
"build:beta": "vite build --mode beta",
"preview": "vite preview",
"test": "vitest run",
"test": "vitest run --no-isolate",
"test:cov": "vitest run --coverage --no-isolate",
"test:watch": "vitest watch --coverage --no-isolate",
"test:silent": "vitest run --silent --no-isolate",
@ -33,7 +33,7 @@
"@hpcc-js/wasm": "^2.22.4",
"@stylistic/eslint-plugin-ts": "^4.1.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.12.13",
"@types/node": "^22.13.14",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@vitest/coverage-istanbul": "^3.0.9",
@ -48,13 +48,14 @@
"typedoc": "^0.28.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.28.0",
"vite": "^6.2.0",
"vite": "^6.3.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.9",
"vitest-canvas-mock": "^0.3.3"
},
"dependencies": {
"@material/material-color-utilities": "^0.2.7",
"compare-versions": "^6.1.1",
"crypto-js": "^4.2.0",
"i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.4",
@ -62,10 +63,10 @@
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.2.0",
"jszip": "^3.10.1",
"phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.80.14"
"phaser": "^3.88.2",
"phaser3-rex-plugins": "^1.80.15"
},
"engines": {
"node": ">=20.0.0"
"node": ">=22.0.0"
}
}

View File

@ -613,12 +613,6 @@
"780",
"781",
"781",
"782",
"782",
"783",
"783",
"784",
"784",
"785",
"785",
"786",
@ -1733,12 +1727,6 @@
"780b",
"781b",
"781b",
"782b",
"782b",
"783b",
"783b",
"784b",
"784b",
"785b",
"785b",
"786b",
@ -2853,12 +2841,6 @@
"780sb",
"781sb",
"781sb",
"782sb",
"782sb",
"783sb",
"783sb",
"784sb",
"784sb",
"785sb",
"785sb",
"786sb",
@ -3978,12 +3960,6 @@
"780s",
"781s",
"781s",
"782s",
"782s",
"783s",
"783s",
"784s",
"784s",
"785s",
"785s",
"786s",

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -516,8 +516,36 @@
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 0, "w": 28, "h": 11 },
"sourceSize": { "w": 28, "h": 11 }
},
"BACK_SLASH.png": {
"frame": { "x": 147, "y": 66, "w": 12, "h": 11 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
"sourceSize": { "w": 12, "h": 11 }
},
"FORWARD_SLASH.png": {
"frame": { "x": 144, "y": 55, "w": 12, "h": 11 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
"sourceSize": { "w": 12, "h": 11 }
},
"COMMA.png": {
"frame": { "x": 144, "y": 44, "w": 12, "h": 11 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
"sourceSize": { "w": 12, "h": 11 }
},
"PERIOD.png": {
"frame": { "x": 143, "y": 22, "w": 11, "h": 11 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 },
"sourceSize": { "w": 11, "h": 11 }
}
},
},
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-dev",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 409 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,41 +1,812 @@
{
"textures": [
{
"image": "784.png",
"format": "RGBA8888",
"size": {
"w": 81,
"h": 81
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 79,
"h": 81
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 79,
"h": 81
},
"frame": {
"x": 0,
"y": 0,
"w": 79,
"h": 81
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:dcfcb88e42dad35e57b259d31fbc6143:26fe93636d2b2e8c3da41c05f0648a08:c2f7ca3ab1075b8c824730653d891244$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 260, "y": 244, "w": 80, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 80, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0002.png",
"frame": { "x": 381, "y": 166, "w": 84, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 4, "w": 84, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0003.png",
"frame": { "x": 291, "y": 161, "w": 88, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 4, "w": 88, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0004.png",
"frame": { "x": 106, "y": 80, "w": 94, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 5, "w": 94, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0005.png",
"frame": { "x": 422, "y": 1, "w": 99, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 6, "w": 99, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0006.png",
"frame": { "x": 1, "y": 1, "w": 103, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 7, "w": 103, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0007.png",
"frame": { "x": 106, "y": 1, "w": 104, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 8, "w": 104, "h": 77 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0008.png",
"frame": { "x": 212, "y": 1, "w": 102, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 7, "w": 102, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0009.png",
"frame": { "x": 316, "y": 79, "w": 96, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 5, "w": 96, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0010.png",
"frame": { "x": 414, "y": 82, "w": 89, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 2, "y": 3, "w": 89, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0011.png",
"frame": { "x": 505, "y": 165, "w": 82, "h": 83 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 2, "w": 82, "h": 83 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0012.png",
"frame": { "x": 325, "y": 333, "w": 77, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 4, "w": 77, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0013.png",
"frame": { "x": 237, "y": 410, "w": 76, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 6, "w": 76, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0014.png",
"frame": { "x": 483, "y": 333, "w": 77, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 5, "w": 77, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0015.png",
"frame": { "x": 585, "y": 332, "w": 78, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 5, "w": 78, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0016.png",
"frame": { "x": 165, "y": 252, "w": 78, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 78, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0017.png",
"frame": { "x": 260, "y": 244, "w": 80, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 80, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0018.png",
"frame": { "x": 381, "y": 166, "w": 84, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 4, "w": 84, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0019.png",
"frame": { "x": 291, "y": 161, "w": 88, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 4, "w": 88, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0020.png",
"frame": { "x": 106, "y": 80, "w": 94, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 5, "w": 94, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0021.png",
"frame": { "x": 422, "y": 1, "w": 99, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 6, "w": 99, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0022.png",
"frame": { "x": 1, "y": 1, "w": 103, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 7, "w": 103, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0023.png",
"frame": { "x": 106, "y": 1, "w": 104, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 8, "w": 104, "h": 77 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0024.png",
"frame": { "x": 212, "y": 1, "w": 102, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 7, "w": 102, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0025.png",
"frame": { "x": 316, "y": 79, "w": 96, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 5, "w": 96, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0026.png",
"frame": { "x": 414, "y": 82, "w": 89, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 2, "y": 3, "w": 89, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0027.png",
"frame": { "x": 505, "y": 165, "w": 82, "h": 83 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 2, "w": 82, "h": 83 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0028.png",
"frame": { "x": 325, "y": 333, "w": 77, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 4, "w": 77, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0029.png",
"frame": { "x": 237, "y": 410, "w": 76, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 6, "w": 76, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0030.png",
"frame": { "x": 483, "y": 333, "w": 77, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 5, "w": 77, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0031.png",
"frame": { "x": 585, "y": 332, "w": 78, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 5, "w": 78, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0032.png",
"frame": { "x": 165, "y": 252, "w": 78, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 78, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0033.png",
"frame": { "x": 260, "y": 244, "w": 80, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 80, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0034.png",
"frame": { "x": 381, "y": 166, "w": 84, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 4, "w": 84, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0035.png",
"frame": { "x": 291, "y": 161, "w": 88, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 4, "w": 88, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0036.png",
"frame": { "x": 106, "y": 80, "w": 94, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 5, "w": 94, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0037.png",
"frame": { "x": 422, "y": 1, "w": 99, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 6, "w": 99, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0038.png",
"frame": { "x": 1, "y": 1, "w": 103, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 7, "w": 103, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0039.png",
"frame": { "x": 106, "y": 1, "w": 104, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 8, "w": 104, "h": 77 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0040.png",
"frame": { "x": 212, "y": 1, "w": 102, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 7, "w": 102, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0041.png",
"frame": { "x": 316, "y": 79, "w": 96, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 5, "w": 96, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0042.png",
"frame": { "x": 414, "y": 82, "w": 89, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 2, "y": 3, "w": 89, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0043.png",
"frame": { "x": 505, "y": 165, "w": 82, "h": 83 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 2, "w": 82, "h": 83 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0044.png",
"frame": { "x": 325, "y": 333, "w": 77, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 4, "w": 77, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0045.png",
"frame": { "x": 237, "y": 410, "w": 76, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 6, "w": 76, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0046.png",
"frame": { "x": 483, "y": 333, "w": 77, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 5, "w": 77, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0047.png",
"frame": { "x": 585, "y": 332, "w": 78, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 5, "w": 78, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0048.png",
"frame": { "x": 165, "y": 252, "w": 78, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 78, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0049.png",
"frame": { "x": 260, "y": 244, "w": 80, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 80, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0050.png",
"frame": { "x": 381, "y": 166, "w": 84, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 4, "w": 84, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0051.png",
"frame": { "x": 291, "y": 161, "w": 88, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 4, "w": 88, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0052.png",
"frame": { "x": 106, "y": 80, "w": 94, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 5, "w": 94, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0053.png",
"frame": { "x": 422, "y": 1, "w": 99, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 6, "w": 99, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0054.png",
"frame": { "x": 1, "y": 1, "w": 103, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 7, "w": 103, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0055.png",
"frame": { "x": 106, "y": 1, "w": 104, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 8, "w": 104, "h": 77 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0056.png",
"frame": { "x": 212, "y": 1, "w": 102, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 0, "y": 7, "w": 102, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0057.png",
"frame": { "x": 316, "y": 79, "w": 96, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 1, "y": 5, "w": 96, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0058.png",
"frame": { "x": 414, "y": 82, "w": 89, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 2, "y": 3, "w": 89, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0059.png",
"frame": { "x": 505, "y": 165, "w": 82, "h": 83 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 2, "w": 82, "h": 83 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0060.png",
"frame": { "x": 325, "y": 333, "w": 77, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 4, "w": 77, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0061.png",
"frame": { "x": 237, "y": 410, "w": 76, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 6, "w": 76, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0062.png",
"frame": { "x": 483, "y": 333, "w": 77, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 5, "w": 77, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0063.png",
"frame": { "x": 585, "y": 332, "w": 78, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 5, "w": 78, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0064.png",
"frame": { "x": 165, "y": 252, "w": 78, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 78, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0065.png",
"frame": { "x": 83, "y": 246, "w": 80, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 80, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0066.png",
"frame": { "x": 589, "y": 166, "w": 83, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 83, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0067.png",
"frame": { "x": 1, "y": 162, "w": 89, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 5, "w": 89, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0068.png",
"frame": { "x": 1, "y": 81, "w": 95, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 6, "w": 95, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0069.png",
"frame": { "x": 624, "y": 1, "w": 100, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 7, "w": 100, "h": 78 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0070.png",
"frame": { "x": 316, "y": 1, "w": 104, "h": 76 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 4, "y": 9, "w": 104, "h": 76 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0071.png",
"frame": { "x": 523, "y": 1, "w": 99, "h": 79 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 6, "w": 99, "h": 79 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0072.png",
"frame": { "x": 505, "y": 82, "w": 90, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 14, "y": 4, "w": 90, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0073.png",
"frame": { "x": 624, "y": 81, "w": 88, "h": 83 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 2, "w": 88, "h": 83 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0074.png",
"frame": { "x": 202, "y": 81, "w": 87, "h": 85 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 0, "w": 87, "h": 85 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0075.png",
"frame": { "x": 92, "y": 162, "w": 84, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 8, "y": 3, "w": 84, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0076.png",
"frame": { "x": 159, "y": 335, "w": 76, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 5, "w": 76, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0077.png",
"frame": { "x": 562, "y": 414, "w": 75, "h": 74 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 14, "w": 75, "h": 74 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0078.png",
"frame": { "x": 1, "y": 412, "w": 75, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 7, "w": 75, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0079.png",
"frame": { "x": 78, "y": 413, "w": 76, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 9, "w": 76, "h": 77 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0080.png",
"frame": { "x": 404, "y": 333, "w": 77, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 6, "w": 77, "h": 80 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0081.png",
"frame": { "x": 245, "y": 327, "w": 78, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 5, "w": 78, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0082.png",
"frame": { "x": 178, "y": 168, "w": 80, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 5, "y": 4, "w": 80, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0083.png",
"frame": { "x": 1, "y": 244, "w": 80, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 6, "y": 3, "w": 80, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0084.png",
"frame": { "x": 342, "y": 249, "w": 79, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 7, "y": 3, "w": 79, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0085.png",
"frame": { "x": 1, "y": 328, "w": 77, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 9, "y": 3, "w": 77, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0086.png",
"frame": { "x": 80, "y": 329, "w": 77, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 3, "w": 77, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0087.png",
"frame": { "x": 423, "y": 249, "w": 79, "h": 82 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 3, "w": 79, "h": 82 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0088.png",
"frame": { "x": 589, "y": 249, "w": 79, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 4, "w": 79, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
},
{
"filename": "0089.png",
"frame": { "x": 504, "y": 250, "w": 79, "h": 81 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 4, "w": 79, "h": 81 },
"sourceSize": { "w": 108, "h": 88 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.13-x64",
"image": "784.png",
"format": "I8",
"size": { "w": 725, "h": 493 },
"scale": "1"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1022 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 19 KiB

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