Merge branch 'beta' into deep-sea-items

This commit is contained in:
Madmadness65 2025-04-15 00:28:31 -05:00 committed by GitHub
commit af9524925d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
229 changed files with 8348 additions and 4147 deletions

View file

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

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

@ -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,58 @@ 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
# 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
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 +64,4 @@ jobs:
with:
project: main
shard: ${{ matrix.shard }}
totalShards: 10
totalShards: 10

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

@ -65,7 +65,8 @@
"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",
@ -99,6 +100,9 @@
"rules": {
"performance": {
"noDelete": "off"
},
"style": {
"noNamespaceImport": "off"
}
}
}

28
package-lock.json generated
View file

@ -1,15 +1,16 @@
{
"name": "pokemon-rogue-battle",
"version": "1.8.1",
"version": "1.8.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pokemon-rogue-battle",
"version": "1.8.1",
"version": "1.8.4",
"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",
@ -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",
@ -2581,12 +2582,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 +3607,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",
@ -7305,9 +7313,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"
},

View file

@ -1,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.8.1",
"version": "1.8.4",
"type": "module",
"scripts": {
"start": "vite",
@ -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",
@ -55,6 +55,7 @@
},
"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",
@ -66,6 +67,6 @@
"phaser3-rex-plugins": "^1.80.14"
},
"engines": {
"node": ">=20.0.0"
"node": ">=22.0.0"
}
}

View file

@ -1,20 +1,755 @@
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 96, "h": 98 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 96, "h": 98 },
"sourceSize": { "w": 96, "h": 98 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.13-x64",
"image": "890-eternamax.png",
"format": "RGBA8888",
"size": { "w": 96, "h": 98 },
"scale": "1"
}
{
"textures": [
{
"image": "890-eternamax.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -1,20 +1,755 @@
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 1, "y": 1, "w": 92, "h": 94 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 1, "w": 92, "h": 94 },
"sourceSize": { "w": 96, "h": 98 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.13-x64",
"image": "890-eternamax.png",
"format": "RGBA8888",
"size": { "w": 94, "h": 96 },
"scale": "1"
}
{
"textures": [
{
"image": "890-eternamax.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:1eb3f67ba4e434995b4589c97560f1be:539129d777c30d08fa799dcebaeb523e:cf277fd83435e8c90cd46073c543568b$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -1,20 +1,755 @@
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 1, "y": 1, "w": 92, "h": 94 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 3, "y": 1, "w": 92, "h": 94 },
"sourceSize": { "w": 96, "h": 98 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.13-x64",
"image": "890-eternamax_2.png",
"format": "RGBA8888",
"size": { "w": 94, "h": 96 },
"scale": "1"
}
}
{
"textures": [
{
"image": "890-eternamax_2.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1,20 +1,755 @@
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 1, "y": 1, "w": 96, "h": 98 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 96, "h": 98 },
"sourceSize": { "w": 96, "h": 98 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.13-x64",
"image": "890-eternamax_3.png",
"format": "RGBA8888",
"size": { "w": 98, "h": 100 },
"scale": "1"
}
}
{
"textures": [
{
"image": "890-eternamax_3.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -0,0 +1,656 @@
{
"3-mega": [0, 2, 2],
"6-mega-x": [0, 2, 2],
"6-mega-y": [0, 2, 2],
"80-mega": [0, 1, 1],
"94-mega": [2, 2, 2],
"127-mega": [0, 1, 1],
"130-mega": [0, 1, 1],
"142-mega": [0, 1, 1],
"150-mega-x": [0, 1, 1],
"150-mega-y": [0, 1, 1],
"181-mega": [0, 1, 2],
"212-mega": [1, 1, 2],
"229-mega": [0, 1, 1],
"248-mega": [0, 1, 1],
"257-mega": [0, 1, 1],
"282-mega": [0, 2, 2],
"302-mega": [0, 1, 1],
"303-mega": [0, 1, 1],
"306-mega": [1, 1, 1],
"308-mega": [0, 1, 1],
"310-mega": [0, 1, 1],
"334-mega": [0, 2, 1],
"354-mega": [0, 1, 1],
"359-mega": [0, 1, 1],
"362-mega": [0, 1, 1],
"373-mega": [0, 1, 1],
"376-mega": [0, 1, 1],
"380-mega": [0, 1, 1],
"381-mega": [0, 1, 1],
"382-primal": [0, 1, 1],
"383-primal": [0, 1, 1],
"384-mega": [0, 2, 1],
"428-mega": [0, 1, 1],
"445-mega": [1, 1, 1],
"448-mega": [1, 1, 1],
"475-mega": [0, 2, 2],
"531-mega": [0, 1, 1],
"653": [0, 1, 1],
"654": [0, 1, 1],
"655": [0, 1, 1],
"656": [0, 1, 1],
"657": [0, 1, 1],
"658": [0, 1, 1],
"658-ash": [0, 1, 1],
"664": [0, 1, 1],
"665": [0, 1, 1],
"666-archipelago": [0, 1, 1],
"666-continental": [0, 1, 1],
"666-elegant": [0, 1, 1],
"666-fancy": [0, 1, 1],
"666-garden": [0, 1, 1],
"666-high-plains": [0, 1, 1],
"666-icy-snow": [0, 1, 1],
"666-jungle": [0, 1, 1],
"666-marine": [0, 1, 1],
"666-meadow": [0, 1, 1],
"666-modern": [0, 1, 1],
"666-monsoon": [0, 1, 1],
"666-ocean": [0, 1, 1],
"666-poke-ball": [0, 1, 1],
"666-polar": [0, 1, 1],
"666-river": [0, 1, 1],
"666-sandstorm": [0, 1, 1],
"666-savanna": [0, 1, 1],
"666-sun": [0, 1, 1],
"666-tundra": [0, 1, 1],
"669-red": [0, 2, 2],
"669-blue": [0, 1, 1],
"669-white": [0, 1, 1],
"669-yellow": [0, 1, 1],
"669-orange": [0, 2, 2],
"670-white": [0, 1, 1],
"670-blue": [0, 1, 1],
"670-orange": [0, 1, 1],
"670-red": [0, 1, 1],
"670-yellow": [0, 1, 1],
"671-red": [0, 1, 2],
"671-blue": [0, 1, 2],
"671-yellow": [0, 1, 1],
"671-white": [0, 1, 2],
"671-orange": [0, 1, 2],
"672": [0, 1, 1],
"673": [0, 1, 1],
"676": [0, 1, 1],
"677": [0, 1, 1],
"678-female": [0, 1, 1],
"678": [0, 1, 1],
"682": [0, 1, 1],
"683": [0, 1, 1],
"684": [0, 1, 1],
"685": [0, 1, 1],
"688": [0, 1, 1],
"689": [0, 1, 1],
"690": [0, 1, 1],
"691": [0, 1, 1],
"696": [0, 1, 1],
"697": [0, 1, 1],
"699": [0, 1, 1],
"700": [0, 1, 1],
"702": [0, 1, 1],
"704": [0, 1, 1],
"705": [0, 1, 1],
"706": [0, 1, 1],
"709": [0, 1, 1],
"710": [0, 1, 1],
"711": [1, 1, 1],
"712": [0, 1, 1],
"713": [0, 1, 1],
"715": [0, 1, 1],
"716-active": [0, 1, 1],
"716-neutral": [0, 1, 1],
"717": [0, 2, 2],
"720-unbound": [1, 1, 1],
"720": [1, 1, 1],
"728": [0, 1, 1],
"729": [0, 1, 1],
"730": [0, 1, 1],
"734": [0, 1, 1],
"735": [0, 1, 1],
"742": [0, 2, 2],
"743": [0, 2, 2],
"747": [0, 2, 2],
"748": [0, 1, 1],
"751": [0, 1, 1],
"752": [0, 1, 1],
"753": [0, 1, 1],
"754": [0, 2, 2],
"755": [0, 1, 1],
"756": [0, 1, 1],
"761": [0, 1, 1],
"762": [0, 1, 1],
"763": [0, 1, 1],
"767": [0, 1, 1],
"768": [0, 1, 1],
"770": [0, 0, 0],
"771": [0, 2, 2],
"772": [0, 1, 1],
"773-fighting": [0, 1, 1],
"773-psychic": [0, 1, 1],
"773-poison": [0, 1, 1],
"773-ground": [0, 1, 1],
"773-ghost": [0, 1, 1],
"773-steel": [0, 1, 1],
"773-rock": [0, 1, 1],
"773-grass": [0, 1, 1],
"773-dragon": [0, 1, 1],
"773-bug": [0, 1, 1],
"773-ice": [0, 1, 1],
"773-dark": [0, 1, 1],
"773": [0, 1, 1],
"773-fairy": [0, 1, 1],
"773-water": [0, 1, 1],
"773-electric": [0, 1, 1],
"773-flying": [0, 1, 1],
"773-fire": [0, 1, 1],
"776": [0, 1, 1],
"777": [0, 1, 1],
"778-busted": [0, 1, 1],
"778-disguised": [0, 1, 1],
"779": [0, 1, 1],
"789": [1, 1, 1],
"790": [0, 1, 1],
"791": [2, 1, 1],
"792": [0, 1, 1],
"793": [0, 2, 2],
"797": [0, 1, 1],
"798": [0, 1, 1],
"800-dawn-wings": [0, 1, 1],
"800-dusk-mane": [0, 1, 1],
"800-ultra": [0, 1, 1],
"800": [0, 1, 1],
"802": [1, 1, 1],
"803": [0, 1, 1],
"804": [0, 1, 1],
"807": [0, 1, 1],
"808": [0, 1, 1],
"809": [0, 1, 1],
"816": [0, 1, 1],
"817": [0, 1, 1],
"818": [1, 1, 1],
"821": [0, 2, 2],
"822": [0, 1, 1],
"823": [0, 1, 1],
"829": [0, 1, 1],
"830": [0, 1, 1],
"835": [0, 1, 1],
"836": [0, 2, 2],
"850": [0, 1, 1],
"851": [0, 1, 1],
"854": [0, 1, 1],
"855": [0, 1, 1],
"856": [0, 1, 1],
"857": [0, 2, 2],
"858": [0, 1, 1],
"859": [0, 1, 1],
"860": [0, 1, 1],
"861": [0, 1, 1],
"862": [0, 1, 1],
"863": [0, 1, 1],
"864": [0, 1, 1],
"867": [0, 1, 1],
"872": [1, 1, 1],
"873": [1, 1, 1],
"876-female": [0, 1, 1],
"876": [0, 1, 1],
"877-hangry": [1, 1, 1],
"877": [1, 1, 1],
"880": [0, 1, 1],
"881": [0, 1, 1],
"882": [0, 2, 1],
"883": [0, 1, 1],
"884": [0, 1, 1],
"885": [1, 1, 1],
"886": [1, 1, 1],
"887": [1, 1, 1],
"888": [0, 1, 1],
"888-crowned": [0, 1, 1],
"889": [0, 1, 1],
"889-crowned": [0, 1, 1],
"890": [0, 2, 1],
"890-eternamax": [0, 1, 1],
"891": [1, 1, 1],
"892-rapid-strike": [1, 1, 1],
"892": [1, 1, 1],
"894": [0, 1, 1],
"895": [0, 1, 1],
"896": [1, 1, 1],
"897": [1, 1, 1],
"898": [1, 1, 1],
"898-ice": [1, 1, 1],
"898-shadow": [1, 1, 1],
"900": [0, 1, 1],
"901": [0, 1, 1],
"903": [0, 1, 1],
"909": [0, 1, 1],
"910": [0, 2, 2],
"911": [0, 2, 2],
"912": [0, 1, 2],
"913": [0, 1, 2],
"914": [0, 2, 1],
"919": [1, 1, 1],
"920": [1, 1, 1],
"924": [1, 1, 1],
"925-four": [1, 2, 2],
"925-three": [1, 2, 2],
"932": [0, 2, 2],
"933": [0, 2, 2],
"934": [0, 1, 1],
"935": [1, 1, 2],
"936": [2, 2, 2],
"937": [2, 2, 2],
"940": [0, 1, 1],
"941": [0, 1, 1],
"944": [0, 1, 1],
"945": [0, 1, 1],
"948": [0, 1, 1],
"949": [0, 1, 1],
"951": [0, 1, 1],
"952": [0, 1, 1],
"953": [0, 1, 1],
"954": [0, 1, 1],
"957": [2, 2, 2],
"958": [2, 2, 2],
"959": [2, 2, 2],
"962": [1, 1, 1],
"967": [0, 1, 1],
"968": [0, 1, 1],
"969": [0, 1, 1],
"970": [0, 1, 1],
"973": [1, 1, 1],
"974": [0, 1, 1],
"975": [0, 1, 1],
"978-curly": [0, 2, 2],
"978-droopy": [0, 2, 2],
"978-stretchy": [0, 2, 2],
"979": [2, 2, 2],
"981": [0, 1, 1],
"982": [0, 1, 1],
"982-three-segment": [0, 1, 1],
"987": [1, 1, 1],
"988": [0, 1, 2],
"993": [0, 1, 1],
"994": [0, 1, 2],
"995": [0, 1, 1],
"996": [0, 1, 1],
"997": [0, 2, 2],
"998": [0, 2, 2],
"999": [2, 1, 1],
"1000": [1, 1, 1],
"1001": [0, 1, 1],
"1003": [0, 1, 1],
"1004": [0, 1, 1],
"1006": [0, 2, 1],
"1007-apex-build": [0, 2, 2],
"1008-ultimate-mode": [1, 1, 1],
"2026": [0, 1, 1],
"2027": [0, 1, 1],
"2028": [0, 1, 1],
"2052": [0, 1, 1],
"2053": [0, 1, 0],
"2103": [0, 1, 1],
"4052": [0, 1, 1],
"4077": [0, 1, 1],
"4078": [0, 1, 1],
"4079": [0, 1, 1],
"4080": [2, 1, 1],
"4144": [0, 1, 1],
"4145": [0, 1, 1],
"4146": [0, 1, 1],
"4199": [2, 1, 1],
"4222": [0, 1, 1],
"4263": [0, 1, 1],
"4264": [0, 1, 1],
"4562": [0, 1, 1],
"6100": [0, 1, 1],
"6101": [0, 1, 1],
"6215": [0, 1, 1],
"6503": [0, 1, 1],
"6549": [0, 1, 1],
"6570": [0, 1, 1],
"6571": [0, 1, 1],
"6705": [0, 1, 1],
"6706": [0, 1, 1],
"6713": [0, 1, 1],
"female": {
"6215": [0, 1, 1]
},
"back": {
"3-mega": [0, 2, 2],
"6-mega-x": [0, 2, 2],
"6-mega-y": [0, 1, 2],
"80-mega": [0, 1, 1],
"94-mega": [1, 1, 1],
"127-mega": [0, 1, 1],
"130-mega": [0, 1, 1],
"142-mega": [0, 1, 1],
"150-mega-x": [0, 1, 1],
"150-mega-y": [0, 1, 1],
"181-mega": [0, 1, 2],
"212-mega": [1, 2, 2],
"229-mega": [0, 1, 1],
"248-mega": [0, 1, 1],
"257-mega": [0, 1, 1],
"282-mega": [0, 1, 1],
"302-mega": [0, 1, 1],
"303-mega": [0, 1, 1],
"306-mega": [1, 1, 1],
"308-mega": [0, 1, 1],
"310-mega": [0, 1, 1],
"334-mega": [0, 1, 1],
"354-mega": [0, 1, 1],
"359-mega": [0, 1, 1],
"362-mega": [0, 1, 1],
"373-mega": [0, 1, 1],
"376-mega": [0, 1, 1],
"380-mega": [0, 1, 1],
"381-mega": [0, 1, 1],
"382-primal": [0, 1, 1],
"383-primal": [0, 1, 1],
"384-mega": [0, 1, 1],
"428-mega": [0, 1, 1],
"445-mega": [1, 1, 1],
"448-mega": [1, 1, 1],
"475-mega": [0, 2, 2],
"531-mega": [0, 1, 1],
"653": [0, 1, 1],
"654": [0, 1, 1],
"655": [0, 1, 1],
"656": [0, 1, 1],
"657": [0, 1, 1],
"658": [0, 1, 1],
"658-ash": [0, 1, 1],
"664": [0, 1, 1],
"665": [0, 1, 1],
"666-archipelago": [0, 1, 1],
"666-continental": [0, 1, 1],
"666-elegant": [0, 1, 1],
"666-fancy": [0, 1, 1],
"666-garden": [0, 1, 1],
"666-high-plains": [0, 1, 1],
"666-icy-snow": [0, 1, 1],
"666-jungle": [0, 1, 1],
"666-marine": [0, 1, 1],
"666-meadow": [0, 1, 1],
"666-modern": [0, 1, 1],
"666-monsoon": [0, 1, 1],
"666-ocean": [0, 1, 1],
"666-poke-ball": [0, 1, 1],
"666-polar": [0, 1, 1],
"666-river": [0, 1, 1],
"666-sandstorm": [0, 1, 1],
"666-savanna": [0, 1, 1],
"666-sun": [0, 1, 1],
"666-tundra": [0, 1, 1],
"669-red": [0, 2, 2],
"669-blue": [0, 2, 2],
"669-white": [0, 2, 2],
"669-yellow": [0, 2, 2],
"669-orange": [0, 2, 2],
"670-white": [0, 1, 1],
"670-blue": [0, 2, 2],
"670-orange": [0, 1, 1],
"670-red": [0, 1, 1],
"670-yellow": [0, 1, 1],
"671-red": [0, 1, 1],
"671-blue": [0, 1, 1],
"671-yellow": [0, 1, 1],
"671-white": [0, 1, 1],
"671-orange": [0, 1, 1],
"672": [0, 1, 1],
"673": [0, 1, 1],
"676": [0, 1, 1],
"677": [0, 1, 1],
"678-female": [0, 1, 1],
"678": [0, 1, 1],
"682": [0, 1, 1],
"683": [0, 1, 1],
"684": [0, 1, 1],
"685": [0, 1, 1],
"688": [0, 1, 1],
"689": [0, 1, 1],
"690": [0, 1, 1],
"691": [0, 1, 1],
"696": [0, 1, 1],
"697": [0, 1, 1],
"699": [0, 2, 2],
"700": [0, 1, 1],
"702": [0, 1, 1],
"704": [0, 1, 1],
"705": [0, 1, 1],
"706": [0, 1, 1],
"709": [0, 1, 1],
"710": [0, 1, 1],
"711": [1, 1, 1],
"712": [0, 1, 1],
"713": [0, 1, 1],
"715": [0, 1, 1],
"716-active": [0, 1, 1],
"716-neutral": [0, 1, 1],
"717": [0, 1, 1],
"720-unbound": [1, 1, 1],
"720": [1, 1, 1],
"728": [0, 1, 1],
"729": [0, 1, 1],
"730": [0, 1, 1],
"734": [0, 1, 1],
"735": [0, 1, 1],
"742": [0, 2, 2],
"743": [0, 2, 2],
"747": [0, 2, 2],
"748": [0, 1, 1],
"751": [0, 1, 1],
"752": [0, 1, 1],
"753": [0, 1, 1],
"754": [0, 2, 2],
"755": [0, 1, 1],
"756": [0, 1, 1],
"761": [0, 1, 1],
"762": [0, 1, 1],
"763": [0, 1, 1],
"767": [0, 1, 1],
"768": [0, 1, 1],
"771": [0, 1, 1],
"772": [0, 1, 1],
"773-fighting": [0, 1, 1],
"773-psychic": [0, 1, 1],
"773-poison": [0, 1, 1],
"773-ground": [0, 1, 1],
"773-ghost": [0, 1, 1],
"773-steel": [0, 1, 1],
"773-rock": [0, 1, 1],
"773-grass": [0, 1, 1],
"773-dragon": [0, 1, 1],
"773-bug": [0, 1, 1],
"773-ice": [0, 1, 1],
"773-dark": [0, 1, 1],
"773": [0, 1, 1],
"773-fairy": [0, 1, 1],
"773-water": [0, 1, 1],
"773-electric": [0, 1, 1],
"773-flying": [0, 1, 1],
"773-fire": [0, 1, 1],
"776": [0, 2, 2],
"777": [0, 1, 1],
"778-busted": [0, 1, 1],
"778-disguised": [0, 1, 1],
"779": [0, 1, 1],
"789": [1, 1, 1],
"790": [0, 1, 1],
"791": [1, 1, 1],
"792": [0, 1, 1],
"793": [0, 1, 1],
"797": [0, 1, 1],
"798": [0, 1, 1],
"800-dawn-wings": [0, 1, 1],
"800-dusk-mane": [0, 1, 1],
"800-ultra": [0, 1, 1],
"800": [0, 1, 1],
"802": [1, 1, 1],
"803": [0, 1, 1],
"804": [0, 1, 1],
"807": [0, 1, 1],
"808": [0, 1, 1],
"809": [0, 1, 1],
"816": [0, 1, 1],
"817": [0, 1, 1],
"818": [0, 1, 1],
"821": [0, 1, 1],
"822": [0, 1, 1],
"823": [0, 1, 1],
"829": [0, 1, 1],
"830": [0, 1, 1],
"835": [0, 1, 1],
"836": [0, 1, 1],
"850": [0, 1, 1],
"851": [0, 1, 1],
"854": [0, 1, 1],
"855": [0, 1, 1],
"856": [0, 1, 1],
"857": [0, 2, 2],
"858": [0, 1, 1],
"859": [0, 1, 1],
"860": [0, 1, 1],
"861": [0, 1, 1],
"862": [0, 1, 1],
"863": [0, 1, 1],
"864": [0, 1, 1],
"867": [0, 1, 1],
"872": [1, 1, 1],
"873": [1, 1, 1],
"876-female": [0, 1, 1],
"876": [0, 1, 1],
"877-hangry": [1, 1, 1],
"877": [1, 1, 1],
"880": [0, 1, 1],
"881": [0, 1, 1],
"882": [0, 1, 1],
"883": [0, 1, 1],
"884": [0, 1, 1],
"885": [1, 1, 1],
"886": [1, 1, 1],
"887": [1, 1, 1],
"888": [0, 1, 1],
"888-crowned": [0, 1, 1],
"889": [0, 1, 1],
"889-crowned": [0, 1, 1],
"890": [0, 1, 1],
"891": [1, 1, 1],
"892-rapid-strike": [1, 1, 1],
"892": [1, 1, 1],
"894": [0, 1, 1],
"895": [0, 1, 1],
"896": [1, 1, 1],
"897": [1, 1, 1],
"898": [1, 1, 1],
"898-ice": [1, 1, 1],
"898-shadow": [1, 1, 1],
"900": [0, 1, 1],
"901": [0, 1, 1],
"903": [0, 1, 1],
"909": [0, 1, 1],
"910": [0, 2, 2],
"911": [0, 1, 1],
"912": [0, 1, 1],
"913": [0, 1, 1],
"914": [0, 2, 2],
"919": [1, 1, 1],
"920": [1, 1, 1],
"924": [1, 1, 1],
"925-four": [1, 2, 2],
"925-three": [1, 2, 2],
"932": [0, 1, 1],
"933": [0, 1, 1],
"934": [0, 1, 1],
"935": [2, 2, 2],
"936": [2, 2, 2],
"937": [2, 2, 2],
"940": [0, 1, 1],
"941": [0, 1, 1],
"944": [0, 1, 1],
"945": [0, 1, 1],
"948": [0, 1, 1],
"949": [0, 1, 1],
"951": [0, 1, 1],
"952": [0, 2, 1],
"953": [0, 1, 1],
"954": [0, 1, 1],
"957": [1, 1, 1],
"958": [1, 1, 1],
"959": [1, 1, 1],
"962": [1, 1, 1],
"967": [0, 1, 1],
"968": [0, 2, 2],
"969": [0, 1, 1],
"970": [0, 1, 1],
"973": [1, 1, 1],
"974": [0, 1, 1],
"975": [0, 1, 1],
"978-curly": [0, 2, 2],
"978-droopy": [0, 2, 2],
"978-stretchy": [0, 1, 1],
"979": [1, 1, 1],
"981": [0, 1, 1],
"982": [0, 1, 1],
"982-three-segment": [0, 1, 1],
"987": [1, 1, 1],
"988": [0, 1, 1],
"993": [0, 1, 1],
"994": [0, 1, 1],
"995": [0, 1, 1],
"996": [0, 1, 1],
"997": [0, 1, 1],
"998": [0, 1, 1],
"999": [1, 1, 1],
"1000": [1, 1, 1],
"1001": [0, 1, 1],
"1003": [0, 1, 1],
"1004": [0, 1, 1],
"1006": [0, 2, 2],
"1007-apex-build": [0, 2, 2],
"1008-ultimate-mode": [1, 1, 1],
"2026": [0, 1, 1],
"2027": [0, 1, 1],
"2028": [0, 1, 1],
"2052": [0, 1, 1],
"2053": [0, 1, 1],
"2103": [0, 1, 1],
"4052": [0, 1, 1],
"4077": [0, 1, 1],
"4078": [0, 1, 1],
"4079": [0, 1, 1],
"4080": [2, 2, 2],
"4144": [0, 1, 1],
"4145": [0, 1, 1],
"4146": [0, 1, 1],
"4199": [2, 1, 1],
"4222": [0, 1, 1],
"4263": [0, 1, 1],
"4264": [0, 1, 1],
"4562": [0, 1, 1],
"6100": [0, 1, 1],
"6101": [0, 1, 1],
"6215": [0, 1, 1],
"6503": [0, 1, 1],
"6549": [0, 1, 1],
"6570": [0, 1, 1],
"6571": [0, 1, 1],
"6705": [0, 1, 1],
"6706": [0, 1, 1],
"6713": [0, 1, 1],
"female": {
"6215": [0, 1, 1]
}
}
}

View file

@ -719,7 +719,7 @@
"888-crowned": [0, 1, 1],
"889": [0, 1, 1],
"889-crowned": [0, 1, 1],
"890-eternamax": [0, 2, 2],
"890-eternamax": [0, 1, 1],
"890": [0, 1, 1],
"891": [1, 1, 1],
"892-gigantamax-rapid": [1, 2, 2],
@ -1813,669 +1813,5 @@
"593": [1, 1, 1],
"6215": [0, 1, 1]
}
},
"exp": {
"3-mega": [0, 2, 2],
"6-mega-x": [0, 2, 2],
"6-mega-y": [0, 2, 2],
"80-mega": [0, 1, 1],
"94-mega": [2, 2, 2],
"127-mega": [0, 1, 1],
"130-mega": [0, 1, 1],
"142-mega": [0, 1, 1],
"150-mega-x": [0, 1, 1],
"150-mega-y": [0, 1, 1],
"181-mega": [0, 1, 2],
"212-mega": [1, 1, 2],
"229-mega": [0, 1, 1],
"248-mega": [0, 1, 1],
"257-mega": [0, 1, 1],
"282-mega": [0, 2, 2],
"302-mega": [0, 1, 1],
"303-mega": [0, 1, 1],
"306-mega": [1, 1, 1],
"308-mega": [0, 1, 1],
"310-mega": [0, 1, 1],
"334-mega": [0, 2, 1],
"354-mega": [0, 1, 1],
"359-mega": [0, 1, 1],
"362-mega": [0, 1, 1],
"373-mega": [0, 1, 1],
"376-mega": [0, 1, 1],
"380-mega": [0, 1, 1],
"381-mega": [0, 1, 1],
"382-primal": [0, 1, 1],
"383-primal": [0, 1, 1],
"384-mega": [0, 2, 1],
"428-mega": [0, 1, 1],
"445-mega": [1, 1, 1],
"448-mega": [1, 1, 1],
"475-mega": [0, 2, 2],
"531-mega": [0, 1, 1],
"653": [0, 1, 1],
"654": [0, 1, 1],
"655": [0, 1, 1],
"656": [0, 1, 1],
"657": [0, 1, 1],
"658": [0, 1, 1],
"658-ash": [0, 1, 1],
"664": [0, 1, 1],
"665": [0, 1, 1],
"666-archipelago": [0, 1, 1],
"666-continental": [0, 1, 1],
"666-elegant": [0, 1, 1],
"666-fancy": [0, 1, 1],
"666-garden": [0, 1, 1],
"666-high-plains": [0, 1, 1],
"666-icy-snow": [0, 1, 1],
"666-jungle": [0, 1, 1],
"666-marine": [0, 1, 1],
"666-meadow": [0, 1, 1],
"666-modern": [0, 1, 1],
"666-monsoon": [0, 1, 1],
"666-ocean": [0, 1, 1],
"666-poke-ball": [0, 1, 1],
"666-polar": [0, 1, 1],
"666-river": [0, 1, 1],
"666-sandstorm": [0, 1, 1],
"666-savanna": [0, 1, 1],
"666-sun": [0, 1, 1],
"666-tundra": [0, 1, 1],
"669-red": [0, 2, 2],
"669-blue": [0, 1, 1],
"669-white": [0, 1, 1],
"669-yellow": [0, 1, 1],
"669-orange": [0, 2, 2],
"670-white": [0, 1, 1],
"670-blue": [0, 1, 1],
"670-orange": [0, 1, 1],
"670-red": [0, 1, 1],
"670-yellow": [0, 1, 1],
"671-red": [0, 1, 2],
"671-blue": [0, 1, 2],
"671-yellow": [0, 1, 1],
"671-white": [0, 1, 2],
"671-orange": [0, 1, 2],
"672": [0, 1, 1],
"673": [0, 1, 1],
"676": [0, 1, 1],
"677": [0, 1, 1],
"678-female": [0, 1, 1],
"678": [0, 1, 1],
"682": [0, 1, 1],
"683": [0, 1, 1],
"684": [0, 1, 1],
"685": [0, 1, 1],
"688": [0, 1, 1],
"689": [0, 1, 1],
"690": [0, 1, 1],
"691": [0, 1, 1],
"696": [0, 1, 1],
"697": [0, 1, 1],
"698": [0, 1, 1],
"699": [0, 1, 1],
"700": [0, 1, 1],
"702": [0, 1, 1],
"703": [0, 1, 1],
"704": [0, 1, 1],
"705": [0, 1, 1],
"706": [0, 1, 1],
"708": [0, 1, 1],
"709": [0, 1, 1],
"710": [0, 1, 1],
"711": [1, 1, 1],
"712": [0, 1, 1],
"713": [0, 1, 1],
"714": [0, 1, 1],
"715": [0, 1, 1],
"716-active": [0, 1, 1],
"716-neutral": [0, 1, 1],
"717": [0, 2, 2],
"720-unbound": [1, 1, 1],
"720": [1, 1, 1],
"728": [0, 1, 1],
"729": [0, 1, 1],
"730": [0, 1, 1],
"734": [0, 1, 1],
"735": [0, 1, 1],
"742": [0, 2, 2],
"743": [0, 2, 2],
"747": [0, 2, 2],
"748": [0, 1, 1],
"751": [0, 1, 1],
"752": [0, 1, 1],
"753": [0, 1, 1],
"754": [0, 2, 2],
"755": [0, 1, 1],
"756": [0, 1, 1],
"761": [0, 1, 1],
"762": [0, 1, 1],
"763": [0, 1, 1],
"767": [0, 1, 1],
"768": [0, 1, 1],
"770": [0, 0, 0],
"771": [0, 2, 2],
"772": [0, 1, 1],
"773-fighting": [0, 1, 1],
"773-psychic": [0, 1, 1],
"773-poison": [0, 1, 1],
"773-ground": [0, 1, 1],
"773-ghost": [0, 1, 1],
"773-steel": [0, 1, 1],
"773-rock": [0, 1, 1],
"773-grass": [0, 1, 1],
"773-dragon": [0, 1, 1],
"773-bug": [0, 1, 1],
"773-ice": [0, 1, 1],
"773-dark": [0, 1, 1],
"773": [0, 1, 1],
"773-fairy": [0, 1, 1],
"773-water": [0, 1, 1],
"773-electric": [0, 1, 1],
"773-flying": [0, 1, 1],
"773-fire": [0, 1, 1],
"776": [0, 1, 1],
"777": [0, 1, 1],
"778-busted": [0, 1, 1],
"778-disguised": [0, 1, 1],
"779": [0, 1, 1],
"789": [1, 1, 1],
"790": [0, 1, 1],
"791": [2, 1, 1],
"792": [0, 1, 1],
"793": [0, 2, 2],
"797": [0, 1, 1],
"798": [0, 1, 1],
"800-dawn-wings": [0, 1, 1],
"800-dusk-mane": [0, 1, 1],
"800-ultra": [0, 1, 1],
"800": [0, 1, 1],
"802": [1, 1, 1],
"803": [0, 1, 1],
"804": [0, 1, 1],
"807": [0, 1, 1],
"808": [0, 1, 1],
"809": [0, 1, 1],
"816": [0, 1, 1],
"817": [0, 1, 1],
"818": [1, 1, 1],
"821": [0, 2, 2],
"822": [0, 1, 1],
"823": [0, 1, 1],
"829": [0, 1, 1],
"830": [0, 1, 1],
"835": [0, 1, 1],
"836": [0, 2, 2],
"850": [0, 1, 1],
"851": [0, 1, 1],
"854": [0, 1, 1],
"855": [0, 1, 1],
"856": [0, 1, 1],
"857": [0, 2, 2],
"858": [0, 1, 1],
"859": [0, 1, 1],
"860": [0, 1, 1],
"861": [0, 1, 1],
"862": [0, 1, 1],
"863": [0, 1, 1],
"864": [0, 1, 1],
"867": [0, 1, 1],
"872": [1, 1, 1],
"873": [1, 1, 1],
"876-female": [0, 1, 1],
"876": [0, 1, 1],
"877-hangry": [1, 1, 1],
"877": [1, 1, 1],
"880": [0, 1, 1],
"881": [0, 1, 1],
"882": [0, 2, 1],
"883": [0, 1, 1],
"884": [0, 1, 1],
"885": [1, 1, 1],
"886": [1, 1, 1],
"887": [1, 1, 1],
"888": [0, 1, 1],
"888-crowned": [0, 1, 1],
"889": [0, 1, 1],
"889-crowned": [0, 1, 1],
"890": [0, 2, 1],
"890-eternamax": [0, 1, 1],
"891": [1, 1, 1],
"892-rapid-strike": [1, 1, 1],
"892": [1, 1, 1],
"894": [0, 1, 1],
"895": [0, 1, 1],
"896": [1, 1, 1],
"897": [1, 1, 1],
"898": [1, 1, 1],
"898-ice": [1, 1, 1],
"898-shadow": [1, 1, 1],
"900": [0, 1, 1],
"901": [0, 1, 1],
"903": [0, 1, 1],
"909": [0, 1, 1],
"910": [0, 2, 2],
"911": [0, 2, 2],
"912": [0, 1, 2],
"913": [0, 1, 2],
"914": [0, 2, 1],
"919": [1, 1, 1],
"920": [1, 1, 1],
"924": [1, 1, 1],
"925-four": [1, 2, 2],
"925-three": [1, 2, 2],
"932": [0, 2, 2],
"933": [0, 2, 2],
"934": [0, 1, 1],
"935": [1, 1, 2],
"936": [2, 2, 2],
"937": [2, 2, 2],
"940": [0, 1, 1],
"941": [0, 1, 1],
"944": [0, 1, 1],
"945": [0, 1, 1],
"948": [0, 1, 1],
"949": [0, 1, 1],
"951": [0, 1, 1],
"952": [0, 1, 1],
"953": [0, 1, 1],
"954": [0, 1, 1],
"957": [2, 2, 2],
"958": [2, 2, 2],
"959": [2, 2, 2],
"962": [1, 1, 1],
"967": [0, 1, 1],
"968": [0, 1, 1],
"969": [0, 1, 1],
"970": [0, 1, 1],
"973": [1, 1, 1],
"974": [0, 1, 1],
"975": [0, 1, 1],
"978-curly": [0, 2, 2],
"978-droopy": [0, 2, 2],
"978-stretchy": [0, 2, 2],
"979": [2, 2, 2],
"981": [0, 1, 1],
"982": [0, 1, 1],
"982-three-segment": [0, 1, 1],
"987": [1, 1, 1],
"988": [0, 1, 2],
"993": [0, 1, 1],
"994": [0, 1, 2],
"995": [0, 1, 1],
"996": [0, 1, 1],
"997": [0, 2, 2],
"998": [0, 2, 2],
"999": [2, 1, 1],
"1000": [1, 1, 1],
"1001": [0, 1, 1],
"1003": [0, 1, 1],
"1004": [0, 1, 1],
"1006": [0, 2, 1],
"1007-apex-build": [0, 2, 2],
"1008-ultimate-mode": [1, 1, 1],
"2026": [0, 1, 1],
"2027": [0, 1, 1],
"2028": [0, 1, 1],
"2052": [0, 1, 1],
"2053": [0, 1, 0],
"2103": [0, 1, 1],
"4052": [0, 1, 1],
"4077": [0, 1, 1],
"4078": [0, 1, 1],
"4079": [0, 1, 1],
"4080": [2, 1, 1],
"4144": [0, 1, 1],
"4145": [0, 1, 1],
"4146": [0, 1, 1],
"4199": [2, 1, 1],
"4222": [0, 1, 1],
"4263": [0, 1, 1],
"4264": [0, 1, 1],
"4562": [0, 1, 1],
"6100": [0, 1, 1],
"6101": [0, 1, 1],
"6215": [0, 1, 1],
"6503": [0, 1, 1],
"6549": [0, 1, 1],
"6570": [0, 1, 1],
"6571": [0, 1, 1],
"6705": [0, 1, 1],
"6706": [0, 1, 1],
"6713": [0, 1, 1],
"female": {
"6215": [0, 1, 1]
},
"back": {
"3-mega": [0, 2, 2],
"6-mega-x": [0, 2, 2],
"6-mega-y": [0, 1, 2],
"80-mega": [0, 1, 1],
"94-mega": [1, 1, 1],
"127-mega": [0, 1, 1],
"130-mega": [0, 1, 1],
"142-mega": [0, 1, 1],
"150-mega-x": [0, 1, 1],
"150-mega-y": [0, 1, 1],
"181-mega": [0, 1, 2],
"212-mega": [1, 2, 2],
"229-mega": [0, 1, 1],
"248-mega": [0, 1, 1],
"257-mega": [0, 1, 1],
"282-mega": [0, 1, 1],
"302-mega": [0, 1, 1],
"303-mega": [0, 1, 1],
"306-mega": [1, 1, 1],
"308-mega": [0, 1, 1],
"310-mega": [0, 1, 1],
"334-mega": [0, 1, 1],
"354-mega": [0, 1, 1],
"359-mega": [0, 1, 1],
"362-mega": [0, 1, 1],
"373-mega": [0, 1, 1],
"376-mega": [0, 1, 1],
"380-mega": [0, 1, 1],
"381-mega": [0, 1, 1],
"382-primal": [0, 1, 1],
"383-primal": [0, 1, 1],
"384-mega": [0, 1, 1],
"428-mega": [0, 1, 1],
"445-mega": [1, 1, 1],
"448-mega": [1, 1, 1],
"475-mega": [0, 2, 2],
"531-mega": [0, 1, 1],
"653": [0, 1, 1],
"654": [0, 1, 1],
"655": [0, 1, 1],
"656": [0, 1, 1],
"657": [0, 1, 1],
"658": [0, 1, 1],
"658-ash": [0, 1, 1],
"664": [0, 1, 1],
"665": [0, 1, 1],
"666-archipelago": [0, 1, 1],
"666-continental": [0, 1, 1],
"666-elegant": [0, 1, 1],
"666-fancy": [0, 1, 1],
"666-garden": [0, 1, 1],
"666-high-plains": [0, 1, 1],
"666-icy-snow": [0, 1, 1],
"666-jungle": [0, 1, 1],
"666-marine": [0, 1, 1],
"666-meadow": [0, 1, 1],
"666-modern": [0, 1, 1],
"666-monsoon": [0, 1, 1],
"666-ocean": [0, 1, 1],
"666-poke-ball": [0, 1, 1],
"666-polar": [0, 1, 1],
"666-river": [0, 1, 1],
"666-sandstorm": [0, 1, 1],
"666-savanna": [0, 1, 1],
"666-sun": [0, 1, 1],
"666-tundra": [0, 1, 1],
"669-red": [0, 2, 2],
"669-blue": [0, 2, 2],
"669-white": [0, 2, 2],
"669-yellow": [0, 2, 2],
"669-orange": [0, 2, 2],
"670-white": [0, 1, 1],
"670-blue": [0, 2, 2],
"670-orange": [0, 1, 1],
"670-red": [0, 1, 1],
"670-yellow": [0, 1, 1],
"671-red": [0, 1, 1],
"671-blue": [0, 1, 1],
"671-yellow": [0, 1, 1],
"671-white": [0, 1, 1],
"671-orange": [0, 1, 1],
"672": [0, 1, 1],
"673": [0, 1, 1],
"676": [0, 1, 1],
"677": [0, 1, 1],
"678-female": [0, 1, 1],
"678": [0, 1, 1],
"682": [0, 1, 1],
"683": [0, 1, 1],
"684": [0, 1, 1],
"685": [0, 1, 1],
"688": [0, 1, 1],
"689": [0, 1, 1],
"690": [0, 1, 1],
"691": [0, 1, 1],
"696": [0, 1, 1],
"697": [0, 1, 1],
"698": [0, 1, 1],
"699": [0, 2, 2],
"700": [0, 1, 1],
"702": [0, 1, 1],
"703": [0, 1, 1],
"704": [0, 1, 1],
"705": [0, 1, 1],
"706": [0, 1, 1],
"708": [0, 1, 1],
"709": [0, 1, 1],
"710": [0, 1, 1],
"711": [1, 1, 1],
"712": [0, 1, 1],
"713": [0, 1, 1],
"714": [0, 1, 1],
"715": [0, 1, 1],
"716-active": [0, 1, 1],
"716-neutral": [0, 1, 1],
"717": [0, 1, 1],
"720-unbound": [1, 1, 1],
"720": [1, 1, 1],
"728": [0, 1, 1],
"729": [0, 1, 1],
"730": [0, 1, 1],
"734": [0, 1, 1],
"735": [0, 1, 1],
"742": [0, 2, 2],
"743": [0, 2, 2],
"747": [0, 2, 2],
"748": [0, 1, 1],
"751": [0, 1, 1],
"752": [0, 1, 1],
"753": [0, 1, 1],
"754": [0, 2, 2],
"755": [0, 1, 1],
"756": [0, 1, 1],
"761": [0, 1, 1],
"762": [0, 1, 1],
"763": [0, 1, 1],
"767": [0, 1, 1],
"768": [0, 1, 1],
"771": [0, 1, 1],
"772": [0, 1, 1],
"773-fighting": [0, 1, 1],
"773-psychic": [0, 1, 1],
"773-poison": [0, 1, 1],
"773-ground": [0, 1, 1],
"773-ghost": [0, 1, 1],
"773-steel": [0, 1, 1],
"773-rock": [0, 1, 1],
"773-grass": [0, 1, 1],
"773-dragon": [0, 1, 1],
"773-bug": [0, 1, 1],
"773-ice": [0, 1, 1],
"773-dark": [0, 1, 1],
"773": [0, 1, 1],
"773-fairy": [0, 1, 1],
"773-water": [0, 1, 1],
"773-electric": [0, 1, 1],
"773-flying": [0, 1, 1],
"773-fire": [0, 1, 1],
"776": [0, 2, 2],
"777": [0, 1, 1],
"778-busted": [0, 1, 1],
"778-disguised": [0, 1, 1],
"779": [0, 1, 1],
"789": [1, 1, 1],
"790": [0, 1, 1],
"791": [1, 1, 1],
"792": [0, 1, 1],
"793": [0, 1, 1],
"797": [0, 1, 1],
"798": [0, 1, 1],
"800-dawn-wings": [0, 1, 1],
"800-dusk-mane": [0, 1, 1],
"800-ultra": [0, 1, 1],
"800": [0, 1, 1],
"802": [1, 1, 1],
"803": [0, 1, 1],
"804": [0, 1, 1],
"807": [0, 1, 1],
"808": [0, 1, 1],
"809": [0, 1, 1],
"816": [0, 1, 1],
"817": [0, 1, 1],
"818": [0, 1, 1],
"821": [0, 1, 1],
"822": [0, 1, 1],
"823": [0, 1, 1],
"829": [0, 1, 1],
"830": [0, 1, 1],
"835": [0, 1, 1],
"836": [0, 1, 1],
"850": [0, 1, 1],
"851": [0, 1, 1],
"854": [0, 1, 1],
"855": [0, 1, 1],
"856": [0, 1, 1],
"857": [0, 2, 2],
"858": [0, 1, 1],
"859": [0, 1, 1],
"860": [0, 1, 1],
"861": [0, 1, 1],
"862": [0, 1, 1],
"863": [0, 1, 1],
"864": [0, 1, 1],
"867": [0, 1, 1],
"872": [1, 1, 1],
"873": [1, 1, 1],
"876-female": [0, 1, 1],
"876": [0, 1, 1],
"877-hangry": [1, 1, 1],
"877": [1, 1, 1],
"880": [0, 1, 1],
"881": [0, 1, 1],
"882": [0, 1, 1],
"883": [0, 1, 1],
"884": [0, 1, 1],
"885": [1, 1, 1],
"886": [1, 1, 1],
"887": [1, 1, 1],
"888": [0, 1, 1],
"888-crowned": [0, 1, 1],
"889": [0, 1, 1],
"889-crowned": [0, 1, 1],
"890": [0, 1, 1],
"891": [1, 1, 1],
"892-rapid-strike": [1, 1, 1],
"892": [1, 1, 1],
"894": [0, 1, 1],
"895": [0, 1, 1],
"896": [1, 1, 1],
"897": [1, 1, 1],
"898": [1, 1, 1],
"898-ice": [1, 1, 1],
"898-shadow": [1, 1, 1],
"900": [0, 1, 1],
"901": [0, 1, 1],
"903": [0, 1, 1],
"909": [0, 1, 1],
"910": [0, 2, 2],
"911": [0, 1, 1],
"912": [0, 1, 1],
"913": [0, 1, 1],
"914": [0, 2, 2],
"919": [1, 1, 1],
"920": [1, 1, 1],
"924": [1, 1, 1],
"925-four": [1, 2, 2],
"925-three": [1, 2, 2],
"932": [0, 1, 1],
"933": [0, 1, 1],
"934": [0, 1, 1],
"935": [2, 2, 2],
"936": [2, 2, 2],
"937": [2, 2, 2],
"940": [0, 1, 1],
"941": [0, 1, 1],
"944": [0, 1, 1],
"945": [0, 1, 1],
"948": [0, 1, 1],
"949": [0, 1, 1],
"951": [0, 1, 1],
"952": [0, 2, 1],
"953": [0, 1, 1],
"954": [0, 1, 1],
"957": [1, 1, 1],
"958": [1, 1, 1],
"959": [1, 1, 1],
"962": [1, 1, 1],
"967": [0, 1, 1],
"968": [0, 2, 2],
"969": [0, 1, 1],
"970": [0, 1, 1],
"973": [1, 1, 1],
"974": [0, 1, 1],
"975": [0, 1, 1],
"978-curly": [0, 2, 2],
"978-droopy": [0, 2, 2],
"978-stretchy": [0, 1, 1],
"979": [1, 1, 1],
"981": [0, 1, 1],
"982": [0, 1, 1],
"982-three-segment": [0, 1, 1],
"987": [1, 1, 1],
"988": [0, 1, 1],
"993": [0, 1, 1],
"994": [0, 1, 1],
"995": [0, 1, 1],
"996": [0, 1, 1],
"997": [0, 1, 1],
"998": [0, 1, 1],
"999": [1, 1, 1],
"1000": [1, 1, 1],
"1001": [0, 1, 1],
"1003": [0, 1, 1],
"1004": [0, 1, 1],
"1006": [0, 2, 2],
"1007-apex-build": [0, 2, 2],
"1008-ultimate-mode": [1, 1, 1],
"2026": [0, 1, 1],
"2027": [0, 1, 1],
"2028": [0, 1, 1],
"2052": [0, 1, 1],
"2053": [0, 1, 1],
"2103": [0, 1, 1],
"4052": [0, 1, 1],
"4077": [0, 1, 1],
"4078": [0, 1, 1],
"4079": [0, 1, 1],
"4080": [2, 2, 2],
"4144": [0, 1, 1],
"4145": [0, 1, 1],
"4146": [0, 1, 1],
"4199": [2, 1, 1],
"4222": [0, 1, 1],
"4263": [0, 1, 1],
"4264": [0, 1, 1],
"4562": [0, 1, 1],
"6100": [0, 1, 1],
"6101": [0, 1, 1],
"6215": [0, 1, 1],
"6503": [0, 1, 1],
"6549": [0, 1, 1],
"6570": [0, 1, 1],
"6571": [0, 1, 1],
"6705": [0, 1, 1],
"6706": [0, 1, 1],
"6713": [0, 1, 1],
"female": {
"6215": [0, 1, 1]
}
}
}
}

View file

@ -1,48 +0,0 @@
{
"1": {
"cbaa84": "44827c",
"b3747e": "4b7465",
"eeffbf": "cdffb5",
"dcffb2": "8eeab9",
"ffbfca": "43bf8d",
"b7ffb2": "72d8ce",
"fff2b2": "9bffa9",
"85b4cc": "cf755d",
"a6e1ff": "efab87",
"101010": "101010",
"cacaca": "cacaca",
"537180": "b04f4b",
"2eaeec": "4dc796",
"1f75a0": "29988e",
"fdfdfd": "fdfdfd",
"d197a1": "d197a1",
"ffdce6": "ffdce6",
"217aa6": "7f99e1",
"30b2f2": "b5dcff",
"f9f9f9": "e6e3b4",
"c0c0c0": "d7cca0"
},
"2": {
"cbaa84": "cc78db",
"b3747e": "c452a6",
"eeffbf": "ed9ff2",
"dcffb2": "d7bbf4",
"ffbfca": "faccff",
"b7ffb2": "dceeff",
"fff2b2": "eb88b9",
"85b4cc": "654a8a",
"a6e1ff": "936daa",
"101010": "101010",
"cacaca": "cacaca",
"537180": "392d65",
"2eaeec": "ad4e6e",
"1f75a0": "8d2656",
"fdfdfd": "fdfdfd",
"d197a1": "d197a1",
"ffdce6": "ffdce6",
"217aa6": "efaa51",
"30b2f2": "ffd169",
"f9f9f9": "373453",
"c0c0c0": "282747"
}
}

View file

@ -1,32 +0,0 @@
{
"1": {
"535763": "292638",
"306090": "c35b2a",
"c3c7d3": "68638e",
"88aacc": "e67c37",
"fefefe": "fefefe",
"a3a7b3": "4d496b",
"737783": "37344e",
"101010": "101010",
"bbddff": "ffa633",
"1fbfdf": "ff9b44",
"5f6060": "e6ac60",
"fcfefe": "ffeed6",
"bfbbbb": "ffd3a1"
},
"2": {
"535763": "976ba9",
"306090": "a03c69",
"c3c7d3": "faecff",
"88aacc": "e25493",
"fefefe": "ffe2ee",
"a3a7b3": "e4cdf9",
"737783": "cca1db",
"101010": "101010",
"bbddff": "f591bd",
"1fbfdf": "de5f8e",
"5f6060": "5a3d84",
"fcfefe": "a473bf",
"bfbbbb": "8359a7"
}
}

View file

@ -1,28 +0,0 @@
{
"1": {
"101010": "101010",
"2b2a3a": "722023",
"603d2b": "36384f",
"215738": "4d362e",
"48484a": "a14743",
"c18760": "7c808c",
"3fa76c": "907f76",
"915e45": "575a6a",
"0b0c0b": "0b0c0b",
"da585b": "5996d2",
"ff8c8f": "87d1ff"
},
"2": {
"101010": "101010",
"2b2a3a": "6f5f80",
"603d2b": "31161d",
"215738": "a94079",
"48484a": "9c92a4",
"c18760": "7e5658",
"3fa76c": "da7ea8",
"915e45": "56323a",
"0b0c0b": "0b0c0b",
"da585b": "e18933",
"ff8c8f": "ffc875"
}
}

View file

@ -1,32 +0,0 @@
{
"1": {
"6a3f73": "731338",
"bd70cc": "a42c54",
"101010": "101010",
"bfacbf": "7047ba",
"8e5499": "8e1d4b",
"f2daf2": "8d7be3",
"404040": "202558",
"665c66": "2f386b",
"ccb43d": "ff8a58",
"f8f8f8": "8d7be3",
"595959": "2f386b",
"ffe14c": "ffc182",
"000000": "101010"
},
"2": {
"6a3f73": "5f151c",
"bd70cc": "c24430",
"101010": "101010",
"bfacbf": "f9e8dd",
"8e5499": "882c27",
"f2daf2": "f8f8f8",
"404040": "5b1922",
"665c66": "7c2928",
"ccb43d": "33d8d0",
"f8f8f8": "f8f8f8",
"595959": "7c2928",
"ffe14c": "49ffcd",
"000000": "101010"
}
}

View file

@ -1,38 +0,0 @@
{
"1": {
"b3747e": "4b7465",
"ffbfca": "43bf8d",
"fff2b2": "9bffa9",
"537180": "b04f4b",
"a6e1ff": "efab87",
"101010": "101010",
"85b4cc": "cf755d",
"217aa6": "7f99e1",
"30b2f2": "b5dcff",
"fdfdfd": "fdfdfd",
"c0c0c0": "d7cca0",
"cacaca": "cacaca",
"cbaa84": "44827c",
"dcffb2": "8eeab9",
"eeffbf": "cdffb5",
"b7ffb2": "72d8ce"
},
"2": {
"b3747e": "c452a6",
"ffbfca": "faccff",
"fff2b2": "eb88b9",
"537180": "392d65",
"a6e1ff": "936daa",
"101010": "101010",
"85b4cc": "654a8a",
"217aa6": "efaa51",
"30b2f2": "ffd169",
"fdfdfd": "fdfdfd",
"c0c0c0": "282747",
"cacaca": "cacaca",
"cbaa84": "cc78db",
"dcffb2": "d7bbf4",
"eeffbf": "ed9ff2",
"b7ffb2": "dceeff"
}
}

View file

@ -1,28 +0,0 @@
{
"1": {
"306090": "c35b2a",
"88aacc": "e67c37",
"fefefe": "fefefe",
"535763": "292638",
"a3a7b3": "4d496b",
"737783": "37344e",
"bbddff": "ffa633",
"101010": "101010",
"5f6060": "e6ac60",
"bfbbbb": "ffd3a1",
"fcfefe": "ffeed6"
},
"2": {
"306090": "a03c69",
"88aacc": "e25493",
"fefefe": "ffe2ee",
"535763": "976ba9",
"a3a7b3": "e4cdf9",
"737783": "cca1db",
"bbddff": "f591bd",
"101010": "101010",
"5f6060": "5a3d84",
"bfbbbb": "8359a7",
"fcfefe": "a473bf"
}
}

View file

@ -1,40 +0,0 @@
{
"1": {
"1a1a1c": "1a1a1c",
"686665": "646085",
"103222": "802c26",
"221b17": "221b17",
"090606": "090606",
"4ab38e": "a14743",
"38956f": "a14743",
"ab9074": "7c808c",
"4e4e4e": "494e5b",
"917860": "7c808c",
"424244": "2b303c",
"78604c": "575a6a",
"6b5442": "40435a",
"5f4939": "36384f",
"4f2a09": "292929",
"6c4513": "36384f",
"353638": "353638"
},
"2": {
"1a1a1c": "1a1a1c",
"686665": "ccc3cf",
"103222": "a94079",
"221b17": "221b17",
"090606": "090606",
"4ab38e": "da7ea8",
"38956f": "da7ea8",
"ab9074": "7e5658",
"4e4e4e": "9c92a4",
"917860": "7e5658",
"424244": "6f5f80",
"78604c": "56323a",
"6b5442": "47232b",
"5f4939": "31161d",
"4f2a09": "250e14",
"6c4513": "31161d",
"353638": "57496b"
}
}

View file

@ -1,24 +0,0 @@
{
"1": {
"101010": "101010",
"6a3f73": "500a25",
"bd70cc": "a42c54",
"8e5499": "8e1d4b",
"404040": "202558",
"595959": "2f386b",
"bfacbf": "8d7be3",
"665c66": "2f386b",
"f2daf2": "8d7be3"
},
"2": {
"101010": "101010",
"6a3f73": "5f151c",
"bd70cc": "c24430",
"8e5499": "882c27",
"404040": "5b1922",
"595959": "7c2928",
"bfacbf": "f9e8dd",
"665c66": "7c2928",
"f2daf2": "f9e8dd"
}
}

@ -1 +1 @@
Subproject commit 8538aa3e0f6f38c9c9c74fd0cf6df1e2f8a0bd6d
Subproject commit e98f0eb9c2022bc78b53f0444424c636498e725a

View file

@ -22,6 +22,9 @@ from typing import Literal as L
MASTERLIST_PATH = os.path.join(
os.path.dirname(os.path.dirname(__file__)), "public", "images", "pokemon", "variant", "_masterlist.json"
)
EXP_MASTERLIST_PATH = os.path.join(
os.path.dirname(os.path.dirname(__file__)), "public", "images", "pokemon", "variant", "_exp_masterlist.json"
)
DEFAULT_OUTPUT_PATH = "sprite-mismatches.csv"
@ -93,6 +96,7 @@ if __name__ == "__main__":
help=f"The path to a file to save the output file. If not specified, will write to {DEFAULT_OUTPUT_PATH}.",
)
p.add_argument("--masterlist", default=MASTERLIST_PATH, help=f"The path to the masterlist file to validate. Defaults to {MASTERLIST_PATH}.")
p.add_argument("--exp-masterlist", default=EXP_MASTERLIST_PATH, help=f"The path to the exp masterlist file to validate against. Defaults to {EXP_MASTERLIST_PATH}.")
args = p.parse_args()
mismatches = make_mismatch_sprite_list(args.masterlist)
write_mismatch_csv(args.output, mismatches)

View file

@ -0,0 +1,6 @@
import type { SessionSaveData } from "#app/system/game-data";
export interface SessionSaveMigrator {
version: string;
migrate: (data: SessionSaveData) => void;
}

View file

@ -0,0 +1,5 @@
export interface SettingsSaveMigrator {
version: string;
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
migrate: (data: Object) => void;
}

View file

@ -0,0 +1,6 @@
import type { SystemSaveData } from "#app/system/game-data";
export interface SystemSaveMigrator {
version: string;
migrate: (data: SystemSaveData) => void;
}

View file

@ -1,11 +1,11 @@
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import type { UserInfo } from "#app/@types/UserInfo";
import { bypassLogin } from "./battle-scene";
import * as Utils from "./utils";
import { bypassLogin } from "#app/battle-scene";
import { randomString } from "#app/utils";
export let loggedInUser: UserInfo | null = null;
// This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting
export const clientSessionId = Utils.randomString(32);
export const clientSessionId = randomString(32);
export function initLoggedInUser(): void {
loggedInUser = {

View file

@ -5,9 +5,20 @@ import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { Constructor } from "#app/utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils";
import {
fixedInt,
deepMergeObjects,
getIvsFromId,
randSeedInt,
getEnumValues,
randomString,
NumberHolder,
shiftCharCodes,
formatMoney,
isNullOrUndefined,
BooleanHolder,
type Constructor,
} from "#app/utils";
import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier";
import {
ConsumableModifier,
@ -106,8 +117,8 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "#app/scene-base";
import CandyBar from "#app/ui/candy-bar";
import type { Variant, VariantSet } from "#app/data/variant";
import { variantColorCache, variantData } from "#app/data/variant";
import type { Variant } from "#app/sprites/variant";
import { variantData, clearVariantData } from "#app/sprites/variant";
import type { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides";
import { InputsController } from "#app/inputs-controller";
@ -167,9 +178,11 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { BattlerTagType } from "#enums/battler-tag-type";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
import { StatusEffect } from "#enums/status-effect";
import { globalScene, initGlobalScene } from "#app/global-scene";
import { initGlobalScene } from "#app/global-scene";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
import { expSpriteKeys } from "./sprites/sprite-keys";
import { hasExpSprite } from "./sprites/sprite-utils";
import { timedEventManager } from "./global-event-manager";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -182,8 +195,6 @@ const OPP_IVS_OVERRIDE_VALIDATED: number[] = (
export const startingWave = Overrides.STARTING_WAVE_OVERRIDE || 1;
const expSpriteKeys: string[] = [];
export let starterColors: StarterColors;
interface StarterColors {
[key: string]: [string, string];
@ -409,7 +420,7 @@ export default class BattleScene extends SceneBase {
}
const variant = atlasPath.includes("variant/") || /_[0-3]$/.test(atlasPath);
if (experimental) {
experimental = this.hasExpSprite(key);
experimental = hasExpSprite(key);
}
if (variant) {
atlasPath = atlasPath.replace("variant/", "");
@ -421,35 +432,6 @@ export default class BattleScene extends SceneBase {
);
}
/**
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
*/
public async loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant): Promise<void> {
const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey);
if (useExpSprite) {
fileRoot = `exp/${fileRoot}`;
}
let variantConfig = variantData;
fileRoot.split("/").map(p => (variantConfig ? (variantConfig = variantConfig[p]) : null));
const variantSet = variantConfig as VariantSet;
return new Promise<void>(resolve => {
if (variantSet && variant !== undefined && variantSet[variant] === 1) {
if (variantColorCache.hasOwnProperty(spriteKey)) {
return resolve();
}
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
.then(res => res.json())
.then(c => {
variantColorCache[spriteKey] = c;
resolve();
});
} else {
resolve();
}
});
}
async preload() {
if (DEBUG_RNG) {
const originalRealInRange = Phaser.Math.RND.realInRange;
@ -762,7 +744,7 @@ export default class BattleScene extends SceneBase {
}
this.playTimeTimer = this.time.addEvent({
delay: Utils.fixedInt(1000),
delay: fixedInt(1000),
repeat: -1,
callback: () => {
if (this.gameData) {
@ -783,53 +765,36 @@ export default class BattleScene extends SceneBase {
}
async initExpSprites(): Promise<void> {
if (expSpriteKeys.length) {
if (expSpriteKeys.size > 0) {
return;
}
this.cachedFetch("./exp-sprites.json")
.then(res => res.json())
.then(keys => {
if (Array.isArray(keys)) {
expSpriteKeys.push(...keys);
for (const key of keys) {
expSpriteKeys.add(key);
}
}
Promise.resolve();
});
}
/**
* Initialize the variant data.
* If experimental sprites are enabled, their entries are replaced via this method.
*/
async initVariantData(): Promise<void> {
for (const key of Object.keys(variantData)) {
delete variantData[key];
clearVariantData();
const otherVariantData = await this.cachedFetch("./images/pokemon/variant/_masterlist.json").then(r => r.json());
for (const k of Object.keys(otherVariantData)) {
variantData[k] = otherVariantData[k];
}
await this.cachedFetch("./images/pokemon/variant/_masterlist.json")
.then(res => res.json())
.then(v => {
for (const k of Object.keys(v)) {
variantData[k] = v[k];
}
if (this.experimentalSprites) {
const expVariantData = variantData["exp"];
const traverseVariantData = (keys: string[]) => {
let variantTree = variantData;
let expTree = expVariantData;
keys.map((k: string, i: number) => {
if (i < keys.length - 1) {
variantTree = variantTree[k];
expTree = expTree[k];
} else if (variantTree.hasOwnProperty(k) && expTree.hasOwnProperty(k)) {
if (["back", "female"].includes(k)) {
traverseVariantData(keys.concat(k));
} else {
variantTree[k] = expTree[k];
}
}
});
};
for (const ek of Object.keys(expVariantData)) {
traverseVariantData([ek]);
}
}
Promise.resolve();
});
if (!this.experimentalSprites) {
return;
}
const expVariantData = await this.cachedFetch("./images/pokemon/variant/_exp_masterlist.json").then(r => r.json());
deepMergeObjects(variantData, expVariantData);
}
cachedFetch(url: string, init?: RequestInit): Promise<Response> {
@ -843,48 +808,15 @@ export default class BattleScene extends SceneBase {
return fetch(url, init);
}
initStarterColors(): Promise<void> {
return new Promise(resolve => {
if (starterColors) {
return resolve();
}
this.cachedFetch("./starter-colors.json")
.then(res => res.json())
.then(sc => {
starterColors = {};
for (const key of Object.keys(sc)) {
starterColors[key] = sc[key];
}
resolve();
});
});
}
hasExpSprite(key: string): boolean {
const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key);
if (!keyMatch) {
return false;
async initStarterColors(): Promise<void> {
if (starterColors) {
return;
}
let k = keyMatch[4]!;
if (keyMatch[2]) {
k += "s";
const sc = await this.cachedFetch("./starter-colors.json").then(res => res.json());
starterColors = {};
for (const key of Object.keys(sc)) {
starterColors[key] = sc[key];
}
if (keyMatch[1]) {
k += "b";
}
if (keyMatch[3]) {
k += "f";
}
if (keyMatch[5]) {
k += keyMatch[5];
}
if (!expSpriteKeys.includes(k)) {
return false;
}
return true;
}
public getPlayerParty(): PlayerPokemon[] {
@ -1067,7 +999,7 @@ export default class BattleScene extends SceneBase {
}
if (boss && !dataSource) {
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296));
const secondaryIvs = getIvsFromId(randSeedInt(4294967296));
for (let s = 0; s < pokemon.ivs.length; s++) {
pokemon.ivs[s] = Math.round(
@ -1140,7 +1072,7 @@ export default class BattleScene extends SceneBase {
container.add(icon);
if (pokemon.isFusion()) {
if (pokemon.isFusion(true)) {
const fusionIcon = this.add.sprite(0, 0, pokemon.getFusionIconAtlasKey(ignoreOverride));
fusionIcon.setName("sprite-fusion-icon");
fusionIcon.setOrigin(0.5, 0);
@ -1226,7 +1158,7 @@ export default class BattleScene extends SceneBase {
* Generates a random number using the current battle's seed
*
* This calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts`
* which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`
* which calls {@linkcode randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`
*
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
* @param min The minimum integer to pick, default `0`
@ -1251,7 +1183,7 @@ export default class BattleScene extends SceneBase {
this.lockModifierTiers = false;
this.pokeballCounts = Object.fromEntries(
Utils.getEnumValues(PokeballType)
getEnumValues(PokeballType)
.filter(p => p <= PokeballType.MASTER_BALL)
.map(t => [t, 0]),
);
@ -1283,7 +1215,7 @@ export default class BattleScene extends SceneBase {
// Reset RNG after end of game or save & quit.
// This needs to happen after clearing this.currentBattle or the seed will be affected by the last wave played
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
this.setSeed(Overrides.SEED_OVERRIDE || randomString(24));
console.log("Seed:", this.seed);
this.resetSeed();
@ -1324,7 +1256,7 @@ export default class BattleScene extends SceneBase {
...allSpecies,
...allMoves,
...allAbilities,
...Utils.getEnumValues(ModifierPoolType)
...getEnumValues(ModifierPoolType)
.map(mpt => getModifierPoolForType(mpt))
.flatMap(mp =>
Object.values(mp)
@ -1364,7 +1296,7 @@ export default class BattleScene extends SceneBase {
}
getDoubleBattleChance(newWaveIndex: number, playerField: PlayerPokemon[]) {
const doubleChance = new Utils.NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
for (const p of playerField) {
applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance);
@ -1421,7 +1353,7 @@ export default class BattleScene extends SceneBase {
if (trainerConfigs[trainerType].doubleOnly) {
doubleTrainer = true;
} else if (trainerConfigs[trainerType].hasDouble) {
doubleTrainer = !Utils.randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
doubleTrainer = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
if (
trainerConfigs[trainerType].trainerTypeDouble &&
@ -1432,7 +1364,7 @@ export default class BattleScene extends SceneBase {
}
const variant = doubleTrainer
? TrainerVariant.DOUBLE
: Utils.randSeedInt(2)
: randSeedInt(2)
? TrainerVariant.FEMALE
: TrainerVariant.DEFAULT;
newTrainer = trainerData !== undefined ? trainerData.toTrainer() : new Trainer(trainerType, variant);
@ -1450,7 +1382,7 @@ export default class BattleScene extends SceneBase {
if (double === undefined && newWaveIndex > 1) {
if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) {
newDouble = !Utils.randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
newDouble = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
} else if (newBattleType === BattleType.TRAINER) {
newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
}
@ -1526,8 +1458,6 @@ export default class BattleScene extends SceneBase {
this.currentBattle.mysteryEncounterType = mysteryEncounterType;
}
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
if (!waveIndex && lastBattle) {
const isWaveIndexMultipleOfTen = !(lastBattle.waveIndex % 10);
const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily;
@ -1640,7 +1570,7 @@ export default class BattleScene extends SceneBase {
scale: scale,
x: (defaultWidth - scaledWidth) / 2,
y: defaultHeight - scaledHeight,
duration: !instant ? Utils.fixedInt(Math.abs(this.field.scale - scale) * 200) : 0,
duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0,
ease: "Sine.easeInOut",
onComplete: () => resolve(),
});
@ -1737,12 +1667,12 @@ export default class BattleScene extends SceneBase {
case Species.SQUAWKABILLY:
case Species.TATSUGIRI:
case Species.PALDEA_TAUROS:
return Utils.randSeedInt(species.forms.length);
return randSeedInt(species.forms.length);
case Species.PIKACHU:
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
}
return Utils.randSeedInt(8);
return randSeedInt(8);
case Species.EEVEE:
if (
this.currentBattle?.battleType === BattleType.TRAINER &&
@ -1751,22 +1681,22 @@ export default class BattleScene extends SceneBase {
) {
return 0; // No Partner Eevee for Wave 12 Preschoolers
}
return Utils.randSeedInt(2);
return randSeedInt(2);
case Species.FROAKIE:
case Species.FROGADIER:
case Species.GRENINJA:
if (this.currentBattle?.battleType === BattleType.TRAINER && !isEggPhase) {
return 0; // Don't give trainers Battle Bond Greninja, Froakie or Frogadier
}
return Utils.randSeedInt(2);
return randSeedInt(2);
case Species.URSHIFU:
return Utils.randSeedInt(2);
return randSeedInt(2);
case Species.ZYGARDE:
return Utils.randSeedInt(4);
return randSeedInt(4);
case Species.MINIOR:
return Utils.randSeedInt(7);
return randSeedInt(7);
case Species.ALCREMIE:
return Utils.randSeedInt(9);
return randSeedInt(9);
case Species.MEOWSTIC:
case Species.INDEEDEE:
case Species.BASCULEGION:
@ -1797,7 +1727,7 @@ export default class BattleScene extends SceneBase {
if (this.gameMode.hasMysteryEncounters && !isEggPhase) {
return 1; // Wandering form
}
return Utils.randSeedInt(species.forms.length);
return randSeedInt(species.forms.length);
}
if (ignoreArena) {
@ -1806,7 +1736,7 @@ export default class BattleScene extends SceneBase {
case Species.WORMADAM:
case Species.ROTOM:
case Species.LYCANROC:
return Utils.randSeedInt(species.forms.length);
return randSeedInt(species.forms.length);
}
return 0;
}
@ -1818,7 +1748,7 @@ export default class BattleScene extends SceneBase {
let ret = false;
this.executeWithSeedOffset(
() => {
ret = !Utils.randSeedInt(2);
ret = !randSeedInt(2);
},
0,
this.seed.toString(),
@ -1830,7 +1760,7 @@ export default class BattleScene extends SceneBase {
let ret = 0;
this.executeWithSeedOffset(
() => {
ret = Utils.randSeedInt(8) * 5;
ret = randSeedInt(8) * 5;
},
0,
this.seed.toString(),
@ -1859,7 +1789,7 @@ export default class BattleScene extends SceneBase {
isBoss =
waveIndex % 10 === 0 ||
(this.gameMode.hasRandomBosses &&
Utils.randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
}, waveIndex << 2);
}
if (!isBoss) {
@ -1886,7 +1816,7 @@ export default class BattleScene extends SceneBase {
const infectedIndexes: number[] = [];
const spread = (index: number, spreadTo: number) => {
const partyMember = party[index + spreadTo];
if (!partyMember.pokerus && !Utils.randSeedInt(10)) {
if (!partyMember.pokerus && !randSeedInt(10)) {
partyMember.pokerus = true;
infectedIndexes.push(index + spreadTo);
}
@ -1912,7 +1842,7 @@ export default class BattleScene extends SceneBase {
resetSeed(waveIndex?: number): void {
const wave = waveIndex || this.currentBattle?.waveIndex || 0;
this.waveSeed = Utils.shiftCharCodes(this.seed, wave);
this.waveSeed = shiftCharCodes(this.seed, wave);
Phaser.Math.RND.sow([this.waveSeed]);
console.log("Wave Seed:", this.waveSeed, wave);
this.rngCounter = 0;
@ -1931,7 +1861,7 @@ export default class BattleScene extends SceneBase {
const tempRngOffset = this.rngOffset;
const tempRngSeedOverride = this.rngSeedOverride;
const state = Phaser.Math.RND.state();
Phaser.Math.RND.sow([Utils.shiftCharCodes(seedOverride || this.seed, offset)]);
Phaser.Math.RND.sow([shiftCharCodes(seedOverride || this.seed, offset)]);
this.rngCounter = 0;
this.rngOffset = offset;
this.rngSeedOverride = seedOverride || "";
@ -2076,7 +2006,7 @@ export default class BattleScene extends SceneBase {
if (this.money === undefined) {
return;
}
const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money);
const formattedMoney = formatMoney(this.moneyFormat, this.money);
this.moneyText.setText(i18next.t("battleScene:moneyOwned", { formattedMoney }));
this.fieldUI.moveAbove(this.moneyText, this.luckText);
if (forceVisible) {
@ -2233,12 +2163,12 @@ export default class BattleScene extends SceneBase {
),
]
: allSpecies.filter(s => s.isCatchable());
return filteredSpecies[Utils.randSeedInt(filteredSpecies.length)];
return filteredSpecies[randSeedInt(filteredSpecies.length)];
}
generateRandomBiome(waveIndex: number): Biome {
const relWave = waveIndex % 250;
const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
const maxDepth = biomeDepths[Biome.END][0] - 2;
const depthWeights = new Array(maxDepth + 1)
.fill(null)
@ -2250,7 +2180,7 @@ export default class BattleScene extends SceneBase {
biomeThresholds.push(totalWeight);
}
const randInt = Utils.randSeedInt(totalWeight);
const randInt = randSeedInt(totalWeight);
for (let i = 0; i < biomes.length; i++) {
if (randInt < biomeThresholds[i]) {
@ -2258,7 +2188,7 @@ export default class BattleScene extends SceneBase {
}
}
return biomes[Utils.randSeedInt(biomes.length)];
return biomes[randSeedInt(biomes.length)];
}
isBgmPlaying(): boolean {
@ -2443,7 +2373,7 @@ export default class BattleScene extends SceneBase {
this.bgmResumeTimer.destroy();
}
if (resumeBgm) {
this.bgmResumeTimer = this.time.delayedCall(pauseDuration || Utils.fixedInt(sound.totalDuration * 1000), () => {
this.bgmResumeTimer = this.time.delayedCall(pauseDuration || fixedInt(sound.totalDuration * 1000), () => {
this.resumeBgm();
this.bgmResumeTimer = null;
});
@ -2665,7 +2595,7 @@ export default class BattleScene extends SceneBase {
case "mystery_encounter_delibirdy": // Firel Delibirdy
return 82.28;
case "title_afd": // Andr06 - PokéRogue Title Remix (AFD)
return 47.660;
return 47.66;
case "battle_rival_3_afd": // Andr06 - Final N Battle Remix (AFD)
return 49.147;
}
@ -2937,14 +2867,19 @@ export default class BattleScene extends SceneBase {
* @param show Whether to show or hide the bar
*/
public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void {
this.unshiftPhase(
show
? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive)
: new HideAbilityPhase(pokemon.getBattlerIndex(), passive),
);
this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase());
this.clearPhaseQueueSplice();
}
/**
* Hides the ability bar if it is currently visible
*/
public hideAbilityBar(): void {
if (this.abilityBar.isVisible()) {
this.unshiftPhase(new HideAbilityPhase());
}
}
/**
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
*/
@ -3031,7 +2966,7 @@ export default class BattleScene extends SceneBase {
const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.NumberHolder(1);
const hpRestoreMultiplier = new NumberHolder(1);
this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier);
args.push(hpRestoreMultiplier.value);
} else {
@ -3039,7 +2974,7 @@ export default class BattleScene extends SceneBase {
}
} else if (modifier instanceof FusePokemonModifier) {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} else if (modifier instanceof RememberMoveModifier && !Utils.isNullOrUndefined(cost)) {
} else if (modifier instanceof RememberMoveModifier && !isNullOrUndefined(cost)) {
args.push(cost);
}
@ -3108,7 +3043,7 @@ export default class BattleScene extends SceneBase {
itemLost = true,
): boolean {
const source = itemModifier.pokemonId ? itemModifier.getPokemon() : null;
const cancelled = new Utils.BooleanHolder(false);
const cancelled = new BooleanHolder(false);
if (source && source.isPlayer() !== target.isPlayer()) {
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
@ -3177,7 +3112,7 @@ export default class BattleScene extends SceneBase {
canTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferQuantity = 1): boolean {
const mod = itemModifier.clone() as PokemonHeldItemModifier;
const source = mod.pokemonId ? mod.getPokemon() : null;
const cancelled = new Utils.BooleanHolder(false);
const cancelled = new BooleanHolder(false);
if (source && source.isPlayer() !== target.isPlayer()) {
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
@ -3271,7 +3206,7 @@ export default class BattleScene extends SceneBase {
}
let count = 0;
for (let c = 0; c < chances; c++) {
if (!Utils.randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) {
if (!randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) {
count++;
}
}
@ -3447,7 +3382,7 @@ export default class BattleScene extends SceneBase {
if (mods.length < 1) {
return mods;
}
const rand = Utils.randSeedInt(mods.length);
const rand = randSeedInt(mods.length);
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
};
modifiers = shuffleModifiers(modifiers);
@ -3673,7 +3608,7 @@ export default class BattleScene extends SceneBase {
*/
initFinalBossPhaseTwo(pokemon: Pokemon): void {
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
this.fadeOutBgm(Utils.fixedInt(2000), false);
this.fadeOutBgm(fixedInt(2000), false);
this.ui.showDialogue(
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,
pokemon.species.name,
@ -3776,7 +3711,7 @@ export default class BattleScene extends SceneBase {
if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) {
expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE;
}
const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier);
const pokemonExp = new NumberHolder(expValue * expMultiplier);
this.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp);
partyMemberExp.push(Math.floor(pokemonExp.value));
}
@ -3925,7 +3860,7 @@ export default class BattleScene extends SceneBase {
while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) {
const candidate = this.mysteryEncounterSaveData.queuedEncounters[i];
const forcedChance = candidate.spawnPercent;
if (Utils.randSeedInt(100) < forcedChance) {
if (randSeedInt(100) < forcedChance) {
encounter = allMysteryEncounters[candidate.type];
}
@ -3958,7 +3893,7 @@ export default class BattleScene extends SceneBase {
}
const totalWeight = tierWeights.reduce((a, b) => a + b);
const tierValue = Utils.randSeedInt(totalWeight);
const tierValue = randSeedInt(totalWeight);
const commonThreshold = totalWeight - tierWeights[0];
const greatThreshold = totalWeight - tierWeights[0] - tierWeights[1];
const ultraThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2];
@ -4050,7 +3985,7 @@ export default class BattleScene extends SceneBase {
console.log("No Mystery Encounters found, falling back to Mysterious Challengers.");
return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS];
}
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
encounter = availableEncounters[randSeedInt(availableEncounters.length)];
// New encounter object to not dirty flags
encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements();

View file

@ -1,6 +1,14 @@
import { globalScene } from "#app/global-scene";
import type { Command } from "./ui/command-ui-handler";
import * as Utils from "./utils";
import {
randomString,
getEnumValues,
NumberHolder,
randSeedInt,
shiftCharCodes,
randSeedItem,
randInt,
} from "#app/utils";
import Trainer, { TrainerVariant } from "./field/trainer";
import type { GameMode } from "./game-mode";
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
@ -99,7 +107,7 @@ export default class Battle {
public postBattleLoot: PokemonHeldItemModifier[] = [];
public escapeAttempts = 0;
public lastMove: Moves;
public battleSeed: string = Utils.randomString(16, true);
public battleSeed: string = randomString(16, true);
private battleSeedState: string | null = null;
public moneyScattered = 0;
/** Primarily for double battles, keeps track of last enemy and player pokemon that triggered its ability or used a move */
@ -181,8 +189,8 @@ export default class Battle {
incrementTurn(): void {
this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [bt, null]));
this.preTurnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [bt, null]));
this.turnCommands = Object.fromEntries(getEnumValues(BattlerIndex).map(bt => [bt, null]));
this.preTurnCommands = Object.fromEntries(getEnumValues(BattlerIndex).map(bt => [bt, null]));
this.battleSeedState = null;
}
@ -211,7 +219,7 @@ export default class Battle {
}
pickUpScatteredMoney(): void {
const moneyAmount = new Utils.NumberHolder(globalScene.currentBattle.moneyScattered);
const moneyAmount = new NumberHolder(globalScene.currentBattle.moneyScattered);
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {
@ -448,7 +456,7 @@ export default class Battle {
}
/**
* Generates a random number using the current battle's seed. Calls {@linkcode Utils.randSeedInt}
* Generates a random number using the current battle's seed. Calls {@linkcode randSeedInt}
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
* @param min The minimum integer to pick, default `0`
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
@ -463,12 +471,12 @@ export default class Battle {
if (this.battleSeedState) {
Phaser.Math.RND.state(this.battleSeedState);
} else {
Phaser.Math.RND.sow([Utils.shiftCharCodes(this.battleSeed, this.turn << 6)]);
Phaser.Math.RND.sow([shiftCharCodes(this.battleSeed, this.turn << 6)]);
console.log("Battle Seed:", this.battleSeed);
}
globalScene.rngCounter = this.rngCounter++;
globalScene.rngSeedOverride = this.battleSeed;
const ret = Utils.randSeedInt(range, min);
const ret = randSeedInt(range, min);
this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state);
globalScene.rngCounter = tempRngCounter;
@ -554,19 +562,19 @@ export function getRandomTrainerFunc(
seedOffset = 0,
): GetTrainerFunc {
return () => {
const rand = Utils.randSeedInt(trainerPool.length);
const rand = randSeedInt(trainerPool.length);
const trainerTypes: TrainerType[] = [];
globalScene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry) : trainerPoolEntry;
const trainerType = Array.isArray(trainerPoolEntry) ? randSeedItem(trainerPoolEntry) : trainerPoolEntry;
trainerTypes.push(trainerType);
}
}, seedOffset);
let trainerGender = TrainerVariant.DEFAULT;
if (randomGender) {
trainerGender = Utils.randInt(2) === 0 ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT;
trainerGender = randInt(2) === 0 ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT;
}
/* 1/3 chance for evil team grunts to be double battles */
@ -585,7 +593,7 @@ export function getRandomTrainerFunc(
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
return new Trainer(trainerTypes[rand], Utils.randInt(3) === 0 ? TrainerVariant.DOUBLE : trainerGender);
return new Trainer(trainerTypes[rand], randInt(3) === 0 ? TrainerVariant.DOUBLE : trainerGender);
}
return new Trainer(trainerTypes[rand], trainerGender);
@ -608,7 +616,7 @@ export const classicFixedBattles: FixedBattleConfigs = {
[ClassicFixedBossWaves.TOWN_YOUNGSTER]: new FixedBattleConfig()
.setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(
() => new Trainer(TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT),
() => new Trainer(TrainerType.YOUNGSTER, randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT),
),
[ClassicFixedBossWaves.RIVAL_1]: new FixedBattleConfig()
.setBattleType(BattleType.TRAINER)

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,6 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { Moves } from "#enums/moves";
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
@ -1160,9 +1159,11 @@ class TailwindTag extends ArenaTag {
);
}
// Raise attack by one stage if party member has WIND_RIDER ability
// TODO: Ability displays should be handled by the ability
if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex()));
globalScene.queueAbilityDisplay(pokemon, false, true);
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true));
globalScene.queueAbilityDisplay(pokemon, false, false);
}
}
}

View file

@ -1,5 +1,5 @@
import { PokemonType } from "#enums/pokemon-type";
import * as Utils from "#app/utils";
import { randSeedInt, getEnumValues } from "#app/utils";
import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import i18next from "i18next";
@ -7710,7 +7710,7 @@ export function initBiomes() {
if (biome === Biome.END) {
const biomeList = Object.keys(Biome).filter(key => !Number.isNaN(Number(key)));
biomeList.pop(); // Removes Biome.END from the list
const randIndex = Utils.randSeedInt(biomeList.length, 1); // Will never be Biome.TOWN
const randIndex = randSeedInt(biomeList.length, 1); // Will never be Biome.TOWN
biome = Biome[biomeList[randIndex]];
}
const linkedBiomes: (Biome | [ Biome, number ])[] = Array.isArray(biomeLinks[biome])
@ -7733,15 +7733,15 @@ export function initBiomes() {
traverseBiome(Biome.TOWN, 0);
biomeDepths[Biome.END] = [ Object.values(biomeDepths).map(d => d[0]).reduce((max: number, value: number) => Math.max(max, value), 0) + 1, 1 ];
for (const biome of Utils.getEnumValues(Biome)) {
for (const biome of getEnumValues(Biome)) {
biomePokemonPools[biome] = {};
biomeTrainerPools[biome] = {};
for (const tier of Utils.getEnumValues(BiomePoolTier)) {
for (const tier of getEnumValues(BiomePoolTier)) {
biomePokemonPools[biome][tier] = {};
biomeTrainerPools[biome][tier] = [];
for (const tod of Utils.getEnumValues(TimeOfDay)) {
for (const tod of getEnumValues(TimeOfDay)) {
biomePokemonPools[biome][tier][tod] = [];
}
}

View file

@ -1,5 +1,5 @@
import { allMoves } from "#app/data/moves/move";
import * as Utils from "#app/utils";
import { getEnumKeys, getEnumValues } from "#app/utils";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
@ -587,8 +587,8 @@ export const speciesEggMoves = {
function parseEggMoves(content: string): void {
let output = "";
const speciesNames = Utils.getEnumKeys(Species);
const speciesValues = Utils.getEnumValues(Species);
const speciesNames = getEnumKeys(Species);
const speciesValues = getEnumValues(Species);
const lines = content.split(/\n/g);
for (const line of lines) {

View file

@ -15,7 +15,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.VENUSAUR]: { 0: Abilities.GRASSY_SURGE, 1: Abilities.SEED_SOWER, 2: Abilities.FLOWER_VEIL },
[Species.CHARMANDER]: { 0: Abilities.SHEER_FORCE },
[Species.CHARMELEON]: { 0: Abilities.BEAST_BOOST },
[Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.INTIMIDATE, 3: Abilities.UNNERVE },
[Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.TURBOBLAZE, 3: Abilities.UNNERVE },
[Species.SQUIRTLE]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.WARTORTLE]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.BLASTOISE]: { 0: Abilities.DAUNTLESS_SHIELD, 1: Abilities.BULLETPROOF, 2: Abilities.BULLETPROOF },
@ -154,14 +154,14 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.LEAFEON]: { 0: Abilities.GRASSY_SURGE },
[Species.GLACEON]: { 0: Abilities.SNOW_WARNING },
[Species.SYLVEON]: { 0: Abilities.COMPETITIVE },
[Species.PORYGON]: { 0: Abilities.LEVITATE },
[Species.PORYGON2]: { 0: Abilities.LEVITATE },
[Species.PORYGON]: { 0: Abilities.TRANSISTOR },
[Species.PORYGON2]: { 0: Abilities.TRANSISTOR },
[Species.PORYGON_Z]: { 0: Abilities.PROTEAN },
[Species.OMANYTE]: { 0: Abilities.STURDY },
[Species.OMASTAR]: { 0: Abilities.STURDY },
[Species.KABUTO]: { 0: Abilities.TOUGH_CLAWS },
[Species.KABUTOPS]: { 0: Abilities.TOUGH_CLAWS },
[Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.DELTA_STREAM },
[Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.INTIMIDATE },
[Species.ARTICUNO]: { 0: Abilities.SNOW_WARNING },
[Species.ZAPDOS]: { 0: Abilities.DRIZZLE },
[Species.MOLTRES]: { 0: Abilities.DROUGHT },
@ -309,7 +309,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.SHIFTRY]: { 0: Abilities.SHARPNESS },
[Species.TAILLOW]: { 0: Abilities.AERILATE },
[Species.SWELLOW]: { 0: Abilities.AERILATE },
[Species.WINGULL]: { 0: Abilities.DRIZZLE },
[Species.WINGULL]: { 0: Abilities.WATER_ABSORB },
[Species.PELIPPER]: { 0: Abilities.SWIFT_SWIM },
[Species.RALTS]: { 0: Abilities.NEUROFORCE },
[Species.KIRLIA]: { 0: Abilities.NEUROFORCE },
@ -612,8 +612,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.REUNICLUS]: { 0: Abilities.PSYCHIC_SURGE },
[Species.DUCKLETT]: { 0: Abilities.DRIZZLE },
[Species.SWANNA]: { 0: Abilities.DRIZZLE },
[Species.VANILLITE]: { 0: Abilities.SNOW_WARNING },
[Species.VANILLISH]: { 0: Abilities.SNOW_WARNING },
[Species.VANILLITE]: { 0: Abilities.REFRIGERATE },
[Species.VANILLISH]: { 0: Abilities.REFRIGERATE },
[Species.VANILLUXE]: { 0: Abilities.SLUSH_RUSH },
[Species.DEERLING]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT },
[Species.SAWSBUCK]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT },
@ -838,7 +838,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.CELESTEELA]: { 0: Abilities.HEATPROOF },
[Species.KARTANA]: { 0: Abilities.TECHNICIAN },
[Species.GUZZLORD]: { 0: Abilities.POISON_HEAL },
[Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.PRISM_ARMOR },
[Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.UNNERVE },
[Species.MAGEARNA]: { 0: Abilities.STEELY_SPIRIT, 1: Abilities.STEELY_SPIRIT },
[Species.MARSHADOW]: { 0: Abilities.IRON_FIST },
[Species.POIPOLE]: { 0: Abilities.LEVITATE },

View file

@ -3,7 +3,7 @@ import { Gender } from "#app/data/gender";
import { PokeballType } from "#enums/pokeball";
import type Pokemon from "#app/field/pokemon";
import { PokemonType } from "#enums/pokemon-type";
import * as Utils from "#app/utils";
import { randSeedInt } from "#app/utils";
import { WeatherType } from "#enums/weather-type";
import { Nature } from "#enums/nature";
import { Biome } from "#enums/biome";
@ -333,7 +333,7 @@ class DunsparceEvolutionCondition extends SpeciesEvolutionCondition {
super(p => {
let ret = false;
if (p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0) {
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
globalScene.executeWithSeedOffset(() => ret = !randSeedInt(4), p.id);
}
return ret;
});
@ -346,7 +346,7 @@ class TandemausEvolutionCondition extends SpeciesEvolutionCondition {
constructor() {
super(p => {
let ret = false;
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
globalScene.executeWithSeedOffset(() => ret = !randSeedInt(4), p.id);
return ret;
});
}

View file

@ -19126,6 +19126,8 @@ export const tmSpecies: TmSpecies = {
Species.KROOKODILE,
Species.SCRAGGY,
Species.SCRAFTY,
Species.YAMASK,
Species.COFAGRIGUS,
Species.SAWSBUCK,
Species.LITWICK,
Species.LAMPENT,
@ -19163,6 +19165,7 @@ export const tmSpecies: TmSpecies = {
Species.SINISTEA,
Species.POLTEAGEIST,
Species.PERRSERKER,
Species.RUNERIGUS,
Species.PINCURCHIN,
Species.STONJOURNER,
Species.CUFANT,
@ -19228,6 +19231,7 @@ export const tmSpecies: TmSpecies = {
Species.GALAR_SLOWBRO,
Species.GALAR_WEEZING,
Species.GALAR_SLOWKING,
Species.GALAR_YAMASK,
Species.HISUI_ELECTRODE,
Species.HISUI_TYPHLOSION,
Species.HISUI_QWILFISH,
@ -30922,6 +30926,7 @@ export const tmSpecies: TmSpecies = {
Species.MURKROW,
Species.SLOWKING,
Species.MISDREAVUS,
Species.UNOWN,
Species.GIRAFARIG,
Species.PINECO,
Species.FORRETRESS,
@ -40134,6 +40139,8 @@ export const tmSpecies: TmSpecies = {
Species.MEOWSTIC,
Species.SPRITZEE,
Species.AROMATISSE,
Species.INKAY,
Species.MALAMAR,
Species.SYLVEON,
Species.CARBINK,
Species.PHANTUMP,
@ -49173,6 +49180,7 @@ export const tmSpecies: TmSpecies = {
Species.KANGASKHAN,
Species.GOLDEEN,
Species.SEAKING,
Species.GYARADOS,
Species.LAPRAS,
Species.VAPOREON,
Species.KABUTOPS,
@ -52587,6 +52595,7 @@ export const tmSpecies: TmSpecies = {
Species.SNORLAX,
Species.MEWTWO,
Species.MEW,
Species.MEGANIUM,
Species.CYNDAQUIL,
Species.QUILAVA,
Species.TYPHLOSION,
@ -66205,7 +66214,11 @@ export const tmSpecies: TmSpecies = {
Species.SQUIRTLE,
Species.WARTORTLE,
Species.BLASTOISE,
Species.CATERPIE,
Species.METAPOD,
Species.BUTTERFREE,
Species.WEEDLE,
Species.KAKUNA,
Species.BEEDRILL,
Species.PIDGEY,
Species.PIDGEOTTO,
@ -66451,7 +66464,10 @@ export const tmSpecies: TmSpecies = {
Species.MIGHTYENA,
Species.ZIGZAGOON,
Species.LINOONE,
Species.WURMPLE,
Species.SILCOON,
Species.BEAUTIFLY,
Species.CASCOON,
Species.DUSTOX,
Species.LOTAD,
Species.LOMBRE,
@ -66987,6 +67003,8 @@ export const tmSpecies: TmSpecies = {
Species.STAKATAKA,
Species.BLACEPHALON,
Species.ZERAORA,
Species.MELTAN,
Species.MELMETAL,
Species.ALOLA_RATTATA,
Species.ALOLA_RATICATE,
Species.ALOLA_RAICHU,
@ -67020,8 +67038,19 @@ export const tmSpecies: TmSpecies = {
Species.ROOKIDEE,
Species.CORVISQUIRE,
Species.CORVIKNIGHT,
Species.BLIPBUG,
Species.DOTTLER,
Species.ORBEETLE,
Species.NICKIT,
Species.THIEVUL,
Species.GOSSIFLEUR,
Species.ELDEGOSS,
Species.WOOLOO,
Species.DUBWOOL,
Species.CHEWTLE,
Species.DREDNAW,
Species.YAMPER,
Species.BOLTUND,
Species.ROLYCOLY,
Species.CARKOL,
Species.COALOSSAL,
@ -67035,6 +67064,10 @@ export const tmSpecies: TmSpecies = {
Species.BARRASKEWDA,
Species.TOXEL,
Species.TOXTRICITY,
Species.SIZZLIPEDE,
Species.CENTISKORCH,
Species.CLOBBOPUS,
Species.GRAPPLOCT,
Species.SINISTEA,
Species.POLTEAGEIST,
Species.HATENNA,
@ -67043,7 +67076,14 @@ export const tmSpecies: TmSpecies = {
Species.IMPIDIMP,
Species.MORGREM,
Species.GRIMMSNARL,
Species.OBSTAGOON,
Species.PERRSERKER,
Species.CURSOLA,
Species.SIRFETCHD,
Species.MR_RIME,
Species.RUNERIGUS,
Species.MILCERY,
Species.ALCREMIE,
Species.FALINKS,
Species.PINCURCHIN,
Species.SNOM,
@ -67054,6 +67094,11 @@ export const tmSpecies: TmSpecies = {
Species.MORPEKO,
Species.CUFANT,
Species.COPPERAJAH,
Species.DRACOZOLT,
Species.ARCTOZOLT,
Species.DRACOVISH,
Species.ARCTOVISH,
Species.DURALUDON,
Species.DREEPY,
Species.DRAKLOAK,
Species.DRAGAPULT,
@ -67195,13 +67240,24 @@ export const tmSpecies: TmSpecies = {
Species.IRON_CROWN,
Species.PECHARUNT,
Species.GALAR_MEOWTH,
Species.GALAR_PONYTA,
Species.GALAR_RAPIDASH,
Species.GALAR_SLOWPOKE,
Species.GALAR_SLOWBRO,
Species.GALAR_FARFETCHD,
Species.GALAR_WEEZING,
Species.GALAR_MR_MIME,
Species.GALAR_ARTICUNO,
Species.GALAR_ZAPDOS,
Species.GALAR_MOLTRES,
Species.GALAR_SLOWKING,
Species.GALAR_CORSOLA,
Species.GALAR_ZIGZAGOON,
Species.GALAR_LINOONE,
Species.GALAR_DARUMAKA,
Species.GALAR_DARMANITAN,
Species.GALAR_YAMASK,
Species.GALAR_STUNFISK,
Species.HISUI_GROWLITHE,
Species.HISUI_ARCANINE,
Species.HISUI_VOLTORB,

View file

@ -1428,7 +1428,8 @@ export class MoveAnim extends BattleAnim {
public move: Moves;
constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField = false) {
super(user, globalScene.getField()[target], playOnEmptyField);
// Set target to the user pokemon if no target is found to avoid crashes
super(user, globalScene.getField()[target] ?? user, playOnEmptyField);
this.move = move;
}

View file

@ -30,7 +30,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MovePhase } from "#app/phases/move-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import i18next from "#app/plugins/i18n";
@ -43,7 +42,7 @@ import { Species } from "#enums/species";
import { EFFECTIVE_STATS, getStatKey, Stat, type BattleStat, type EffectiveStat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { WeatherType } from "#enums/weather-type";
import * as Utils from "../utils";
import { isNullOrUndefined } from "#app/utils";
export enum BattlerTagLapseType {
FAINT,
@ -303,7 +302,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
super.onAdd(pokemon);
const move = pokemon.getLastXMoves(-1).find(m => !m.virtual);
if (Utils.isNullOrUndefined(move) || move.move === Moves.STRUGGLE || move.move === Moves.NONE) {
if (isNullOrUndefined(move) || move.move === Moves.STRUGGLE || move.move === Moves.NONE) {
return;
}
@ -1901,12 +1900,14 @@ export class TruantTag extends AbilityBattlerTag {
if (lastMove && lastMove.move !== Moves.NONE) {
(globalScene.getCurrentPhase() as MovePhase).cancel();
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive));
// TODO: Ability displays should be handled by the ability
globalScene.queueAbilityDisplay(pokemon, passive, true);
globalScene.queueMessage(
i18next.t("battlerTags:truantLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
}),
);
globalScene.queueAbilityDisplay(pokemon, passive, false);
}
return true;

View file

@ -2,7 +2,7 @@ import { getPokemonNameWithAffix } from "../messages";
import type Pokemon from "../field/pokemon";
import { HitResult } from "../field/pokemon";
import { getStatusEffectHealText } from "./status-effect";
import * as Utils from "../utils";
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils";
import {
DoubleBerryEffectAbAttr,
PostItemLostAbAttr,
@ -43,7 +43,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
case BerryType.APICOT:
case BerryType.SALAC:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
const threshold = new NumberHolder(0.25);
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
const stat: BattleStat = berryType - BerryType.ENIGMA;
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
@ -51,19 +51,19 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
};
case BerryType.LANSAT:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
const threshold = new NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
};
case BerryType.STARF:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
const threshold = new NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
return pokemon.getHpRatio() < 0.25;
};
case BerryType.LEPPA:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
const threshold = new NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
};
@ -80,7 +80,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType);
}
const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4));
const hpHealed = new NumberHolder(toDmgValue(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
globalScene.unshiftPhase(
new PokemonHealPhase(
@ -118,7 +118,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
}
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
const stat: BattleStat = berryType - BerryType.ENIGMA;
const statStages = new Utils.NumberHolder(1);
const statStages = new NumberHolder(1);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages);
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], statStages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
@ -136,8 +136,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType);
}
const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK);
const stages = new Utils.NumberHolder(2);
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
const stages = new NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randStat], stages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);

View file

@ -1,4 +1,4 @@
import * as Utils from "#app/utils";
import { BooleanHolder, type NumberHolder, randSeedItem, deepCopy } from "#app/utils";
import i18next from "i18next";
import type { DexAttrProps, GameData } from "#app/system/game-data";
import { defaultStarterSpecies } from "#app/system/game-data";
@ -283,30 +283,30 @@ export abstract class Challenge {
/**
* An apply function for STARTER_CHOICE challenges. Derived classes should alter this.
* @param _pokemon {@link PokemonSpecies} The pokemon to check the validity of.
* @param _valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param _dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
* @returns {@link boolean} Whether this function did anything.
*/
applyStarterChoice(_pokemon: PokemonSpecies, _valid: Utils.BooleanHolder, _dexAttr: DexAttrProps): boolean {
applyStarterChoice(_pokemon: PokemonSpecies, _valid: BooleanHolder, _dexAttr: DexAttrProps): boolean {
return false;
}
/**
* An apply function for STARTER_POINTS challenges. Derived classes should alter this.
* @param _points {@link Utils.NumberHolder} The amount of points you have available.
* @param _points {@link NumberHolder} The amount of points you have available.
* @returns {@link boolean} Whether this function did anything.
*/
applyStarterPoints(_points: Utils.NumberHolder): boolean {
applyStarterPoints(_points: NumberHolder): boolean {
return false;
}
/**
* An apply function for STARTER_COST challenges. Derived classes should alter this.
* @param _species {@link Species} The pokemon to change the cost of.
* @param _cost {@link Utils.NumberHolder} The cost of the starter.
* @param _cost {@link NumberHolder} The cost of the starter.
* @returns {@link boolean} Whether this function did anything.
*/
applyStarterCost(_species: Species, _cost: Utils.NumberHolder): boolean {
applyStarterCost(_species: Species, _cost: NumberHolder): boolean {
return false;
}
@ -322,10 +322,10 @@ export abstract class Challenge {
/**
* An apply function for POKEMON_IN_BATTLE challenges. Derived classes should alter this.
* @param _pokemon {@link Pokemon} The pokemon to check the validity of.
* @param _valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @returns {@link boolean} Whether this function did anything.
*/
applyPokemonInBattle(_pokemon: Pokemon, _valid: Utils.BooleanHolder): boolean {
applyPokemonInBattle(_pokemon: Pokemon, _valid: BooleanHolder): boolean {
return false;
}
@ -341,42 +341,42 @@ export abstract class Challenge {
/**
* An apply function for TYPE_EFFECTIVENESS challenges. Derived classes should alter this.
* @param _effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
* @param _effectiveness {@linkcode NumberHolder} The current effectiveness of the move.
* @returns Whether this function did anything.
*/
applyTypeEffectiveness(_effectiveness: Utils.NumberHolder): boolean {
applyTypeEffectiveness(_effectiveness: NumberHolder): boolean {
return false;
}
/**
* An apply function for AI_LEVEL challenges. Derived classes should alter this.
* @param _level {@link Utils.NumberHolder} The generated level.
* @param _level {@link NumberHolder} The generated level.
* @param _levelCap {@link Number} The current level cap.
* @param _isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param _isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @returns {@link boolean} Whether this function did anything.
*/
applyLevelChange(_level: Utils.NumberHolder, _levelCap: number, _isTrainer: boolean, _isBoss: boolean): boolean {
applyLevelChange(_level: NumberHolder, _levelCap: number, _isTrainer: boolean, _isBoss: boolean): boolean {
return false;
}
/**
* An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this.
* @param pokemon {@link Pokemon} The pokemon that is being considered.
* @param moveSlots {@link Utils.NumberHolder} The amount of move slots.
* @param moveSlots {@link NumberHolder} The amount of move slots.
* @returns {@link boolean} Whether this function did anything.
*/
applyMoveSlot(_pokemon: Pokemon, _moveSlots: Utils.NumberHolder): boolean {
applyMoveSlot(_pokemon: Pokemon, _moveSlots: NumberHolder): boolean {
return false;
}
/**
* An apply function for PASSIVE_ACCESS challenges. Derived classes should alter this.
* @param pokemon {@link Pokemon} The pokemon to change.
* @param hasPassive {@link Utils.BooleanHolder} Whether it should have its passive.
* @param hasPassive {@link BooleanHolder} Whether it should have its passive.
* @returns {@link boolean} Whether this function did anything.
*/
applyPassiveAccess(_pokemon: Pokemon, _hasPassive: Utils.BooleanHolder): boolean {
applyPassiveAccess(_pokemon: Pokemon, _hasPassive: BooleanHolder): boolean {
return false;
}
@ -393,15 +393,10 @@ export abstract class Challenge {
* @param _pokemon {@link Pokemon} What pokemon would learn the move.
* @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param _move {@link Moves} The move in question.
* @param _level {@link Utils.NumberHolder} The level threshold for access.
* @param _level {@link NumberHolder} The level threshold for access.
* @returns {@link boolean} Whether this function did anything.
*/
applyMoveAccessLevel(
_pokemon: Pokemon,
_moveSource: MoveSourceType,
_move: Moves,
_level: Utils.NumberHolder,
): boolean {
applyMoveAccessLevel(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean {
return false;
}
@ -410,10 +405,10 @@ export abstract class Challenge {
* @param _pokemon {@link Pokemon} What pokemon would learn the move.
* @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param _move {@link Moves} The move in question.
* @param _weight {@link Utils.NumberHolder} The base weight of the move
* @param _weight {@link NumberHolder} The base weight of the move
* @returns {@link boolean} Whether this function did anything.
*/
applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: Utils.NumberHolder): boolean {
applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean {
return false;
}
@ -438,7 +433,7 @@ export class SingleGenerationChallenge extends Challenge {
super(Challenges.SINGLE_GENERATION, 9);
}
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean {
applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean {
if (pokemon.generation !== this.value) {
valid.value = false;
return true;
@ -446,7 +441,7 @@ export class SingleGenerationChallenge extends Challenge {
return false;
}
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean {
const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0;
if (
@ -575,7 +570,7 @@ export class SingleGenerationChallenge extends Challenge {
TrainerType.AARON,
TrainerType.SHAUNTAL,
TrainerType.MALVA,
Utils.randSeedItem([TrainerType.HALA, TrainerType.MOLAYNE]),
randSeedItem([TrainerType.HALA, TrainerType.MOLAYNE]),
TrainerType.MARNIE_ELITE,
TrainerType.RIKA,
];
@ -602,7 +597,7 @@ export class SingleGenerationChallenge extends Challenge {
TrainerType.GRIMSLEY,
TrainerType.WIKSTROM,
TrainerType.ACEROLA,
Utils.randSeedItem([TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE]),
randSeedItem([TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE]),
TrainerType.LARRY_ELITE,
];
break;
@ -622,14 +617,14 @@ export class SingleGenerationChallenge extends Challenge {
case ClassicFixedBossWaves.CHAMPION:
trainerTypes = [
TrainerType.BLUE,
Utils.randSeedItem([TrainerType.RED, TrainerType.LANCE_CHAMPION]),
Utils.randSeedItem([TrainerType.STEVEN, TrainerType.WALLACE]),
randSeedItem([TrainerType.RED, TrainerType.LANCE_CHAMPION]),
randSeedItem([TrainerType.STEVEN, TrainerType.WALLACE]),
TrainerType.CYNTHIA,
Utils.randSeedItem([TrainerType.ALDER, TrainerType.IRIS]),
randSeedItem([TrainerType.ALDER, TrainerType.IRIS]),
TrainerType.DIANTHA,
Utils.randSeedItem([TrainerType.KUKUI, TrainerType.HAU]),
Utils.randSeedItem([TrainerType.LEON, TrainerType.MUSTARD]),
Utils.randSeedItem([TrainerType.GEETA, TrainerType.NEMONA]),
randSeedItem([TrainerType.KUKUI, TrainerType.HAU]),
randSeedItem([TrainerType.LEON, TrainerType.MUSTARD]),
randSeedItem([TrainerType.GEETA, TrainerType.NEMONA]),
];
break;
}
@ -718,7 +713,7 @@ export class SingleTypeChallenge extends Challenge {
super(Challenges.SINGLE_TYPE, 18);
}
override applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps): boolean {
override applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder, dexAttr: DexAttrProps): boolean {
const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex);
const types = [speciesForm.type1, speciesForm.type2];
if (!types.includes(this.value - 1)) {
@ -728,7 +723,7 @@ export class SingleTypeChallenge extends Challenge {
return false;
}
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean {
if (
pokemon.isPlayer() &&
!pokemon.isOfType(this.value - 1, false, false, true) &&
@ -798,7 +793,7 @@ export class FreshStartChallenge extends Challenge {
super(Challenges.FRESH_START, 1);
}
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean {
applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean {
if (!defaultStarterSpecies.includes(pokemon.speciesId)) {
valid.value = false;
return true;
@ -806,7 +801,7 @@ export class FreshStartChallenge extends Challenge {
return false;
}
applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean {
applyStarterCost(species: Species, cost: NumberHolder): boolean {
if (defaultStarterSpecies.includes(species)) {
cost.value = speciesStarterCosts[species];
return true;
@ -864,7 +859,7 @@ export class InverseBattleChallenge extends Challenge {
return 0;
}
applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean {
applyTypeEffectiveness(effectiveness: NumberHolder): boolean {
if (effectiveness.value < 1) {
effectiveness.value = 2;
return true;
@ -887,7 +882,7 @@ export class FlipStatChallenge extends Challenge {
}
override applyFlipStat(_pokemon: Pokemon, baseStats: number[]) {
const origStats = Utils.deepCopy(baseStats);
const origStats = deepCopy(baseStats);
baseStats[0] = origStats[5];
baseStats[1] = origStats[4];
baseStats[2] = origStats[3];
@ -923,7 +918,7 @@ export class LowerStarterMaxCostChallenge extends Challenge {
return (DEFAULT_PARTY_MAX_COST - overrideValue).toString();
}
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean {
applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean {
if (speciesStarterCosts[pokemon.speciesId] > DEFAULT_PARTY_MAX_COST - this.value) {
valid.value = false;
return true;
@ -957,7 +952,7 @@ export class LowerStarterPointsChallenge extends Challenge {
return (DEFAULT_PARTY_MAX_COST - overrideValue).toString();
}
applyStarterPoints(points: Utils.NumberHolder): boolean {
applyStarterPoints(points: NumberHolder): boolean {
points.value -= this.value;
return true;
}
@ -974,34 +969,34 @@ export class LowerStarterPointsChallenge extends Challenge {
* Apply all challenges that modify starter choice.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE
* @param pokemon {@link PokemonSpecies} The pokemon to check the validity of.
* @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_CHOICE,
pokemon: PokemonSpecies,
valid: Utils.BooleanHolder,
valid: BooleanHolder,
dexAttr: DexAttrProps,
): boolean;
/**
* Apply all challenges that modify available total starter points.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS
* @param points {@link Utils.NumberHolder} The amount of points you have available.
* @param points {@link NumberHolder} The amount of points you have available.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: Utils.NumberHolder): boolean;
export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean;
/**
* Apply all challenges that modify the cost of a starter.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST
* @param species {@link Species} The pokemon to change the cost of.
* @param points {@link Utils.NumberHolder} The cost of the pokemon.
* @param points {@link NumberHolder} The cost of the pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_COST,
species: Species,
cost: Utils.NumberHolder,
cost: NumberHolder,
): boolean;
/**
* Apply all challenges that modify a starter after selection.
@ -1014,13 +1009,13 @@ export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pok
* Apply all challenges that what pokemon you can have in battle.
* @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE
* @param pokemon {@link Pokemon} The pokemon to check the validity of.
* @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_IN_BATTLE,
pokemon: Pokemon,
valid: Utils.BooleanHolder,
valid: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify what fixed battles there are.
@ -1037,17 +1032,14 @@ export function applyChallenges(
/**
* Apply all challenges that modify type effectiveness.
* @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS
* @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
* @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.TYPE_EFFECTIVENESS,
effectiveness: Utils.NumberHolder,
): boolean;
export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean;
/**
* Apply all challenges that modify what level AI are.
* @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL
* @param level {@link Utils.NumberHolder} The generated level of the pokemon.
* @param level {@link NumberHolder} The generated level of the pokemon.
* @param levelCap {@link Number} The maximum level cap for the current wave.
* @param isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
@ -1055,7 +1047,7 @@ export function applyChallenges(
*/
export function applyChallenges(
challengeType: ChallengeType.AI_LEVEL,
level: Utils.NumberHolder,
level: NumberHolder,
levelCap: number,
isTrainer: boolean,
isBoss: boolean,
@ -1064,25 +1056,25 @@ export function applyChallenges(
* Apply all challenges that modify how many move slots the AI has.
* @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS
* @param pokemon {@link Pokemon} The pokemon being considered.
* @param moveSlots {@link Utils.NumberHolder} The amount of move slots.
* @param moveSlots {@link NumberHolder} The amount of move slots.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.AI_MOVE_SLOTS,
pokemon: Pokemon,
moveSlots: Utils.NumberHolder,
moveSlots: NumberHolder,
): boolean;
/**
* Apply all challenges that modify whether a pokemon has its passive.
* @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS
* @param pokemon {@link Pokemon} The pokemon to modify.
* @param hasPassive {@link Utils.BooleanHolder} Whether it has its passive.
* @param hasPassive {@link BooleanHolder} Whether it has its passive.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.PASSIVE_ACCESS,
pokemon: Pokemon,
hasPassive: Utils.BooleanHolder,
hasPassive: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify the game modes settings.
@ -1096,7 +1088,7 @@ export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY):
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question.
* @param level {@link Utils.NumberHolder} The level threshold for access.
* @param level {@link NumberHolder} The level threshold for access.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
@ -1104,7 +1096,7 @@ export function applyChallenges(
pokemon: Pokemon,
moveSource: MoveSourceType,
move: Moves,
level: Utils.NumberHolder,
level: NumberHolder,
): boolean;
/**
* Apply all challenges that modify what weight a pokemon gives to move generation
@ -1112,7 +1104,7 @@ export function applyChallenges(
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link Moves} The move in question.
* @param weight {@link Utils.NumberHolder} The weight of the move.
* @param weight {@link NumberHolder} The weight of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
@ -1120,7 +1112,7 @@ export function applyChallenges(
pokemon: Pokemon,
moveSource: MoveSourceType,
move: Moves,
weight: Utils.NumberHolder,
weight: NumberHolder,
): boolean;
export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
@ -1225,7 +1217,7 @@ export function initChallenges() {
*/
export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
if (!soft) {
const isValidForChallenge = new Utils.BooleanHolder(true);
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
return isValidForChallenge.value;
}
@ -1263,7 +1255,7 @@ export function checkStarterValidForChallenge(species: PokemonSpecies, props: De
* @returns `true` if the species is considered valid.
*/
function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
const isValidForChallenge = new Utils.BooleanHolder(true);
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) {
return isValidForChallenge.value;
@ -1272,23 +1264,22 @@ function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrPr
if (soft && isValidForChallenge.value) {
return true;
}
pokemonFormChanges[species.speciesId].forEach(f1 => {
// Exclude form changes that require the mon to be on the field to begin with,
// such as Castform
if (!("item" in f1)) {
return;
const result = pokemonFormChanges[species.speciesId].some(f1 => {
// Exclude form changes that require the mon to be on the field to begin with
if (!("item" in f1.trigger)) {
return false;
}
species.forms.forEach((f2, formIndex) => {
return species.forms.some((f2, formIndex) => {
if (f1.formKey === f2.formKey) {
const formProps = { ...props };
formProps.formIndex = formIndex;
const isFormValidForChallenge = new Utils.BooleanHolder(true);
const formProps = { ...props, formIndex };
const isFormValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps);
if (isFormValidForChallenge.value) {
return true;
}
return isFormValidForChallenge.value;
}
return false;
});
});
return false;
return result;
}

View file

@ -3,7 +3,7 @@ import type { Species } from "#enums/species";
import { globalScene } from "#app/global-scene";
import { PlayerPokemon } from "#app/field/pokemon";
import type { Starter } from "#app/ui/starter-select-ui-handler";
import * as Utils from "#app/utils";
import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils";
import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
@ -43,8 +43,8 @@ export function getDailyRunStarters(seed: string): Starter[] {
}
const starterCosts: number[] = [];
starterCosts.push(Math.min(Math.round(3.5 + Math.abs(Utils.randSeedGauss(1))), 8));
starterCosts.push(Utils.randSeedInt(9 - starterCosts[0], 1));
starterCosts.push(Math.min(Math.round(3.5 + Math.abs(randSeedGauss(1))), 8));
starterCosts.push(randSeedInt(9 - starterCosts[0], 1));
starterCosts.push(10 - (starterCosts[0] + starterCosts[1]));
for (let c = 0; c < starterCosts.length; c++) {
@ -52,7 +52,7 @@ export function getDailyRunStarters(seed: string): Starter[] {
const costSpecies = Object.keys(speciesStarterCosts)
.map(s => Number.parseInt(s) as Species)
.filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies));
const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(
randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER),
);
@ -143,7 +143,7 @@ const dailyBiomeWeights: BiomeWeights = {
};
export function getDailyStartingBiome(): Biome {
const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
let totalWeight = 0;
const biomeThresholds: number[] = [];
@ -155,7 +155,7 @@ export function getDailyStartingBiome(): Biome {
biomeThresholds.push(totalWeight);
}
const randInt = Utils.randSeedInt(totalWeight);
const randInt = randSeedInt(totalWeight);
for (let i = 0; i < biomes.length; i++) {
if (randInt < biomeThresholds[i]) {
@ -164,5 +164,5 @@ export function getDailyStartingBiome(): Biome {
}
// Fallback in case something went wrong
return biomes[Utils.randSeedInt(biomes.length)];
return biomes[randSeedInt(biomes.length)];
}

View file

@ -4,7 +4,7 @@ import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { VariantTier } from "#enums/variant-tier";
import * as Utils from "#app/utils";
import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils";
import Overrides from "#app/overrides";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import type { PlayerPokemon } from "#app/field/pokemon";
@ -171,7 +171,7 @@ export class Egg {
this.checkForPityTierOverrides();
}
this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier);
this._id = eggOptions?.id ?? randInt(EGG_SEED, EGG_SEED * this._tier);
this._sourceType = eggOptions?.sourceType ?? undefined;
this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves();
@ -203,7 +203,7 @@ export class Egg {
}
};
const seedOverride = Utils.randomString(24);
const seedOverride = randomString(24);
globalScene.executeWithSeedOffset(
() => {
generateEggProperties(eggOptions);
@ -248,18 +248,15 @@ export class Egg {
let pokemonSpecies = getPokemonSpecies(this._species);
// Special condition to have Phione eggs also have a chance of generating Manaphy
if (this._species === Species.PHIONE && this._sourceType === EggSourceType.SAME_SPECIES_EGG) {
pokemonSpecies = getPokemonSpecies(
Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY,
);
pokemonSpecies = getPokemonSpecies(randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY);
}
// Sets the hidden ability if a hidden ability exists and
// the override is set or the egg hits the chance
let abilityIndex: number | undefined = undefined;
const sameSpeciesEggHACheck =
this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE);
const gachaEggHACheck =
!(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !Utils.randSeedInt(GACHA_EGG_HA_RATE);
this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE);
const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE);
if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) {
abilityIndex = 2;
}
@ -269,7 +266,7 @@ export class Egg {
ret.shiny = this._isShiny;
ret.variant = this._variantTier;
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295));
const secondaryIvs = getIvsFromId(randSeedInt(4294967295));
for (let s = 0; s < ret.ivs.length; s++) {
ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]);
@ -370,7 +367,7 @@ export class Egg {
}
const tierMultiplier = this.isManaphyEgg() ? 2 : Math.pow(2, 3 - this.tier);
return Utils.randSeedInt(baseChance * tierMultiplier) ? Utils.randSeedInt(3) : 3;
return randSeedInt(baseChance * tierMultiplier) ? randSeedInt(3) : 3;
}
private getEggTierDefaultHatchWaves(eggTier?: EggTier): number {
@ -392,7 +389,7 @@ export class Egg {
private rollEggTier(): EggTier {
const tierValueOffset =
this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
const tierValue = Utils.randInt(256);
const tierValue = randInt(256);
return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset
? EggTier.COMMON
: tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset
@ -417,11 +414,11 @@ export class Egg {
* when Utils.randSeedInt(8) = 1, and by making the generatePlayerPokemon() species
* check pass when Utils.randSeedInt(8) = 0, we can tell them apart during tests.
*/
const rand = Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
return rand ? Species.PHIONE : Species.MANAPHY;
}
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!Utils.randSeedInt(2)) {
if (!randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
}
}
@ -501,7 +498,7 @@ export class Egg {
let species: Species;
const rand = Utils.randSeedInt(totalWeight);
const rand = randSeedInt(totalWeight);
for (let s = 0; s < speciesWeights.length; s++) {
if (rand < speciesWeights[s]) {
species = speciesPool[s];
@ -539,7 +536,7 @@ export class Egg {
break;
}
return !Utils.randSeedInt(shinyChance);
return !randSeedInt(shinyChance);
}
// Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one
@ -550,7 +547,7 @@ export class Egg {
return VariantTier.STANDARD;
}
const rand = Utils.randSeedInt(10);
const rand = randSeedInt(10);
if (rand >= SHINY_VARIANT_CHANCE) {
return VariantTier.STANDARD; // 6/10
}

File diff suppressed because it is too large Load diff

View file

@ -985,12 +985,11 @@ function doTradeReceivedSequence(
function generateRandomTraderName() {
const length = TrainerType.YOUNGSTER - TrainerType.ACE_TRAINER + 1;
// +1 avoids TrainerType.UNKNOWN
const trainerTypePool = i18next.t("trainersCommon:" + TrainerType[randInt(length) + 1], { returnObjects: true });
const classKey = `trainersCommon:${TrainerType[randInt(length) + 1]}`;
// Some trainers have 2 gendered pools, some do not
const gender = randInt(2) === 0 ? "MALE" : "FEMALE";
const trainerNameString = randSeedItem(
Object.values(trainerTypePool.hasOwnProperty(gender) ? trainerTypePool[gender] : trainerTypePool),
) as string;
const genderKey = i18next.exists(`${classKey}.MALE`) ? (randInt(2) === 0 ? ".MALE" : ".FEMALE") : "";
const trainerNameKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })));
const trainerNameString = i18next.t(`${classKey}${genderKey}.${trainerNameKey}`);
// Some names have an '&' symbol and need to be trimmed to a single name instead of a double name
const trainerNames = trainerNameString.split(" & ");
return trainerNames[randInt(trainerNames.length)];

View file

@ -12,7 +12,7 @@ import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength";
import { globalScene } from "#app/global-scene";
import * as Utils from "#app/utils";
import { randSeedInt } from "#app/utils";
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -46,7 +46,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const normalConfig = trainerConfigs[normalTrainerType].clone();
let female = false;
if (normalConfig.hasGenders) {
female = !!Utils.randSeedInt(2);
female = !!randSeedInt(2);
}
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
encounter.enemyPartyConfigs.push({
@ -76,7 +76,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
hardConfig.setPartyTemplates(hardTemplate);
female = false;
if (hardConfig.hasGenders) {
female = !!Utils.randSeedInt(2);
female = !!randSeedInt(2);
}
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
encounter.enemyPartyConfigs.push({
@ -96,7 +96,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func
female = false;
if (brutalConfig.hasGenders) {
female = !!Utils.randSeedInt(2);
female = !!randSeedInt(2);
}
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
encounter.enemyPartyConfigs.push({

View file

@ -5,7 +5,7 @@ import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
import * as Utils from "#app/utils";
import { randSeedInt } from "#app/utils";
import type { StatusEffect } from "#enums/status-effect";
import type { OptionTextDisplay } from "./mystery-encounter-dialogue";
import type MysteryEncounterDialogue from "./mystery-encounter-dialogue";
@ -378,13 +378,13 @@ export default class MysteryEncounter implements IMysteryEncounter {
}
if (truePrimaryPool.length > 0) {
// Always choose from the non-overlapping pokemon first
this.primaryPokemon = truePrimaryPool[Utils.randSeedInt(truePrimaryPool.length, 0)];
this.primaryPokemon = truePrimaryPool[randSeedInt(truePrimaryPool.length, 0)];
return true;
}
// If there are multiple overlapping pokemon, we're okay - just choose one and take it out of the primary pokemon pool
if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) {
// is this working?
this.primaryPokemon = overlap[Utils.randSeedInt(overlap.length, 0)];
this.primaryPokemon = overlap[randSeedInt(overlap.length, 0)];
this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon);
return true;
}
@ -394,7 +394,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
return false;
}
// this means we CAN have the same pokemon be a primary and secondary pokemon, so just choose any qualifying one randomly.
this.primaryPokemon = qualified[Utils.randSeedInt(qualified.length, 0)];
this.primaryPokemon = qualified[randSeedInt(qualified.length, 0)];
return true;
}

View file

@ -30,8 +30,7 @@ import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-optio
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
import { PartyUiMode } from "#app/ui/party-ui-handler";
import { Mode } from "#app/ui/ui";
import * as Utils from "#app/utils";
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils";
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils";
import type { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome";
import type { TrainerType } from "#enums/trainer-type";
@ -58,7 +57,7 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { GameOverPhase } from "#app/phases/game-over-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { PartyExpPhase } from "#app/phases/party-exp-phase";
import type { Variant } from "#app/data/variant";
import type { Variant } from "#app/sprites/variant";
import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
import { getPokemonSpecies } from "#app/data/pokemon-species";
@ -168,7 +167,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle);
doubleBattle = doubleTrainer;
const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!Utils.randSeedInt(2) : partyConfig.female;
const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!randSeedInt(2) : partyConfig.female;
const newTrainer = new Trainer(
trainerConfig.trainerType,
doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
@ -286,7 +285,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
// Generate new id, reset status and HP in case using data source
if (config.dataSource) {
enemyPokemon.id = Utils.randSeedInt(4294967296);
enemyPokemon.id = randSeedInt(4294967296);
}
// Set form
@ -1115,7 +1114,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
const validMEfloorsByBiome = new Map<string, number>(biomes.map(b => [b, 0]));
let currentBiome = Biome.TOWN;
let currentArena = globalScene.newArena(currentBiome);
globalScene.setSeed(Utils.randomString(24));
globalScene.setSeed(randomString(24));
globalScene.resetSeed();
for (let i = 10; i < 180; i++) {
// Boss
@ -1130,16 +1129,16 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
globalScene.executeWithSeedOffset(() => {
biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[])
.filter(b => {
return !Array.isArray(b) || !Utils.randSeedInt(b[1]);
return !Array.isArray(b) || !randSeedInt(b[1]);
})
.map(b => (!Array.isArray(b) ? b : b[0]));
}, i * 100);
if (biomes! && biomes.length > 0) {
const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b));
if (specialBiomes.length > 0) {
currentBiome = specialBiomes[Utils.randSeedInt(specialBiomes.length)];
currentBiome = specialBiomes[randSeedInt(specialBiomes.length)];
} else {
currentBiome = biomes[Utils.randSeedInt(biomes.length)];
currentBiome = biomes[randSeedInt(biomes.length)];
}
}
} else if (biomeLinks.hasOwnProperty(currentBiome)) {
@ -1167,7 +1166,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
// Otherwise, roll encounter
const roll = Utils.randSeedInt(256);
const roll = randSeedInt(256);
validMEfloorsByBiome.set(Biome[currentBiome], (validMEfloorsByBiome.get(Biome[currentBiome]) ?? 0) + 1);
// If total number of encounters is lower than expected for the run, slightly favor a new encounter
@ -1192,7 +1191,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
tierWeights[1] = tierWeights[1] - 4 * numEncounters[1];
const totalWeight = tierWeights.reduce((a, b) => a + b);
const tierValue = Utils.randSeedInt(totalWeight);
const tierValue = randSeedInt(totalWeight);
const commonThreshold = totalWeight - tierWeights[0]; // 64 - 32 = 32
const uncommonThreshold = totalWeight - tierWeights[0] - tierWeights[1]; // 64 - 32 - 16 = 16
const rareThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2]; // 64 - 32 - 16 - 10 = 6
@ -1281,7 +1280,7 @@ export function calculateRareSpawnAggregateStats(luckValue: number) {
const calculateNumRareEncounters = (): any[] => {
const bossEncountersByRarity = [0, 0, 0, 0];
globalScene.setSeed(Utils.randomString(24));
globalScene.setSeed(randomString(24));
globalScene.resetSeed();
// There are 12 wild boss floors
for (let i = 0; i < 12; i++) {
@ -1291,7 +1290,7 @@ export function calculateRareSpawnAggregateStats(luckValue: number) {
if (!Number.isNaN(luckValue)) {
luckModifier = luckValue * 0.5;
}
const tierValue = Utils.randSeedInt(64 - luckModifier);
const tierValue = randSeedInt(64 - luckModifier);
const tier =
tierValue >= 20
? BiomePoolTier.BOSS

View file

@ -1,4 +1,4 @@
import * as Utils from "../utils";
import { toReadableString } from "#app/utils";
import { TextStyle, getBBCodeFrag } from "../ui/text";
import { Nature } from "#enums/nature";
import { UiTheme } from "#enums/ui-theme";
@ -12,7 +12,7 @@ export function getNatureName(
ignoreBBCode = false,
uiTheme: UiTheme = UiTheme.DEFAULT,
): string {
let ret = Utils.toReadableString(Nature[nature]);
let ret = toReadableString(Nature[nature]);
//Translating nature
if (i18next.exists(`nature:${ret}`)) {
ret = i18next.t(`nature:${ret}` as any);

View file

@ -8,7 +8,7 @@ import type { AnySound } from "#app/battle-scene";
import { globalScene } from "#app/global-scene";
import type { GameMode } from "#app/game-mode";
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
import * as Utils from "#app/utils";
import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils";
import { uncatchableSpecies } from "#app/data/balance/biomes";
import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { GrowthRate } from "#app/data/exp";
@ -26,11 +26,12 @@ import {
pokemonSpeciesLevelMoves,
} from "#app/data/balance/pokemon-level-moves";
import type { Stat } from "#enums/stat";
import type { Variant, VariantSet } from "#app/data/variant";
import { variantData } from "#app/data/variant";
import type { Variant, VariantSet } from "#app/sprites/variant";
import { variantData } from "#app/sprites/variant";
import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
import { SpeciesFormKey } from "#enums/species-form-key";
import { starterPassiveAbilities } from "#app/data/balance/passives";
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
export enum Region {
NORMAL,
@ -289,7 +290,7 @@ export abstract class PokemonSpeciesForm {
* @returns The id of the ability
*/
getPassiveAbility(formIndex?: number): Abilities {
if (Utils.isNullOrUndefined(formIndex)) {
if (isNullOrUndefined(formIndex)) {
formIndex = this.formIndex;
}
let starterSpeciesId = this.speciesId;
@ -387,6 +388,7 @@ export abstract class PokemonSpeciesForm {
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
}
/** Compute the sprite ID of the pokemon form. */
getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string {
if (formIndex === undefined || this instanceof PokemonForm) {
formIndex = this.formIndex;
@ -394,7 +396,9 @@ export abstract class PokemonSpeciesForm {
const formSpriteKey = this.getFormSpriteKey(formIndex);
const showGenderDiffs =
this.genderDiffs && female && ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].find(k => formSpriteKey === k);
this.genderDiffs &&
female &&
![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
const baseSpriteKey = `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`;
@ -585,18 +589,19 @@ export abstract class PokemonSpeciesForm {
return true;
}
loadAssets(
async loadAssets(
female: boolean,
formIndex?: number,
shiny?: boolean,
shiny = false,
variant?: Variant,
startLoad?: boolean,
back?: boolean,
startLoad = false,
back = false,
): Promise<void> {
return new Promise(resolve => {
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back);
globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back));
globalScene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`);
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back);
globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back));
globalScene.load.audio(this.getCryKey(formIndex), `audio/${this.getCryKey(formIndex)}.m4a`);
return new Promise<void>(resolve => {
globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => {
const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot
@ -621,7 +626,9 @@ export abstract class PokemonSpeciesForm {
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)
.replace("variant/", "")
.replace(/_[1-3]$/, "");
globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve());
if (!isNullOrUndefined(variant)) {
loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve());
}
});
if (startLoad) {
if (!globalScene.load.isLoading()) {
@ -845,8 +852,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
*/
getFormNameToDisplay(formIndex = 0, append = false): string {
const formKey = this.forms?.[formIndex!]?.formKey;
const formText = Utils.capitalizeString(formKey, "-", false, false) || "";
const speciesName = Utils.capitalizeString(Species[this.speciesId], "_", true, false);
const formText = capitalizeString(formKey, "-", false, false) || "";
const speciesName = capitalizeString(Species[this.speciesId], "_", true, false);
let ret = "";
const region = this.getRegion();
@ -877,7 +884,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
if (i18next.exists(i18key)) {
ret = i18next.t(i18key);
} else {
const rootSpeciesName = Utils.capitalizeString(Species[this.getRootSpeciesId()], "_", true, false);
const rootSpeciesName = capitalizeString(Species[this.getRootSpeciesId()], "_", true, false);
const i18RootKey = `pokemonForm:${rootSpeciesName}${formText}`;
ret = i18next.exists(i18RootKey) ? i18next.t(i18RootKey) : formText;
}
@ -1072,7 +1079,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
return this.speciesId;
}
const randValue = evolutionPool.size === 1 ? 0 : Utils.randSeedInt(totalWeight);
const randValue = evolutionPool.size === 1 ? 0 : randSeedInt(totalWeight);
for (const weight of evolutionPool.keys()) {
if (randValue < weight) {
@ -1157,7 +1164,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
Math.min(
Math.max(
evolution?.level! +
Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) -
Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) -
1,
2,
evolution?.level!,
@ -1175,7 +1182,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
Math.min(
Math.max(
lastPrevolutionLevel +
Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
lastPrevolutionLevel + 1,
evolution?.level!,
),
@ -1360,7 +1367,7 @@ export function getPokerusStarters(): PokemonSpecies[] {
globalScene.executeWithSeedOffset(
() => {
while (pokerusStarters.length < POKERUS_STARTER_COUNT) {
const randomSpeciesId = Number.parseInt(Utils.randSeedItem(Object.keys(speciesStarterCosts)), 10);
const randomSpeciesId = Number.parseInt(randSeedItem(Object.keys(speciesStarterCosts)), 10);
const species = getPokemonSpecies(randomSpeciesId);
if (!pokerusStarters.includes(species)) {
pokerusStarters.push(species);

View file

@ -1,12 +1,12 @@
import { TrainerType } from "#enums/trainer-type";
import * as Utils from "../utils";
import { toReadableString } from "#app/utils";
class TrainerNameConfig {
public urls: string[];
public femaleUrls: string[] | null;
constructor(type: TrainerType, ...urls: string[]) {
this.urls = urls.length ? urls : [Utils.toReadableString(TrainerType[type]).replace(/ /g, "_")];
this.urls = urls.length ? urls : [toReadableString(TrainerType[type]).replace(/ /g, "_")];
}
hasGenderVariant(...femaleUrls: string[]): TrainerNameConfig {

View file

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#app/modifier/modifier-type";
import { PokemonMove } from "#app/field/pokemon";
import * as Utils from "#app/utils";
import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils";
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { tmSpecies } from "#app/data/balance/tms";
@ -116,7 +116,6 @@ export class TrainerConfig {
public modifierRewardFuncs: ModifierTypeFunc[] = [];
public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc;
public eventRewardFuncs: ModifierTypeFunc[] = [];
public partyMemberFuncs: PartyMemberFuncs = {};
public speciesPools: TrainerTierPools;
public speciesFilter: PokemonSpeciesFilter;
@ -139,7 +138,7 @@ export class TrainerConfig {
constructor(trainerType: TrainerType, allowLegendaries?: boolean) {
this.trainerType = trainerType;
this.trainerAI = new TrainerAI();
this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]);
this.name = toReadableString(TrainerType[this.getDerivedType()]);
this.battleBgm = "battle_trainer";
this.mixedBattleBgm = "battle_trainer";
this.victoryBgm = "victory_trainer";
@ -482,10 +481,10 @@ export class TrainerConfig {
.fill(null)
.map((_, i) => i)
.filter(i => shedinjaCanTera || party[i].species.speciesId !== Species.SHEDINJA); // Shedinja can only Tera on Bug specialty type (or no specialty type)
const setPartySlot = !Utils.isNullOrUndefined(slot) ? Phaser.Math.Wrap(slot, 0, party.length) : -1; // If we have a tera slot defined, wrap it to party size.
const setPartySlot = !isNullOrUndefined(slot) ? Phaser.Math.Wrap(slot, 0, party.length) : -1; // If we have a tera slot defined, wrap it to party size.
for (let t = 0; t < Math.min(count(), party.length); t++) {
const randomIndex =
partyMemberIndexes.indexOf(setPartySlot) > -1 ? setPartySlot : Utils.randSeedItem(partyMemberIndexes);
partyMemberIndexes.indexOf(setPartySlot) > -1 ? setPartySlot : randSeedItem(partyMemberIndexes);
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
if (this.hasSpecialtyType()) {
party[randomIndex].teraType = this.specialtyType;
@ -517,16 +516,6 @@ export class TrainerConfig {
// return ret;
// }
/**
* Sets eventRewardFuncs to the active event rewards for the specified wave
* @param wave Associated with {@linkcode getFixedBattleEventRewards}
* @returns this
*/
setEventModifierRewardFuncs(wave: number): TrainerConfig {
this.eventRewardFuncs = timedEventManager.getFixedBattleEventRewards(wave).map(r => modifierTypes[r]);
return this;
}
setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig {
this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => {
const modifierTypeFunc = func();
@ -555,7 +544,7 @@ export class TrainerConfig {
initI18n();
}
if (!Utils.isNullOrUndefined(specialtyType)) {
if (!isNullOrUndefined(specialtyType)) {
this.setSpecialtyType(specialtyType);
}
@ -636,7 +625,7 @@ export class TrainerConfig {
}
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
});
if (!Utils.isNullOrUndefined(specialtyType)) {
if (!isNullOrUndefined(specialtyType)) {
this.setSpeciesFilter(p => p.isOfType(specialtyType));
this.setSpecialtyType(specialtyType);
}
@ -749,7 +738,7 @@ export class TrainerConfig {
});
// Set species filter and specialty type if provided, otherwise filter by base total.
if (!Utils.isNullOrUndefined(specialtyType)) {
if (!isNullOrUndefined(specialtyType)) {
this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST);
this.setSpecialtyType(specialtyType);
} else {
@ -927,7 +916,7 @@ export class TrainerConfig {
* @returns true if specialtyType is defined and not Type.UNKNOWN
*/
hasSpecialtyType(): boolean {
return !Utils.isNullOrUndefined(this.specialtyType) && this.specialtyType !== PokemonType.UNKNOWN;
return !isNullOrUndefined(this.specialtyType) && this.specialtyType !== PokemonType.UNKNOWN;
}
/**
@ -1006,7 +995,7 @@ export function getRandomPartyMemberFunc(
postProcess?: (enemyPokemon: EnemyPokemon) => void,
) {
return (level: number, strength: PartyMemberStrength) => {
let species = Utils.randSeedItem(speciesPool);
let species = randSeedItem(speciesPool);
if (!ignoreEvolution) {
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(
level,
@ -1329,7 +1318,16 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerPoolTier.RARE]: [Species.BELLOSSOM, Species.HITMONTOP, Species.MIME_JR, Species.ORICORIO],
[TrainerPoolTier.SUPER_RARE]: [Species.QUAXLY, Species.JANGMO_O],
}),
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t).setMoneyMultiplier(1.45).setEncounterBgm(TrainerType.CLERK),
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t)
.setMoneyMultiplier(1.45)
.setEncounterBgm(TrainerType.CLERK)
.setPartyTemplates(
trainerPartyTemplates.TWO_AVG,
trainerPartyTemplates.THREE_WEAK,
trainerPartyTemplates.THREE_AVG,
trainerPartyTemplates.FOUR_WEAK,
)
.setSpeciesFilter(s => s.isOfType(PokemonType.GROUND)),
[TrainerType.DOCTOR]: new TrainerConfig(++t)
.setHasGenders("Nurse", "lass")
.setHasDouble("Medical Team")
@ -1380,7 +1378,6 @@ export const trainerConfigs: TrainerConfigs = {
Species.CHINCHOU,
Species.CORSOLA,
Species.WAILMER,
Species.BARBOACH,
Species.CLAMPERL,
Species.LUVDISC,
Species.MANTYKE,
@ -2236,12 +2233,7 @@ export const trainerConfigs: TrainerConfigs = {
Species.PHANTUMP,
Species.PUMPKABOO,
],
[TrainerPoolTier.RARE]: [
Species.SNEASEL,
Species.LITWICK,
Species.PAWNIARD,
Species.NOIBAT,
],
[TrainerPoolTier.RARE]: [Species.SNEASEL, Species.LITWICK, Species.PAWNIARD, Species.NOIBAT],
[TrainerPoolTier.SUPER_RARE]: [Species.SLIGGOO, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG],
}),
[TrainerType.BRYONY]: new TrainerConfig(++t)
@ -3554,7 +3546,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(
5,
getRandomPartyMemberFunc([Species.URSHIFU], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(2, 2); // Random G-Max Urshifu
p.formIndex = randSeedInt(2, 2); // Random G-Max Urshifu
p.generateAndPopulateMoveset();
p.generateName();
p.gender = Gender.MALE;
@ -3664,10 +3656,10 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(
4,
getRandomPartyMemberFunc([Species.OGERPON], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(4); // Random Ogerpon Tera Mask
p.formIndex = randSeedInt(4); // Random Ogerpon Tera Mask
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) {
if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) {
// Check if Ivy Cudgel is in the moveset, if not, replace the first move with Ivy Cudgel.
p.moveset[0] = new PokemonMove(Moves.IVY_CUDGEL);
}
@ -3697,7 +3689,6 @@ export const trainerConfigs: TrainerConfigs = {
() => modifierTypes.SUPER_EXP_CHARM,
() => modifierTypes.EXP_SHARE,
)
.setEventModifierRewardFuncs(8)
.setPartyMemberFunc(
0,
getRandomPartyMemberFunc(
@ -3765,7 +3756,6 @@ export const trainerConfigs: TrainerConfigs = {
.setMixedBattleBgm("battle_rival")
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
.setEventModifierRewardFuncs(25)
.setPartyMemberFunc(
0,
getRandomPartyMemberFunc(
@ -4718,10 +4708,10 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(
2,
getRandomPartyMemberFunc([Species.SILVALLY], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(18); // Random Silvally Form
p.formIndex = randSeedInt(18); // Random Silvally Form
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.MULTI_ATTACK)) {
if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.MULTI_ATTACK)) {
// Check if Multi Attack is in the moveset, if not, replace the first move with Multi Attack.
p.moveset[0] = new PokemonMove(Moves.MULTI_ATTACK);
}
@ -4838,8 +4828,8 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.formIndex = Utils.randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive
if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.TECHNO_BLAST)) {
p.formIndex = randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive
if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.TECHNO_BLAST)) {
// Check if Techno Blast is in the moveset, if not, replace the first move with Techno Blast.
p.moveset[2] = new PokemonMove(Moves.TECHNO_BLAST);
}
@ -5011,7 +5001,7 @@ export const trainerConfigs: TrainerConfigs = {
1,
getRandomPartyMemberFunc([Species.ROTOM], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.formIndex = Utils.randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow
p.formIndex = randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow
}),
)
.setPartyMemberFunc(
@ -5024,7 +5014,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(
3,
getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => {
p.formIndex = Utils.randSeedInt(5, 1); // Random Starmobile form
p.formIndex = randSeedInt(5, 1); // Random Starmobile form
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}),

View file

@ -1,31 +0,0 @@
import { VariantTier } from "#app/enums/variant-tier";
export type Variant = 0 | 1 | 2;
export type VariantSet = [Variant, Variant, Variant];
export const variantData: any = {};
export const variantColorCache = {};
export function getVariantTint(variant: Variant): number {
switch (variant) {
case 0:
return 0xf8c020;
case 1:
return 0x20f8f0;
case 2:
return 0xe81048;
}
}
export function getVariantIcon(variant: Variant): number {
switch (variant) {
case 0:
return VariantTier.STANDARD;
case 1:
return VariantTier.RARE;
case 2:
return VariantTier.EPIC;
}
}

View file

@ -5,7 +5,7 @@ import type Pokemon from "../field/pokemon";
import { PokemonType } from "#enums/pokemon-type";
import type Move from "./moves/move";
import { AttackMove } from "./moves/move";
import * as Utils from "../utils";
import { randSeedInt } from "#app/utils";
import { SuppressWeatherEffectAbAttr } from "./ability";
import { TerrainType, getTerrainName } from "./terrain";
import i18next from "i18next";
@ -416,7 +416,7 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
totalWeight += w.weight;
}
const rand = Utils.randSeedInt(totalWeight);
const rand = randSeedInt(totalWeight);
let w = 0;
for (const weather of weatherPool) {
w += weather.weight;

View file

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene";
import { PokeballType } from "#enums/pokeball";
import type { Variant } from "#app/data/variant";
import type { Variant } from "#app/sprites/variant";
import { getFrameMs, randGauss } from "#app/utils";
export function addPokeballOpenParticles(x: number, y: number, pokeballType: PokeballType): void {

View file

@ -1,8 +1,7 @@
import { globalScene } from "#app/global-scene";
import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biomes";
import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes";
import type { Constructor } from "#app/utils";
import * as Utils from "#app/utils";
import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import {
@ -40,7 +39,6 @@ import { TrainerType } from "#enums/trainer-type";
import { Abilities } from "#enums/abilities";
import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms";
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { WeatherType } from "#enums/weather-type";
import { FieldEffectModifier } from "#app/modifier/modifier";
@ -125,7 +123,7 @@ export class Arena {
if (typeof luckValue !== "undefined") {
luckModifier = luckValue * (isBossSpecies ? 0.5 : 2);
}
const tierValue = Utils.randSeedInt(randVal - luckModifier);
const tierValue = randSeedInt(randVal - luckModifier);
let tier = !isBossSpecies
? tierValue >= 156
? BiomePoolTier.COMMON
@ -154,7 +152,7 @@ export class Arena {
if (!tierPool.length) {
ret = globalScene.randomSpecies(waveIndex, level);
} else {
const entry = tierPool[Utils.randSeedInt(tierPool.length)];
const entry = tierPool[randSeedInt(tierPool.length)];
let species: Species;
if (typeof entry === "number") {
species = entry as Species;
@ -165,7 +163,7 @@ export class Arena {
if (level >= levelThreshold) {
const speciesIds = entry[levelThreshold];
if (speciesIds.length > 1) {
species = speciesIds[Utils.randSeedInt(speciesIds.length)];
species = speciesIds[randSeedInt(speciesIds.length)];
} else {
species = speciesIds[0];
}
@ -212,7 +210,7 @@ export class Arena {
!!this.trainerPool[BiomePoolTier.BOSS].length &&
(globalScene.gameMode.isTrainerBoss(waveIndex, this.biomeType, globalScene.offsetGym) || isBoss);
console.log(isBoss, this.trainerPool);
const tierValue = Utils.randSeedInt(!isTrainerBoss ? 512 : 64);
const tierValue = randSeedInt(!isTrainerBoss ? 512 : 64);
let tier = !isTrainerBoss
? tierValue >= 156
? BiomePoolTier.COMMON
@ -236,7 +234,7 @@ export class Arena {
tier--;
}
const tierPool = this.trainerPool[tier] || [];
return !tierPool.length ? TrainerType.BREEDER : tierPool[Utils.randSeedInt(tierPool.length)];
return !tierPool.length ? TrainerType.BREEDER : tierPool[randSeedInt(tierPool.length)];
}
getSpeciesFormIndex(species: PokemonSpecies): number {
@ -337,9 +335,9 @@ export class Arena {
return false;
}
const weatherDuration = new Utils.NumberHolder(0);
const weatherDuration = new NumberHolder(0);
if (!Utils.isNullOrUndefined(user)) {
if (!isNullOrUndefined(user)) {
weatherDuration.value = 5;
globalScene.applyModifier(FieldEffectModifier, user.isPlayer(), user, weatherDuration);
}
@ -378,7 +376,6 @@ export class Arena {
const isCherrimWithFlowerGift = p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM;
if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(p.getBattlerIndex());
globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
}
});
@ -395,7 +392,6 @@ export class Arena {
p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM;
if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(p.getBattlerIndex());
return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
}
});
@ -420,9 +416,9 @@ export class Arena {
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
const terrainDuration = new Utils.NumberHolder(0);
const terrainDuration = new NumberHolder(0);
if (!Utils.isNullOrUndefined(user)) {
if (!isNullOrUndefined(user)) {
terrainDuration.value = 5;
globalScene.applyModifier(FieldEffectModifier, user.isPlayer(), user, terrainDuration);
}
@ -1016,7 +1012,7 @@ export class ArenaBase extends Phaser.GameObjects.Container {
if (!this.player) {
globalScene.executeWithSeedOffset(
() => {
this.propValue = propValue === undefined ? (hasProps ? Utils.randSeedInt(8) : 0) : propValue;
this.propValue = propValue === undefined ? (hasProps ? randSeedInt(8) : 0) : propValue;
this.props.forEach((prop, p) => {
const propKey = `${biomeKey}_b${hasProps ? `_${p + 1}` : ""}`;
prop.setTexture(propKey);

View file

@ -2,7 +2,7 @@ import { TextStyle, addTextObject } from "../ui/text";
import type { DamageResult } from "./pokemon";
import type Pokemon from "./pokemon";
import { HitResult } from "./pokemon";
import * as Utils from "../utils";
import { formatStat, fixedInt } from "#app/utils";
import type { BattlerIndex } from "../battle";
import { globalScene } from "#app/global-scene";
@ -30,7 +30,7 @@ export default class DamageNumberHandler {
const damageNumber = addTextObject(
target.x,
-(globalScene.game.canvas.height / 6) + target.y - target.getSprite().height / 2,
Utils.formatStat(amount, true),
formatStat(amount, true),
TextStyle.SUMMARY,
);
damageNumber.setName("text-damage-number");
@ -86,14 +86,14 @@ export default class DamageNumberHandler {
if (globalScene.damageNumbersMode === 1) {
globalScene.tweens.add({
targets: damageNumber,
duration: Utils.fixedInt(750),
duration: fixedInt(750),
alpha: 1,
y: "-=32",
});
globalScene.tweens.add({
delay: 375,
targets: damageNumber,
duration: Utils.fixedInt(625),
duration: fixedInt(625),
alpha: 0,
ease: "Sine.easeIn",
onComplete: () => {
@ -110,7 +110,7 @@ export default class DamageNumberHandler {
targets: damageNumber,
tweens: [
{
duration: Utils.fixedInt(250),
duration: fixedInt(250),
alpha: 1,
scaleX: 0.75 * baseScale,
scaleY: 1.25 * baseScale,
@ -118,7 +118,7 @@ export default class DamageNumberHandler {
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(175),
duration: fixedInt(175),
alpha: 1,
scaleX: 0.875 * baseScale,
scaleY: 1.125 * baseScale,
@ -126,59 +126,59 @@ export default class DamageNumberHandler {
ease: "Cubic.easeIn",
},
{
duration: Utils.fixedInt(100),
duration: fixedInt(100),
scaleX: 1.25 * baseScale,
scaleY: 0.75 * baseScale,
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(175),
duration: fixedInt(175),
scaleX: 0.875 * baseScale,
scaleY: 1.125 * baseScale,
y: "-=8",
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(50),
duration: fixedInt(50),
scaleX: 0.925 * baseScale,
scaleY: 1.075 * baseScale,
y: "+=8",
ease: "Cubic.easeIn",
},
{
duration: Utils.fixedInt(100),
duration: fixedInt(100),
scaleX: 1.125 * baseScale,
scaleY: 0.875 * baseScale,
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(175),
duration: fixedInt(175),
scaleX: 0.925 * baseScale,
scaleY: 1.075 * baseScale,
y: "-=4",
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(50),
duration: fixedInt(50),
scaleX: 0.975 * baseScale,
scaleY: 1.025 * baseScale,
y: "+=4",
ease: "Cubic.easeIn",
},
{
duration: Utils.fixedInt(100),
duration: fixedInt(100),
scaleX: 1.075 * baseScale,
scaleY: 0.925 * baseScale,
ease: "Cubic.easeOut",
},
{
duration: Utils.fixedInt(25),
duration: fixedInt(25),
scaleX: baseScale,
scaleY: baseScale,
ease: "Cubic.easeOut",
},
{
delay: Utils.fixedInt(500),
delay: fixedInt(500),
alpha: 0,
onComplete: () => {
this.damageNumbers

View file

@ -4,9 +4,10 @@ import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounte
import type { Species } from "#enums/species";
import { isNullOrUndefined } from "#app/utils";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import type { Variant } from "#app/data/variant";
import type { Variant } from "#app/sprites/variant";
import { doShinySparkleAnim } from "#app/field/anims";
import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig;
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
type KnownFileRoot =
| "arenas"
@ -233,8 +234,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
this.spriteConfigs.forEach(config => {
if (config.isPokemon) {
globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
if (config.isShiny) {
shinyPromises.push(globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant));
if (config.isShiny && !isNullOrUndefined(config.variant)) {
shinyPromises.push(loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant));
}
} else if (config.isItem) {
globalScene.loadAtlas("items", "");

View file

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene";
import Pokemon from "./pokemon";
import * as Utils from "../utils";
import { fixedInt, randInt } from "#app/utils";
export default class PokemonSpriteSparkleHandler {
private sprites: Set<Phaser.GameObjects.Sprite>;
@ -9,7 +9,7 @@ export default class PokemonSpriteSparkleHandler {
this.sprites = new Set();
globalScene.tweens.addCounter({
duration: Utils.fixedInt(200),
duration: fixedInt(200),
from: 0,
to: 1,
yoyo: true,
@ -36,7 +36,7 @@ export default class PokemonSpriteSparkleHandler {
const parent = (pokemon || s).parentContainer;
const texture = s.texture;
const [width, height] = [texture.source[0].width, texture.source[0].height];
const [pixelX, pixelY] = [Utils.randInt(width), Utils.randInt(height)];
const [pixelX, pixelY] = [randInt(width), randInt(height)];
const ratioX = s.width / width;
const ratioY = s.height / height;
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE");
@ -51,7 +51,7 @@ export default class PokemonSpriteSparkleHandler {
sparkle.setName("sprite-tera-sparkle");
sparkle.play("tera_sparkle");
parent.add(sparkle);
s.scene.time.delayedCall(Utils.fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy());
s.scene.time.delayedCall(fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy());
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
import { TeraAIMode } from "#enums/tera-ai-mode";
import type { EnemyPokemon } from "#app/field/pokemon";
import * as Utils from "#app/utils";
import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils";
import type { PersistentModifier } from "#app/modifier/modifier";
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
@ -33,14 +33,16 @@ export default class Trainer extends Phaser.GameObjects.Container {
public partyTemplateIndex: number;
public name: string;
public partnerName: string;
public nameKey: string;
public partnerNameKey: string | undefined;
public originalIndexes: { [key: number]: number } = {};
constructor(
trainerType: TrainerType,
variant: TrainerVariant,
partyTemplateIndex?: number,
name?: string,
partnerName?: string,
nameKey?: string,
partnerNameKey?: string,
trainerConfigOverride?: TrainerConfig,
) {
super(globalScene, -72, 80);
@ -56,31 +58,42 @@ export default class Trainer extends Phaser.GameObjects.Container {
this.partyTemplateIndex = Math.min(
partyTemplateIndex !== undefined
? partyTemplateIndex
: Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
: randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
this.config.partyTemplates.length - 1,
);
if (i18next.exists("trainersCommon:" + TrainerType[trainerType], { returnObjects: true })) {
const namePool = i18next.t("trainersCommon:" + TrainerType[trainerType], { returnObjects: true });
this.name =
name ||
Utils.randSeedItem(
Object.values(
namePool.hasOwnProperty("MALE")
? namePool[variant === TrainerVariant.FEMALE ? "FEMALE" : "MALE"]
: namePool,
),
);
const classKey = `trainersCommon:${TrainerType[trainerType]}`;
if (i18next.exists(classKey, { returnObjects: true })) {
if (nameKey) {
this.nameKey = nameKey;
} else {
const genderKey = i18next.exists(`${classKey}.MALE`)
? variant === TrainerVariant.FEMALE
? ".FEMALE"
: ".MALE"
: "";
const trainerKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })));
this.nameKey = `${classKey}${genderKey}.${trainerKey}`;
}
this.name = i18next.t(this.nameKey);
if (variant === TrainerVariant.DOUBLE) {
if (this.config.doubleOnly) {
if (partnerName) {
this.partnerName = partnerName;
if (partnerNameKey) {
this.partnerNameKey = partnerNameKey;
this.partnerName = i18next.t(this.partnerNameKey);
} else {
[this.name, this.partnerName] = this.name.split(" & ");
}
} else {
this.partnerName =
partnerName ||
Utils.randSeedItem(Object.values(namePool.hasOwnProperty("FEMALE") ? namePool["FEMALE"] : namePool));
const partnerGenderKey = i18next.exists(`${classKey}.FEMALE`) ? ".FEMALE" : "";
const partnerTrainerKey = randSeedItem(
Object.keys(
i18next.t(`${classKey}${partnerGenderKey}`, {
returnObjects: true,
}),
),
);
this.partnerNameKey = `${classKey}${partnerGenderKey}.${partnerTrainerKey}`;
this.partnerName = i18next.t(this.partnerNameKey);
}
}
}
@ -405,7 +418,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
// If useNewSpeciesPool is true, we need to generate a new species from the new species pool, otherwise we generate a random species
let species = useNewSpeciesPool
? getPokemonSpecies(newSpeciesPool[Math.floor(Utils.randSeedInt(newSpeciesPool.length))])
? getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))])
: template.isSameSpecies(index) && index > offset
? getPokemonSpecies(
battle.enemyParty[offset].species.getTrainerSpeciesForLevel(
@ -446,7 +459,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
let baseSpecies: PokemonSpecies;
if (this.config.speciesPools) {
const tierValue = Utils.randSeedInt(512);
const tierValue = randSeedInt(512);
let tier =
tierValue >= 156
? TrainerPoolTier.COMMON
@ -465,7 +478,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
tier--;
}
const tierPool = this.config.speciesPools[tier];
baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool));
baseSpecies = getPokemonSpecies(randSeedItem(tierPool));
} else {
baseSpecies = globalScene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
}
@ -604,7 +617,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (maxScorePartyMemberIndexes.length > 1) {
let rand: number;
globalScene.executeWithSeedOffset(
() => (rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length)),
() => (rand = randSeedInt(maxScorePartyMemberIndexes.length)),
globalScene.currentBattle.turn << 2,
);
return maxScorePartyMemberIndexes[rand!];

View file

@ -7,7 +7,7 @@ import type PokemonSpecies from "./data/pokemon-species";
import { allSpecies } from "./data/pokemon-species";
import type { Arena } from "./field/arena";
import Overrides from "#app/overrides";
import * as Utils from "./utils";
import { randSeedInt, randSeedItem } from "#app/utils";
import { Biome } from "#enums/biome";
import { Species } from "#enums/species";
import { Challenges } from "./enums/challenges";
@ -186,7 +186,7 @@ export class GameMode implements GameModeConfig {
if (w < waveIndex) {
globalScene.executeWithSeedOffset(() => {
const waveTrainerChance = arena.getTrainerChance();
if (!Utils.randSeedInt(waveTrainerChance)) {
if (!randSeedInt(waveTrainerChance)) {
allowTrainerBattle = false;
}
}, w);
@ -196,7 +196,7 @@ export class GameMode implements GameModeConfig {
}
}
}
return Boolean(allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance));
return Boolean(allowTrainerBattle && trainerChance && !randSeedInt(trainerChance));
}
return false;
}
@ -222,7 +222,7 @@ export class GameMode implements GameModeConfig {
s.speciesId !== Species.ETERNATUS &&
s.speciesId !== Species.ARCEUS,
);
return Utils.randSeedItem(allFinalBossSpecies);
return randSeedItem(allFinalBossSpecies);
}
return null;

View file

@ -1,6 +1,5 @@
import Phaser from "phaser";
import * as Utils from "./utils";
import { deepCopy } from "./utils";
import { deepCopy, getEnumValues } from "#app/utils";
import pad_generic from "./configs/inputs/pad_generic";
import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/inputs/pad_xbox360";
@ -102,7 +101,7 @@ export class InputsController {
[Device.KEYBOARD]: "default",
};
for (const b of Utils.getEnumValues(Button)) {
for (const b of getEnumValues(Button)) {
this.interactions[b] = {
pressTime: false,
isPressed: false,

View file

@ -4,7 +4,7 @@ import CacheBustedLoaderPlugin from "#app/plugins/cache-busted-loader-plugin";
import { SceneBase } from "#app/scene-base";
import { WindowVariant, getWindowVariantSuffix } from "#app/ui/ui-theme";
import { isMobile } from "#app/touch-controls";
import * as Utils from "#app/utils";
import { localPing, getEnumValues, hasAllLocalizedSprites, getEnumKeys } from "#app/utils";
import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions";
import { initBiomes } from "#app/data/balance/biomes";
import { initEggMoves } from "#app/data/balance/egg-moves";
@ -34,7 +34,7 @@ export class LoadingScene extends SceneBase {
}
preload() {
Utils.localPing();
localPing();
this.load["manifest"] = this.game["manifest"];
this.loadImage("loading_bg", "arenas");
@ -49,7 +49,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("friendship_overlay", "ui");
this.loadImage("cursor", "ui");
this.loadImage("cursor_reverse", "ui");
for (const wv of Utils.getEnumValues(WindowVariant)) {
for (const wv of getEnumValues(WindowVariant)) {
for (let w = 1; w <= 5; w++) {
this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, "ui/windows");
}
@ -177,7 +177,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("default_bg", "arenas");
// Load arena images
Utils.getEnumValues(Biome).map(bt => {
getEnumValues(Biome).map(bt => {
const btKey = Biome[bt].toLowerCase();
const isBaseAnimated = btKey === "end";
const baseAKey = `${btKey}_a`;
@ -239,7 +239,7 @@ export class LoadingScene extends SceneBase {
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_<lang>
const lang = i18next.resolvedLanguage;
if (lang !== "en") {
if (Utils.hasAllLocalizedSprites(lang)) {
if (hasAllLocalizedSprites(lang)) {
this.loadAtlas(`statuses_${lang}`, "");
this.loadAtlas(`types_${lang}`, "");
} else {
@ -268,7 +268,7 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("egg_icons", "egg");
this.loadAtlas("egg_shard", "egg");
this.loadAtlas("egg_lightrays", "egg");
for (const gt of Utils.getEnumKeys(GachaType)) {
for (const gt of getEnumKeys(GachaType)) {
const key = gt.toLowerCase();
this.loadImage(`gacha_${key}`, "egg");
this.loadAtlas(`gacha_underlay_${key}`, "egg");

View file

@ -6,9 +6,10 @@ import i18next from "i18next";
/**
* Retrieves the Pokemon's name, potentially with an affix indicating its role (wild or foe) in the current battle context, translated
* @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance
* @param {boolean} useIllusion - Whether we want the name of the illusion or not. Default value : true
* @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage"
*/
export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string {
export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusion = true): string {
if (!pokemon) {
return "Missigno";
}
@ -18,19 +19,17 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string {
return !pokemon.isPlayer()
? pokemon.hasTrainer()
? i18next.t("battle:foePokemonWithAffix", {
pokemonName: pokemon.getNameToRender(),
pokemonName: pokemon.getNameToRender(useIllusion),
})
: i18next.t("battle:wildPokemonWithAffix", {
pokemonName: pokemon.getNameToRender(),
pokemonName: pokemon.getNameToRender(useIllusion),
})
: pokemon.getNameToRender();
: pokemon.getNameToRender(useIllusion);
case BattleSpec.FINAL_BOSS:
return !pokemon.isPlayer()
? i18next.t("battle:foePokemonWithAffix", {
pokemonName: pokemon.getNameToRender(),
})
: pokemon.getNameToRender();
? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) })
: pokemon.getNameToRender(useIllusion);
default:
return pokemon.getNameToRender();
return pokemon.getNameToRender(useIllusion);
}
}

View file

@ -2734,7 +2734,7 @@ export class PokemonMoveAccuracyBoosterModifier extends PokemonHeldItemModifier
* @returns always `true`
*/
override apply(_pokemon: Pokemon, moveAccuracy: NumberHolder): boolean {
moveAccuracy.value = Math.min(moveAccuracy.value + this.accuracyAmount * this.getStackCount(), 100);
moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount();
return true;
}

View file

@ -2,7 +2,7 @@ import { type PokeballCounts } from "#app/battle-scene";
import { EvolutionItem } from "#app/data/balance/pokemon-evolutions";
import { Gender } from "#app/data/gender";
import { FormChangeItem } from "#app/data/pokemon-forms";
import { Variant } from "#app/data/variant";
import { Variant } from "#app/sprites/variant";
import { type ModifierOverride } from "#app/modifier/modifier-type";
import { Unlockables } from "#app/system/unlockables";
import { Abilities } from "#enums/abilities";

View file

@ -9,10 +9,6 @@ import { Phase } from "#app/phase";
import { globalScene } from "#app/global-scene";
export class AddEnemyBuffModifierPhase extends Phase {
constructor() {
super();
}
start() {
super.start();

View file

@ -1,10 +1,10 @@
import { applyAbAttrs, applyPreLeaveFieldAbAttrs, PreLeaveFieldAbAttr, RunSuccessAbAttr } from "#app/data/ability";
import { Stat } from "#app/enums/stat";
import { StatusEffect } from "#app/enums/status-effect";
import { Stat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import i18next from "i18next";
import * as Utils from "#app/utils";
import { NumberHolder } from "#app/utils";
import { BattleEndPhase } from "./battle-end-phase";
import { NewBattlePhase } from "./new-battle-phase";
import { PokemonPhase } from "./pokemon-phase";
@ -22,7 +22,7 @@ export class AttemptRunPhase extends PokemonPhase {
const playerPokemon = this.getPokemon();
const escapeChance = new Utils.NumberHolder(0);
const escapeChance = new NumberHolder(0);
this.attemptRunAway(playerField, enemyField, escapeChance);
@ -63,7 +63,7 @@ export class AttemptRunPhase extends PokemonPhase {
this.end();
}
attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: Utils.NumberHolder) {
attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: NumberHolder) {
/** Sum of the speed of all enemy pokemon on the field */
const enemySpeed = enemyField.reduce(
(total: number, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD),

View file

@ -17,6 +17,25 @@ export class BattleEndPhase extends BattlePhase {
start() {
super.start();
// cull any extra `BattleEnd` phases from the queue.
globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => {
if (phase instanceof BattleEndPhase) {
this.isVictory ||= phase.isVictory;
return false;
}
return true;
});
// `phaseQueuePrepend` is private, so we have to use this inefficient loop.
while (
globalScene.tryRemoveUnshiftedPhase(phase => {
if (phase instanceof BattleEndPhase) {
this.isVictory ||= phase.isVictory;
return true;
}
return false;
})
) {}
globalScene.gameData.gameStats.battles++;
if (
globalScene.gameMode.isEndless &&
@ -54,6 +73,13 @@ export class BattleEndPhase extends BattlePhase {
}
globalScene.clearEnemyHeldItemModifiers();
for (const p of globalScene.getEnemyParty()) {
try {
p.destroy();
} catch {
console.warn("Unable to destroy stale pokemon object in BattleEndPhase:", p);
}
}
const lapsingModifiers = globalScene.findModifiers(
m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier,

View file

@ -3,13 +3,13 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { Phase } from "#app/phase";
export class BattlePhase extends Phase {
constructor() {
super();
}
showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void {
const sprites = globalScene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct?
const tintSprites = globalScene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct?
if (!globalScene.currentBattle.trainer) {
console.warn("Enemy trainer is missing!");
return;
}
const sprites = globalScene.currentBattle.trainer.getSprites();
const tintSprites = globalScene.currentBattle.trainer.getTintSprites();
for (let i = 0; i < sprites.length; i++) {
const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2;
[sprites[i], tintSprites[i]].map(sprite => {

View file

@ -4,7 +4,7 @@ import { BerryUsedEvent } from "#app/events/battle-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { BerryModifier } from "#app/modifier/modifier";
import i18next from "i18next";
import * as Utils from "#app/utils";
import { BooleanHolder } from "#app/utils";
import { FieldPhase } from "./field-phase";
import { CommonAnimPhase } from "./common-anim-phase";
import { globalScene } from "#app/global-scene";
@ -20,7 +20,7 @@ export class BerryPhase extends FieldPhase {
}, pokemon.isPlayer());
if (hasUsableBerry) {
const cancelled = new Utils.BooleanHolder(false);
const cancelled = new BooleanHolder(false);
pokemon.getOpponents().map(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
if (cancelled.value) {
@ -44,7 +44,7 @@ export class BerryPhase extends FieldPhase {
globalScene.updateModifiers(pokemon.isPlayer());
applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false));
applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new BooleanHolder(false));
}
}
});

View file

@ -6,13 +6,18 @@ import { PokemonPhase } from "./pokemon-phase";
export class CommonAnimPhase extends PokemonPhase {
private anim: CommonAnim | null;
private targetIndex: number | undefined;
private targetIndex?: BattlerIndex;
private playOnEmptyField: boolean;
constructor(battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex, anim?: CommonAnim, playOnEmptyField = false) {
constructor(
battlerIndex?: BattlerIndex,
targetIndex?: BattlerIndex,
anim: CommonAnim | null = null,
playOnEmptyField = false,
) {
super(battlerIndex);
this.anim = anim!; // TODO: is this bang correct?
this.anim = anim;
this.targetIndex = targetIndex;
this.playOnEmptyField = playOnEmptyField;
}

View file

@ -10,11 +10,16 @@ export class DamageAnimPhase extends PokemonPhase {
private damageResult: DamageResult;
private critical: boolean;
constructor(battlerIndex: BattlerIndex, amount: number, damageResult?: DamageResult, critical = false) {
constructor(
battlerIndex: BattlerIndex,
amount: number,
damageResult: DamageResult = HitResult.EFFECTIVE,
critical = false,
) {
super(battlerIndex);
this.amount = amount;
this.damageResult = damageResult || HitResult.EFFECTIVE;
this.damageResult = damageResult;
this.critical = critical;
}

View file

@ -11,7 +11,7 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import * as Utils from "#app/utils";
import { fixedInt, getFrameMs, randInt } from "#app/utils";
import type { EggLapsePhase } from "./egg-lapse-phase";
import type { EggHatchData } from "#app/data/egg-hatch-data";
import { doShinySparkleAnim } from "#app/field/anims";
@ -306,17 +306,17 @@ export class EggHatchPhase extends Phase {
this.canSkip = false;
this.hatched = true;
if (this.evolutionBgm) {
SoundFade.fadeOut(globalScene, this.evolutionBgm, Utils.fixedInt(100));
SoundFade.fadeOut(globalScene, this.evolutionBgm, fixedInt(100));
}
for (let e = 0; e < 5; e++) {
globalScene.time.delayedCall(Utils.fixedInt(375 * e), () =>
globalScene.time.delayedCall(fixedInt(375 * e), () =>
globalScene.playSound("se/egg_hatch", { volume: 1 - e * 0.2 }),
);
}
this.eggLightraysOverlay.setVisible(true);
this.eggLightraysOverlay.play("egg_lightrays");
globalScene.tweens.add({
duration: Utils.fixedInt(125),
duration: fixedInt(125),
targets: this.eggHatchOverlay,
alpha: 1,
ease: "Cubic.easeIn",
@ -325,7 +325,7 @@ export class EggHatchPhase extends Phase {
this.canSkip = true;
},
});
globalScene.time.delayedCall(Utils.fixedInt(1500), () => {
globalScene.time.delayedCall(fixedInt(1500), () => {
this.canSkip = false;
if (!this.skipped) {
this.doReveal();
@ -363,46 +363,43 @@ export class EggHatchPhase extends Phase {
this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny);
this.pokemonSprite.setPipelineData("variant", this.pokemon.variant);
this.pokemonSprite.setVisible(true);
globalScene.time.delayedCall(Utils.fixedInt(250), () => {
globalScene.time.delayedCall(fixedInt(250), () => {
this.eggsToHatchCount--;
this.eggHatchHandler.eventTarget.dispatchEvent(new EggCountChangedEvent(this.eggsToHatchCount));
this.pokemon.cry();
if (isShiny) {
globalScene.time.delayedCall(Utils.fixedInt(500), () => {
globalScene.time.delayedCall(fixedInt(500), () => {
doShinySparkleAnim(this.pokemonShinySparkle, this.pokemon.variant);
});
}
globalScene.time.delayedCall(
Utils.fixedInt(!this.skipped ? (!isShiny ? 1250 : 1750) : !isShiny ? 250 : 750),
() => {
this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1);
globalScene.time.delayedCall(fixedInt(!this.skipped ? (!isShiny ? 1250 : 1750) : !isShiny ? 250 : 750), () => {
this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1);
globalScene.playSoundWithoutBgm("evolution_fanfare");
globalScene.playSoundWithoutBgm("evolution_fanfare");
globalScene.ui.showText(
i18next.t("egg:hatchFromTheEgg", {
pokemonName: this.pokemon.species.getExpandedSpeciesName(),
}),
null,
() => {
globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
globalScene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(value => {
this.eggHatchData.setEggMoveUnlocked(value);
globalScene.ui.showText("", 0);
this.end();
});
globalScene.ui.showText(
i18next.t("egg:hatchFromTheEgg", {
pokemonName: this.pokemon.species.getExpandedSpeciesName(),
}),
null,
() => {
globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
globalScene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(value => {
this.eggHatchData.setEggMoveUnlocked(value);
globalScene.ui.showText("", 0);
this.end();
});
},
null,
true,
3000,
);
},
);
});
},
null,
true,
3000,
);
});
});
globalScene.tweens.add({
duration: Utils.fixedInt(this.skipped ? 500 : 3000),
duration: fixedInt(this.skipped ? 500 : 3000),
targets: this.eggHatchOverlay,
alpha: 0,
ease: "Cubic.easeOut",
@ -427,9 +424,9 @@ export class EggHatchPhase extends Phase {
doSpray(intensity: number, offsetY?: number) {
globalScene.tweens.addCounter({
repeat: intensity,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
this.doSprayParticle(Utils.randInt(8), offsetY || 0);
this.doSprayParticle(randInt(8), offsetY || 0);
},
});
}
@ -448,12 +445,12 @@ export class EggHatchPhase extends Phase {
let f = 0;
let yOffset = 0;
const speed = 3 - Utils.randInt(8);
const amp = 24 + Utils.randInt(32);
const speed = 3 - randInt(8);
const amp = 24 + randInt(32);
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},

View file

@ -1,7 +1,7 @@
import { BattlerIndex, BattleType } from "#app/battle";
import { globalScene } from "#app/global-scene";
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/ability";
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
import { getCharVariantFromDialogue } from "#app/data/dialogue";
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -43,10 +43,10 @@ import { getNatureName } from "#app/data/nature";
export class EncounterPhase extends BattlePhase {
private loaded: boolean;
constructor(loaded?: boolean) {
constructor(loaded = false) {
super();
this.loaded = !!loaded;
this.loaded = loaded;
}
start() {
@ -259,6 +259,9 @@ export class EncounterPhase extends BattlePhase {
}
if (e < (battle.double ? 2 : 1)) {
if (battle.battleType === BattleType.WILD) {
for (const pokemon of globalScene.getField()) {
applyPreSummonAbAttrs(PreSummonAbAttr, pokemon, []);
}
globalScene.field.add(enemyPokemon);
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
const playerPokemon = globalScene.getPlayerPokemon();
@ -545,7 +548,7 @@ export class EncounterPhase extends BattlePhase {
const enemyField = globalScene.getEnemyField();
enemyField.forEach((enemyPokemon, e) => {
if (enemyPokemon.isShiny()) {
if (enemyPokemon.isShiny(true)) {
globalScene.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e));
}
/** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */

View file

@ -39,7 +39,7 @@ export class EnemyCommandPhase extends FieldPhase {
if (
battle.double &&
enemyPokemon.hasAbility(Abilities.COMMANDER) &&
enemyPokemon.getAlly().getTag(BattlerTagType.COMMANDED)
enemyPokemon.getAlly()?.getTag(BattlerTagType.COMMANDED)
) {
this.skipTurn = true;
}

View file

@ -5,7 +5,7 @@ import { globalScene } from "#app/global-scene";
import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import { FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import type EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
import * as Utils from "#app/utils";
import { fixedInt, getFrameMs, randInt } from "#app/utils";
import { Mode } from "#app/ui/ui";
import { cos, sin } from "#app/field/anims";
import type { PlayerPokemon } from "#app/field/pokemon";
@ -332,9 +332,9 @@ export class EvolutionPhase extends Phase {
() => this.end(),
null,
true,
Utils.fixedInt(4000),
fixedInt(4000),
);
globalScene.time.delayedCall(Utils.fixedInt(4250), () => globalScene.playBgm());
globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm());
});
});
};
@ -392,7 +392,7 @@ export class EvolutionPhase extends Phase {
globalScene.tweens.addCounter({
repeat: 64,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
if (f < 64) {
if (!(f & 7)) {
@ -411,7 +411,7 @@ export class EvolutionPhase extends Phase {
globalScene.tweens.addCounter({
repeat: 96,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
if (f < 96) {
if (f < 6) {
@ -461,7 +461,7 @@ export class EvolutionPhase extends Phase {
globalScene.tweens.addCounter({
repeat: 48,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
if (!f) {
for (let i = 0; i < 16; i++) {
@ -482,14 +482,14 @@ export class EvolutionPhase extends Phase {
globalScene.tweens.addCounter({
repeat: 48,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
if (!f) {
for (let i = 0; i < 8; i++) {
this.doSprayParticle(i);
}
} else if (f < 50) {
this.doSprayParticle(Utils.randInt(8));
this.doSprayParticle(randInt(8));
}
f++;
},
@ -506,7 +506,7 @@ export class EvolutionPhase extends Phase {
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
@ -543,7 +543,7 @@ export class EvolutionPhase extends Phase {
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
@ -575,7 +575,7 @@ export class EvolutionPhase extends Phase {
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
@ -605,12 +605,12 @@ export class EvolutionPhase extends Phase {
let f = 0;
let yOffset = 0;
const speed = 3 - Utils.randInt(8);
const amp = 48 + Utils.randInt(64);
const speed = 3 - randInt(8);
const amp = 48 + randInt(64);
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: Utils.getFrameMs(1),
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},

View file

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { ExpBoosterModifier } from "#app/modifier/modifier";
import i18next from "i18next";
import * as Utils from "#app/utils";
import { NumberHolder } from "#app/utils";
import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase";
import { LevelUpPhase } from "./level-up-phase";
@ -19,7 +19,7 @@ export class ExpPhase extends PlayerPartyMemberPokemonPhase {
super.start();
const pokemon = this.getPokemon();
const exp = new Utils.NumberHolder(this.expValue);
const exp = new NumberHolder(this.expValue);
globalScene.applyModifiers(ExpBoosterModifier, true, exp);
exp.value = Math.floor(exp.value);
globalScene.ui.showText(

View file

@ -209,8 +209,8 @@ export class FaintPhase extends PokemonPhase {
}
// in double battles redirect potential moves off fainted pokemon
if (globalScene.currentBattle.double) {
const allyPokemon = pokemon.getAlly();
const allyPokemon = pokemon.getAlly();
if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) {
globalScene.redirectPokemonMoves(pokemon, allyPokemon);
}

View file

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene";
import * as Utils from "../utils";
import { fixedInt } from "#app/utils";
import { achvs } from "../system/achv";
import type { SpeciesFormChange } from "../data/pokemon-forms";
import { getSpeciesFormChangeMessage } from "../data/pokemon-forms";
@ -9,7 +9,7 @@ import type PartyUiHandler from "../ui/party-ui-handler";
import { getPokemonNameWithAffix } from "../messages";
import { EndEvolutionPhase } from "./end-evolution-phase";
import { EvolutionPhase } from "./evolution-phase";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import { BattlerTagType } from "#enums/battler-tag-type";
import { SpeciesFormKey } from "#enums/species-form-key";
export class FormChangePhase extends EvolutionPhase {
@ -151,9 +151,9 @@ export class FormChangePhase extends EvolutionPhase {
() => this.end(),
null,
true,
Utils.fixedInt(delay),
fixedInt(delay),
);
globalScene.time.delayedCall(Utils.fixedInt(delay + 250), () =>
globalScene.time.delayedCall(fixedInt(delay + 250), () =>
globalScene.playBgm(),
);
});

View file

@ -20,7 +20,7 @@ import { UnlockPhase } from "#app/phases/unlock-phase";
import { achvs, ChallengeAchv } from "#app/system/achv";
import { Unlockables } from "#app/system/unlockables";
import { Mode } from "#app/ui/ui";
import * as Utils from "#app/utils";
import { isLocal, isLocalServerConnected } from "#app/utils";
import { PlayerGender } from "#enums/player-gender";
import { TrainerType } from "#enums/trainer-type";
import i18next from "i18next";
@ -45,6 +45,8 @@ export class GameOverPhase extends BattlePhase {
start() {
super.start();
globalScene.hideAbilityBar();
// Failsafe if players somehow skip floor 200 in classic mode
if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) {
this.isVictory = true;
@ -217,7 +219,7 @@ export class GameOverPhase extends BattlePhase {
/* Added a local check to see if the game is running offline
If Online, execute apiFetch as intended
If Offline, execute offlineNewClear() only for victory, a localStorage implementation of newClear daily run checks */
if (!Utils.isLocal || Utils.isLocalServerConnected) {
if (!isLocal || isLocalServerConnected) {
pokerogueApi.savedata.session
.newclear({
slot: globalScene.sessionSlotId,

View file

@ -1,27 +1,12 @@
import { globalScene } from "#app/global-scene";
import type { BattlerIndex } from "#app/battle";
import { PokemonPhase } from "./pokemon-phase";
export class HideAbilityPhase extends PokemonPhase {
private passive: boolean;
constructor(battlerIndex: BattlerIndex, passive = false) {
super(battlerIndex);
this.passive = passive;
}
import { Phase } from "#app/phase";
export class HideAbilityPhase extends Phase {
start() {
super.start();
const pokemon = this.getPokemon();
if (pokemon) {
globalScene.abilityBar.hide().then(() => {
this.end();
});
} else {
globalScene.abilityBar.hide().then(() => {
this.end();
}
});
}
}

View file

@ -71,6 +71,7 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
if (!this.pokemon.pauseEvolutions) {
const evolution = this.pokemon.getEvolution();
if (evolution) {
this.pokemon.breakIllusion()
globalScene.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel));
}
}

View file

@ -5,26 +5,26 @@ import { Phase } from "#app/phase";
import { handleTutorial, Tutorial } from "#app/tutorial";
import { Mode } from "#app/ui/ui";
import i18next, { t } from "i18next";
import * as Utils from "#app/utils";
import { getCookie, sessionIdKey, executeIf, removeCookie } from "#app/utils";
import { SelectGenderPhase } from "./select-gender-phase";
import { UnavailablePhase } from "./unavailable-phase";
export class LoginPhase extends Phase {
private showText: boolean;
constructor(showText?: boolean) {
constructor(showText = true) {
super();
this.showText = showText === undefined || !!showText;
this.showText = showText;
}
start(): void {
super.start();
const hasSession = !!Utils.getCookie(Utils.sessionIdKey);
const hasSession = !!getCookie(sessionIdKey);
globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] });
Utils.executeIf(bypassLogin || hasSession, updateUserInfo).then(response => {
executeIf(bypassLogin || hasSession, updateUserInfo).then(response => {
const success = response ? response[0] : false;
const statusCode = response ? response[1] : null;
if (!success) {
@ -38,7 +38,7 @@ export class LoginPhase extends Phase {
const loadData = () => {
updateUserInfo().then(success => {
if (!success[0]) {
Utils.removeCookie(Utils.sessionIdKey);
removeCookie(sessionIdKey);
globalScene.reset(true, true);
return;
}
@ -60,7 +60,7 @@ export class LoginPhase extends Phase {
globalScene.ui.playSelect();
updateUserInfo().then(success => {
if (!success[0]) {
Utils.removeCookie(Utils.sessionIdKey);
removeCookie(sessionIdKey);
globalScene.reset(true, true);
return;
}
@ -89,7 +89,7 @@ export class LoginPhase extends Phase {
],
});
} else if (statusCode === 401) {
Utils.removeCookie(Utils.sessionIdKey);
removeCookie(sessionIdKey);
globalScene.reset(true, true);
} else {
globalScene.unshiftPhase(new UnavailablePhase());

View file

@ -3,9 +3,9 @@ import { Phase } from "#app/phase";
export class MessagePhase extends Phase {
private text: string;
private callbackDelay: number | null;
private prompt: boolean | null;
private promptDelay: number | null;
private callbackDelay?: number | null;
private prompt?: boolean | null;
private promptDelay?: number | null;
private speaker?: string;
constructor(
@ -18,9 +18,9 @@ export class MessagePhase extends Phase {
super();
this.text = text;
this.callbackDelay = callbackDelay!; // TODO: is this bang correct?
this.prompt = prompt!; // TODO: is this bang correct?
this.promptDelay = promptDelay!; // TODO: is this bang correct?
this.callbackDelay = callbackDelay;
this.prompt = prompt;
this.promptDelay = promptDelay;
this.speaker = speaker;
}

View file

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { ArenaTagType } from "#app/enums/arena-tag-type";
import { MoneyMultiplierModifier } from "#app/modifier/modifier";
import i18next from "i18next";
import * as Utils from "#app/utils";
import { NumberHolder } from "#app/utils";
import { BattlePhase } from "./battle-phase";
export class MoneyRewardPhase extends BattlePhase {
@ -15,7 +15,7 @@ export class MoneyRewardPhase extends BattlePhase {
}
start() {
const moneyAmount = new Utils.NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);

View file

@ -1,50 +0,0 @@
import { globalScene } from "#app/global-scene";
import { initMoveAnim, loadMoveAnimAssets, MoveAnim } from "#app/data/battle-anims";
import { allMoves, SelfStatusMove } from "#app/data/moves/move";
import { Moves } from "#app/enums/moves";
import * as Utils from "#app/utils";
import { BattlePhase } from "./battle-phase";
export class MoveAnimTestPhase extends BattlePhase {
private moveQueue: Moves[];
constructor(moveQueue?: Moves[]) {
super();
this.moveQueue = moveQueue || Utils.getEnumValues(Moves).slice(1);
}
start() {
const moveQueue = this.moveQueue.slice(0);
this.playMoveAnim(moveQueue, true);
}
playMoveAnim(moveQueue: Moves[], player: boolean) {
const moveId = player ? moveQueue[0] : moveQueue.shift();
if (moveId === undefined) {
this.playMoveAnim(this.moveQueue.slice(0), true);
return;
}
if (player) {
console.log(Moves[moveId]);
}
initMoveAnim(moveId).then(() => {
loadMoveAnimAssets([moveId], true).then(() => {
const user = player ? globalScene.getPlayerPokemon()! : globalScene.getEnemyPokemon()!;
const target =
player !== allMoves[moveId] instanceof SelfStatusMove
? globalScene.getEnemyPokemon()!
: globalScene.getPlayerPokemon()!;
new MoveAnim(moveId, user, target.getBattlerIndex()).play(allMoves[moveId].hitsSubstitute(user, target), () => {
// TODO: are the bangs correct here?
if (player) {
this.playMoveAnim(moveQueue, false);
} else {
this.playMoveAnim(moveQueue, true);
}
});
});
});
}
}

View file

@ -26,6 +26,7 @@ import {
} from "#app/data/battler-tags";
import type { MoveAttr } from "#app/data/moves/move";
import {
AddArenaTrapTagAttr,
applyFilteredMoveAttrs,
applyMoveAttrs,
AttackMove,
@ -69,6 +70,7 @@ import type { Phase } from "#app/phase";
import { ShowAbilityPhase } from "./show-ability-phase";
import { MovePhase } from "./move-phase";
import { MoveEndPhase } from "./move-end-phase";
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
export class MoveEffectPhase extends PokemonPhase {
public move: PokemonMove;
@ -208,12 +210,12 @@ export class MoveEffectPhase extends PokemonPhase {
targets.some(t => t.hasAbilityWithAttr(ReflectStatusMoveAbAttr) || !!t.getTag(BattlerTagType.MAGIC_COAT));
/**
* If no targets are left for the move to hit (FAIL), or the invoked move is non-reflectable, single-target
* If no targets are left for the move to hit and it is not a hazard move (FAIL), or the invoked move is non-reflectable, single-target
* (and not random target) and failed the hit check against its target (MISS), log the move
* as FAILed or MISSed (depending on the conditions above) and end this phase.
*/
if (
!hasActiveTargets ||
(!hasActiveTargets && !move.hasAttr(AddArenaTrapTagAttr)) ||
(!mayBounce &&
!move.hasAttr(VariableTargetAttr) &&
!move.isMultiTarget() &&
@ -238,18 +240,28 @@ export class MoveEffectPhase extends PokemonPhase {
return this.end();
}
const playOnEmptyField = globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false;
// Move animation only needs one target
new MoveAnim(move.id as Moves, user, this.getFirstTarget()!.getBattlerIndex(), playOnEmptyField).play(
move.hitsSubstitute(user, this.getFirstTarget()!),
() => {
/** Has the move successfully hit a target (for damage) yet? */
let hasHit = false;
const playOnEmptyField =
(globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false) ||
(!hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr));
// Move animation only needs one target. The attacker is used as a fallback.
new MoveAnim(
move.id as Moves,
user,
this.getFirstTarget()?.getBattlerIndex() ?? BattlerIndex.ATTACKER,
playOnEmptyField,
).play(move.hitsSubstitute(user, this.getFirstTarget()!), () => {
/** Has the move successfully hit a target (for damage) yet? */
let hasHit = false;
// Prevent ENEMY_SIDE targeted moves from occurring twice in double battles
// and check which target will magic bounce.
const trueTargets: Pokemon[] =
move.moveTarget !== MoveTarget.ENEMY_SIDE
// Prevent ENEMY_SIDE targeted moves from occurring twice in double battles
// and check which target will magic bounce.
// In the event that the move is a hazard move, there may be no target and the move should still succeed.
// In this case, the user is used as the "target" to prevent a crash.
// This should not affect normal execution of the move otherwise.
const trueTargets: Pokemon[] =
!hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr)
? [user]
: move.moveTarget !== MoveTarget.ENEMY_SIDE
? targets
: (() => {
const magicCoatTargets = targets.filter(
@ -263,242 +275,243 @@ export class MoveEffectPhase extends PokemonPhase {
return [magicCoatTargets[0]];
})();
const queuedPhases: Phase[] = [];
for (const target of trueTargets) {
/** The {@linkcode ArenaTagSide} to which the target belongs */
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
/** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */
const hasConditionalProtectApplied = new BooleanHolder(false);
/** Does the applied conditional protection bypass Protect-ignoring effects? */
const bypassIgnoreProtect = new BooleanHolder(false);
/** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */
if (!this.move.getMove().isAllyTarget()) {
globalScene.arena.applyTagsForSide(
ConditionalProtectTag,
targetSide,
false,
hasConditionalProtectApplied,
user,
target,
move.id,
bypassIgnoreProtect,
);
}
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
const isProtected =
![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) &&
(bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) &&
(hasConditionalProtectApplied.value ||
(!target.findTags(t => t instanceof DamageProtectedTag).length &&
target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) ||
(this.move.getMove().category !== MoveCategory.STATUS &&
target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
/** Is the target hidden by the effects of its Commander ability? */
const isCommanding =
globalScene.currentBattle.double &&
target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target;
/** Is the target reflecting status moves from the magic coat move? */
const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT);
/** Is the target's magic bounce ability not ignored and able to reflect this move? */
const canMagicBounce =
!isReflecting &&
!move.checkFlag(MoveFlags.IGNORE_ABILITIES, user, target) &&
target.hasAbilityWithAttr(ReflectStatusMoveAbAttr);
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
/** Is the target reflecting the effect, not protected, and not in an semi-invulnerable state?*/
const willBounce =
!isProtected &&
!this.reflected &&
!isCommanding &&
move.hasFlag(MoveFlags.REFLECTABLE) &&
(isReflecting || canMagicBounce) &&
!semiInvulnerableTag;
// If the move will bounce, then queue the bounce and move on to the next target
if (!target.switchOutStatus && willBounce) {
const newTargets = move.isMultiTarget()
? getMoveTargets(target, move.id).targets
: [user.getBattlerIndex()];
if (!isReflecting) {
queuedPhases.push(
new ShowAbilityPhase(
target.getBattlerIndex(),
target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr),
),
);
}
queuedPhases.push(
new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true),
);
continue;
}
/** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */
const isImmune =
target.hasAbilityWithAttr(TypeImmunityAbAttr) &&
target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) &&
!semiInvulnerableTag;
/**
* If the move missed a target, stop all future hits against that target
* and move on to the next target (if there is one).
*/
if (
target.switchOutStatus ||
isCommanding ||
(!isImmune && !isProtected && !targetHitChecks[target.getBattlerIndex()])
) {
this.stopMultiHit(target);
if (!target.switchOutStatus) {
globalScene.queueMessage(
i18next.t("battle:attackMissed", {
pokemonNameWithAffix: getPokemonNameWithAffix(target),
}),
);
}
if (moveHistoryEntry.result === MoveResult.PENDING) {
moveHistoryEntry.result = MoveResult.MISS;
}
user.pushMoveHistory(moveHistoryEntry);
applyMoveAttrs(MissEffectAttr, user, null, move);
continue;
}
/** Does this phase represent the invoked move's first strike? */
const firstHit = user.turnData.hitsLeft === user.turnData.hitCount;
// Only log the move's result on the first strike
if (firstHit) {
user.pushMoveHistory(moveHistoryEntry);
}
/**
* Since all fail/miss checks have applied, the move is considered successfully applied.
* It's worth noting that if the move has no effect or is protected against, this assignment
* is overwritten and the move is logged as a FAIL.
*/
moveHistoryEntry.result = MoveResult.SUCCESS;
/**
* Stores the result of applying the invoked move to the target.
* If the target is protected, the result is always `NO_EFFECT`.
* Otherwise, the hit result is based on type effectiveness, immunities,
* and other factors that may negate the attack or status application.
*
* Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated
* (for attack moves) and the target's HP is updated. However, this isn't
* made visible to the user until the resulting {@linkcode DamagePhase}
* is invoked.
*/
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
const dealsDamage = [
HitResult.EFFECTIVE,
HitResult.SUPER_EFFECTIVE,
HitResult.NOT_VERY_EFFECTIVE,
HitResult.ONE_HIT_KO,
].includes(hitResult);
/** Is this target the first one hit by the move on its current strike? */
const firstTarget = dealsDamage && !hasHit;
if (firstTarget) {
hasHit = true;
}
/**
* If the move has no effect on the target (i.e. the target is protected or immune),
* change the logged move result to FAIL.
*/
if (hitResult === HitResult.NO_EFFECT) {
moveHistoryEntry.result = MoveResult.FAIL;
}
/** Does this phase represent the invoked move's last strike? */
const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive();
/**
* If the user can change forms by using the invoked move,
* it only changes forms after the move's last hit
* (see Relic Song's interaction with Parental Bond when used by Meloetta).
*/
if (lastHit) {
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
/**
* Multi-Lens, Multi Hit move and Parental Bond check for PostDamageAbAttr
* other damage source are calculated in damageAndUpdate in pokemon.ts
*/
if (user.turnData.hitCount > 1) {
applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user);
}
}
applyFilteredMoveAttrs(
(attr: MoveAttr) =>
attr instanceof MoveEffectAttr &&
attr.trigger === MoveEffectTrigger.PRE_APPLY &&
(!attr.firstHitOnly || firstHit) &&
(!attr.lastHitOnly || lastHit) &&
hitResult !== HitResult.NO_EFFECT,
const queuedPhases: Phase[] = [];
for (const target of trueTargets) {
/** The {@linkcode ArenaTagSide} to which the target belongs */
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
/** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */
const hasConditionalProtectApplied = new BooleanHolder(false);
/** Does the applied conditional protection bypass Protect-ignoring effects? */
const bypassIgnoreProtect = new BooleanHolder(false);
/** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */
if (!this.move.getMove().isAllyTarget()) {
globalScene.arena.applyTagsForSide(
ConditionalProtectTag,
targetSide,
false,
hasConditionalProtectApplied,
user,
target,
move,
move.id,
bypassIgnoreProtect,
);
}
if (hitResult !== HitResult.FAIL) {
this.applySelfTargetEffects(user, target, firstHit, lastHit);
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
const isProtected =
![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) &&
(bypassIgnoreProtect.value ||
!this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) &&
(hasConditionalProtectApplied.value ||
(!target.findTags(t => t instanceof DamageProtectedTag).length &&
target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) ||
(this.move.getMove().category !== MoveCategory.STATUS &&
target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
if (hitResult !== HitResult.NO_EFFECT) {
this.applyPostApplyEffects(user, target, firstHit, lastHit);
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget);
} else {
applyMoveAttrs(NoEffectAttr, user, null, move);
}
/** Is the target hidden by the effects of its Commander ability? */
const isCommanding =
globalScene.currentBattle.double &&
target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target;
/** Is the target reflecting status moves from the magic coat move? */
const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT);
/** Is the target's magic bounce ability not ignored and able to reflect this move? */
const canMagicBounce =
!isReflecting &&
!move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) &&
target.hasAbilityWithAttr(ReflectStatusMoveAbAttr);
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
/** Is the target reflecting the effect, not protected, and not in an semi-invulnerable state?*/
const willBounce =
!isProtected &&
!this.reflected &&
!isCommanding &&
move.hasFlag(MoveFlags.REFLECTABLE) &&
(isReflecting || canMagicBounce) &&
!semiInvulnerableTag;
// If the move will bounce, then queue the bounce and move on to the next target
if (!target.switchOutStatus && willBounce) {
const newTargets = move.isMultiTarget() ? getMoveTargets(target, move.id).targets : [user.getBattlerIndex()];
if (!isReflecting) {
// TODO: Ability displays should be handled by the ability
queuedPhases.push(
new ShowAbilityPhase(
target.getBattlerIndex(),
target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr),
),
);
queuedPhases.push(new HideAbilityPhase());
}
queuedPhases.push(new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true));
continue;
}
// Apply queued phases
if (queuedPhases.length) {
globalScene.appendToPhase(queuedPhases, MoveEndPhase);
/** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */
const isImmune =
target.hasAbilityWithAttr(TypeImmunityAbAttr) &&
target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) &&
!semiInvulnerableTag;
/**
* If the move missed a target, stop all future hits against that target
* and move on to the next target (if there is one).
*/
if (
target.switchOutStatus ||
isCommanding ||
(!isImmune &&
!isProtected &&
!targetHitChecks[target.getBattlerIndex()] &&
!move.hasAttr(AddArenaTrapTagAttr))
) {
this.stopMultiHit(target);
if (!target.switchOutStatus) {
globalScene.queueMessage(
i18next.t("battle:attackMissed", {
pokemonNameWithAffix: getPokemonNameWithAffix(target),
}),
);
}
if (moveHistoryEntry.result === MoveResult.PENDING) {
moveHistoryEntry.result = MoveResult.MISS;
}
user.pushMoveHistory(moveHistoryEntry);
applyMoveAttrs(MissEffectAttr, user, null, move);
continue;
}
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) {
applyFilteredMoveAttrs(
(attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET,
user,
null,
move,
);
/** Does this phase represent the invoked move's first strike? */
const firstHit = user.turnData.hitsLeft === user.turnData.hitCount;
// Only log the move's result on the first strike
if (firstHit) {
user.pushMoveHistory(moveHistoryEntry);
}
/**
* Remove the target's substitute (if it exists and has expired)
* after all targeted effects have applied.
* This prevents blocked effects from applying until after this hit resolves.
* Since all fail/miss checks have applied, the move is considered successfully applied.
* It's worth noting that if the move has no effect or is protected against, this assignment
* is overwritten and the move is logged as a FAIL.
*/
targets.forEach(target => {
const substitute = target.getTag(SubstituteTag);
if (substitute && substitute.hp <= 0) {
target.lapseTag(BattlerTagType.SUBSTITUTE);
}
});
moveHistoryEntry.result = MoveResult.SUCCESS;
const moveType = user.getMoveType(move, true);
if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
user.stellarTypesBoosted.push(moveType);
/**
* Stores the result of applying the invoked move to the target.
* If the target is protected, the result is always `NO_EFFECT`.
* Otherwise, the hit result is based on type effectiveness, immunities,
* and other factors that may negate the attack or status application.
*
* Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated
* (for attack moves) and the target's HP is updated. However, this isn't
* made visible to the user until the resulting {@linkcode DamagePhase}
* is invoked.
*/
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
const dealsDamage = [
HitResult.EFFECTIVE,
HitResult.SUPER_EFFECTIVE,
HitResult.NOT_VERY_EFFECTIVE,
HitResult.ONE_HIT_KO,
].includes(hitResult);
/** Is this target the first one hit by the move on its current strike? */
const firstTarget = dealsDamage && !hasHit;
if (firstTarget) {
hasHit = true;
}
this.end();
},
);
/**
* If the move has no effect on the target (i.e. the target is protected or immune),
* change the logged move result to FAIL.
*/
if (hitResult === HitResult.NO_EFFECT) {
moveHistoryEntry.result = MoveResult.FAIL;
}
/** Does this phase represent the invoked move's last strike? */
const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive();
/**
* If the user can change forms by using the invoked move,
* it only changes forms after the move's last hit
* (see Relic Song's interaction with Parental Bond when used by Meloetta).
*/
if (lastHit) {
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
/**
* Multi-Lens, Multi Hit move and Parental Bond check for PostDamageAbAttr
* other damage source are calculated in damageAndUpdate in pokemon.ts
*/
if (user.turnData.hitCount > 1) {
applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user);
}
}
applyFilteredMoveAttrs(
(attr: MoveAttr) =>
attr instanceof MoveEffectAttr &&
attr.trigger === MoveEffectTrigger.PRE_APPLY &&
(!attr.firstHitOnly || firstHit) &&
(!attr.lastHitOnly || lastHit) &&
hitResult !== HitResult.NO_EFFECT,
user,
target,
move,
);
if (hitResult !== HitResult.FAIL) {
this.applySelfTargetEffects(user, target, firstHit, lastHit);
if (hitResult !== HitResult.NO_EFFECT) {
this.applyPostApplyEffects(user, target, firstHit, lastHit);
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget);
} else {
applyMoveAttrs(NoEffectAttr, user, null, move);
}
}
}
// Apply queued phases
if (queuedPhases.length) {
globalScene.appendToPhase(queuedPhases, MoveEndPhase);
}
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) {
applyFilteredMoveAttrs(
(attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET,
user,
null,
move,
);
}
/**
* Remove the target's substitute (if it exists and has expired)
* after all targeted effects have applied.
* This prevents blocked effects from applying until after this hit resolves.
*/
targets.forEach(target => {
const substitute = target.getTag(SubstituteTag);
if (substitute && substitute.hp <= 0) {
target.lapseTag(BattlerTagType.SUBSTITUTE);
}
});
const moveType = user.getMoveType(move, true);
if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
user.stellarTypesBoosted.push(moveType);
}
this.end();
});
}
public override end(): void {

View file

@ -1,18 +1,38 @@
import { globalScene } from "#app/global-scene";
import { BattlerTagLapseType } from "#app/data/battler-tags";
import { PokemonPhase } from "./pokemon-phase";
import type { BattlerIndex } from "#app/battle";
import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/ability";
import type Pokemon from "#app/field/pokemon";
export class MoveEndPhase extends PokemonPhase {
private wasFollowUp: boolean;
/** Targets from the preceding MovePhase */
private targets: Pokemon[];
constructor(battlerIndex: BattlerIndex, targets: Pokemon[], wasFollowUp = false) {
super(battlerIndex);
this.targets = targets;
this.wasFollowUp = wasFollowUp;
}
start() {
super.start();
const pokemon = this.getPokemon();
if (pokemon.isActive(true)) {
if (!this.wasFollowUp && pokemon?.isActive(true)) {
pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
}
globalScene.arena.setIgnoreAbilities(false);
// Remove effects which were set on a Pokemon which removes them on summon (i.e. via Mold Breaker)
for (const target of this.targets) {
if (target) {
applyPostSummonAbAttrs(PostSummonRemoveEffectAbAttr, target);
}
}
this.end();
}
}

View file

@ -15,6 +15,7 @@ import type { DelayedAttackTag } from "#app/data/arena-tag";
import { CommonAnim } from "#app/data/battle-anims";
import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags";
import {
AddArenaTrapTagAttr,
allMoves,
applyMoveAttrs,
BypassRedirectAttr,
@ -42,7 +43,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { MoveChargePhase } from "#app/phases/move-charge-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MoveEndPhase } from "#app/phases/move-end-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { NumberHolder } from "#app/utils";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
@ -169,7 +169,11 @@ export class MovePhase extends BattlePhase {
// Check move to see if arena.ignoreAbilities should be true.
if (!this.followUp || this.reflected) {
if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) {
if (
this.move
.getMove()
.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })
) {
globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
}
}
@ -202,7 +206,10 @@ export class MovePhase extends BattlePhase {
const targets = this.getActiveTargetPokemon();
const moveQueue = this.pokemon.getMoveQueue();
if (targets.length === 0 || (moveQueue.length && moveQueue[0].move === Moves.NONE)) {
if (
(targets.length === 0 && !this.move.getMove().hasAttr(AddArenaTrapTagAttr)) ||
(moveQueue.length && moveQueue[0].move === Moves.NONE)
) {
this.showMoveText();
this.showFailedText();
this.cancel();
@ -228,7 +235,7 @@ export class MovePhase extends BattlePhase {
(!this.pokemon.randSeedInt(4) || Overrides.STATUS_ACTIVATION_OVERRIDE === true) &&
Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
break;
case StatusEffect.SLEEP:
case StatusEffect.SLEEP: {
applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove());
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
applyAbAttrs(
@ -243,6 +250,7 @@ export class MovePhase extends BattlePhase {
healed = this.pokemon.status.sleepTurnsRemaining <= 0;
activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP);
break;
}
case StatusEffect.FREEZE:
healed =
!!this.move
@ -466,13 +474,12 @@ export class MovePhase extends BattlePhase {
}
/**
* Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`,
* then ends the phase.
* Queues a {@linkcode MoveEndPhase} and then ends the phase
*/
public end(): void {
if (!this.followUp && this.canMove()) {
globalScene.unshiftPhase(new MoveEndPhase(this.pokemon.getBattlerIndex()));
}
globalScene.unshiftPhase(
new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), this.followUp),
);
super.end();
}
@ -535,11 +542,16 @@ export class MovePhase extends BattlePhase {
if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) {
redirectTarget.value = currentTarget;
globalScene.unshiftPhase(
new ShowAbilityPhase(
this.pokemon.getBattlerIndex(),
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
),
// TODO: Ability displays should be handled by the ability
globalScene.queueAbilityDisplay(
this.pokemon,
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
true,
);
globalScene.queueAbilityDisplay(
this.pokemon,
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
false,
);
}

View file

@ -26,8 +26,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { IvScannerModifier } from "../modifier/modifier";
import { Phase } from "../phase";
import { Mode } from "../ui/ui";
import * as Utils from "../utils";
import { isNullOrUndefined } from "../utils";
import { isNullOrUndefined, randSeedItem } from "#app/utils";
/**
* Will handle (in order):
@ -387,7 +386,7 @@ export class MysteryEncounterBattlePhase extends Phase {
const trainer = globalScene.currentBattle.trainer;
let message: string;
globalScene.executeWithSeedOffset(
() => (message = Utils.randSeedItem(encounterMessages)),
() => (message = randSeedItem(encounterMessages)),
globalScene.currentBattle.mysteryEncounter?.getSeedOffset(),
);
message = message!; // tell TS compiler it's defined now

View file

@ -5,6 +5,11 @@ export class NewBattlePhase extends BattlePhase {
start() {
super.start();
// cull any extra `NewBattle` phases from the queue.
globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !(phase instanceof NewBattlePhase));
// `phaseQueuePrepend` is private, so we have to use this inefficient loop.
while (globalScene.tryRemoveUnshiftedPhase(phase => phase instanceof NewBattlePhase)) {}
globalScene.newBattle();
this.end();

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