diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 23c4a0a2159..eb270dc8f80 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: Bug Report description: Create a report to help us improve title: "[Bug] " -labels: ["Bug"] +labels: ["Bug", "Triage"] body: - type: markdown attributes: @@ -19,21 +19,12 @@ body: value: | --- - type: textarea - id: session-file + id: repro attributes: - label: Session export file - description: Open Menu → ManageData → Export Session → Select slot. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)). - placeholder: Focus me and then drop your file here (or use the upload button at the bottom) + label: Reproduction + description: Describe the steps to reproduce this bug. If applicable attach user/session data at the bottom validations: - required: false - - type: textarea - id: data-file - attributes: - label: User data export file - description: Open Menu → ManageData → Export Data. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)). - placeholder: Focus me and then drop your file here (or use the upload button at the bottom) - validations: - required: false + required: true - type: markdown attributes: value: | @@ -60,48 +51,20 @@ body: attributes: value: | --- - - type: dropdown - id: os + - type: textarea + id: session-file attributes: - label: What OS did you observe the bug on? - multiple: true - options: - - PC/Windows - - Mac/OSX - - Linux - - iOS - - Android - - Other - validations: - required: true - - type: input - id: os-other - attributes: - label: If other please specify + label: Session export file + description: Open Menu → ManageData → Export Session → Select slot. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)). + placeholder: Focus me and then drop your file here (or use the upload button at the bottom) validations: required: false - - type: markdown + - type: textarea + id: data-file attributes: - value: | - --- - - type: dropdown - id: browser - attributes: - label: Which browser do you use? - multiple: true - options: - - Chrome - - Firefox - - Safari - - Edge - - Opera - - Other - validations: - required: true - - type: input - id: browser-other - attributes: - label: If other please specify + label: User data export file + description: Open Menu → ManageData → Export Data. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)). + placeholder: Focus me and then drop your file here (or use the upload button at the bottom) validations: required: false - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 843f7feab59..29a79b38158 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: Feature Request description: Suggest an idea for this project title: "[Feature] " -labels: ["Enhancement"] +labels: ["Enhancement", "Triage"] body: - type: markdown attributes: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3e6b8bf6d0d..a30cb642a46 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -30,7 +30,7 @@ - [ ] The PR is self-contained and cannot be split into smaller PRs? - [ ] Have I provided a clear explanation of the changes? - [ ] Have I considered writing automated tests for the issue? -- [ ] If I have text, did I add make it translatable and added a key in the English language? +- [ ] If I have text, did I make it translatable and add a key in the English locale file(s)? - [ ] Have I tested the changes (manually)? - [ ] Are all unit tests still passing? (`npm run test`) - [ ] Are the changes visual? diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 9068f1ae9a2..2850418bc59 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -11,6 +11,8 @@ on: branches: - main # Trigger on pull request events targeting the main branch - beta # Trigger on pull request events targeting the beta branch + merge_group: + types: [checks_requested] jobs: run-linters: # Define a job named "run-linters" diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 3b7617c45f4..a092ccb425a 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -8,6 +8,8 @@ on: branches: - main - beta + merge_group: + types: [checks_requested] jobs: pages: diff --git a/.github/workflows/test-shard-template.yml b/.github/workflows/test-shard-template.yml new file mode 100644 index 00000000000..ac89b503f0c --- /dev/null +++ b/.github/workflows/test-shard-template.yml @@ -0,0 +1,30 @@ +name: Test Template + +on: + workflow_call: + inputs: + project: + required: true + type: string + shard: + required: true + type: number + totalShards: + required: true + type: number + +jobs: + test: + name: Shard ${{ inputs.shard }} of ${{ inputs.totalShards }} + runs-on: ubuntu-latest + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Node.js dependencies + run: npm ci + - name: Run tests + run: npx vitest --project ${{ inputs.project }} --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce1d1c5038..66cc3ecc139 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,23 +11,37 @@ on: branches: - main # Trigger on pull request events targeting the main branch - beta # Trigger on pull request events targeting the beta branch + merge_group: + types: [checks_requested] jobs: - run-tests: # Define a job named "run-tests" - name: Run tests # Human-readable name for the job - runs-on: ubuntu-latest # Specify the latest Ubuntu runner for the job - + pre-test: + name: Run Pre-test + runs-on: ubuntu-latest steps: - - name: Check out Git repository # Step to check out the repository - uses: actions/checkout@v4 # Use the checkout action version 4 - - - name: Set up Node.js # Step to set up Node.js environment - uses: actions/setup-node@v4 # Use the setup-node action version 4 + - name: Check out Git repository + uses: actions/checkout@v4 with: - node-version: 20 # Specify Node.js version 20 + path: tests-action + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Node.js dependencies + working-directory: tests-action + run: npm ci + - name: Run Pre-test + working-directory: tests-action + run: npx vitest run --project pre ${{ !runner.debug && '--silent' || '' }} - - name: Install Node.js dependencies # Step to install Node.js dependencies - run: npm ci # Use 'npm ci' to install dependencies - - - name: tests # Step to run tests - run: npm run test:silent \ No newline at end of file + run-tests: + name: Run Tests + needs: [pre-test] + strategy: + matrix: + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + uses: ./.github/workflows/test-shard-template.yml + with: + project: main + shard: ${{ matrix.shard }} + totalShards: 10 \ No newline at end of file diff --git a/create-test-boilerplate.js b/create-test-boilerplate.js new file mode 100644 index 00000000000..d9cdbd4e7cf --- /dev/null +++ b/create-test-boilerplate.js @@ -0,0 +1,110 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +/** + * This script creates a test boilerplate file for a move or ability. + * @param {string} type - The type of test to create. Either "move", "ability", + * or "item". + * @param {string} fileName - The name of the file to create. + * @example npm run create-test move tackle + */ + +// Get the directory name of the current module file +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Get the arguments from the command line +const args = process.argv.slice(2); +const type = args[0]; // "move" or "ability" +let fileName = args[1]; // The file name + +if (!type || !fileName) { + console.error('Please provide a type ("move", "ability", or "item") and a file name.'); + process.exit(1); +} + +// Convert fileName from kebab-case or camelCase to snake_case +fileName = fileName + .replace(/-+/g, '_') // Convert kebab-case (dashes) to underscores + .replace(/([a-z])([A-Z])/g, '$1_$2') // Convert camelCase to snake_case + .toLowerCase(); // Ensure all lowercase + +// Format the description for the test case +const formattedName = fileName + .replace(/_/g, ' ') + .replace(/\b\w/g, char => char.toUpperCase()); + +// Determine the directory based on the type +let dir; +let description; +if (type === 'move') { + dir = path.join(__dirname, 'src', 'test', 'moves'); + description = `Moves - ${formattedName}`; +} else if (type === 'ability') { + dir = path.join(__dirname, 'src', 'test', 'abilities'); + description = `Abilities - ${formattedName}`; +} else if (type === "item") { + dir = path.join(__dirname, 'src', 'test', 'items'); + description = `Items - ${formattedName}`; +} else { + console.error('Invalid type. Please use "move", "ability", or "item".'); + process.exit(1); +} + +// Ensure the directory exists +if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); +} + +// Create the file with the given name +const filePath = path.join(dir, `${fileName}.test.ts`); + +if (fs.existsSync(filePath)) { + console.error(`File "${fileName}.test.ts" already exists.`); + process.exit(1); +} + +// Define the content template +const content = `import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; + +describe("${description}", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH]) + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("test case", async () => { + // await game.classicMode.startBattle([Species.MAGIKARP]); + // game.move.select(Moves.SPLASH); + }, TIMEOUT); +}); +`; + +// Write the template content to the file +fs.writeFileSync(filePath, content, 'utf8'); + +console.log(`File created at: ${filePath}`); diff --git a/docs/enemy-ai.md b/docs/enemy-ai.md index f53a8511893..46482f56a90 100644 --- a/docs/enemy-ai.md +++ b/docs/enemy-ai.md @@ -191,15 +191,15 @@ Now that the enemy Pokémon with the best matchup score is on the field (assumin We then need to apply a 2x multiplier for the move's type effectiveness and a 1.5x multiplier since STAB applies. After applying these multipliers, the final score for this move is **75**. -- **Swords Dance**: As a non-attacking move, this move's benefit score is derived entirely from the sum of its attributes' benefit scores. Swords Dance's `StatChangeAttr` has a user benefit score of 0 and a target benefit score that, in this case, simplifies to +- **Swords Dance**: As a non-attacking move, this move's benefit score is derived entirely from the sum of its attributes' benefit scores. Swords Dance's `StatStageChangeAttr` has a user benefit score of 0 and a target benefit score that, in this case, simplifies to $\text{TBS}=4\times \text{levels} + (-2\times \text{sign(levels)})$ where `levels` is the number of stat stages added by the attribute (in this case, +2). The final score for this move is **6** (Note: because this move is self-targeted, we don't flip the sign of TBS when computing the target score). -- **Crush Claw**: This move is a 75-power Normal-type physical attack with a 50 percent chance to lower the target's Defense by one stage. The additional effect is implemented by the same `StatChangeAttr` as Swords Dance, so we can use the same formulas from before to compute the total TBS and base target score. +- **Crush Claw**: This move is a 75-power Normal-type physical attack with a 50 percent chance to lower the target's Defense by one stage. The additional effect is implemented by the same `StatStageChangeAttr` as Swords Dance, so we can use the same formulas from before to compute the total TBS and base target score. - $\text{TBS}=\text{getTargetBenefitScore(StatChangeAttr)}-\text{attackScore}$ + $\text{TBS}=\text{getTargetBenefitScore(StatStageChangeAttr)}-\text{attackScore}$ $\text{TBS}=(-4 + 2)-(-2\times 2 + \lfloor \frac{75}{5} \rfloor)=-2-11=-13$ @@ -221,4 +221,4 @@ When implementing a new move attribute, it's important to override `MoveAttr`'s - A move's **user benefit score (UBS)** incentivizes (or discourages) the move's usage in general. A positive UBS gives the move more incentive to be used, while a negative UBS gives the move less incentive. - A move's **target benefit score (TBS)** incentivizes (or discourages) the move's usage on a specific target. A positive TBS indicates the move is better used on the user or its allies, while a negative TBS indicates the move is better used on enemies. - **The total benefit score (UBS + TBS) of a move should never be 0.** The move selection algorithm assumes the move's benefit score is unimplemented if the total score is 0 and penalizes the move's usage as a result. With status moves especially, it's important to have some form of implementation among the move's attributes to avoid this scenario. -- **Score functions that use formulas should include comments.** If your attribute requires complex logic or formulas to calculate benefit scores, please add comments to explain how the logic works and its intended effect on the enemy's decision making. \ No newline at end of file +- **Score functions that use formulas should include comments.** If your attribute requires complex logic or formulas to calculate benefit scores, please add comments to explain how the logic works and its intended effect on the enemy's decision making. diff --git a/eslint.config.js b/eslint.config.js index eeea38e3178..80e9e67b525 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,7 @@ import tseslint from '@typescript-eslint/eslint-plugin'; import stylisticTs from '@stylistic/eslint-plugin-ts' import parser from '@typescript-eslint/parser'; -// import imports from 'eslint-plugin-import'; // Disabled due to not being compatible with eslint v9 +import importX from 'eslint-plugin-import-x'; export default [ { @@ -11,7 +11,7 @@ export default [ parser: parser }, plugins: { - // imports: imports.configs.recommended // Disabled due to not being compatible with eslint v9 + "import-x": importX, '@stylistic/ts': stylisticTs, '@typescript-eslint': tseslint }, @@ -39,7 +39,8 @@ export default [ }], "space-before-blocks": ["error", "always"], // Enforces a space before blocks "keyword-spacing": ["error", { "before": true, "after": true }], // Enforces spacing before and after keywords - "comma-spacing": ["error", { "before": false, "after": true }] // Enforces spacing after comma + "comma-spacing": ["error", { "before": false, "after": true }], // Enforces spacing after comma + "import-x/extensions": ["error", "never", { "json": "always" }], // Enforces no extension for imports unless json } } ] diff --git a/index.css b/index.css index abf4f9f708c..1274f2fcead 100644 --- a/index.css +++ b/index.css @@ -23,15 +23,6 @@ body { } } -#links { - width: 90%; - text-align: center; - position: fixed; - bottom: 0; - display: flex; - justify-content: space-around; -} - #app { display: flex; justify-content: center; @@ -93,7 +84,7 @@ input:-internal-autofill-selected { @media (orientation: landscape) { #touchControls { - --controls-size: 20vh; + --controls-size: 20vh; --text-shadow-size: 1.3vh; --small-button-offset: 4vh; } diff --git a/index.html b/index.html index 29b4c0d1a6e..390a29fb365 100644 --- a/index.html +++ b/index.html @@ -39,7 +39,6 @@ - diff --git a/lefthook.yml b/lefthook.yml index 01ffebc69c7..662d3b5617b 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -2,6 +2,15 @@ pre-commit: parallel: true commands: eslint: - glob: '*.{js,jsx,ts,tsx}' + glob: "*.{js,jsx,ts,tsx}" run: npx eslint --fix {staged_files} - stage_fixed: true \ No newline at end of file + stage_fixed: true + skip: + - merge + - rebase + +pre-push: + commands: + eslint: + glob: "*.{js,ts,jsx,tsx}" + run: npx eslint --fix {push_files} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0605b299dab..4a447554819 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@vitest/coverage-istanbul": "^2.0.4", "dependency-cruiser": "^16.3.10", "eslint": "^9.7.0", + "eslint-plugin-import-x": "^4.2.1", "jsdom": "^24.0.0", "lefthook": "^1.6.12", "phaser3spectorjs": "^0.0.8", @@ -2505,6 +2506,19 @@ "node": ">=8" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2687,6 +2701,155 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import-x": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.2.1.tgz", + "integrity": "sha512-WWi2GedccIJa0zXxx3WDnTgouGQTtdYK1nhXMwywbqqAgB0Ov+p1pYBsWh3VaB0bvBOwLse6OfVII7jZD9xo5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.1.0", + "debug": "^4.3.4", + "doctrine": "^3.0.0", + "eslint-import-resolver-node": "^0.3.9", + "get-tsconfig": "^4.7.3", + "is-glob": "^4.0.3", + "minimatch": "^9.0.3", + "semver": "^7.6.3", + "stable-hash": "^0.0.4", + "tslib": "^2.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-import-x/node_modules/@typescript-eslint/scope-manager": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", + "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-import-x/node_modules/@typescript-eslint/types": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", + "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-import-x/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", + "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-import-x/node_modules/@typescript-eslint/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/typescript-estree": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-import-x/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", + "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/eslint-scope": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", @@ -3143,6 +3306,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4854,6 +5030,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5069,6 +5255,13 @@ "node": ">=0.10.0" } }, + "node_modules/stable-hash": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", + "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "dev": true, + "license": "MIT" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -5460,6 +5653,13 @@ "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index b85ac639a1b..dddf5aedebd 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "eslint-ci": "eslint .", "docs": "typedoc", "depcruise": "depcruise src", - "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg" + "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", + "create-test": "node ./create-test-boilerplate.js" }, "devDependencies": { "@eslint/js": "^9.3.0", @@ -31,6 +32,7 @@ "@vitest/coverage-istanbul": "^2.0.4", "dependency-cruiser": "^16.3.10", "eslint": "^9.7.0", + "eslint-plugin-import-x": "^4.2.1", "jsdom": "^24.0.0", "lefthook": "^1.6.12", "phaser3spectorjs": "^0.0.8", diff --git a/public/images/events/egg-update_de.png b/public/images/events/egg-update_de.png new file mode 100644 index 00000000000..5de94877d5c Binary files /dev/null and b/public/images/events/egg-update_de.png differ diff --git a/public/images/events/egg-update_en.png b/public/images/events/egg-update_en.png new file mode 100644 index 00000000000..7104d340ca0 Binary files /dev/null and b/public/images/events/egg-update_en.png differ diff --git a/public/images/events/egg-update_es.png b/public/images/events/egg-update_es.png new file mode 100644 index 00000000000..ec5f5c46d17 Binary files /dev/null and b/public/images/events/egg-update_es.png differ diff --git a/public/images/events/egg-update_fr.png b/public/images/events/egg-update_fr.png new file mode 100644 index 00000000000..e0505fa96dd Binary files /dev/null and b/public/images/events/egg-update_fr.png differ diff --git a/public/images/events/egg-update_it.png b/public/images/events/egg-update_it.png new file mode 100644 index 00000000000..fc347bce9cf Binary files /dev/null and b/public/images/events/egg-update_it.png differ diff --git a/public/images/events/egg-update_ja.png b/public/images/events/egg-update_ja.png new file mode 100644 index 00000000000..2259cbb4d9a Binary files /dev/null and b/public/images/events/egg-update_ja.png differ diff --git a/public/images/events/egg-update_ko.png b/public/images/events/egg-update_ko.png new file mode 100644 index 00000000000..99dcc662402 Binary files /dev/null and b/public/images/events/egg-update_ko.png differ diff --git a/public/images/events/egg-update_pt-BR.png b/public/images/events/egg-update_pt-BR.png new file mode 100644 index 00000000000..ee347d35654 Binary files /dev/null and b/public/images/events/egg-update_pt-BR.png differ diff --git a/public/images/events/egg-update_zh-CN.png b/public/images/events/egg-update_zh-CN.png new file mode 100644 index 00000000000..02d780fab89 Binary files /dev/null and b/public/images/events/egg-update_zh-CN.png differ diff --git a/public/images/events/september-update-de.png b/public/images/events/september-update-de.png new file mode 100644 index 00000000000..1ecb46e408c Binary files /dev/null and b/public/images/events/september-update-de.png differ diff --git a/public/images/events/september-update-en.png b/public/images/events/september-update-en.png new file mode 100644 index 00000000000..57dd130b98d Binary files /dev/null and b/public/images/events/september-update-en.png differ diff --git a/public/images/events/september-update-es.png b/public/images/events/september-update-es.png new file mode 100644 index 00000000000..8c294d21403 Binary files /dev/null and b/public/images/events/september-update-es.png differ diff --git a/public/images/events/september-update-fr.png b/public/images/events/september-update-fr.png new file mode 100644 index 00000000000..4be33c85e9a Binary files /dev/null and b/public/images/events/september-update-fr.png differ diff --git a/public/images/events/september-update-it.png b/public/images/events/september-update-it.png new file mode 100644 index 00000000000..62542f4eb9b Binary files /dev/null and b/public/images/events/september-update-it.png differ diff --git a/public/images/events/september-update-ja.png b/public/images/events/september-update-ja.png new file mode 100644 index 00000000000..93e18c51223 Binary files /dev/null and b/public/images/events/september-update-ja.png differ diff --git a/public/images/events/september-update-ko.png b/public/images/events/september-update-ko.png new file mode 100644 index 00000000000..13585327fce Binary files /dev/null and b/public/images/events/september-update-ko.png differ diff --git a/public/images/events/september-update-pt-BR.png b/public/images/events/september-update-pt-BR.png new file mode 100644 index 00000000000..8dd8b8759e9 Binary files /dev/null and b/public/images/events/september-update-pt-BR.png differ diff --git a/public/images/events/september-update-zh-CN.png b/public/images/events/september-update-zh-CN.png new file mode 100644 index 00000000000..ee56d644d24 Binary files /dev/null and b/public/images/events/september-update-zh-CN.png differ diff --git a/public/images/inputs/keyboard.json b/public/images/inputs/keyboard.json index b1902df10d6..c9b3c79fbfb 100644 --- a/public/images/inputs/keyboard.json +++ b/public/images/inputs/keyboard.json @@ -1,596 +1,529 @@ -{"frames": [ - -{ - "filename": "0.png", - "frame": {"x":0,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "1.png", - "frame": {"x":12,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "2.png", - "frame": {"x":24,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "3.png", - "frame": {"x":36,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "4.png", - "frame": {"x":48,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "5.png", - "frame": {"x":60,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "6.png", - "frame": {"x":72,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "7.png", - "frame": {"x":84,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "8.png", - "frame": {"x":96,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "9.png", - "frame": {"x":108,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "A.png", - "frame": {"x":120,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "ALT.png", - "frame": {"x":132,"y":0,"w":16,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":16,"h":12}, - "sourceSize": {"w":16,"h":12} -}, -{ - "filename": "B.png", - "frame": {"x":148,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "BACK.png", - "frame": {"x":160,"y":0,"w":24,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":24,"h":12}, - "sourceSize": {"w":24,"h":12} -}, -{ - "filename": "C.png", - "frame": {"x":184,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "CTRL.png", - "frame": {"x":196,"y":0,"w":22,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":22,"h":12}, - "sourceSize": {"w":22,"h":12} -}, -{ - "filename": "D.png", - "frame": {"x":218,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "DEL.png", - "frame": {"x":230,"y":0,"w":17,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":17,"h":12}, - "sourceSize": {"w":17,"h":12} -}, -{ - "filename": "E.png", - "frame": {"x":247,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "END.png", - "frame": {"x":259,"y":0,"w":18,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":18,"h":12}, - "sourceSize": {"w":18,"h":12} -}, -{ - "filename": "ENTER.png", - "frame": {"x":277,"y":0,"w":27,"h":11}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":27,"h":11}, - "sourceSize": {"w":27,"h":11} -}, -{ - "filename": "ESC.png", - "frame": {"x":304,"y":0,"w":17,"h":11}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":17,"h":11}, - "sourceSize": {"w":17,"h":11} -}, -{ - "filename": "F.png", - "frame": {"x":321,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "F1.png", - "frame": {"x":333,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F2.png", - "frame": {"x":346,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F3.png", - "frame": {"x":359,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F4.png", - "frame": {"x":372,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F5.png", - "frame": {"x":385,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F6.png", - "frame": {"x":398,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F7.png", - "frame": {"x":411,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F8.png", - "frame": {"x":424,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F9.png", - "frame": {"x":437,"y":0,"w":13,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":13,"h":12}, - "sourceSize": {"w":13,"h":12} -}, -{ - "filename": "F10.png", - "frame": {"x":450,"y":0,"w":16,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":16,"h":12}, - "sourceSize": {"w":16,"h":12} -}, -{ - "filename": "F11.png", - "frame": {"x":466,"y":0,"w":15,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":15,"h":12}, - "sourceSize": {"w":15,"h":12} -}, -{ - "filename": "F12.png", - "frame": {"x":481,"y":0,"w":16,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":16,"h":12}, - "sourceSize": {"w":16,"h":12} -}, -{ - "filename": "G.png", - "frame": {"x":497,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "H.png", - "frame": {"x":509,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "HOME.png", - "frame": {"x":521,"y":0,"w":23,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":23,"h":12}, - "sourceSize": {"w":23,"h":12} -}, -{ - "filename": "I.png", - "frame": {"x":544,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "INS.png", - "frame": {"x":556,"y":0,"w":16,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":16,"h":12}, - "sourceSize": {"w":16,"h":12} -}, -{ - "filename": "J.png", - "frame": {"x":572,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "K.png", - "frame": {"x":584,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "KEY_ARROW_DOWN.png", - "frame": {"x":596,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "KEY_ARROW_LEFT.png", - "frame": {"x":608,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "KEY_ARROW_RIGHT.png", - "frame": {"x":620,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "KEY_ARROW_UP.png", - "frame": {"x":632,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "L.png", - "frame": {"x":644,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "LEFT_BRACKET.png", - "frame": {"x":656,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "M.png", - "frame": {"x":668,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "MINUS.png", - "frame": {"x":680,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "N.png", - "frame": {"x":692,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "O.png", - "frame": {"x":704,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "P.png", - "frame": {"x":716,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "PAGE_DOWN.png", - "frame": {"x":728,"y":0,"w":20,"h":11}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":20,"h":11}, - "sourceSize": {"w":20,"h":11} -}, -{ - "filename": "PAGE_UP.png", - "frame": {"x":748,"y":0,"w":20,"h":11}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":20,"h":11}, - "sourceSize": {"w":20,"h":11} -}, -{ - "filename": "PLUS.png", - "frame": {"x":768,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "Q.png", - "frame": {"x":780,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "QUOTE.png", - "frame": {"x":792,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "R.png", - "frame": {"x":804,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "RIGHT_BRACKET.png", - "frame": {"x":816,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "S.png", - "frame": {"x":828,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "SEMICOLON.png", - "frame": {"x":840,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "SHIFT.png", - "frame": {"x":852,"y":0,"w":23,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":23,"h":12}, - "sourceSize": {"w":23,"h":12} -}, -{ - "filename": "SPACE.png", - "frame": {"x":875,"y":0,"w":25,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":25,"h":12}, - "sourceSize": {"w":25,"h":12} -}, -{ - "filename": "T.png", - "frame": {"x":900,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "TAB.png", - "frame": {"x":912,"y":0,"w":19,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":19,"h":12}, - "sourceSize": {"w":19,"h":12} -}, -{ - "filename": "TILDE.png", - "frame": {"x":931,"y":0,"w":15,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":15,"h":12}, - "sourceSize": {"w":15,"h":12} -}, -{ - "filename": "U.png", - "frame": {"x":946,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "V.png", - "frame": {"x":958,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "W.png", - "frame": {"x":970,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "X.png", - "frame": {"x":982,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "Y.png", - "frame": {"x":994,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}, -{ - "filename": "Z.png", - "frame": {"x":1006,"y":0,"w":12,"h":12}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, - "sourceSize": {"w":12,"h":12} -}], -"meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "1.0", - "image": "keyboard.png", - "format": "RGBA8888", - "size": {"w":1018,"h":12}, - "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:085d4353a5c4d18c90f82f8926710d72:45908b22b446cf7f4904d4e0b658b16a:bad03abb89ad027d879c383c13fd51bc$" -} -} +{ "frames": { + "0.png": { + "frame": { "x": 12, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "1.png": { + "frame": { "x": 36, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "2.png": { + "frame": { "x": 0, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "3.png": { + "frame": { "x": 12, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "4.png": { + "frame": { "x": 24, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "5.png": { + "frame": { "x": 84, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "6.png": { + "frame": { "x": 96, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "7.png": { + "frame": { "x": 120, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "8.png": { + "frame": { "x": 132, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "9.png": { + "frame": { "x": 52, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "A.png": { + "frame": { "x": 64, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "ALT.png": { + "frame": { "x": 0, "y": 22, "w": 16, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 11 }, + "sourceSize": { "w": 16, "h": 11 } + }, + "B.png": { + "frame": { "x": 76, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "BACK.png": { + "frame": { "x": 80, "y": 0, "w": 24, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 24, "h": 11 }, + "sourceSize": { "w": 24, "h": 11 } + }, + "C.png": { + "frame": { "x": 88, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "CTRL.png": { + "frame": { "x": 0, "y": 11, "w": 22, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 11 }, + "sourceSize": { "w": 22, "h": 11 } + }, + "D.png": { + "frame": { "x": 100, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "DEL.png": { + "frame": { "x": 116, "y": 11, "w": 17, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 11 }, + "sourceSize": { "w": 17, "h": 11 } + }, + "E.png": { + "frame": { "x": 112, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "END.png": { + "frame": { "x": 81, "y": 11, "w": 18, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 18, "h": 11 }, + "sourceSize": { "w": 18, "h": 11 } + }, + "ENTER.png": { + "frame": { "x": 28, "y": 0, "w": 27, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 27, "h": 11 }, + "sourceSize": { "w": 27, "h": 11 } + }, + "ESC.png": { + "frame": { "x": 99, "y": 11, "w": 17, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 11 }, + "sourceSize": { "w": 17, "h": 11 } + }, + "F.png": { + "frame": { "x": 124, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "F1.png": { + "frame": { "x": 78, "y": 22, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F2.png": { + "frame": { "x": 91, "y": 22, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F3.png": { + "frame": { "x": 104, "y": 22, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F4.png": { + "frame": { "x": 117, "y": 22, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F5.png": { + "frame": { "x": 130, "y": 22, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F6.png": { + "frame": { "x": 0, "y": 33, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F7.png": { + "frame": { "x": 13, "y": 33, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F8.png": { + "frame": { "x": 26, "y": 33, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F9.png": { + "frame": { "x": 39, "y": 33, "w": 13, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 11 }, + "sourceSize": { "w": 13, "h": 11 } + }, + "F10.png": { + "frame": { "x": 16, "y": 22, "w": 16, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 11 }, + "sourceSize": { "w": 16, "h": 11 } + }, + "F11.png": { + "frame": { "x": 48, "y": 22, "w": 15, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 15, "h": 11 }, + "sourceSize": { "w": 15, "h": 11 } + }, + "F12.png": { + "frame": { "x": 133, "y": 11, "w": 16, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 11 }, + "sourceSize": { "w": 16, "h": 11 } + }, + "G.png": { + "frame": { "x": 136, "y": 33, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "H.png": { + "frame": { "x": 0, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "HOME.png": { + "frame": { "x": 104, "y": 0, "w": 23, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 23, "h": 11 }, + "sourceSize": { "w": 23, "h": 11 } + }, + "I.png": { + "frame": { "x": 24, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "INS.png": { + "frame": { "x": 32, "y": 22, "w": 16, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 11 }, + "sourceSize": { "w": 16, "h": 11 } + }, + "J.png": { + "frame": { "x": 48, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "K.png": { + "frame": { "x": 60, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "KEY_ARROW_DOWN.png": { + "frame": { "x": 72, "y": 66, "w": 11, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 }, + "sourceSize": { "w": 11, "h": 11 } + }, + "KEY_ARROW_LEFT.png": { + "frame": { "x": 72, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "KEY_ARROW_RIGHT.png": { + "frame": { "x": 84, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "KEY_ARROW_UP.png": { + "frame": { "x": 94, "y": 66, "w": 11, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 }, + "sourceSize": { "w": 11, "h": 11 } + }, + "L.png": { + "frame": { "x": 96, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "LEFT_BRACKET.png": { + "frame": { "x": 127, "y": 66, "w": 10, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 11 }, + "sourceSize": { "w": 10, "h": 11 } + }, + "M.png": { + "frame": { "x": 108, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "MINUS.png": { + "frame": { "x": 105, "y": 66, "w": 11, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 }, + "sourceSize": { "w": 11, "h": 11 } + }, + "N.png": { + "frame": { "x": 120, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "O.png": { + "frame": { "x": 12, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "P.png": { + "frame": { "x": 132, "y": 44, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "PAGE_DOWN.png": { + "frame": { "x": 22, "y": 11, "w": 20, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 11 }, + "sourceSize": { "w": 20, "h": 11 } + }, + "PAGE_UP.png": { + "frame": { "x": 42, "y": 11, "w": 20, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 11 }, + "sourceSize": { "w": 20, "h": 11 } + }, + "PLUS.png": { + "frame": { "x": 116, "y": 66, "w": 11, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 }, + "sourceSize": { "w": 11, "h": 11 } + }, + "Q.png": { + "frame": { "x": 36, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "QUOTE.png": { + "frame": { "x": 83, "y": 66, "w": 11, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 }, + "sourceSize": { "w": 11, "h": 11 } + }, + "R.png": { + "frame": { "x": 48, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "RIGHT_BRACKET.png": { + "frame": { "x": 137, "y": 66, "w": 10, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 11 }, + "sourceSize": { "w": 10, "h": 11 } + }, + "S.png": { + "frame": { "x": 60, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "SEMICOLON.png": { + "frame": { "x": 72, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "SHIFT.png": { + "frame": { "x": 127, "y": 0, "w": 23, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 23, "h": 11 }, + "sourceSize": { "w": 23, "h": 11 } + }, + "SPACE.png": { + "frame": { "x": 55, "y": 0, "w": 25, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 25, "h": 11 }, + "sourceSize": { "w": 25, "h": 11 } + }, + "T.png": { + "frame": { "x": 108, "y": 55, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "TAB.png": { + "frame": { "x": 62, "y": 11, "w": 19, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 11 }, + "sourceSize": { "w": 19, "h": 11 } + }, + "TILDE.png": { + "frame": { "x": 63, "y": 22, "w": 15, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 15, "h": 11 }, + "sourceSize": { "w": 15, "h": 11 } + }, + "U.png": { + "frame": { "x": 0, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "V.png": { + "frame": { "x": 12, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "W.png": { + "frame": { "x": 24, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "X.png": { + "frame": { "x": 36, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "Y.png": { + "frame": { "x": 48, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "Z.png": { + "frame": { "x": 60, "y": 66, "w": 12, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 }, + "sourceSize": { "w": 12, "h": 11 } + }, + "ACTION.png": { + "frame": { "x": 0, "y": 0, "w": 28, "h": 11 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 0, "w": 28, "h": 11 }, + "sourceSize": { "w": 28, "h": 11 } + } + }, + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.7-dev", + "image": "keyboard.png", + "format": "RGBA8888", + "size": { "w": 150, "h": 77 }, + "scale": "1" + } +} diff --git a/public/images/inputs/keyboard.png b/public/images/inputs/keyboard.png index 67b26af12de..e4d849be0fb 100644 Binary files a/public/images/inputs/keyboard.png and b/public/images/inputs/keyboard.png differ diff --git a/public/images/items.json b/public/images/items.json index bb86b46aa4d..442b93d657b 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -2971,7 +2971,7 @@ } }, { - "filename": "blank_memory", + "filename": "bug_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -2992,7 +2992,7 @@ } }, { - "filename": "bug_memory", + "filename": "charcoal", "rotated": false, "trimmed": true, "sourceSize": { @@ -3013,7 +3013,7 @@ } }, { - "filename": "charcoal", + "filename": "dark_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -3034,7 +3034,7 @@ } }, { - "filename": "dark_memory", + "filename": "dire_hit", "rotated": false, "trimmed": true, "sourceSize": { @@ -3433,7 +3433,7 @@ } }, { - "filename": "dire_hit", + "filename": "dna_splicers", "rotated": false, "trimmed": true, "sourceSize": { @@ -3727,7 +3727,7 @@ } }, { - "filename": "dna_splicers", + "filename": "dragon_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -3979,7 +3979,7 @@ } }, { - "filename": "dragon_memory", + "filename": "electirizer", "rotated": false, "trimmed": true, "sourceSize": { @@ -4168,7 +4168,7 @@ } }, { - "filename": "electirizer", + "filename": "electric_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4189,7 +4189,7 @@ } }, { - "filename": "electric_memory", + "filename": "enigma_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -4231,7 +4231,7 @@ } }, { - "filename": "enigma_berry", + "filename": "fairy_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4252,7 +4252,7 @@ } }, { - "filename": "fairy_memory", + "filename": "fighting_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4272,27 +4272,6 @@ "h": 22 } }, - { - "filename": "fighting_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 193, - "y": 212, - "w": 22, - "h": 22 - } - }, { "filename": "fire_memory", "rotated": false, @@ -4309,7 +4288,7 @@ }, "frame": { "x": 193, - "y": 234, + "y": 212, "w": 22, "h": 22 } @@ -4330,7 +4309,7 @@ }, "frame": { "x": 193, - "y": 256, + "y": 234, "w": 22, "h": 22 } @@ -4350,8 +4329,8 @@ "h": 22 }, "frame": { - "x": 215, - "y": 212, + "x": 193, + "y": 256, "w": 22, "h": 22 } @@ -4372,7 +4351,7 @@ }, "frame": { "x": 215, - "y": 234, + "y": 212, "w": 22, "h": 22 } @@ -4393,7 +4372,7 @@ }, "frame": { "x": 215, - "y": 256, + "y": 234, "w": 22, "h": 22 } @@ -4412,6 +4391,27 @@ "w": 22, "h": 22 }, + "frame": { + "x": 215, + "y": 256, + "w": 22, + "h": 22 + } + }, + { + "filename": "guard_spec", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, "frame": { "x": 195, "y": 278, @@ -4420,7 +4420,7 @@ } }, { - "filename": "guard_spec", + "filename": "hard_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -4428,15 +4428,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 7, "y": 5, - "w": 22, + "w": 20, "h": 22 }, "frame": { "x": 217, "y": 278, - "w": 22, + "w": 20, "h": 22 } }, @@ -4476,8 +4476,8 @@ "h": 22 }, "frame": { - "x": 236, - "y": 127, + "x": 258, + "y": 105, "w": 22, "h": 22 } @@ -4497,8 +4497,8 @@ "h": 22 }, "frame": { - "x": 258, - "y": 105, + "x": 236, + "y": 127, "w": 22, "h": 22 } @@ -4525,7 +4525,7 @@ } }, { - "filename": "poison_memory", + "filename": "normal_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4546,7 +4546,7 @@ } }, { - "filename": "protector", + "filename": "poison_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4588,7 +4588,7 @@ } }, { - "filename": "psychic_memory", + "filename": "protector", "rotated": false, "trimmed": true, "sourceSize": { @@ -4609,7 +4609,7 @@ } }, { - "filename": "rock_memory", + "filename": "psychic_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4650,48 +4650,6 @@ "h": 23 } }, - { - "filename": "hard_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 237, - "y": 235, - "w": 20, - "h": 22 - } - }, - { - "filename": "liechi_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 237, - "y": 257, - "w": 22, - "h": 21 - } - }, { "filename": "potion", "rotated": false, @@ -4734,6 +4692,27 @@ "h": 21 } }, + { + "filename": "liechi_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 302, + "y": 128, + "w": 22, + "h": 21 + } + }, { "filename": "reviver_seed", "rotated": false, @@ -4749,12 +4728,54 @@ "h": 20 }, "frame": { - "x": 302, - "y": 128, + "x": 342, + "y": 107, "w": 23, "h": 20 } }, + { + "filename": "shell_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 23, + "h": 20 + }, + "frame": { + "x": 365, + "y": 107, + "w": 23, + "h": 20 + } + }, + { + "filename": "rock_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 237, + "y": 235, + "w": 22, + "h": 22 + } + }, { "filename": "scroll_of_darkness", "rotated": false, @@ -4770,8 +4791,8 @@ "h": 22 }, "frame": { - "x": 342, - "y": 107, + "x": 237, + "y": 257, "w": 22, "h": 22 } @@ -4791,75 +4812,12 @@ "h": 22 }, "frame": { - "x": 364, - "y": 107, + "x": 237, + "y": 279, "w": 22, "h": 22 } }, - { - "filename": "shed_shell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 386, - "y": 107, - "w": 22, - "h": 22 - } - }, - { - "filename": "oval_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 19 - }, - "frame": { - "x": 408, - "y": 108, - "w": 18, - "h": 19 - } - }, - { - "filename": "sitrus_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 239, - "y": 278, - "w": 20, - "h": 22 - } - }, { "filename": "upgrade", "rotated": false, @@ -4882,7 +4840,7 @@ } }, { - "filename": "starf_berry", + "filename": "shed_shell", "rotated": false, "trimmed": true, "sourceSize": { @@ -4903,7 +4861,7 @@ } }, { - "filename": "steel_memory", + "filename": "starf_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -4924,7 +4882,7 @@ } }, { - "filename": "thick_club", + "filename": "steel_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4945,7 +4903,7 @@ } }, { - "filename": "thunder_stone", + "filename": "big_nugget", "rotated": false, "trimmed": true, "sourceSize": { @@ -4953,16 +4911,37 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 6, + "y": 6, + "w": 20, + "h": 20 }, "frame": { - "x": 257, - "y": 234, - "w": 22, - "h": 22 + "x": 388, + "y": 107, + "w": 20, + "h": 20 + } + }, + { + "filename": "oval_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 19 + }, + "frame": { + "x": 408, + "y": 108, + "w": 18, + "h": 19 } }, { @@ -4987,7 +4966,28 @@ } }, { - "filename": "tm_bug", + "filename": "sitrus_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 281, + "y": 168, + "w": 20, + "h": 22 + } + }, + { + "filename": "thick_club", "rotated": false, "trimmed": true, "sourceSize": { @@ -5001,14 +5001,14 @@ "h": 22 }, "frame": { - "x": 281, - "y": 168, + "x": 301, + "y": 149, "w": 22, "h": 22 } }, { - "filename": "tm_dark", + "filename": "thunder_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -5029,7 +5029,7 @@ } }, { - "filename": "tm_dragon", + "filename": "tm_bug", "rotated": false, "trimmed": true, "sourceSize": { @@ -5050,7 +5050,7 @@ } }, { - "filename": "tm_electric", + "filename": "tm_dark", "rotated": false, "trimmed": true, "sourceSize": { @@ -5064,14 +5064,14 @@ "h": 22 }, "frame": { - "x": 279, + "x": 259, "y": 234, "w": 22, "h": 22 } }, { - "filename": "tm_fairy", + "filename": "tm_dragon", "rotated": false, "trimmed": true, "sourceSize": { @@ -5092,7 +5092,7 @@ } }, { - "filename": "tm_fighting", + "filename": "tm_electric", "rotated": false, "trimmed": true, "sourceSize": { @@ -5112,6 +5112,48 @@ "h": 22 } }, + { + "filename": "tm_fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 281, + "y": 234, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_fighting", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 281, + "y": 256, + "w": 22, + "h": 22 + } + }, { "filename": "tm_fire", "rotated": false, @@ -5128,11 +5170,53 @@ }, "frame": { "x": 281, - "y": 256, + "y": 278, "w": 22, "h": 22 } }, + { + "filename": "lum_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 301, + "y": 171, + "w": 20, + "h": 19 + } + }, + { + "filename": "metal_coat", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 302, + "y": 190, + "w": 19, + "h": 22 + } + }, { "filename": "tm_flying", "rotated": false, @@ -5148,56 +5232,14 @@ "h": 22 }, "frame": { - "x": 281, - "y": 278, + "x": 301, + "y": 212, "w": 22, "h": 22 } }, { - "filename": "baton", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 408, - "y": 127, - "w": 18, - "h": 18 - } - }, - { - "filename": "golden_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 325, - "y": 128, - "w": 17, - "h": 20 - } - }, - { - "filename": "shell_bell", + "filename": "tm_ghost", "rotated": false, "trimmed": true, "sourceSize": { @@ -5206,19 +5248,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 7, - "w": 23, - "h": 20 + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 342, - "y": 129, - "w": 23, - "h": 20 + "x": 303, + "y": 234, + "w": 22, + "h": 22 } }, { - "filename": "deep_sea_scale", + "filename": "tm_grass", "rotated": false, "trimmed": true, "sourceSize": { @@ -5227,15 +5269,36 @@ }, "spriteSourceSize": { "x": 5, - "y": 6, + "y": 5, "w": 22, - "h": 20 + "h": 22 }, "frame": { - "x": 365, - "y": 129, + "x": 303, + "y": 256, "w": 22, - "h": 20 + "h": 22 + } + }, + { + "filename": "tm_ground", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 303, + "y": 278, + "w": 22, + "h": 22 } }, { @@ -5253,14 +5316,14 @@ "h": 21 }, "frame": { - "x": 387, - "y": 129, + "x": 324, + "y": 128, "w": 21, "h": 21 } }, { - "filename": "candy", + "filename": "tm_ice", "rotated": false, "trimmed": true, "sourceSize": { @@ -5268,15 +5331,498 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 11, - "w": 18, + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 323, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_normal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 321, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_poison", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 345, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_psychic", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 345, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_rock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 343, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_steel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 367, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 367, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 365, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 389, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 15, + "h": 23 + }, + "frame": { + "x": 411, + "y": 127, + "w": 15, + "h": 23 + } + }, + { + "filename": "x_accuracy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 389, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "leftovers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 15, + "h": 22 + }, + "frame": { + "x": 411, + "y": 150, + "w": 15, + "h": 22 + } + }, + { + "filename": "x_attack", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 387, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 409, + "y": 172, + "w": 17, + "h": 23 + } + }, + { + "filename": "power_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 321, + "y": 193, + "w": 20, + "h": 19 + } + }, + { + "filename": "x_defense", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 323, + "y": 212, + "w": 22, + "h": 22 + } + }, + { + "filename": "razor_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 341, + "y": 193, + "w": 20, + "h": 19 + } + }, + { + "filename": "x_sp_atk", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 234, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_sp_def", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 256, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_speed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 278, + "w": 22, + "h": 22 + } + }, + { + "filename": "deep_sea_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 361, + "y": 193, + "w": 22, + "h": 20 + } + }, + { + "filename": "fairy_feather", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 20 + }, + "frame": { + "x": 383, + "y": 193, + "w": 22, + "h": 20 + } + }, + { + "filename": "shiny_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 405, + "y": 195, + "w": 21, + "h": 21 + } + }, + { + "filename": "mystery_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, "h": 18 }, "frame": { - "x": 408, - "y": 145, - "w": 18, + "x": 345, + "y": 212, + "w": 16, "h": 18 } }, @@ -5295,35 +5841,14 @@ "h": 17 }, "frame": { - "x": 195, - "y": 300, + "x": 361, + "y": 213, "w": 23, "h": 17 } }, { - "filename": "relic_crown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 18 - }, - "frame": { - "x": 218, - "y": 300, - "w": 23, - "h": 18 - } - }, - { - "filename": "fairy_feather", + "filename": "masterpiece_teacup", "rotated": false, "trimmed": true, "sourceSize": { @@ -5333,13 +5858,286 @@ "spriteSourceSize": { "x": 5, "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 384, + "y": 213, + "w": 21, + "h": 18 + } + }, + { + "filename": "zoom_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 405, + "y": 216, + "w": 21, + "h": 21 + } + }, + { + "filename": "sweet_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 230, + "w": 22, + "h": 21 + } + }, + { + "filename": "syrupy_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 251, + "w": 22, + "h": 21 + } + }, + { + "filename": "tart_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 272, + "w": 22, + "h": 21 + } + }, + { + "filename": "eviolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 369, + "y": 230, + "w": 15, + "h": 15 + } + }, + { + "filename": "sharp_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 21, + "h": 18 + }, + "frame": { + "x": 384, + "y": 231, + "w": 21, + "h": 18 + } + }, + { + "filename": "unremarkable_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 405, + "y": 237, + "w": 21, + "h": 18 + } + }, + { + "filename": "prism_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 369, + "y": 245, + "w": 15, + "h": 15 + } + }, + { + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 369, + "y": 260, + "w": 17, + "h": 22 + } + }, + { + "filename": "quick_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 386, + "y": 249, + "w": 19, + "h": 21 + } + }, + { + "filename": "blue_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, "h": 20 }, "frame": { - "x": 241, - "y": 300, - "w": 22, + "x": 405, + "y": 255, + "w": 20, + "h": 20 + } + }, + { + "filename": "candy_jar", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 386, + "y": 270, + "w": 19, + "h": 20 + } + }, + { + "filename": "golden_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 369, + "y": 282, + "w": 17, "h": 20 } }, @@ -5358,14 +6156,56 @@ "h": 20 }, "frame": { - "x": 263, - "y": 300, + "x": 347, + "y": 293, "w": 22, "h": 20 } }, { - "filename": "big_nugget", + "filename": "everstone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 17 + }, + "frame": { + "x": 405, + "y": 275, + "w": 20, + "h": 17 + } + }, + { + "filename": "hard_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 386, + "y": 290, + "w": 19, + "h": 20 + } + }, + { + "filename": "gb", "rotated": false, "trimmed": true, "sourceSize": { @@ -5379,7 +6219,217 @@ "h": 20 }, "frame": { - "x": 285, + "x": 405, + "y": 292, + "w": 20, + "h": 20 + } + }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 369, + "y": 302, + "w": 17, + "h": 20 + } + }, + { + "filename": "miracle_seed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 19, + "h": 19 + }, + "frame": { + "x": 386, + "y": 310, + "w": 19, + "h": 19 + } + }, + { + "filename": "magnet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 405, + "y": 312, + "w": 20, + "h": 20 + } + }, + { + "filename": "relic_crown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 18 + }, + "frame": { + "x": 195, + "y": 300, + "w": 23, + "h": 18 + } + }, + { + "filename": "spell_tag", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 218, + "y": 300, + "w": 19, + "h": 21 + } + }, + { + "filename": "tera_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 237, + "y": 301, + "w": 22, + "h": 20 + } + }, + { + "filename": "mb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 259, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "pb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 279, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "pb_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 299, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "rb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 319, "y": 300, "w": 20, "h": 20 @@ -5407,7 +6457,7 @@ } }, { - "filename": "lucky_egg", + "filename": "soothe_bell", "rotated": false, "trimmed": true, "sourceSize": { @@ -5415,16 +6465,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, + "x": 8, + "y": 5, "w": 17, - "h": 20 + "h": 22 }, "frame": { "x": 178, "y": 305, "w": 17, - "h": 20 + "h": 22 } }, { @@ -5443,13 +6493,13 @@ }, "frame": { "x": 195, - "y": 317, + "y": 318, "w": 23, "h": 17 } }, { - "filename": "sweet_apple", + "filename": "smooth_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -5457,20 +6507,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 7, "y": 6, - "w": 22, - "h": 21 + "w": 20, + "h": 20 }, "frame": { "x": 218, - "y": 318, - "w": 22, - "h": 21 + "y": 321, + "w": 20, + "h": 20 } }, { - "filename": "syrupy_apple", + "filename": "strange_ball", "rotated": false, "trimmed": true, "sourceSize": { @@ -5478,58 +6528,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 6, "y": 6, - "w": 22, - "h": 21 + "w": 20, + "h": 20 }, "frame": { - "x": 240, - "y": 320, - "w": 22, - "h": 21 - } - }, - { - "filename": "tart_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 262, - "y": 320, - "w": 22, - "h": 21 - } - }, - { - "filename": "shiny_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 284, - "y": 320, - "w": 21, - "h": 21 + "x": 238, + "y": 321, + "w": 20, + "h": 20 } }, { @@ -5554,7 +6562,7 @@ } }, { - "filename": "tm_ghost", + "filename": "ub", "rotated": false, "trimmed": true, "sourceSize": { @@ -5562,541 +6570,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 6, + "y": 6, + "w": 20, + "h": 20 }, "frame": { "x": 135, "y": 325, - "w": 22, - "h": 22 - } - }, - { - "filename": "zoom_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 157, - "y": 322, - "w": 21, - "h": 21 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 178, - "y": 325, - "w": 17, - "h": 22 - } - }, - { - "filename": "tm_grass", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 136, - "y": 347, - "w": 22, - "h": 22 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, "w": 20, "h": 20 - }, - "frame": { - "x": 158, - "y": 343, - "w": 20, - "h": 20 - } - }, - { - "filename": "super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 141, - "y": 369, - "w": 17, - "h": 23 - } - }, - { - "filename": "tm_ground", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 158, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "metal_coat", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 141, - "y": 392, - "w": 19, - "h": 22 - } - }, - { - "filename": "tera_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 195, - "y": 334, - "w": 22, - "h": 20 - } - }, - { - "filename": "tm_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 217, - "y": 339, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_normal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 239, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_poison", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 261, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_psychic", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 283, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_rock", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 160, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "lum_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 160, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "power_herb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 180, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "altarianite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 178, - "y": 347, - "w": 16, - "h": 16 - } - }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 180, - "y": 363, - "w": 15, - "h": 22 - } - }, - { - "filename": "tm_steel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 182, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "razor_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 200, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "tm_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 195, - "y": 354, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 217, - "y": 361, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 239, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_accuracy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 261, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_attack", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 283, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "soothe_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 204, - "y": 385, - "w": 17, - "h": 22 } }, { @@ -6114,537 +6597,12 @@ "h": 19 }, "frame": { - "x": 220, - "y": 407, + "x": 155, + "y": 322, "w": 20, "h": 19 } }, - { - "filename": "x_defense", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 221, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_sp_atk", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 243, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "miracle_seed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 19, - "h": 19 - }, - "frame": { - "x": 240, - "y": 407, - "w": 19, - "h": 19 - } - }, - { - "filename": "x_sp_def", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 265, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_speed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 287, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "masterpiece_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 259, - "y": 407, - "w": 21, - "h": 18 - } - }, - { - "filename": "sharp_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 21, - "h": 18 - }, - "frame": { - "x": 280, - "y": 407, - "w": 21, - "h": 18 - } - }, - { - "filename": "dark_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 301, - "y": 407, - "w": 18, - "h": 18 - } - }, - { - "filename": "relic_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 15, - "h": 11 - }, - "frame": { - "x": 0, - "y": 414, - "w": 15, - "h": 11 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 301, - "y": 212, - "w": 15, - "h": 23 - } - }, - { - "filename": "quick_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 301, - "y": 235, - "w": 19, - "h": 21 - } - }, - { - "filename": "spell_tag", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 303, - "y": 256, - "w": 19, - "h": 21 - } - }, - { - "filename": "candy_jar", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 303, - "y": 277, - "w": 19, - "h": 20 - } - }, - { - "filename": "gb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 297, - "w": 20, - "h": 20 - } - }, - { - "filename": "magnet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 317, - "w": 20, - "h": 20 - } - }, - { - "filename": "mb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 337, - "w": 20, - "h": 20 - } - }, - { - "filename": "pb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 357, - "w": 20, - "h": 20 - } - }, - { - "filename": "hard_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 309, - "y": 377, - "w": 19, - "h": 20 - } - }, - { - "filename": "pb_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 302, - "y": 148, - "w": 20, - "h": 20 - } - }, - { - "filename": "rb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 322, - "y": 148, - "w": 20, - "h": 20 - } - }, - { - "filename": "smooth_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 303, - "y": 168, - "w": 20, - "h": 20 - } - }, - { - "filename": "strange_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 342, - "y": 149, - "w": 20, - "h": 20 - } - }, - { - "filename": "ub", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 362, - "y": 149, - "w": 20, - "h": 20 - } - }, - { - "filename": "flame_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 323, - "y": 168, - "w": 18, - "h": 18 - } - }, - { - "filename": "unremarkable_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 341, - "y": 169, - "w": 21, - "h": 18 - } - }, { "filename": "wl_ability_urge", "rotated": false, @@ -6660,8 +6618,8 @@ "h": 18 }, "frame": { - "x": 362, - "y": 169, + "x": 175, + "y": 327, "w": 20, "h": 18 } @@ -6681,12 +6639,33 @@ "h": 18 }, "frame": { - "x": 382, - "y": 150, + "x": 136, + "y": 345, "w": 20, "h": 18 } }, + { + "filename": "baton", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 156, + "y": 341, + "w": 18, + "h": 18 + } + }, { "filename": "wl_awakening", "rotated": false, @@ -6702,8 +6681,8 @@ "h": 18 }, "frame": { - "x": 382, - "y": 168, + "x": 174, + "y": 345, "w": 20, "h": 18 } @@ -6723,33 +6702,12 @@ "h": 18 }, "frame": { - "x": 402, - "y": 163, + "x": 195, + "y": 335, "w": 20, "h": 18 } }, - { - "filename": "everstone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 17 - }, - "frame": { - "x": 402, - "y": 181, - "w": 20, - "h": 17 - } - }, { "filename": "wl_custom_spliced", "rotated": false, @@ -6765,8 +6723,8 @@ "h": 18 }, "frame": { - "x": 382, - "y": 186, + "x": 215, + "y": 341, "w": 20, "h": 18 } @@ -6786,8 +6744,8 @@ "h": 18 }, "frame": { - "x": 402, - "y": 198, + "x": 235, + "y": 341, "w": 20, "h": 18 } @@ -6807,33 +6765,12 @@ "h": 18 }, "frame": { - "x": 303, - "y": 188, + "x": 194, + "y": 353, "w": 20, "h": 18 } }, - { - "filename": "light_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 323, - "y": 186, - "w": 18, - "h": 18 - } - }, { "filename": "wl_ether", "rotated": false, @@ -6849,8 +6786,8 @@ "h": 18 }, "frame": { - "x": 341, - "y": 187, + "x": 214, + "y": 359, "w": 20, "h": 18 } @@ -6870,14 +6807,14 @@ "h": 18 }, "frame": { - "x": 361, - "y": 187, + "x": 234, + "y": 359, "w": 20, "h": 18 } }, { - "filename": "light_stone", + "filename": "candy", "rotated": false, "trimmed": true, "sourceSize": { @@ -6886,13 +6823,13 @@ }, "spriteSourceSize": { "x": 7, - "y": 7, + "y": 11, "w": 18, "h": 18 }, "frame": { - "x": 323, - "y": 204, + "x": 156, + "y": 359, "w": 18, "h": 18 } @@ -6912,8 +6849,8 @@ "h": 18 }, "frame": { - "x": 341, - "y": 205, + "x": 174, + "y": 363, "w": 20, "h": 18 } @@ -6933,8 +6870,8 @@ "h": 18 }, "frame": { - "x": 361, - "y": 205, + "x": 194, + "y": 371, "w": 20, "h": 18 } @@ -6954,8 +6891,8 @@ "h": 18 }, "frame": { - "x": 381, - "y": 204, + "x": 214, + "y": 377, "w": 20, "h": 18 } @@ -6975,12 +6912,33 @@ "h": 18 }, "frame": { - "x": 401, - "y": 216, + "x": 234, + "y": 377, "w": 20, "h": 18 } }, + { + "filename": "relic_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 11, + "w": 15, + "h": 11 + }, + "frame": { + "x": 141, + "y": 363, + "w": 15, + "h": 11 + } + }, { "filename": "wl_item_drop", "rotated": false, @@ -6996,8 +6954,8 @@ "h": 18 }, "frame": { - "x": 381, - "y": 222, + "x": 141, + "y": 377, "w": 20, "h": 18 } @@ -7017,8 +6975,8 @@ "h": 18 }, "frame": { - "x": 401, - "y": 234, + "x": 141, + "y": 395, "w": 20, "h": 18 } @@ -7038,8 +6996,8 @@ "h": 18 }, "frame": { - "x": 320, - "y": 222, + "x": 161, + "y": 381, "w": 20, "h": 18 } @@ -7059,8 +7017,8 @@ "h": 18 }, "frame": { - "x": 340, - "y": 223, + "x": 161, + "y": 399, "w": 20, "h": 18 } @@ -7080,96 +7038,12 @@ "h": 18 }, "frame": { - "x": 360, - "y": 223, + "x": 181, + "y": 389, "w": 20, "h": 18 } }, - { - "filename": "ampharosite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 320, - "y": 240, - "w": 16, - "h": 16 - } - }, - { - "filename": "mystery_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 18 - }, - "frame": { - "x": 322, - "y": 256, - "w": 16, - "h": 18 - } - }, - { - "filename": "toxic_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 322, - "y": 274, - "w": 18, - "h": 18 - } - }, - { - "filename": "eviolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 336, - "y": 241, - "w": 15, - "h": 15 - } - }, { "filename": "wl_max_revive", "rotated": false, @@ -7185,8 +7059,8 @@ "h": 18 }, "frame": { - "x": 351, - "y": 241, + "x": 181, + "y": 407, "w": 20, "h": 18 } @@ -7206,8 +7080,8 @@ "h": 18 }, "frame": { - "x": 325, - "y": 292, + "x": 201, + "y": 395, "w": 20, "h": 18 } @@ -7227,12 +7101,54 @@ "h": 18 }, "frame": { - "x": 325, - "y": 310, + "x": 221, + "y": 395, "w": 20, "h": 18 } }, + { + "filename": "dark_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 241, + "y": 395, + "w": 18, + "h": 18 + } + }, + { + "filename": "flame_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 255, + "y": 341, + "w": 18, + "h": 18 + } + }, { "filename": "wl_reset_urge", "rotated": false, @@ -7248,8 +7164,8 @@ "h": 18 }, "frame": { - "x": 325, - "y": 328, + "x": 254, + "y": 359, "w": 20, "h": 18 } @@ -7269,12 +7185,33 @@ "h": 18 }, "frame": { - "x": 325, - "y": 346, + "x": 254, + "y": 377, "w": 20, "h": 18 } }, + { + "filename": "light_ball", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 259, + "y": 395, + "w": 18, + "h": 18 + } + }, { "filename": "wl_super_potion", "rotated": false, @@ -7290,12 +7227,96 @@ "h": 18 }, "frame": { - "x": 371, - "y": 241, + "x": 259, + "y": 320, "w": 20, "h": 18 } }, + { + "filename": "light_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 279, + "y": 320, + "w": 18, + "h": 18 + } + }, + { + "filename": "toxic_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 297, + "y": 320, + "w": 18, + "h": 18 + } + }, + { + "filename": "altarianite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 315, + "y": 320, + "w": 16, + "h": 16 + } + }, + { + "filename": "ampharosite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 273, + "y": 338, + "w": 16, + "h": 16 + } + }, { "filename": "audinite", "rotated": false, @@ -7311,8 +7332,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 364, + "x": 289, + "y": 338, "w": 16, "h": 16 } @@ -7332,33 +7353,12 @@ "h": 16 }, "frame": { - "x": 328, - "y": 380, + "x": 274, + "y": 354, "w": 16, "h": 16 } }, - { - "filename": "prism_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 338, - "y": 259, - "w": 15, - "h": 15 - } - }, { "filename": "beedrillite", "rotated": false, @@ -7374,8 +7374,8 @@ "h": 16 }, "frame": { - "x": 353, - "y": 259, + "x": 274, + "y": 370, "w": 16, "h": 16 } @@ -7395,8 +7395,8 @@ "h": 16 }, "frame": { - "x": 369, - "y": 259, + "x": 290, + "y": 354, "w": 16, "h": 16 } @@ -7416,8 +7416,8 @@ "h": 16 }, "frame": { - "x": 385, - "y": 259, + "x": 290, + "y": 370, "w": 16, "h": 16 } @@ -7437,8 +7437,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 252, + "x": 305, + "y": 338, "w": 16, "h": 16 } @@ -7458,8 +7458,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 268, + "x": 306, + "y": 354, "w": 16, "h": 16 } @@ -7479,8 +7479,8 @@ "h": 16 }, "frame": { - "x": 340, - "y": 275, + "x": 306, + "y": 370, "w": 16, "h": 16 } @@ -7500,8 +7500,8 @@ "h": 16 }, "frame": { - "x": 356, - "y": 275, + "x": 331, + "y": 320, "w": 16, "h": 16 } @@ -7521,8 +7521,8 @@ "h": 16 }, "frame": { - "x": 372, - "y": 275, + "x": 321, + "y": 336, "w": 16, "h": 16 } @@ -7542,8 +7542,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 291, + "x": 322, + "y": 352, "w": 16, "h": 16 } @@ -7563,8 +7563,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 307, + "x": 322, + "y": 368, "w": 16, "h": 16 } @@ -7584,8 +7584,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 291, + "x": 337, + "y": 336, "w": 16, "h": 16 } @@ -7605,8 +7605,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 323, + "x": 338, + "y": 352, "w": 16, "h": 16 } @@ -7626,8 +7626,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 307, + "x": 338, + "y": 368, "w": 16, "h": 16 } @@ -7647,8 +7647,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 339, + "x": 347, + "y": 313, "w": 16, "h": 16 } @@ -7668,8 +7668,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 323, + "x": 277, + "y": 386, "w": 16, "h": 16 } @@ -7689,8 +7689,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 339, + "x": 293, + "y": 386, "w": 16, "h": 16 } @@ -7710,8 +7710,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 291, + "x": 309, + "y": 386, "w": 16, "h": 16 } @@ -7731,8 +7731,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 307, + "x": 325, + "y": 384, "w": 16, "h": 16 } @@ -7752,8 +7752,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 323, + "x": 341, + "y": 384, "w": 16, "h": 16 } @@ -7773,8 +7773,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 339, + "x": 277, + "y": 402, "w": 16, "h": 16 } @@ -7794,8 +7794,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 355, + "x": 293, + "y": 402, "w": 16, "h": 16 } @@ -7815,8 +7815,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 355, + "x": 309, + "y": 402, "w": 16, "h": 16 } @@ -7836,8 +7836,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 355, + "x": 325, + "y": 400, "w": 16, "h": 16 } @@ -7857,8 +7857,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 371, + "x": 341, + "y": 400, "w": 16, "h": 16 } @@ -7878,8 +7878,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 371, + "x": 353, + "y": 329, "w": 16, "h": 16 } @@ -7899,8 +7899,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 371, + "x": 369, + "y": 322, "w": 16, "h": 16 } @@ -7920,8 +7920,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 387, + "x": 354, + "y": 345, "w": 16, "h": 16 } @@ -7941,8 +7941,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 387, + "x": 354, + "y": 361, "w": 16, "h": 16 } @@ -7962,8 +7962,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 387, + "x": 385, + "y": 329, "w": 16, "h": 16 } @@ -7983,8 +7983,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 396, + "x": 401, + "y": 332, "w": 16, "h": 16 } @@ -8004,8 +8004,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 403, + "x": 357, + "y": 377, "w": 16, "h": 16 } @@ -8025,8 +8025,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 403, + "x": 357, + "y": 393, "w": 16, "h": 16 } @@ -8046,8 +8046,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 403, + "x": 357, + "y": 409, "w": 16, "h": 16 } @@ -8067,8 +8067,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 284, + "x": 370, + "y": 345, "w": 16, "h": 16 } @@ -8088,8 +8088,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 300, + "x": 370, + "y": 361, "w": 16, "h": 16 } @@ -8109,8 +8109,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 316, + "x": 373, + "y": 377, "w": 16, "h": 16 } @@ -8130,8 +8130,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 332, + "x": 373, + "y": 393, "w": 16, "h": 16 } @@ -8151,8 +8151,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 348, + "x": 373, + "y": 409, "w": 16, "h": 16 } @@ -8172,8 +8172,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 284, + "x": 386, + "y": 348, "w": 16, "h": 16 } @@ -8193,8 +8193,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 300, + "x": 402, + "y": 348, "w": 16, "h": 16 } @@ -8214,8 +8214,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 316, + "x": 389, + "y": 364, "w": 16, "h": 16 } @@ -8235,8 +8235,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 332, + "x": 389, + "y": 380, "w": 16, "h": 16 } @@ -8247,6 +8247,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:6dd58685cd89890a4361a424b59bfe65:ff9159978f3a103ee1d656fb76e37457:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:c004184e48566e1da6f13477a3348fd3:dc1a5489f7821641aade35ba290bbea7:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index 23c87cf3204..4c366e4d72a 100644 Binary files a/public/images/items.png and b/public/images/items.png differ diff --git a/public/images/items/blank_memory.png b/public/images/items/normal_memory.png similarity index 100% rename from public/images/items/blank_memory.png rename to public/images/items/normal_memory.png diff --git a/public/images/pokemon/279.png b/public/images/pokemon/279.png index a50bf952433..e7243586515 100644 Binary files a/public/images/pokemon/279.png and b/public/images/pokemon/279.png differ diff --git a/public/images/pokemon/327.png b/public/images/pokemon/327.png index 81e636db632..c103de3916d 100644 Binary files a/public/images/pokemon/327.png and b/public/images/pokemon/327.png differ diff --git a/public/images/pokemon/exp/864.png b/public/images/pokemon/exp/864.png index 036e2b9e40f..2776e938e82 100644 Binary files a/public/images/pokemon/exp/864.png and b/public/images/pokemon/exp/864.png differ diff --git a/public/images/pokemon/exp/shiny/1002b.png b/public/images/pokemon/exp/shiny/1002b.png deleted file mode 100644 index 85dfb1c4bd6..00000000000 Binary files a/public/images/pokemon/exp/shiny/1002b.png and /dev/null differ diff --git a/public/images/pokemon/exp/shiny/1002s.png b/public/images/pokemon/exp/shiny/1002s.png deleted file mode 100644 index 835b3dcd73b..00000000000 Binary files a/public/images/pokemon/exp/shiny/1002s.png and /dev/null differ diff --git a/public/images/pokemon/exp/shiny/1002sb.png b/public/images/pokemon/exp/shiny/1002sb.png deleted file mode 100644 index f87e2fc4239..00000000000 Binary files a/public/images/pokemon/exp/shiny/1002sb.png and /dev/null differ diff --git a/public/images/pokemon/exp/shiny/864.png b/public/images/pokemon/exp/shiny/864.png index 079b1f6a681..66d648edf79 100644 Binary files a/public/images/pokemon/exp/shiny/864.png and b/public/images/pokemon/exp/shiny/864.png differ diff --git a/public/images/pokemon/shiny/279.png b/public/images/pokemon/shiny/279.png index c43634e8602..26dba547913 100644 Binary files a/public/images/pokemon/shiny/279.png and b/public/images/pokemon/shiny/279.png differ diff --git a/public/images/pokemon/shiny/327.png b/public/images/pokemon/shiny/327.png index f875dea77cd..56a473a8416 100644 Binary files a/public/images/pokemon/shiny/327.png and b/public/images/pokemon/shiny/327.png differ diff --git a/public/images/pokemon/variant/279.json b/public/images/pokemon/variant/279.json index a7b97ac3161..5c193f7939b 100644 --- a/public/images/pokemon/variant/279.json +++ b/public/images/pokemon/variant/279.json @@ -1,38 +1,49 @@ { "0": { + "bc4524": "af5457", "31638c": "324a26", - "101010": "101010", "5aa5ce": "40683c", - "a5e6ff": "b6d9ac", - "7bceef": "789c6e", - "ced6ef": "c09e99", - "737384": "774644", + "ce4252": "af2c4f", "ffffff": "f1dcd8", "8c4231": "420b0c", - "ffde4a": "c66f68", - "c57b31": "551917", "ffffad": "f4bfb6", + "ffde4a": "c66f68", + "7bceef": "789c6e", + "a5e6ff": "b6d9ac", + "737384": "774644", "f7b531": "af5457", - "ce4252": "af2c4f", - "bc4524": "af5457", - "00e5e7": "00e5e7" + "c57b31": "551917", + "ced6ef": "c09e99" }, "1": { + "bc4524": "3d325e", "31638c": "143a72", - "101010": "101010", "5aa5ce": "4060bc", - "a5e6ff": "b4b3ff", - "7bceef": "657ddf", - "ced6ef": "a8b5dd", - "737384": "737384", + "ce4252": "b75558", "ffffff": "e5ecff", "8c4231": "17103f", - "ffde4a": "534e72", - "c57b31": "2a1f50", "ffffad": "87879b", + "ffde4a": "534e72", + "7bceef": "657ddf", + "a5e6ff": "b4b3ff", "f7b531": "3d325e", - "ce4252": "b75558", - "bc4524": "3d325e", - "00e5e7": "00e5e7" + "c57b31": "2a1f50", + "ced6ef": "a8b5dd" + }, + "2": { + "ce4252": "215991", + "ffde4a": "f16f40", + "ffffad": "ffb274", + "737384": "884c43", + "c57b31": "761c03", + "7bceef": "be3d2f", + "8c4231": "5a0700", + "5aa5ce": "892722", + "8c4232": "761c03", + "ffffff": "f5e1d1", + "a5e6ff": "dd533a", + "f7b531": "bc4524", + "ced6ef": "d19e92", + "31638c": "610f0e" } -} \ No newline at end of file +} diff --git a/public/images/pokemon/variant/279_1.json b/public/images/pokemon/variant/279_1.json deleted file mode 100644 index 3f884262e14..00000000000 --- a/public/images/pokemon/variant/279_1.json +++ /dev/null @@ -1,3611 +0,0 @@ -{ - "textures": [ - { - "image": "279_1.png", - "format": "RGBA8888", - "size": { - "w": 422, - "h": 422 - }, - "scale": 1, - "frames": [ - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 9, - "w": 82, - "h": 60 - }, - "frame": { - "x": 186, - "y": 158, - "w": 82, - "h": 60 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 94, - "h": 52 - }, - "frame": { - "x": 268, - "y": 159, - "w": 94, - "h": 52 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 81, - "h": 60 - }, - "frame": { - "x": 0, - "y": 159, - "w": 81, - "h": 60 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:37124082e206aa3cddb045440ab1e9cf:8d64288649efa3066ed9b372190e868f:b1d275d5bba320dd22d2f2c7e56d52ec$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/279_1.png b/public/images/pokemon/variant/279_1.png deleted file mode 100644 index f2aa7b8ac3a..00000000000 Binary files a/public/images/pokemon/variant/279_1.png and /dev/null differ diff --git a/public/images/pokemon/variant/279_2.json b/public/images/pokemon/variant/279_2.json deleted file mode 100644 index 441f7841115..00000000000 --- a/public/images/pokemon/variant/279_2.json +++ /dev/null @@ -1,3611 +0,0 @@ -{ - "textures": [ - { - "image": "279_2.png", - "format": "RGBA8888", - "size": { - "w": 422, - "h": 422 - }, - "scale": 1, - "frames": [ - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 9, - "w": 82, - "h": 60 - }, - "frame": { - "x": 186, - "y": 158, - "w": 82, - "h": 60 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 94, - "h": 52 - }, - "frame": { - "x": 268, - "y": 159, - "w": 94, - "h": 52 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 81, - "h": 60 - }, - "frame": { - "x": 0, - "y": 159, - "w": 81, - "h": 60 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:37124082e206aa3cddb045440ab1e9cf:8d64288649efa3066ed9b372190e868f:b1d275d5bba320dd22d2f2c7e56d52ec$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/279_2.png b/public/images/pokemon/variant/279_2.png deleted file mode 100644 index cde1727d123..00000000000 Binary files a/public/images/pokemon/variant/279_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/279_3.json b/public/images/pokemon/variant/279_3.json deleted file mode 100644 index e2415bee4cd..00000000000 --- a/public/images/pokemon/variant/279_3.json +++ /dev/null @@ -1,3611 +0,0 @@ -{ - "textures": [ - { - "image": "279_3.png", - "format": "RGBA8888", - "size": { - "w": 422, - "h": 422 - }, - "scale": 1, - "frames": [ - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 9, - "w": 82, - "h": 60 - }, - "frame": { - "x": 186, - "y": 158, - "w": 82, - "h": 60 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 94, - "h": 52 - }, - "frame": { - "x": 268, - "y": 159, - "w": 94, - "h": 52 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 81, - "h": 60 - }, - "frame": { - "x": 0, - "y": 159, - "w": 81, - "h": 60 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:37124082e206aa3cddb045440ab1e9cf:8d64288649efa3066ed9b372190e868f:b1d275d5bba320dd22d2f2c7e56d52ec$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/279_3.png b/public/images/pokemon/variant/279_3.png deleted file mode 100644 index a3b93047551..00000000000 Binary files a/public/images/pokemon/variant/279_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/465.json b/public/images/pokemon/variant/465.json new file mode 100644 index 00000000000..a7ff2338057 --- /dev/null +++ b/public/images/pokemon/variant/465.json @@ -0,0 +1,22 @@ +{ + "1": { + "529cc5": "8153c7", + "d65a94": "5ad662", + "3a73ad": "6b3aad", + "bd216b": "21bd69", + "5a193a": "195a2a", + "193a63": "391963", + "295a84": "472984" + }, + "2": { + "529cc5": "ffedb6", + "d65a94": "e67d2f", + "3a73ad": "ebc582", + "bd216b": "b35131", + "31313a": "3d1519", + "5a193a": "752e2e", + "193a63": "705040", + "295a84": "ad875a", + "4a4a52": "57211a" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/465_2.json b/public/images/pokemon/variant/465_2.json deleted file mode 100644 index 7c6d5ea081a..00000000000 --- a/public/images/pokemon/variant/465_2.json +++ /dev/null @@ -1,4094 +0,0 @@ -{ - "textures": [ - { - "image": "465_2.png", - "format": "RGBA8888", - "size": { - "w": 407, - "h": 407 - }, - "scale": 1, - "frames": [ - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0172.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0185.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0186.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0187.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0188.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0179.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0180.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0173.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0174.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0189.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0190.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0177.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0178.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0193.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0194.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0181.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0182.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0175.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0176.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0183.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0184.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 198, - "w": 94, - "h": 66 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 198, - "w": 94, - "h": 66 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 198, - "w": 97, - "h": 66 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 198, - "w": 97, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 86, - "h": 66 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 86, - "h": 66 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0191.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0192.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 254, - "y": 264, - "w": 97, - "h": 66 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 254, - "y": 264, - "w": 97, - "h": 66 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 330, - "w": 98, - "h": 66 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 330, - "w": 98, - "h": 66 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 98, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 98, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 195, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 195, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 292, - "y": 330, - "w": 94, - "h": 66 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 292, - "y": 330, - "w": 94, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:9fb6238f8e585be9be5e520abb59e23d:2204e0edc6a9e184240a16c4ee5faa7c:06d67de9e8d7f60fc986e0c00145d6b1$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/465_2.png b/public/images/pokemon/variant/465_2.png deleted file mode 100644 index db25e88ad98..00000000000 Binary files a/public/images/pokemon/variant/465_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/465_3.json b/public/images/pokemon/variant/465_3.json deleted file mode 100644 index c092ec585e7..00000000000 --- a/public/images/pokemon/variant/465_3.json +++ /dev/null @@ -1,4094 +0,0 @@ -{ - "textures": [ - { - "image": "465_3.png", - "format": "RGBA8888", - "size": { - "w": 407, - "h": 407 - }, - "scale": 1, - "frames": [ - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0172.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 74, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0185.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0186.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0187.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0188.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0179.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0180.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 314, - "y": 0, - "w": 83, - "h": 66 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0173.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0174.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 0, - "y": 64, - "w": 64, - "h": 66 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 64, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 162, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0189.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0190.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 64, - "h": 66 - }, - "frame": { - "x": 250, - "y": 65, - "w": 64, - "h": 66 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 314, - "y": 66, - "w": 93, - "h": 66 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0177.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0178.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0193.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0194.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 76, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0181.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0182.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 152, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0175.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0176.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 240, - "y": 131, - "w": 70, - "h": 67 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0183.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0184.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 310, - "y": 132, - "w": 93, - "h": 66 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 197, - "w": 90, - "h": 66 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 198, - "w": 94, - "h": 66 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 198, - "w": 94, - "h": 66 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 198, - "w": 97, - "h": 66 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 198, - "w": 97, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 86, - "h": 66 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 86, - "h": 66 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0191.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0192.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 86, - "y": 263, - "w": 70, - "h": 67 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 156, - "y": 264, - "w": 98, - "h": 66 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 254, - "y": 264, - "w": 97, - "h": 66 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 254, - "y": 264, - "w": 97, - "h": 66 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 330, - "w": 98, - "h": 66 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 330, - "w": 98, - "h": 66 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 98, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 98, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 195, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 195, - "y": 330, - "w": 97, - "h": 66 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 292, - "y": 330, - "w": 94, - "h": 66 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 292, - "y": 330, - "w": 94, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:9fb6238f8e585be9be5e520abb59e23d:2204e0edc6a9e184240a16c4ee5faa7c:06d67de9e8d7f60fc986e0c00145d6b1$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/465_3.png b/public/images/pokemon/variant/465_3.png deleted file mode 100644 index fe8e764028b..00000000000 Binary files a/public/images/pokemon/variant/465_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index 87f80fd17c4..938c92f5056 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -1017,7 +1017,7 @@ "279": [ 1, 1, - 2 + 1 ], "280": [ 0, @@ -1691,8 +1691,8 @@ ], "465": [ 0, - 2, - 2 + 1, + 1 ], "466": [ 1, @@ -3980,6 +3980,11 @@ 1, 1 ], + "465": [ + 0, + 1, + 1 + ], "592": [ 1, 1, @@ -5690,7 +5695,7 @@ "465": [ 0, 1, - 2 + 1 ], "466": [ 2, @@ -8008,6 +8013,11 @@ 1, 1 ], + "465": [ + 0, + 1, + 1 + ], "592": [ 1, 1, diff --git a/public/images/pokemon/variant/back/465.json b/public/images/pokemon/variant/back/465.json index c8dad539b75..cc868023c2b 100644 --- a/public/images/pokemon/variant/back/465.json +++ b/public/images/pokemon/variant/back/465.json @@ -8,5 +8,14 @@ "bd216b": "21bd69", "31313a": "31313a", "d65a94": "5ad662" + }, + "2": { + "5a193a": "752e2e", + "31313a": "3d1519", + "d65a94": "e67d2f", + "3a73ad": "ebc582", + "295a84": "ad875a", + "bd216b": "b35131", + "193a63": "705040" } -} \ No newline at end of file +} diff --git a/public/images/pokemon/variant/back/465_3.json b/public/images/pokemon/variant/back/465_3.json deleted file mode 100644 index 3141b3c6cee..00000000000 --- a/public/images/pokemon/variant/back/465_3.json +++ /dev/null @@ -1,4094 +0,0 @@ -{ - "textures": [ - { - "image": "465_3.png", - "format": "RGBA8888", - "size": { - "w": 407, - "h": 407 - }, - "scale": 1, - "frames": [ - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0185.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0186.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 83, - "h": 64 - }, - "frame": { - "x": 0, - "y": 0, - "w": 83, - "h": 64 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0187.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0188.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 3, - "w": 74, - "h": 64 - }, - "frame": { - "x": 83, - "y": 0, - "w": 74, - "h": 64 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 83, - "h": 65 - }, - "frame": { - "x": 157, - "y": 0, - "w": 83, - "h": 65 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0172.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 74, - "h": 65 - }, - "frame": { - "x": 240, - "y": 0, - "w": 74, - "h": 65 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0183.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0184.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 65 - }, - "frame": { - "x": 314, - "y": 0, - "w": 93, - "h": 65 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0189.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0190.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 2, - "w": 65, - "h": 65 - }, - "frame": { - "x": 0, - "y": 64, - "w": 65, - "h": 65 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0179.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0180.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 83, - "h": 66 - }, - "frame": { - "x": 65, - "y": 64, - "w": 83, - "h": 66 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 148, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 148, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 148, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 98, - "h": 65 - }, - "frame": { - "x": 148, - "y": 65, - "w": 98, - "h": 65 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 246, - "y": 65, - "w": 88, - "h": 66 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0173.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0174.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 17, - "y": 1, - "w": 65, - "h": 66 - }, - "frame": { - "x": 334, - "y": 65, - "w": 65, - "h": 66 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 93, - "h": 66 - }, - "frame": { - "x": 0, - "y": 130, - "w": 93, - "h": 66 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0177.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0178.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 93, - "y": 130, - "w": 76, - "h": 66 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0191.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0192.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 70, - "h": 66 - }, - "frame": { - "x": 169, - "y": 130, - "w": 70, - "h": 66 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0181.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0182.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 88, - "h": 66 - }, - "frame": { - "x": 239, - "y": 131, - "w": 88, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0193.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0194.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 76, - "h": 66 - }, - "frame": { - "x": 327, - "y": 131, - "w": 76, - "h": 66 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 0, - "y": 196, - "w": 86, - "h": 66 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 196, - "w": 90, - "h": 66 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 196, - "w": 90, - "h": 66 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 196, - "w": 90, - "h": 66 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 90, - "h": 66 - }, - "frame": { - "x": 86, - "y": 196, - "w": 90, - "h": 66 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 197, - "w": 94, - "h": 66 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 176, - "y": 197, - "w": 94, - "h": 66 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 197, - "w": 97, - "h": 66 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 270, - "y": 197, - "w": 97, - "h": 66 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 98, - "h": 66 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 98, - "h": 66 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 98, - "h": 66 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 0, - "y": 262, - "w": 98, - "h": 66 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0175.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0176.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 70, - "h": 67 - }, - "frame": { - "x": 98, - "y": 262, - "w": 70, - "h": 67 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 168, - "y": 263, - "w": 97, - "h": 66 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 168, - "y": 263, - "w": 97, - "h": 66 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 265, - "y": 263, - "w": 98, - "h": 66 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 98, - "h": 66 - }, - "frame": { - "x": 265, - "y": 263, - "w": 98, - "h": 66 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 0, - "y": 328, - "w": 97, - "h": 66 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 0, - "y": 328, - "w": 97, - "h": 66 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 97, - "y": 329, - "w": 97, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 97, - "h": 66 - }, - "frame": { - "x": 97, - "y": 329, - "w": 97, - "h": 66 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 194, - "y": 329, - "w": 94, - "h": 66 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 94, - "h": 66 - }, - "frame": { - "x": 194, - "y": 329, - "w": 94, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 288, - "y": 329, - "w": 86, - "h": 66 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 98, - "h": 67 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 86, - "h": 66 - }, - "frame": { - "x": 288, - "y": 329, - "w": 86, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:cd959f202c47f9ac8c3bb6f34f5e04e0:4fa06c9f7e584689369e76122c6343e8:06d67de9e8d7f60fc986e0c00145d6b1$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/465_3.png b/public/images/pokemon/variant/back/465_3.png deleted file mode 100644 index 876725447af..00000000000 Binary files a/public/images/pokemon/variant/back/465_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/back/female/465.json b/public/images/pokemon/variant/back/female/465.json new file mode 100644 index 00000000000..ed257655add --- /dev/null +++ b/public/images/pokemon/variant/back/female/465.json @@ -0,0 +1,21 @@ +{ + "1": { + "193a63": "391963", + "295a84": "472984", + "3a73ad": "6b3aad", + "000000": "000000", + "5a193a": "195a2a", + "bd216b": "21bd69", + "31313a": "31313a", + "d65a94": "5ad662" + }, + "2": { + "5a193a": "752e2e", + "31313a": "3d1519", + "d65a94": "e67d2f", + "3a73ad": "ebc582", + "295a84": "ad875a", + "bd216b": "b35131", + "193a63": "705040" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/864.json b/public/images/pokemon/variant/exp/864.json index cf8ae42b0fd..ccfc0f2d88d 100644 --- a/public/images/pokemon/variant/exp/864.json +++ b/public/images/pokemon/variant/exp/864.json @@ -3,44 +3,36 @@ "bcb9be": "ae4c95", "f9f2fc": "ffc0e5", "7b787c": "793d6d", - "e1dfe2": "e88cc5", - "d0cfd0": "ce6bac", - "b8b4ba": "d7d2f6", + "dcd9dd": "e88cc5", + "c9c0ce": "ce6bac", + "cbc2d1": "d7d2f6", "938f94": "b465b9", - "6c7275": "d3ffff", - "9362e6": "80a4ff", - "fcfcfc": "fcfcfc", - "4a494e": "a7e6e5", - "c6a4ff": "bed5ff", - "101010": "101010", - "3b3a3f": "4d8894", - "aeadae": "e88cc5", - "686568": "686568", - "6f6d71": "793d6d", - "b5b4b6": "ce6bac", - "706e6d": "7d7c75", + "fbf2ff": "d3ffff", + "e66294": "80a4ff", + "c6bbcb": "a7e6e5", + "ffa4c5": "bed5ff", + "7f806a": "4d8894", + "a8a0ac": "e88cc5", + "7c7a78": "793d6d", + "bbb4bc": "ce6bac", "af9e9e": "42a2b1" }, "2": { "bcb9be": "055946", "f9f2fc": "21be70", "7b787c": "004140", - "e1dfe2": "12a169", - "d0cfd0": "0a7a57", - "b8b4ba": "567f83", + "dcd9dd": "12a169", + "c9c0ce": "0a7a57", + "cbc2d1": "567f83", "938f94": "2b5458", - "6c7275": "874059", - "9362e6": "15c05f", - "fcfcfc": "fcfcfc", - "4a494e": "773050", - "c6a4ff": "8ff3a3", - "101010": "101010", - "3b3a3f": "4b1f28", - "aeadae": "12a169", - "686568": "686568", - "6f6d71": "004140", - "b5b4b6": "0a7a57", - "706e6d": "7d7c75", + "fbf2ff": "874059", + "e66294": "15c05f", + "c6bbcb": "773050", + "ffa4c5": "8ff3a3", + "7f806a": "4b1f28", + "a8a0ac": "12a169", + "7c7a78": "004140", + "bbb4bc": "0a7a57", "af9e9e": "48c492" } -} \ No newline at end of file +} diff --git a/public/images/pokemon/variant/female/465.json b/public/images/pokemon/variant/female/465.json new file mode 100644 index 00000000000..a7ff2338057 --- /dev/null +++ b/public/images/pokemon/variant/female/465.json @@ -0,0 +1,22 @@ +{ + "1": { + "529cc5": "8153c7", + "d65a94": "5ad662", + "3a73ad": "6b3aad", + "bd216b": "21bd69", + "5a193a": "195a2a", + "193a63": "391963", + "295a84": "472984" + }, + "2": { + "529cc5": "ffedb6", + "d65a94": "e67d2f", + "3a73ad": "ebc582", + "bd216b": "b35131", + "31313a": "3d1519", + "5a193a": "752e2e", + "193a63": "705040", + "295a84": "ad875a", + "4a4a52": "57211a" + } +} \ No newline at end of file diff --git a/public/images/types_ja.png b/public/images/types_ja.png index e60d8e071aa..a6f3b00607f 100644 Binary files a/public/images/types_ja.png and b/public/images/types_ja.png differ diff --git a/public/images/ui/egg_summary_bg.png b/public/images/ui/egg_summary_bg.png new file mode 100644 index 00000000000..e81934b9d75 Binary files /dev/null and b/public/images/ui/egg_summary_bg.png differ diff --git a/public/images/ui/egg_summary_bg_blank.png b/public/images/ui/egg_summary_bg_blank.png new file mode 100644 index 00000000000..09bcb63cfa3 Binary files /dev/null and b/public/images/ui/egg_summary_bg_blank.png differ diff --git a/public/images/ui/icon_egg_move.png b/public/images/ui/icon_egg_move.png new file mode 100644 index 00000000000..a5b0bff4ace Binary files /dev/null and b/public/images/ui/icon_egg_move.png differ diff --git a/public/images/ui/icon_lock.png b/public/images/ui/icon_lock.png new file mode 100644 index 00000000000..6a12efa15e8 Binary files /dev/null and b/public/images/ui/icon_lock.png differ diff --git a/public/images/ui/icon_stop.png b/public/images/ui/icon_stop.png new file mode 100644 index 00000000000..6d9c201695a Binary files /dev/null and b/public/images/ui/icon_stop.png differ diff --git a/public/images/ui/legacy/egg_summary_bg.png b/public/images/ui/legacy/egg_summary_bg.png new file mode 100644 index 00000000000..658f5df0e96 Binary files /dev/null and b/public/images/ui/legacy/egg_summary_bg.png differ diff --git a/public/images/ui/legacy/icon_egg_move.png b/public/images/ui/legacy/icon_egg_move.png new file mode 100644 index 00000000000..a5b0bff4ace Binary files /dev/null and b/public/images/ui/legacy/icon_egg_move.png differ diff --git a/public/images/ui/legacy/icon_lock.png b/public/images/ui/legacy/icon_lock.png new file mode 100644 index 00000000000..6a12efa15e8 Binary files /dev/null and b/public/images/ui/legacy/icon_lock.png differ diff --git a/public/images/ui/legacy/icon_stop.png b/public/images/ui/legacy/icon_stop.png new file mode 100644 index 00000000000..6d9c201695a Binary files /dev/null and b/public/images/ui/legacy/icon_stop.png differ diff --git a/public/images/ui/legacy/settings_icon.png b/public/images/ui/legacy/settings_icon.png new file mode 100644 index 00000000000..21680cce7fc Binary files /dev/null and b/public/images/ui/legacy/settings_icon.png differ diff --git a/public/images/ui/legacy/summary_moves_effect_de.png b/public/images/ui/legacy/summary_moves_effect_de.png deleted file mode 100644 index 8d6ef024cf3..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_de.png and /dev/null differ diff --git a/public/images/ui/legacy/summary_moves_effect_es.png b/public/images/ui/legacy/summary_moves_effect_es.png deleted file mode 100644 index a48f90cc8f6..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_es.png and /dev/null differ diff --git a/public/images/ui/legacy/summary_moves_effect_fr.png b/public/images/ui/legacy/summary_moves_effect_fr.png deleted file mode 100644 index 9d65c920176..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_fr.png and /dev/null differ diff --git a/public/images/ui/legacy/summary_moves_effect_it.png b/public/images/ui/legacy/summary_moves_effect_it.png deleted file mode 100644 index 8d6ef024cf3..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_it.png and /dev/null differ diff --git a/public/images/ui/legacy/summary_moves_effect_pt-BR.png b/public/images/ui/legacy/summary_moves_effect_pt-BR.png deleted file mode 100644 index f5a0c2ea736..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_pt-BR.png and /dev/null differ diff --git a/public/images/ui/legacy/summary_moves_effect_zh-CN.png b/public/images/ui/legacy/summary_moves_effect_zh-CN.png deleted file mode 100644 index 8d6ef024cf3..00000000000 Binary files a/public/images/ui/legacy/summary_moves_effect_zh-CN.png and /dev/null differ diff --git a/public/images/ui/settings_icon.png b/public/images/ui/settings_icon.png new file mode 100644 index 00000000000..21680cce7fc Binary files /dev/null and b/public/images/ui/settings_icon.png differ diff --git a/src/@types/common.ts b/src/@types/common.ts index 6868d766008..fcd946656dc 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -1,3 +1,3 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean; diff --git a/src/@types/i18next.d.ts b/src/@types/i18next.d.ts index d895659acef..9e8418a8977 100644 --- a/src/@types/i18next.d.ts +++ b/src/@types/i18next.d.ts @@ -1,4 +1,4 @@ -import { type enConfig } from "#app/locales/en/config.js"; +import { type enConfig } from "#app/locales/en/config"; import { TOptions } from "i18next"; //TODO: this needs to be type properly in the future diff --git a/src/battle-scene.ts b/src/battle-scene.ts index df4e3c37733..94c2da7fdf0 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -63,7 +63,7 @@ import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { UiTheme } from "#enums/ui-theme"; -import { TimedEventManager } from "#app/timed-event-manager.js"; +import { TimedEventManager } from "#app/timed-event-manager"; import i18next from "i18next"; import { TrainerType } from "#enums/trainer-type"; import { battleSpecDialogue } from "./data/dialogue"; @@ -130,7 +130,7 @@ export default class BattleScene extends SceneBase { public gameSpeed: integer = 1; public damageNumbersMode: integer = 0; public reroll: boolean = false; - public shopCursorTarget: number = ShopCursorTarget.CHECK_TEAM; + public shopCursorTarget: number = ShopCursorTarget.REWARDS; public showMovesetFlyout: boolean = true; public showArenaFlyout: boolean = true; public showTimeOfDayWidget: boolean = true; @@ -161,6 +161,13 @@ export default class BattleScene extends SceneBase { public moveAnimations: boolean = true; public expGainsSpeed: integer = 0; public skipSeenDialogues: boolean = false; + /** + * Determines if the egg hatching animation should be skipped + * - 0 = Never (never skip animation) + * - 1 = Ask (ask to skip animation when hatching 2 or more eggs) + * - 2 = Always (automatically skip animation when hatching 2 or more eggs) + */ + public eggSkipPreference: number = 0; /** * Defines the experience gain display mode. @@ -841,12 +848,13 @@ export default class BattleScene extends SceneBase { } addEnemyPokemon(species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean = false, dataSource?: PokemonData, postProcess?: (enemyPokemon: EnemyPokemon) => void): EnemyPokemon { + if (Overrides.OPP_LEVEL_OVERRIDE > 0) { + level = Overrides.OPP_LEVEL_OVERRIDE; + } if (Overrides.OPP_SPECIES_OVERRIDE) { species = getPokemonSpecies(Overrides.OPP_SPECIES_OVERRIDE); - } - - if (Overrides.OPP_LEVEL_OVERRIDE !== 0) { - level = Overrides.OPP_LEVEL_OVERRIDE; + // The fact that a Pokemon is a boss or not can change based on its Species and level + boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1; } const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource); @@ -854,7 +862,7 @@ export default class BattleScene extends SceneBase { overrideModifiers(this, false); overrideHeldItems(this, pokemon, false); if (boss && !dataSource) { - const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295)); + const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296)); for (let s = 0; s < pokemon.ivs.length; s++) { pokemon.ivs[s] = Math.round(Phaser.Math.Linear(Math.min(pokemon.ivs[s], secondaryIvs[s]), Math.max(pokemon.ivs[s], secondaryIvs[s]), 0.75)); @@ -960,6 +968,16 @@ export default class BattleScene extends SceneBase { this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym(); } + /** + * Generates a random number using the current battle's seed + * + * This calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts` + * which calls {@linkcode Utils.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` + * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) + */ randBattleSeedInt(range: integer, min: integer = 0): integer { return this.currentBattle?.randSeedInt(this, range, min); } @@ -973,6 +991,7 @@ export default class BattleScene extends SceneBase { this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24)); console.log("Seed:", this.seed); + this.resetSeed(); // Properly resets RNG after saving and quitting a session this.disableMenu = false; @@ -1110,7 +1129,8 @@ export default class BattleScene extends SceneBase { doubleTrainer = false; } } - newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); + const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); + newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant); this.field.add(newTrainer); } } @@ -1327,6 +1347,13 @@ export default class BattleScene extends SceneBase { } getEncounterBossSegments(waveIndex: integer, level: integer, species?: PokemonSpecies, forceBoss: boolean = false): integer { + if (Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE > 1) { + return Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE; + } else if (Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE === 1) { + // The rest of the code expects to be returned 0 and not 1 if the enemy is not a boss + return 0; + } + if (this.gameMode.isDaily && this.gameMode.isWaveFinal(waveIndex)) { return 5; } @@ -1791,6 +1818,7 @@ export default class BattleScene extends SceneBase { config = config ?? {}; try { const keyDetails = key.split("/"); + config["volume"] = config["volume"] ?? 1; switch (keyDetails[0]) { case "level_up_fanfare": case "item_fanfare": @@ -1800,11 +1828,11 @@ export default class BattleScene extends SceneBase { case "evolution_fanfare": // These sounds are loaded in as BGM, but played as sound effects // When these sounds are updated in updateVolume(), they are treated as BGM however because they are placed in the BGM Cache through being called by playSoundWithoutBGM() - config["volume"] = this.masterVolume * this.bgmVolume; + config["volume"] *= (this.masterVolume * this.bgmVolume); break; case "battle_anims": case "cry": - config["volume"] = this.masterVolume * this.fieldVolume; + config["volume"] *= (this.masterVolume * this.fieldVolume); //PRSFX sound files are unusually loud if (keyDetails[1].startsWith("PRSFX- ")) { config["volume"] *= 0.5; @@ -1812,10 +1840,10 @@ export default class BattleScene extends SceneBase { break; case "ui": //As of, right now this applies to the "select", "menu_open", "error" sound effects - config["volume"] = this.masterVolume * this.uiVolume; + config["volume"] *= (this.masterVolume * this.uiVolume); break; case "se": - config["volume"] = this.masterVolume * this.seVolume; + config["volume"] *= (this.masterVolume * this.seVolume); break; } this.sound.play(key, config); @@ -2172,8 +2200,14 @@ export default class BattleScene extends SceneBase { return true; } - findPhase(phaseFilter: (phase: Phase) => boolean): Phase | undefined { - return this.phaseQueue.find(phaseFilter); + /** + * Find a specific {@linkcode Phase} in the phase queue. + * + * @param phaseFilter filter function to use to find the wanted phase + * @returns the found phase or undefined if none found + */ + findPhase

(phaseFilter: (phase: P) => boolean): P | undefined { + return this.phaseQueue.find(phaseFilter) as P; } tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean { @@ -2610,7 +2644,7 @@ export default class BattleScene extends SceneBase { if (mods.length < 1) { return mods; } - const rand = Math.floor(Utils.randSeedInt(mods.length)); + const rand = Utils.randSeedInt(mods.length); return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))]; }; modifiers = shuffleModifiers(modifiers); @@ -2732,6 +2766,35 @@ export default class BattleScene extends SceneBase { (window as any).gameInfo = gameInfo; } + /** + * This function retrieves the sprite and audio keys for active Pokemon. + * Active Pokemon include both enemy and player Pokemon of the current wave. + * Note: Questions on garbage collection go to @frutescens + * @returns a string array of active sprite and audio keys that should not be deleted + */ + getActiveKeys(): string[] { + const keys: string[] = []; + const playerParty = this.getParty(); + playerParty.forEach(p => { + keys.push(p.getSpriteKey(true)); + keys.push(p.getBattleSpriteKey(true, true)); + keys.push("cry/" + p.species.getCryKey(p.formIndex)); + if (p.fusionSpecies) { + keys.push("cry/"+p.fusionSpecies.getCryKey(p.fusionFormIndex)); + } + }); + // enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon + const enemyParty = this.getEnemyParty(); + enemyParty.forEach(p => { + keys.push(p.getSpriteKey(true)); + keys.push("cry/" + p.species.getCryKey(p.formIndex)); + if (p.fusionSpecies) { + keys.push("cry/"+p.fusionSpecies.getCryKey(p.fusionFormIndex)); + } + }); + return keys; + } + /** * Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus) * @param pokemon The (enemy) pokemon diff --git a/src/battle.ts b/src/battle.ts index f9afbf09604..a3e7b0a4336 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -1,12 +1,12 @@ import BattleScene from "./battle-scene"; -import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./field/pokemon"; import { Command } from "./ui/command-ui-handler"; import * as Utils from "./utils"; import Trainer, { TrainerVariant } from "./field/trainer"; import { GameMode } from "./game-mode"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; -import {trainerConfigs} from "#app/data/trainer-config"; +import { trainerConfigs } from "#app/data/trainer-config"; +import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; import { Moves } from "#enums/moves"; @@ -31,46 +31,55 @@ export enum BattlerIndex { export interface TurnCommand { command: Command; - cursor?: integer; + cursor?: number; move?: QueuedMove; targets?: BattlerIndex[]; skip?: boolean; args?: any[]; } +export interface FaintLogEntry { + pokemon: Pokemon, + turn: number +} + interface TurnCommands { - [key: integer]: TurnCommand | null + [key: number]: TurnCommand | null } export default class Battle { protected gameMode: GameMode; - public waveIndex: integer; + public waveIndex: number; public battleType: BattleType; public battleSpec: BattleSpec; public trainer: Trainer | null; - public enemyLevels: integer[] | undefined; - public enemyParty: EnemyPokemon[]; - public seenEnemyPartyMemberIds: Set; + public enemyLevels: number[] | undefined; + public enemyParty: EnemyPokemon[] = []; + public seenEnemyPartyMemberIds: Set = new Set(); public double: boolean; - public started: boolean; - public enemySwitchCounter: integer; - public turn: integer; + public started: boolean = false; + public enemySwitchCounter: number = 0; + public turn: number = 0; public turnCommands: TurnCommands; - public playerParticipantIds: Set; - public battleScore: integer; - public postBattleLoot: PokemonHeldItemModifier[]; - public escapeAttempts: integer; + public playerParticipantIds: Set = new Set(); + public battleScore: number = 0; + public postBattleLoot: PokemonHeldItemModifier[] = []; + public escapeAttempts: number = 0; public lastMove: Moves; - public battleSeed: string; - private battleSeedState: string | null; - public moneyScattered: number; - public lastUsedPokeball: PokeballType | null; - public playerFaints: number; // The amount of times pokemon on the players side have fainted - public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted + public battleSeed: string = Utils.randomString(16, true); + private battleSeedState: string | null = null; + public moneyScattered: number = 0; + public lastUsedPokeball: PokeballType | null = null; + /** The number of times a Pokemon on the player's side has fainted this battle */ + public playerFaints: number = 0; + /** The number of times a Pokemon on the enemy's side has fainted this battle */ + public enemyFaints: number = 0; + public playerFaintsHistory: FaintLogEntry[] = []; + public enemyFaintsHistory: FaintLogEntry[] = []; - private rngCounter: integer = 0; + private rngCounter: number = 0; - constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer?: Trainer, double?: boolean) { + constructor(gameMode: GameMode, waveIndex: number, battleType: BattleType, trainer?: Trainer, double?: boolean) { this.gameMode = gameMode; this.waveIndex = waveIndex; this.battleType = battleType; @@ -79,22 +88,7 @@ export default class Battle { this.enemyLevels = battleType !== BattleType.TRAINER ? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave()) : trainer?.getPartyLevels(this.waveIndex); - this.enemyParty = []; - this.seenEnemyPartyMemberIds = new Set(); - this.double = !!double; - this.enemySwitchCounter = 0; - this.turn = 0; - this.playerParticipantIds = new Set(); - this.battleScore = 0; - this.postBattleLoot = []; - this.escapeAttempts = 0; - this.started = false; - this.battleSeed = Utils.randomString(16, true); - this.battleSeedState = null; - this.moneyScattered = 0; - this.lastUsedPokeball = null; - this.playerFaints = 0; - this.enemyFaints = 0; + this.double = double ?? false; } private initBattleSpec(): void { @@ -105,7 +99,7 @@ export default class Battle { this.battleSpec = spec; } - private getLevelForWave(): integer { + private getLevelForWave(): number { const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex); const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2); const bossMultiplier = 1.2; @@ -138,7 +132,7 @@ export default class Battle { return rand / value; } - getBattlerCount(): integer { + getBattlerCount(): number { return this.double ? 2 : 1; } @@ -367,7 +361,13 @@ export default class Battle { return null; } - randSeedInt(scene: BattleScene, range: integer, min: integer = 0): integer { + /** + * Generates a random number using the current battle's seed. Calls {@linkcode Utils.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) + */ + randSeedInt(scene: BattleScene, range: number, min: number = 0): number { if (range <= 1) { return min; } @@ -392,7 +392,7 @@ export default class Battle { } export class FixedBattle extends Battle { - constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) { + constructor(scene: BattleScene, waveIndex: number, config: FixedBattleConfig) { super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double); if (config.getEnemyParty) { this.enemyParty = config.getEnemyParty(scene); @@ -408,7 +408,7 @@ export class FixedBattleConfig { public double: boolean; public getTrainer: GetTrainerFunc; public getEnemyParty: GetEnemyPartyFunc; - public seedOffsetWaveIndex: integer; + public seedOffsetWaveIndex: number; setBattleType(battleType: BattleType): FixedBattleConfig { this.battleType = battleType; @@ -430,7 +430,7 @@ export class FixedBattleConfig { return this; } - setSeedOffsetWave(seedOffsetWaveIndex: integer): FixedBattleConfig { + setSeedOffsetWave(seedOffsetWaveIndex: number): FixedBattleConfig { this.seedOffsetWaveIndex = seedOffsetWaveIndex; return this; } @@ -476,7 +476,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand } export interface FixedBattleConfigs { - [key: integer]: FixedBattleConfig + [key: number]: FixedBattleConfig } /** * Youngster/Lass on 5 diff --git a/src/configs/inputs/pad_procon.ts b/src/configs/inputs/pad_procon.ts index 8cb707f882a..be751cc3acc 100644 --- a/src/configs/inputs/pad_procon.ts +++ b/src/configs/inputs/pad_procon.ts @@ -1,4 +1,4 @@ -import {SettingGamepad} from "#app/system/settings/settings-gamepad.js"; +import {SettingGamepad} from "#app/system/settings/settings-gamepad"; import {Button} from "#enums/buttons"; /** diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000000..a2f7e47b996 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1 @@ +export const PLAYER_PARTY_MAX_SIZE = 6; diff --git a/src/data/ability.ts b/src/data/ability.ts old mode 100644 new mode 100755 index 8b7a7772efe..6acf77cfca5 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2,20 +2,18 @@ import Pokemon, { HitResult, PlayerPokemon, PokemonMove } from "../field/pokemon import { Type } from "./type"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; -import { BattleStat, getBattleStatName } from "./battle-stat"; import { getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; -import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr } from "./move"; +import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; -import { Stat, getStatName } from "./pokemon-stat"; import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { TerrainType } from "./terrain"; import { SpeciesFormChangeManualTrigger, SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "./pokemon-forms"; import i18next from "i18next"; -import { Localizable } from "#app/interfaces/locales.js"; +import { Localizable } from "#app/interfaces/locales"; import { Command } from "../ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; import { getPokeballName } from "./pokeball"; @@ -25,10 +23,11 @@ import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; import { MovePhase } from "#app/phases/move-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import BattleScene from "#app/battle-scene"; export class Ability implements Localizable { @@ -126,7 +125,7 @@ type AbAttrCondition = (pokemon: Pokemon) => boolean; type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; -type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean; +type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean; export abstract class AbAttr { public showAbility: boolean; @@ -203,38 +202,36 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { } } -export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { +export class PostBattleInitStatStageChangeAbAttr extends PostBattleInitAbAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; private selfTarget: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean) { + constructor(stats: BattleStat[], stages: number, selfTarget?: boolean) { super(); - this.stats = typeof(stats) === "number" - ? [ stats as BattleStat ] - : stats as BattleStat[]; - this.levels = levels; + this.stats = stats; + this.stages = stages; this.selfTarget = !!selfTarget; } applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const statChangePhases: StatChangePhase[] = []; + const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { if (this.selfTarget) { - statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + statStageChangePhases.push(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages)); } else { for (const opponent of pokemon.getOpponents()) { - statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + statStageChangePhases.push(new StatStageChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.stages)); } } - for (const statChangePhase of statChangePhases) { - if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { - pokemon.scene.pushPhase(statChangePhase); + for (const statStageChangePhase of statStageChangePhases) { + if (!this.selfTarget && !statStageChangePhase.getPokemon()?.summonData) { + pokemon.scene.pushPhase(statStageChangePhase); } else { // TODO: This causes the ability bar to be shown at the wrong time - pokemon.scene.unshiftPhase(statChangePhase); + pokemon.scene.unshiftPhase(statStageChangePhase); } } } @@ -313,7 +310,7 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr { constructor(moveType: Type, damageMultiplier: number) { - super((user, target, move) => move.type === moveType, damageMultiplier); + super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier); } } @@ -402,15 +399,15 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { } } -class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr { +class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { private stat: BattleStat; - private levels: integer; + private stages: number; - constructor(immuneType: Type, stat: BattleStat, levels: integer, condition?: AbAttrCondition) { + constructor(immuneType: Type, stat: BattleStat, stages: number, condition?: AbAttrCondition) { super(immuneType, condition); this.stat = stat; - this.levels = levels; + this.stages = stages; } applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -419,7 +416,7 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr { if (ret) { cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } } @@ -458,7 +455,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(pokemon.getMoveType(move), attacker) < 2) { + if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker) < 2) { cancelled.value = true; // Suppresses "No Effect" message (args[0] as Utils.NumberHolder).value = 0; return true; @@ -475,6 +472,47 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } } +/** + * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Tera_Shell_(Ability) | Tera Shell} + * When the source is at full HP, incoming attacks will have a maximum 0.5x type effectiveness multiplier. + * @extends PreDefendAbAttr + */ +export class FullHpResistTypeAbAttr extends PreDefendAbAttr { + /** + * Reduces a type multiplier to 0.5 if the source is at full HP. + * @param pokemon {@linkcode Pokemon} the Pokemon with this ability + * @param passive n/a + * @param simulated n/a (this doesn't change game state) + * @param attacker n/a + * @param move {@linkcode Move} the move being used on the source + * @param cancelled n/a + * @param args `[0]` a container for the move's current type effectiveness multiplier + * @returns `true` if the move's effectiveness is reduced; `false` otherwise + */ + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + const typeMultiplier = args[0]; + if (!(typeMultiplier && typeMultiplier instanceof Utils.NumberHolder)) { + return false; + } + + if (move && move.hasAttr(FixedDamageAttr)) { + return false; + } + + if (pokemon.isFullHp() && typeMultiplier.value > 0.5) { + typeMultiplier.value = 0.5; + return true; + } + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return i18next.t("abilityTriggers:fullHpResistType", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) + }); + } +} + export class PostDefendAbAttr extends AbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; @@ -518,7 +556,7 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { } if (battlerTag.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ BattleStat.DEF ], -1)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1)); } else { attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); } @@ -547,8 +585,8 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { } } -export class PostStatChangeAbAttr extends AbAttr { - applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { +export class PostStatStageChangeAbAttr extends AbAttr { + applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], stagesChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { return false; } } @@ -594,20 +632,20 @@ export class WonderSkinAbAttr extends PreDefendAbAttr { } } -export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr { +export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { private stat: BattleStat; - private levels: integer; + private stages: number; - constructor(immuneCondition: PreDefendAbAttrCondition, stat: BattleStat, levels: integer) { + constructor(immuneCondition: PreDefendAbAttrCondition, stat: BattleStat, stages: number) { super(immuneCondition); this.stat = stat; - this.levels = levels; + this.stages = stages; } applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret && !simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } return ret; @@ -642,19 +680,19 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { } } -export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { +export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; private stat: BattleStat; - private levels: integer; + private stages: number; private selfTarget: boolean; private allOthers: boolean; - constructor(condition: PokemonDefendCondition, stat: BattleStat, levels: integer, selfTarget: boolean = true, allOthers: boolean = false) { + constructor(condition: PokemonDefendCondition, stat: BattleStat, stages: number, selfTarget: boolean = true, allOthers: boolean = false) { super(true); this.condition = condition; this.stat = stat; - this.levels = levels; + this.stages = stages; this.selfTarget = selfTarget; this.allOthers = allOthers; } @@ -668,11 +706,11 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { if (this.allOthers) { const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); for (const other of otherPokemon) { - other.scene.unshiftPhase(new StatChangePhase(other.scene, (other).getBattlerIndex(), false, [ this.stat ], this.levels)); + other.scene.unshiftPhase(new StatStageChangePhase(other.scene, (other).getBattlerIndex(), false, [ this.stat ], this.stages)); } return true; } - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); return true; } @@ -680,20 +718,20 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { } } -export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr { +export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; private hpGate: number; private stats: BattleStat[]; - private levels: integer; + private stages: number; private selfTarget: boolean; - constructor(condition: PokemonDefendCondition, hpGate: number, stats: BattleStat[], levels: integer, selfTarget: boolean = true) { + constructor(condition: PokemonDefendCondition, hpGate: number, stats: BattleStat[], stages: number, selfTarget: boolean = true) { super(true); this.condition = condition; this.hpGate = hpGate; this.stats = stats; - this.levels = levels; + this.stages = stages; this.selfTarget = selfTarget; } @@ -703,8 +741,8 @@ export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr { const damageReceived = lastAttackReceived?.damage || 0; if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) { - if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels)); + if (!simulated ) { + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); } return true; } @@ -809,7 +847,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { } export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { - private chance: integer; + public chance: integer; private effects: StatusEffect[]; constructor(chance: integer, ...effects: StatusEffect[]) { @@ -872,20 +910,20 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { } } -export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr { +export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { private stat: BattleStat; - private levels: integer; + private stages: number; - constructor(stat: BattleStat, levels: integer) { + constructor(stat: BattleStat, stages: number) { super(); this.stat = stat; - this.levels = levels; + this.stages = stages; } applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } return true; @@ -1047,7 +1085,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { } applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (!attacker.summonData.disabledMove) { + if (attacker.getTag(BattlerTagType.DISABLED) === null) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) { if (simulated) { return true; @@ -1055,40 +1093,31 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.attacker = attacker; this.move = move; - - attacker.summonData.disabledMove = move.id; - attacker.summonData.disabledTurns = 4; + this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id); return true; } } return false; } - - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:postDefendMoveDisable", { - pokemonNameWithAffix: getPokemonNameWithAffix(this.attacker), - moveName: this.move.name, - }); - } } -export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { - private condition: PokemonStatChangeCondition; +export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChangeAbAttr { + private condition: PokemonStatStageChangeCondition; private statsToChange: BattleStat[]; - private levels: integer; + private stages: number; - constructor(condition: PokemonStatChangeCondition, statsToChange: BattleStat[], levels: integer) { + constructor(condition: PokemonStatStageChangeCondition, statsToChange: BattleStat[], stages: number) { super(true); this.condition = condition; this.statsToChange = statsToChange; - this.levels = levels; + this.stages = stages; } - applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean { - if (this.condition(pokemon, statsChanged, levelsChanged) && !selfTarget) { + applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: number, selfTarget: boolean, args: any[]): boolean { + if (this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget) { if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); } return true; } @@ -1169,13 +1198,13 @@ export class FieldPreventExplosiveMovesAbAttr extends AbAttr { } /** - * Multiplies a BattleStat if the checked Pokemon lacks this ability. + * Multiplies a Stat if the checked Pokemon lacks this ability. * If this ability cannot stack, a BooleanHolder can be used to prevent this from stacking. - * @see {@link applyFieldBattleStatMultiplierAbAttrs} - * @see {@link applyFieldBattleStat} + * @see {@link applyFieldStatMultiplierAbAttrs} + * @see {@link applyFieldStat} * @see {@link Utils.BooleanHolder} */ -export class FieldMultiplyBattleStatAbAttr extends AbAttr { +export class FieldMultiplyStatAbAttr extends AbAttr { private stat: Stat; private multiplier: number; private canStack: boolean; @@ -1189,7 +1218,7 @@ export class FieldMultiplyBattleStatAbAttr extends AbAttr { } /** - * applyFieldBattleStat: Tries to multiply a Pokemon's BattleStat + * applyFieldStat: Tries to multiply a Pokemon's Stat * @param pokemon {@linkcode Pokemon} the Pokemon using this ability * @param passive {@linkcode boolean} unused * @param stat {@linkcode Stat} the type of the checked stat @@ -1199,12 +1228,12 @@ export class FieldMultiplyBattleStatAbAttr extends AbAttr { * @param args {any[]} unused * @returns true if this changed the checked stat, false otherwise. */ - applyFieldBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { + applyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { if (!this.canStack && hasApplied.value) { return false; } - if (this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyBattleStatAbAttr).every(attr => (attr as FieldMultiplyBattleStatAbAttr).stat !== stat)) { + if (this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyStatAbAttr).every(attr => (attr as FieldMultiplyStatAbAttr).stat !== stat)) { statValue.value *= this.multiplier; hasApplied.value = true; return true; @@ -1424,7 +1453,7 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { export class MoveTypePowerBoostAbAttr extends MovePowerBoostAbAttr { constructor(boostedType: Type, powerMultiplier?: number) { - super((pokemon, defender, move) => move.type === boostedType, powerMultiplier || 1.5); + super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5); } } @@ -1508,7 +1537,7 @@ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostA * @param powerMultiplier - The multiplier to apply to the move's power, defaults to 1.5 if not provided. */ constructor(boostedType: Type, powerMultiplier?: number) { - super((pokemon, defender, move) => move.type === boostedType, powerMultiplier || 1.5); + super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5); } } @@ -1538,22 +1567,22 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr } } -export class BattleStatMultiplierAbAttr extends AbAttr { - private battleStat: BattleStat; +export class StatMultiplierAbAttr extends AbAttr { + private stat: BattleStat; private multiplier: number; private condition: PokemonAttackCondition | null; - constructor(battleStat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { + constructor(stat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { super(false); - this.battleStat = battleStat; + this.stat = stat; this.multiplier = multiplier; this.condition = condition ?? null; } - applyBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { + applyStatStage(pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { const move = (args[0] as Move); - if (battleStat === this.battleStat && (!this.condition || this.condition(pokemon, null, move))) { + if (stat === this.stat && (!this.condition || this.condition(pokemon, null, move))) { statValue.value *= this.multiplier; return true; } @@ -1566,8 +1595,8 @@ export class PostAttackAbAttr extends AbAttr { private attackCondition: PokemonAttackCondition; /** The default attackCondition requires that the selected move is a damaging move */ - constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS)) { - super(); + constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS), showAbility: boolean = true) { + super(showAbility); this.attackCondition = attackCondition; } @@ -1595,6 +1624,40 @@ export class PostAttackAbAttr extends AbAttr { } } +/** + * Ability attribute for Gorilla Tactics + * @extends PostAttackAbAttr + */ +export class GorillaTacticsAbAttr extends PostAttackAbAttr { + constructor() { + super((user, target, move) => true, false); + } + + /** + * + * @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability + * @param passive n/a + * @param simulated whether the ability is being simulated + * @param defender n/a + * @param move n/a + * @param hitResult n/a + * @param args n/a + * @returns `true` if the ability is applied + */ + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + if (simulated) { + return simulated; + } + + if (pokemon.getTag(BattlerTagType.GORILLA_TACTICS)) { + return false; + } + + pokemon.addTag(BattlerTagType.GORILLA_TACTICS); + return true; + } +} + export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { private stealCondition: PokemonAttackCondition | null; @@ -1724,15 +1787,15 @@ export class PostVictoryAbAttr extends AbAttr { } } -class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr { +class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { private stat: BattleStat | ((p: Pokemon) => BattleStat); - private levels: integer; + private stages: number; - constructor(stat: BattleStat | ((p: Pokemon) => BattleStat), levels: integer) { + constructor(stat: BattleStat | ((p: Pokemon) => BattleStat), stages: number) { super(); this.stat = stat; - this.levels = levels; + this.stages = stages; } applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { @@ -1740,7 +1803,7 @@ class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr { ? this.stat(pokemon) : this.stat; if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } return true; } @@ -1774,15 +1837,15 @@ export class PostKnockOutAbAttr extends AbAttr { } } -export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { +export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { private stat: BattleStat | ((p: Pokemon) => BattleStat); - private levels: integer; + private stages: number; - constructor(stat: BattleStat | ((p: Pokemon) => BattleStat), levels: integer) { + constructor(stat: BattleStat | ((p: Pokemon) => BattleStat), stages: number) { super(); this.stat = stat; - this.levels = levels; + this.stages = stages; } applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { @@ -1790,7 +1853,7 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { ? this.stat(pokemon) : this.stat; if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } return true; } @@ -1814,37 +1877,21 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { } } -export class IgnoreOpponentStatChangesAbAttr extends AbAttr { - constructor() { +export class IgnoreOpponentStatStagesAbAttr extends AbAttr { + private stats: readonly BattleStat[]; + + constructor(stats?: BattleStat[]) { super(false); + + this.stats = stats ?? BATTLE_STATS; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { - (args[0] as Utils.IntegerHolder).value = 0; - - return true; - } -} -/** - * Ignores opponent's evasion stat changes when determining if a move hits or not - * @extends AbAttr - * @see {@linkcode apply} - */ -export class IgnoreOpponentEvasionAbAttr extends AbAttr { - constructor() { - super(false); - } - /** - * Checks if enemy Pokemon is trapped by an Arena Trap-esque ability - * @param pokemon N/A - * @param passive N/A - * @param cancelled N/A - * @param args [0] {@linkcode Utils.IntegerHolder} of BattleStat.EVA - * @returns if evasion level was successfully considered as 0 - */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { - (args[0] as Utils.IntegerHolder).value = 0; - return true; + apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]) { + if (this.stats.includes(args[0])) { + (args[1] as Utils.BooleanHolder).value = true; + return true; + } + return false; } } @@ -1866,21 +1913,21 @@ export class IntimidateImmunityAbAttr extends AbAttr { } } -export class PostIntimidateStatChangeAbAttr extends AbAttr { +export class PostIntimidateStatStageChangeAbAttr extends AbAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; private overwrites: boolean; - constructor(stats: BattleStat[], levels: integer, overwrites?: boolean) { + constructor(stats: BattleStat[], stages: number, overwrites?: boolean) { super(true); this.stats = stats; - this.levels = levels; + this.stages = stages; this.overwrites = !!overwrites; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!simulated) { - pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels)); + pokemon.scene.pushPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.stages)); } cancelled.value = this.overwrites; return true; @@ -1985,19 +2032,17 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { } } -export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { +export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; private selfTarget: boolean; private intimidate: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, intimidate?: boolean) { + constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, intimidate?: boolean) { super(false); - this.stats = typeof(stats) === "number" - ? [ stats as BattleStat ] - : stats as BattleStat[]; - this.levels = levels; + this.stats = stats; + this.stages = stages; this.selfTarget = !!selfTarget; this.intimidate = !!intimidate; } @@ -2009,20 +2054,19 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { queueShowAbility(pokemon, passive); // TODO: Better solution than manually showing the ability here if (this.selfTarget) { - // we unshift the StatChangePhase to put it right after the showAbility and not at the end of the + // we unshift the StatStageChangePhase to put it right after the showAbility and not at the end of the // phase list (which could be after CommandPhase for example) - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages)); return true; } for (const opponent of pokemon.getOpponents()) { const cancelled = new Utils.BooleanHolder(false); if (this.intimidate) { applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); - applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled, simulated); + applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated); } if (!cancelled.value) { - const statChangePhase = new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels); - pokemon.scene.unshiftPhase(statChangePhase); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.stages)); } } return true; @@ -2063,7 +2107,7 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { * @param args N/A * @returns if the move was successful */ -export class PostSummonClearAllyStatsAbAttr extends PostSummonAbAttr { +export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { constructor() { super(); } @@ -2072,8 +2116,8 @@ export class PostSummonClearAllyStatsAbAttr extends PostSummonAbAttr { const target = pokemon.getAlly(); if (target?.isActive(true)) { if (!simulated) { - for (let s = 0; s < target.summonData.battleStats.length; s++) { - target.summonData.battleStats[s] = 0; + for (const s of BATTLE_STATS) { + target.setStatStage(s, 0); } target.scene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); @@ -2102,7 +2146,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { // TODO: Implement the Substitute feature(s) once move is implemented. /** * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account - * vitamins and items, so it needs to use the BattleStat and the stat alone. + * vitamins and items, so it needs to use the Stat and the stat alone. * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. * @param {boolean} passive N/A * @param {any[]} args N/A @@ -2115,21 +2159,21 @@ export class DownloadAbAttr extends PostSummonAbAttr { for (const opponent of pokemon.getOpponents()) { this.enemyCountTally++; - this.enemyDef += opponent.getBattleStat(Stat.DEF); - this.enemySpDef += opponent.getBattleStat(Stat.SPDEF); + this.enemyDef += opponent.getEffectiveStat(Stat.DEF); + this.enemySpDef += opponent.getEffectiveStat(Stat.SPDEF); } this.enemyDef = Math.round(this.enemyDef / this.enemyCountTally); this.enemySpDef = Math.round(this.enemySpDef / this.enemyCountTally); if (this.enemyDef < this.enemySpDef) { - this.stats = [BattleStat.ATK]; + this.stats = [ Stat.ATK ]; } else { - this.stats = [BattleStat.SPATK]; + this.stats = [ Stat.SPATK ]; } if (this.enemyDef > 0 && this.enemySpDef > 0) { // only activate if there's actually an enemy to download from if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); } return true; } @@ -2298,12 +2342,14 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } const ally = pokemon.getAlly(); - if (!ally || ally.summonData.battleStats.every((change) => change === 0)) { + if (!ally || ally.getStatStages().every(s => s === 0)) { return false; } if (!simulated) { - pokemon.summonData.battleStats = ally.summonData.battleStats; + for (const s of BATTLE_STATS) { + pokemon.setStatStage(s, ally.getStatStage(s)); + } pokemon.updateInfo(); } @@ -2342,14 +2388,27 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.summonData.ability = target.getAbility().id; pokemon.summonData.gender = target.getGender(); pokemon.summonData.fusionGender = target.getFusionGender(); - pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1)); - pokemon.summonData.battleStats = target.summonData.battleStats.slice(0); + + // Copy all stats (except HP) + for (const s of EFFECTIVE_STATS) { + pokemon.setStat(s, target.getStat(s, false), false); + } + + // Copy all stat stages + for (const s of BATTLE_STATS) { + pokemon.setStatStage(s, target.getStatStage(s)); + } + pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m!.moveId, m!.ppUsed, m!.ppUp)); // TODO: are those bangs correct? pokemon.summonData.types = target.getTypes(); + pokemon.scene.playSound("battle_anims/PRSFX- Transform"); - pokemon.loadAssets(false).then(() => pokemon.playAnim()); + pokemon.loadAssets(false).then(() => { + pokemon.playAnim(); + pokemon.updateInfo(); + }); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), targetName: target.name, })); @@ -2387,7 +2446,7 @@ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAtt /** * Triggers weather-based form change when summoned into an active weather. - * Used by Forecast. + * Used by Forecast and Flower Gift. * @extends PostSummonAbAttr */ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { @@ -2410,7 +2469,10 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { * @returns whether the form change was triggered */ applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { if (simulated) { return simulated; } @@ -2550,13 +2612,13 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { } -export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { +export class PreStatStageChangeAbAttr extends AbAttr { + applyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } -export class ProtectStatAbAttr extends PreStatChangeAbAttr { +export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { private protectedStat?: BattleStat; constructor(protectedStat?: BattleStat) { @@ -2565,7 +2627,7 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { this.protectedStat = protectedStat; } - applyPreStatChange(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean { if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) { cancelled.value = true; return true; @@ -2574,11 +2636,11 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statName: this.protectedStat ? getBattleStatName(this.protectedStat) : i18next.t("battle:stats") + statName: this.protectedStat ? i18next.t(getStatKey(this.protectedStat)) : i18next.t("battle:stats") }); } } @@ -2614,7 +2676,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { if (simulated) { return defender.canAddTag(BattlerTagType.CONFUSED); } else { - return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3, 2), move.id, defender.id); + return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id); } } return false; @@ -3083,37 +3145,41 @@ export class PostWeatherChangeAbAttr extends AbAttr { /** * Triggers weather-based form change when weather changes. - * Used by Forecast. + * Used by Forecast and Flower Gift. * @extends PostWeatherChangeAbAttr */ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { private ability: Abilities; + private formRevertingWeathers: WeatherType[]; - constructor(ability: Abilities) { + constructor(ability: Abilities, formRevertingWeathers: WeatherType[]) { super(false); this.ability = ability; + this.formRevertingWeathers = formRevertingWeathers; } /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} - * @param {Pokemon} pokemon the Pokemon that changed the weather + * @param {Pokemon} pokemon the Pokemon with this ability * @param passive n/a * @param weather n/a * @param args n/a * @returns whether the form change was triggered */ applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { if (simulated) { return simulated; } - const formRevertingWeathers: WeatherType[] = [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]; const weatherType = pokemon.scene.arena.weather?.weatherType; - if (weatherType && formRevertingWeathers.includes(weatherType)) { + if (weatherType && this.formRevertingWeathers.includes(weatherType)) { pokemon.scene.arena.triggerWeatherBasedFormChangesToNormal(); } else { pokemon.scene.arena.triggerWeatherBasedFormChanges(); @@ -3417,51 +3483,53 @@ export class MoodyAbAttr extends PostTurnAbAttr { super(true); } /** - * Randomly increases one BattleStat by 2 stages and decreases a different BattleStat by 1 stage + * Randomly increases one stat stage by 2 and decreases a different stat stage by 1 * @param {Pokemon} pokemon Pokemon that has this ability * @param passive N/A * @param simulated true if applying in a simulated call. * @param args N/A * @returns true * - * Any BattleStats at +6 or -6 are excluded from being increased or decreased, respectively - * If the pokemon already has all BattleStats raised to stage 6, it will only decrease one BattleStat by 1 stage - * If the pokemon already has all BattleStats lowered to stage -6, it will only increase one BattleStat by 2 stages + * Any stat stages at +6 or -6 are excluded from being increased or decreased, respectively + * If the pokemon already has all stat stages raised to 6, it will only decrease one stat stage by 1 + * If the pokemon already has all stat stages lowered to -6, it will only increase one stat stage by 2 */ applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const selectableStats = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; - const increaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] < 6); - let decreaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] > -6); + const canRaise = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) < 6); + let canLower = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) > -6); - if (!simulated && increaseStatArray.length > 0) { - const increaseStat = increaseStatArray[Utils.randInt(increaseStatArray.length)]; - decreaseStatArray = decreaseStatArray.filter(s => s !== increaseStat); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [increaseStat], 2)); - } - if (!simulated && decreaseStatArray.length > 0) { - const decreaseStat = decreaseStatArray[Utils.randInt(decreaseStatArray.length)]; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [decreaseStat], -1)); + if (!simulated) { + if (canRaise.length > 0) { + const raisedStat = canRaise[pokemon.randSeedInt(canRaise.length)]; + canLower = canRaise.filter(s => s !== raisedStat); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); + } + if (canLower.length > 0) { + const loweredStat = canLower[pokemon.randSeedInt(canLower.length)]; + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); + } } + return true; } } -export class PostTurnStatChangeAbAttr extends PostTurnAbAttr { +export class PostTurnStatStageChangeAbAttr extends PostTurnAbAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; - constructor(stats: BattleStat | BattleStat[], levels: integer) { + constructor(stats: BattleStat[], stages: number) { super(true); this.stats = Array.isArray(stats) ? stats : [ stats ]; - this.levels = levels; + this.stages = stages; } applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages)); } return true; } @@ -3647,10 +3715,10 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true, true)); } else if (move.getMove() instanceof SelfStatusMove) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true, true)); } } return true; @@ -3673,7 +3741,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { } } -export class StatChangeMultiplierAbAttr extends AbAttr { +export class StatStageChangeMultiplierAbAttr extends AbAttr { private multiplier: integer; constructor(multiplier: integer) { @@ -3689,10 +3757,10 @@ export class StatChangeMultiplierAbAttr extends AbAttr { } } -export class StatChangeCopyAbAttr extends AbAttr { +export class StatStageChangeCopyAbAttr extends AbAttr { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as number), true, false, false)); } return true; } @@ -3889,7 +3957,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } export class PostFaintAbAttr extends AbAttr { - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { return false; } } @@ -3940,7 +4008,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { * @param args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; @@ -3988,8 +4056,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + if (move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { //If the mon didn't die to indirect damage const cancelled = new Utils.BooleanHolder(false); pokemon.scene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { @@ -4018,8 +4086,8 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { super (); } - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (!simulated) { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + if (move !== undefined && attacker !== undefined && !simulated) { //If the mon didn't die to indirect damage const damage = pokemon.turnData.attacksReceived[0].damage; attacker.damageAndUpdate((damage), HitResult.OTHER); attacker.turnData.damageTaken += damage; @@ -4092,22 +4160,22 @@ export class FlinchEffectAbAttr extends AbAttr { } } -export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr { +export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; - constructor(stats: BattleStat | BattleStat[], levels: integer) { + constructor(stats: BattleStat[], stages: number) { super(); this.stats = Array.isArray(stats) ? stats : [ stats ]; - this.levels = levels; + this.stages = stages; } apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages)); } return true; } @@ -4307,9 +4375,9 @@ export class MoneyAbAttr extends PostBattleAbAttr { * Applies a stat change after a Pokémon is summoned, * conditioned on the presence of a specific arena tag. * - * @extends {PostSummonStatChangeAbAttr} + * @extends {PostSummonStatStageChangeAbAttr} */ -export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAttr { +export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageChangeAbAttr { /** * The type of arena tag that conditions the stat change. * @private @@ -4318,13 +4386,13 @@ export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAtt private tagType: ArenaTagType; /** - * Creates an instance of PostSummonStatChangeOnArenaAbAttr. + * Creates an instance of PostSummonStatStageChangeOnArenaAbAttr. * Initializes the stat change to increase Attack by 1 stage if the specified arena tag is present. * * @param {ArenaTagType} tagType - The type of arena tag to check for. */ constructor(tagType: ArenaTagType) { - super([BattleStat.ATK], 1, true, false); + super([ Stat.ATK ], 1, true, false); this.tagType = tagType; } @@ -4571,14 +4639,14 @@ export function applyPostMoveUsedAbAttrs(attrType: Constructor(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), args, false, simulated); } -export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, simulated, battleStat, statValue, args), args, false, simulated); +export function applyStatMultiplierAbAttrs(attrType: Constructor, + pokemon: Pokemon, stat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyStatStage(pokemon, passive, simulated, stat, statValue, args), args); } /** - * Applies a field Battle Stat multiplier attribute - * @param attrType {@linkcode FieldMultiplyBattleStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being + * Applies a field Stat multiplier attribute + * @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being * @param pokemon {@linkcode Pokemon} the Pokemon applying this ability * @param stat {@linkcode Stat} the type of the checked stat * @param statValue {@linkcode Utils.NumberHolder} the value of the checked stat @@ -4586,9 +4654,9 @@ export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, +export function applyFieldStatMultiplierAbAttrs(attrType: Constructor, pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args, false, simulated); + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args); } export function applyPreAttackAbAttrs(attrType: Constructor, @@ -4621,14 +4689,14 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated); } -export function applyPreStatChangeAbAttrs(attrType: Constructor, +export function applyPreStatStageChangeAbAttrs(attrType: Constructor, pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated); + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated); } -export function applyPostStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, simulated: boolean = false, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, simulated, stats, levels, selfTarget, args), args, false, simulated); +export function applyPostStatStageChangeAbAttrs(attrType: Constructor, + pokemon: Pokemon, stats: BattleStat[], stages: integer, selfTarget: boolean, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, _passive) => attr.applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), args, false, simulated); } export function applyPreSetStatusAbAttrs(attrType: Constructor, @@ -4677,7 +4745,7 @@ export function applyPostBattleAbAttrs(attrType: Constructor, } export function applyPostFaintAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, simulated: boolean = false, ...args: any[]): Promise { + pokemon: Pokemon, attacker?: Pokemon, move?: Move, hitResult?: HitResult, simulated: boolean = false, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated); } @@ -4703,7 +4771,8 @@ function setAbilityRevealed(pokemon: Pokemon): void { */ function getPokemonWithWeatherBasedForms(scene: BattleScene) { return scene.getField(true).filter(p => - p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM + (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM) + || (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM) ); } @@ -4717,7 +4786,7 @@ export function initAbilities() { .attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN), new Ability(Abilities.SPEED_BOOST, 3) - .attr(PostTurnStatChangeAbAttr, BattleStat.SPD, 1), + .attr(PostTurnStatStageChangeAbAttr, [ Stat.SPD ], 1), new Ability(Abilities.BATTLE_ARMOR, 3) .attr(BlockCritAbAttr) .ignorable(), @@ -4732,7 +4801,7 @@ export function initAbilities() { .attr(StatusEffectImmunityAbAttr, StatusEffect.PARALYSIS) .ignorable(), new Ability(Abilities.SAND_VEIL, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.EVA, 1.2) + .attr(StatMultiplierAbAttr, Stat.EVA, 1.2) .attr(BlockWeatherDamageAttr, WeatherType.SANDSTORM) .condition(getWeatherCondition(WeatherType.SANDSTORM)) .ignorable(), @@ -4758,7 +4827,7 @@ export function initAbilities() { .attr(PostFaintUnsuppressedWeatherFormChangeAbAttr) .bypassFaint(), new Ability(Abilities.COMPOUND_EYES, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3), + .attr(StatMultiplierAbAttr, Stat.ACC, 1.3), new Ability(Abilities.INSOMNIA, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP) .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) @@ -4783,7 +4852,7 @@ export function initAbilities() { .attr(ForceSwitchOutImmunityAbAttr) .ignorable(), new Ability(Abilities.INTIMIDATE, 3) - .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, -1, false, true), + .attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], -1, false, true), new Ability(Abilities.SHADOW_TAG, 3) .attr(ArenaTrapAbAttr, (user, target) => { if (target.hasAbility(Abilities.SHADOW_TAG)) { @@ -4814,26 +4883,26 @@ export function initAbilities() { .attr(PreSwitchOutResetStatusAbAttr), new Ability(Abilities.LIGHTNING_ROD, 3) .attr(RedirectTypeMoveAbAttr, Type.ELECTRIC) - .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1) + .attr(TypeImmunityStatStageChangeAbAttr, Type.ELECTRIC, Stat.SPATK, 1) .ignorable(), new Ability(Abilities.SERENE_GRACE, 3) .attr(MoveEffectChanceMultiplierAbAttr, 2) .partial(), new Ability(Abilities.SWIFT_SWIM, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) + .attr(StatMultiplierAbAttr, Stat.SPD, 2) .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), new Ability(Abilities.CHLOROPHYLL, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) + .attr(StatMultiplierAbAttr, Stat.SPD, 2) .condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)), new Ability(Abilities.ILLUMINATE, 3) - .attr(ProtectStatAbAttr, BattleStat.ACC) + .attr(ProtectStatAbAttr, Stat.ACC) .attr(DoubleBattleChanceAbAttr) .ignorable(), new Ability(Abilities.TRACE, 3) .attr(PostSummonCopyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.HUGE_POWER, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 2), + .attr(StatMultiplierAbAttr, Stat.ATK, 2), new Ability(Abilities.POISON_POINT, 3) .attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON) .bypassFaint(), @@ -4878,31 +4947,31 @@ export function initAbilities() { new Ability(Abilities.RUN_AWAY, 3) .attr(RunSuccessAbAttr), new Ability(Abilities.KEEN_EYE, 3) - .attr(ProtectStatAbAttr, BattleStat.ACC) + .attr(ProtectStatAbAttr, Stat.ACC) .ignorable(), new Ability(Abilities.HYPER_CUTTER, 3) - .attr(ProtectStatAbAttr, BattleStat.ATK) + .attr(ProtectStatAbAttr, Stat.ATK) .ignorable(), new Ability(Abilities.PICKUP, 3) .attr(PostBattleLootAbAttr), new Ability(Abilities.TRUANT, 3) .attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.TRUANT, 1, false), new Ability(Abilities.HUSTLE, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5) - .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 0.8, (user, target, move) => move.category === MoveCategory.PHYSICAL), + .attr(StatMultiplierAbAttr, Stat.ATK, 1.5) + .attr(StatMultiplierAbAttr, Stat.ACC, 0.8, (_user, _target, move) => move.category === MoveCategory.PHYSICAL), new Ability(Abilities.CUTE_CHARM, 3) .attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED), new Ability(Abilities.PLUS, 3) - .conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), BattleStatMultiplierAbAttr, BattleStat.SPATK, 1.5) + .conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5) .ignorable(), new Ability(Abilities.MINUS, 3) - .conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), BattleStatMultiplierAbAttr, BattleStat.SPATK, 1.5) + .conditionalAttr(p => p.scene.currentBattle.double && [Abilities.PLUS, Abilities.MINUS].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5) .ignorable(), new Ability(Abilities.FORECAST, 3) .attr(UncopiableAbilityAbAttr) .attr(NoFusionAbilityAbAttr) .attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FORECAST) - .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST), + .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]), new Ability(Abilities.STICKY_HOLD, 3) .attr(BlockItemTheftAbAttr) .bypassFaint() @@ -4911,9 +4980,9 @@ export function initAbilities() { .conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr), new Ability(Abilities.GUTS, 3) .attr(BypassBurnDamageReductionAbAttr) - .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5), + .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.ATK, 1.5), new Ability(Abilities.MARVEL_SCALE, 3) - .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5) + .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.DEF, 1.5) .ignorable(), new Ability(Abilities.LIQUID_OOZE, 3) .attr(ReverseDrainAbAttr), @@ -4946,7 +5015,7 @@ export function initAbilities() { .attr(ProtectStatAbAttr) .ignorable(), new Ability(Abilities.PURE_POWER, 3) - .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 2), + .attr(StatMultiplierAbAttr, Stat.ATK, 2), new Ability(Abilities.SHELL_ARMOR, 3) .attr(BlockCritAbAttr) .ignorable(), @@ -4957,25 +5026,25 @@ export function initAbilities() { .attr(PostFaintUnsuppressedWeatherFormChangeAbAttr) .bypassFaint(), new Ability(Abilities.TANGLED_FEET, 4) - .conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), BattleStatMultiplierAbAttr, BattleStat.EVA, 2) + .conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), StatMultiplierAbAttr, Stat.EVA, 2) .ignorable(), new Ability(Abilities.MOTOR_DRIVE, 4) - .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPD, 1) + .attr(TypeImmunityStatStageChangeAbAttr, Type.ELECTRIC, Stat.SPD, 1) .ignorable(), new Ability(Abilities.RIVALRY, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true) .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75), new Ability(Abilities.STEADFAST, 4) - .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), + .attr(FlinchStatStageChangeAbAttr, [ Stat.SPD ], 1), new Ability(Abilities.SNOW_CLOAK, 4) - .attr(BattleStatMultiplierAbAttr, BattleStat.EVA, 1.2) + .attr(StatMultiplierAbAttr, Stat.EVA, 1.2) .attr(BlockWeatherDamageAttr, WeatherType.HAIL) .condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)) .ignorable(), new Ability(Abilities.GLUTTONY, 4) .attr(ReduceBerryUseThresholdAbAttr), new Ability(Abilities.ANGER_POINT, 4) - .attr(PostDefendCritStatChangeAbAttr, BattleStat.ATK, 6), + .attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6), new Ability(Abilities.UNBURDEN, 4) .unimplemented(), new Ability(Abilities.HEATPROOF, 4) @@ -4983,7 +5052,7 @@ export function initAbilities() { .attr(ReduceBurnDamageAbAttr, 0.5) .ignorable(), new Ability(Abilities.SIMPLE, 4) - .attr(StatChangeMultiplierAbAttr, 2) + .attr(StatStageChangeMultiplierAbAttr, 2) .ignorable(), new Ability(Abilities.DRY_SKIN, 4) .attr(PostWeatherLapseDamageAbAttr, 2, WeatherType.SUNNY, WeatherType.HARSH_SUN) @@ -5008,11 +5077,11 @@ export function initAbilities() { .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), new Ability(Abilities.SOLAR_POWER, 4) .attr(PostWeatherLapseDamageAbAttr, 2, WeatherType.SUNNY, WeatherType.HARSH_SUN) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPATK, 1.5) + .attr(StatMultiplierAbAttr, Stat.SPATK, 1.5) .condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)), new Ability(Abilities.QUICK_FEET, 4) - .conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, BattleStatMultiplierAbAttr, BattleStat.SPD, 2) - .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5), + .conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, StatMultiplierAbAttr, Stat.SPD, 2) + .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5), new Ability(Abilities.NORMALIZE, 4) .attr(MoveTypeChangeAbAttr, Type.NORMAL, 1.2, (user, target, move) => { return ![Moves.HIDDEN_POWER, Moves.WEATHER_BALL, Moves.NATURAL_GIFT, Moves.JUDGMENT, Moves.TECHNO_BLAST].includes(move.id); @@ -5025,7 +5094,7 @@ export function initAbilities() { .attr(AlwaysHitAbAttr) .attr(DoubleBattleChanceAbAttr), new Ability(Abilities.STALL, 4) - .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.5), + .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.2), new Ability(Abilities.TECHNICIAN, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => { const power = new Utils.NumberHolder(move.power); @@ -5052,13 +5121,13 @@ export function initAbilities() { new Ability(Abilities.FOREWARN, 4) .attr(ForewarnAbAttr), new Ability(Abilities.UNAWARE, 4) - .attr(IgnoreOpponentStatChangesAbAttr) + .attr(IgnoreOpponentStatStagesAbAttr) .ignorable(), new Ability(Abilities.TINTED_LENS, 4) //@ts-ignore - .attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), // TODO: fix TS issues + .attr(DamageBoostAbAttr, 2, (user, target, move) => target?.getMoveEffectiveness(user, move) <= 0.5), // TODO: fix TS issues new Ability(Abilities.FILTER, 4) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) .ignorable(), new Ability(Abilities.SLOW_START, 4) .attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5), @@ -5067,14 +5136,14 @@ export function initAbilities() { .attr(IntimidateImmunityAbAttr), new Ability(Abilities.STORM_DRAIN, 4) .attr(RedirectTypeMoveAbAttr, Type.WATER) - .attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1) + .attr(TypeImmunityStatStageChangeAbAttr, Type.WATER, Stat.SPATK, 1) .ignorable(), new Ability(Abilities.ICE_BODY, 4) .attr(BlockWeatherDamageAttr, WeatherType.HAIL) .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW) .partial(), // Healing not blocked by Heal Block new Ability(Abilities.SOLID_ROCK, 4) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) .ignorable(), new Ability(Abilities.SNOW_WARNING, 4) .attr(PostSummonWeatherChangeAbAttr, WeatherType.SNOW) @@ -5091,12 +5160,14 @@ export function initAbilities() { .attr(UnsuppressableAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.FLOWER_GIFT, 4) - .conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5) - .conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.SPDEF, 1.5) + .conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), StatMultiplierAbAttr, Stat.ATK, 1.5) + .conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), StatMultiplierAbAttr, Stat.SPDEF, 1.5) .attr(UncopiableAbilityAbAttr) .attr(NoFusionAbilityAbAttr) - .ignorable() - .partial(), + .attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FLOWER_GIFT) + .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]) + .partial() // Should also boosts stats of ally + .ignorable(), new Ability(Abilities.BAD_DREAMS, 4) .attr(PostTurnHurtIfSleepingAbAttr), new Ability(Abilities.PICKPOCKET, 5) @@ -5107,15 +5178,15 @@ export function initAbilities() { .attr(MoveEffectChanceMultiplierAbAttr, 0) .partial(), new Ability(Abilities.CONTRARY, 5) - .attr(StatChangeMultiplierAbAttr, -1) + .attr(StatStageChangeMultiplierAbAttr, -1) .ignorable(), new Ability(Abilities.UNNERVE, 5) .attr(PreventBerryUseAbAttr), new Ability(Abilities.DEFIANT, 5) - .attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.ATK], 2), + .attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [Stat.ATK], 2), new Ability(Abilities.DEFEATIST, 5) - .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 0.5) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPATK, 0.5) + .attr(StatMultiplierAbAttr, Stat.ATK, 0.5) + .attr(StatMultiplierAbAttr, Stat.SPATK, 0.5) .condition((pokemon) => pokemon.getHpRatio() <= 0.5), new Ability(Abilities.CURSED_BODY, 5) .attr(PostDefendMoveDisableAbAttr, 30) @@ -5126,8 +5197,8 @@ export function initAbilities() { .ignorable() .unimplemented(), new Ability(Abilities.WEAK_ARMOR, 5) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, BattleStat.DEF, -1) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, BattleStat.SPD, 2), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.DEF, -1) + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.SPD, 2), new Ability(Abilities.HEAVY_METAL, 5) .attr(WeightMultiplierAbAttr, 2) .ignorable(), @@ -5163,10 +5234,10 @@ export function initAbilities() { new Ability(Abilities.REGENERATOR, 5) .attr(PreSwitchOutHealAbAttr), new Ability(Abilities.BIG_PECKS, 5) - .attr(ProtectStatAbAttr, BattleStat.DEF) + .attr(ProtectStatAbAttr, Stat.DEF) .ignorable(), new Ability(Abilities.SAND_RUSH, 5) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) + .attr(StatMultiplierAbAttr, Stat.SPD, 2) .attr(BlockWeatherDamageAttr, WeatherType.SANDSTORM) .condition(getWeatherCondition(WeatherType.SANDSTORM)), new Ability(Abilities.WONDER_SKIN, 5) @@ -5188,18 +5259,21 @@ export function initAbilities() { .attr(PostDefendAbilityGiveAbAttr, Abilities.MUMMY) .bypassFaint(), new Ability(Abilities.MOXIE, 5) - .attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1), + .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1), new Ability(Abilities.JUSTIFIED, 5) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.DARK && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.DARK && move.category !== MoveCategory.STATUS, Stat.ATK, 1), new Ability(Abilities.RATTLED, 5) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS && (move.type === Type.DARK || move.type === Type.BUG || - move.type === Type.GHOST), BattleStat.SPD, 1) - .attr(PostIntimidateStatChangeAbAttr, [BattleStat.SPD], 1), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => { + const moveType = user.getMoveType(move); + return move.category !== MoveCategory.STATUS + && (moveType === Type.DARK || moveType === Type.BUG || moveType === Type.GHOST); + }, Stat.SPD, 1) + .attr(PostIntimidateStatStageChangeAbAttr, [Stat.SPD], 1), new Ability(Abilities.MAGIC_BOUNCE, 5) .ignorable() .unimplemented(), new Ability(Abilities.SAP_SIPPER, 5) - .attr(TypeImmunityStatChangeAbAttr, Type.GRASS, BattleStat.ATK, 1) + .attr(TypeImmunityStatStageChangeAbAttr, Type.GRASS, Stat.ATK, 1) .ignorable(), new Ability(Abilities.PRANKSTER, 5) .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS, 1), @@ -5222,7 +5296,7 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) .bypassFaint(), new Ability(Abilities.VICTORY_STAR, 5) - .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.1) + .attr(StatMultiplierAbAttr, Stat.ACC, 1.1) .partial(), new Ability(Abilities.TURBOBLAZE, 5) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) @@ -5251,7 +5325,7 @@ export function initAbilities() { .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.BALLBOMB_MOVE)) .ignorable(), new Ability(Abilities.COMPETITIVE, 6) - .attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.SPATK], 2), + .attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [Stat.SPATK], 2), new Ability(Abilities.STRONG_JAW, 6) .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5), new Ability(Abilities.REFRIGERATE, 6) @@ -5267,11 +5341,11 @@ export function initAbilities() { .attr(UnsuppressableAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.GALE_WINGS, 6) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => pokemon.isFullHp() && move.type === Type.FLYING, 1), + .attr(ChangeMovePriorityAbAttr, (pokemon, move) => pokemon.isFullHp() && pokemon.getMoveType(move) === Type.FLYING, 1), new Ability(Abilities.MEGA_LAUNCHER, 6) .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5), new Ability(Abilities.GRASS_PELT, 6) - .conditionalAttr(getTerrainCondition(TerrainType.GRASSY), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5) + .conditionalAttr(getTerrainCondition(TerrainType.GRASSY), StatMultiplierAbAttr, Stat.DEF, 1.5) .ignorable(), new Ability(Abilities.SYMBIOSIS, 6) .unimplemented(), @@ -5280,7 +5354,7 @@ export function initAbilities() { new Ability(Abilities.PIXILATE, 6) .attr(MoveTypeChangeAbAttr, Type.FAIRY, 1.2, (user, target, move) => move.type === Type.NORMAL && !move.hasAttr(VariableMoveTypeAttr)), new Ability(Abilities.GOOEY, 6) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), Stat.SPD, -1, false), new Ability(Abilities.AERILATE, 6) .attr(MoveTypeChangeAbAttr, Type.FLYING, 1.2, (user, target, move) => move.type === Type.NORMAL && !move.hasAttr(VariableMoveTypeAttr)), new Ability(Abilities.PARENTAL_BOND, 6) @@ -5293,8 +5367,10 @@ export function initAbilities() { .attr(FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 4 / 3), new Ability(Abilities.AURA_BREAK, 6) .ignorable() - .conditionalAttr(target => target.hasAbility(Abilities.DARK_AURA), FieldMoveTypePowerBoostAbAttr, Type.DARK, 9 / 16) - .conditionalAttr(target => target.hasAbility(Abilities.FAIRY_AURA), FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 9 / 16), + .conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.DARK_AURA)), FieldMoveTypePowerBoostAbAttr, Type.DARK, 9 / 16) + .conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.FAIRY_AURA)), FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 9 / 16) + .conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.DARK_AURA) || p.hasAbility(Abilities.FAIRY_AURA)), + PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAuraBreak", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })), new Ability(Abilities.PRIMORDIAL_SEA, 6) .attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN) @@ -5314,7 +5390,7 @@ export function initAbilities() { .attr(PostFaintClearWeatherAbAttr) .bypassFaint(), new Ability(Abilities.STAMINA, 7) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1), new Ability(Abilities.WIMP_OUT, 7) .condition(getSheerForceHitDisableAbCondition()) .unimplemented(), @@ -5322,7 +5398,7 @@ export function initAbilities() { .condition(getSheerForceHitDisableAbCondition()) .unimplemented(), new Ability(Abilities.WATER_COMPACTION, 7) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2), new Ability(Abilities.MERCILESS, 7) .attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), new Ability(Abilities.SHIELDS_DOWN, 7) @@ -5346,10 +5422,10 @@ export function initAbilities() { new Ability(Abilities.STEELWORKER, 7) .attr(MoveTypePowerBoostAbAttr, Type.STEEL), new Ability(Abilities.BERSERK, 7) - .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [BattleStat.SPATK], 1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [Stat.SPATK], 1) .condition(getSheerForceHitDisableAbCondition()), new Ability(Abilities.SLUSH_RUSH, 7) - .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) + .attr(StatMultiplierAbAttr, Stat.SPD, 2) .condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)), new Ability(Abilities.LONG_REACH, 7) .attr(IgnoreContactAbAttr), @@ -5360,7 +5436,7 @@ export function initAbilities() { new Ability(Abilities.GALVANIZE, 7) .attr(MoveTypeChangeAbAttr, Type.ELECTRIC, 1.2, (user, target, move) => move.type === Type.NORMAL && !move.hasAttr(VariableMoveTypeAttr)), new Ability(Abilities.SURGE_SURFER, 7) - .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2), + .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), StatMultiplierAbAttr, Stat.SPD, 2), new Ability(Abilities.SCHOOLING, 7) .attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostSummonFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1) @@ -5378,7 +5454,7 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) // Add BattlerTagType.DISGUISE if the pokemon is in its disguised form .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) - .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE, + .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) @@ -5423,15 +5499,15 @@ export function initAbilities() { .attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL], 1.3), new Ability(Abilities.FLUFFY, 7) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.type === Type.FIRE, 2) + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE, 2) .ignorable(), new Ability(Abilities.DAZZLING, 7) .attr(FieldPriorityMoveImmunityAbAttr) .ignorable(), new Ability(Abilities.SOUL_HEART, 7) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), + .attr(PostKnockOutStatStageChangeAbAttr, Stat.SPATK, 1), new Ability(Abilities.TANGLING_HAIR, 7) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), Stat.SPD, -1, false), new Ability(Abilities.RECEIVER, 7) .attr(CopyFaintedAllyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), @@ -5439,18 +5515,17 @@ export function initAbilities() { .attr(CopyFaintedAllyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.BEAST_BOOST, 7) - .attr(PostVictoryStatChangeAbAttr, p => { - const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3).map(s => s as BattleStat); - let highestBattleStat = 0; - let highestBattleStatIndex = 0; - battleStats.map((bs: BattleStat, i: integer) => { - const stat = p.getStat(bs + 1); - if (stat > highestBattleStat) { - highestBattleStatIndex = i; - highestBattleStat = stat; + .attr(PostVictoryStatStageChangeAbAttr, p => { + let highestStat: EffectiveStat; + let highestValue = 0; + for (const s of EFFECTIVE_STATS) { + const value = p.getStat(s, false); + if (value > highestValue) { + highestStat = s; + highestValue = value; } - }); - return highestBattleStatIndex; + } + return highestStat!; }, 1), new Ability(Abilities.RKS_SYSTEM, 7) .attr(UncopiableAbilityAbAttr) @@ -5474,15 +5549,15 @@ export function initAbilities() { new Ability(Abilities.SHADOW_SHIELD, 7) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.isFullHp(), 0.5), new Ability(Abilities.PRISM_ARMOR, 7) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75), + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75), new Ability(Abilities.NEUROFORCE, 7) //@ts-ignore - .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), // TODO: fix TS issues + .attr(MovePowerBoostAbAttr, (user, target, move) => target?.getMoveEffectiveness(user, move) >= 2, 1.25), // TODO: fix TS issues new Ability(Abilities.INTREPID_SWORD, 8) - .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true) + .attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), new Ability(Abilities.DAUNTLESS_SHIELD, 8) - .attr(PostSummonStatChangeAbAttr, BattleStat.DEF, 1, true) + .attr(PostSummonStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .condition(getOncePerBattleCondition(Abilities.DAUNTLESS_SHIELD)), new Ability(Abilities.LIBERO, 8) .attr(PokemonTypeChangeAbAttr), @@ -5491,7 +5566,7 @@ export function initAbilities() { .attr(FetchBallAbAttr) .condition(getOncePerBattleCondition(Abilities.BALL_FETCH)), new Ability(Abilities.COTTON_DOWN, 8) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true) + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.SPD, -1, false, true) .bypassFaint(), new Ability(Abilities.PROPELLER_TAIL, 8) .attr(BlockRedirectAbAttr), @@ -5508,7 +5583,11 @@ export function initAbilities() { new Ability(Abilities.STALWART, 8) .attr(BlockRedirectAbAttr), new Ability(Abilities.STEAM_ENGINE, 8) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => (move.type === Type.FIRE || move.type === Type.WATER) && move.category !== MoveCategory.STATUS, BattleStat.SPD, 6), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => { + const moveType = user.getMoveType(move); + return move.category !== MoveCategory.STATUS + && (moveType === Type.FIRE || moveType === Type.WATER); + }, Stat.SPD, 6), new Ability(Abilities.PUNK_ROCK, 8) .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5) @@ -5552,7 +5631,7 @@ export function initAbilities() { .bypassFaint() .partial(), new Ability(Abilities.GORILLA_TACTICS, 8) - .unimplemented(), + .attr(GorillaTacticsAbAttr), new Ability(Abilities.NEUTRALIZING_GAS, 8) .attr(SuppressFieldAbilitiesAbAttr) .attr(UncopiableAbilityAbAttr) @@ -5579,26 +5658,26 @@ export function initAbilities() { new Ability(Abilities.UNSEEN_FIST, 8) .attr(IgnoreProtectOnContactAbAttr), new Ability(Abilities.CURIOUS_MEDICINE, 8) - .attr(PostSummonClearAllyStatsAbAttr), + .attr(PostSummonClearAllyStatStagesAbAttr), new Ability(Abilities.TRANSISTOR, 8) .attr(MoveTypePowerBoostAbAttr, Type.ELECTRIC), new Ability(Abilities.DRAGONS_MAW, 8) .attr(MoveTypePowerBoostAbAttr, Type.DRAGON), new Ability(Abilities.CHILLING_NEIGH, 8) - .attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1), + .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1), new Ability(Abilities.GRIM_NEIGH, 8) - .attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1), + .attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1), new Ability(Abilities.AS_ONE_GLASTRIER, 8) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneGlastrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PreventBerryUseAbAttr) - .attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1) + .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), new Ability(Abilities.AS_ONE_SPECTRIER, 8) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneSpectrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PreventBerryUseAbAttr) - .attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1) + .attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), @@ -5608,26 +5687,26 @@ export function initAbilities() { new Ability(Abilities.SEED_SOWER, 9) .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY), new Ability(Abilities.THERMAL_EXCHANGE, 9) - .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1) + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) .ignorable(), new Ability(Abilities.ANGER_SHELL, 9) - .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 1) - .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.DEF, BattleStat.SPDEF ], -1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.DEF, Stat.SPDEF ], -1) .condition(getSheerForceHitDisableAbCondition()), new Ability(Abilities.PURIFYING_SALT, 9) .attr(StatusEffectImmunityAbAttr) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.GHOST, 0.5) .ignorable(), new Ability(Abilities.WELL_BAKED_BODY, 9) - .attr(TypeImmunityStatChangeAbAttr, Type.FIRE, BattleStat.DEF, 2) + .attr(TypeImmunityStatStageChangeAbAttr, Type.FIRE, Stat.DEF, 2) .ignorable(), new Ability(Abilities.WIND_RIDER, 9) - .attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.WIND_MOVE) && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1) - .attr(PostSummonStatChangeOnArenaAbAttr, ArenaTagType.TAILWIND) + .attr(MoveImmunityStatStageChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.WIND_MOVE) && move.category !== MoveCategory.STATUS, Stat.ATK, 1) + .attr(PostSummonStatStageChangeOnArenaAbAttr, ArenaTagType.TAILWIND) .ignorable(), new Ability(Abilities.GUARD_DOG, 9) - .attr(PostIntimidateStatChangeAbAttr, [BattleStat.ATK], 1, true) + .attr(PostIntimidateStatStageChangeAbAttr, [Stat.ATK], 1, true) .attr(ForceSwitchOutImmunityAbAttr) .ignorable(), new Ability(Abilities.ROCKY_PAYLOAD, 9) @@ -5668,31 +5747,31 @@ export function initAbilities() { .ignorable() .partial(), new Ability(Abilities.VESSEL_OF_RUIN, 9) - .attr(FieldMultiplyBattleStatAbAttr, Stat.SPATK, 0.75) - .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: getStatName(Stat.SPATK) })) + .attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75) + .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) })) .ignorable(), new Ability(Abilities.SWORD_OF_RUIN, 9) - .attr(FieldMultiplyBattleStatAbAttr, Stat.DEF, 0.75) - .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonSwordOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: getStatName(Stat.DEF) })) + .attr(FieldMultiplyStatAbAttr, Stat.DEF, 0.75) + .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonSwordOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.DEF)) })) .ignorable(), new Ability(Abilities.TABLETS_OF_RUIN, 9) - .attr(FieldMultiplyBattleStatAbAttr, Stat.ATK, 0.75) - .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonTabletsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: getStatName(Stat.ATK) })) + .attr(FieldMultiplyStatAbAttr, Stat.ATK, 0.75) + .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonTabletsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) })) .ignorable(), new Ability(Abilities.BEADS_OF_RUIN, 9) - .attr(FieldMultiplyBattleStatAbAttr, Stat.SPDEF, 0.75) - .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonBeadsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: getStatName(Stat.SPDEF) })) + .attr(FieldMultiplyStatAbAttr, Stat.SPDEF, 0.75) + .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonBeadsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPDEF)) })) .ignorable(), new Ability(Abilities.ORICHALCUM_PULSE, 9) .attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY) - .conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.ATK, 4 / 3), + .conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), StatMultiplierAbAttr, Stat.ATK, 4 / 3), new Ability(Abilities.HADRON_ENGINE, 9) .attr(PostSummonTerrainChangeAbAttr, TerrainType.ELECTRIC) .attr(PostBiomeChangeTerrainChangeAbAttr, TerrainType.ELECTRIC) - .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPATK, 4 / 3), + .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), StatMultiplierAbAttr, Stat.SPATK, 4 / 3), new Ability(Abilities.OPPORTUNIST, 9) - .attr(StatChangeCopyAbAttr), + .attr(StatStageChangeCopyAbAttr), new Ability(Abilities.CUD_CHEW, 9) .unimplemented(), new Ability(Abilities.SHARPNESS, 9) @@ -5713,16 +5792,16 @@ export function initAbilities() { .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.MYCELIUM_MIGHT, 9) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.5) + .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.2) .attr(PreventBypassSpeedChanceAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS) .attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS), new Ability(Abilities.MINDS_EYE, 9) .attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [Type.NORMAL, Type.FIGHTING]) - .attr(ProtectStatAbAttr, BattleStat.ACC) - .attr(IgnoreOpponentEvasionAbAttr) + .attr(ProtectStatAbAttr, Stat.ACC) + .attr(IgnoreOpponentStatStagesAbAttr, [ Stat.EVA ]) .ignorable(), new Ability(Abilities.SUPERSWEET_SYRUP, 9) - .attr(PostSummonStatChangeAbAttr, BattleStat.EVA, -1) + .attr(PostSummonStatStageChangeAbAttr, [ Stat.EVA ], -1) .condition(getOncePerBattleCondition(Abilities.SUPERSWEET_SYRUP)), new Ability(Abilities.HOSPITALITY, 9) .attr(PostSummonAllyHealAbAttr, 4, true) @@ -5730,25 +5809,25 @@ export function initAbilities() { new Ability(Abilities.TOXIC_CHAIN, 9) .attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC), new Ability(Abilities.EMBODY_ASPECT_TEAL, 9) - .attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true) + .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPD ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .partial(), new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9) - .attr(PostBattleInitStatChangeAbAttr, BattleStat.SPDEF, 1, true) + .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .partial(), new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9) - .attr(PostBattleInitStatChangeAbAttr, BattleStat.ATK, 1, true) + .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .partial(), new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9) - .attr(PostBattleInitStatChangeAbAttr, BattleStat.DEF, 1, true) + .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) @@ -5761,10 +5840,10 @@ export function initAbilities() { .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.TERA_SHELL, 9) + .attr(FullHpResistTypeAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .ignorable() - .unimplemented(), + .ignorable(), new Ability(Abilities.TERAFORM_ZERO, 9) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index a60ea5c2981..fdc32b75c19 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -7,17 +7,17 @@ import Pokemon, { HitResult, PokemonMove } from "../field/pokemon"; import { StatusEffect } from "./status-effect"; import { BattlerIndex } from "../battle"; import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability"; -import { BattleStat } from "./battle-stat"; +import { Stat } from "#enums/stat"; import { CommonAnim, CommonBattleAnim } from "./battle-anims"; import i18next from "i18next"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; +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"; export enum ArenaTagSide { BOTH, @@ -786,8 +786,8 @@ class StickyWebTag extends ArenaTrapTag { applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled); if (!cancelled.value) { pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() })); - const statLevels = new Utils.NumberHolder(-1); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [BattleStat.SPD], statLevels.value)); + const stages = new Utils.NumberHolder(-1); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value)); } } @@ -875,7 +875,7 @@ class TailwindTag extends ArenaTag { // Raise attack by one stage if party member has WIND_RIDER ability if (pokemon.hasAbility(Abilities.WIND_RIDER)) { pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex())); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK], 1, true)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true)); } } } @@ -905,6 +905,21 @@ class HappyHourTag extends ArenaTag { } } +class SafeguardTag extends ArenaTag { + constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side); + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } + + onRemove(arena: Arena): void { + arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } +} + + export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: @@ -950,6 +965,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new TailwindTag(turnCount, sourceId, side); case ArenaTagType.HAPPY_HOUR: return new HappyHourTag(turnCount, sourceId, side); + case ArenaTagType.SAFEGUARD: + return new SafeguardTag(turnCount, sourceId, side); default: return null; } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index a2f6e41f4ae..da4e7f6a33b 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -788,10 +788,10 @@ export abstract class BattleAnim { targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ]; targetSprite.setAngle(0); if (!this.isHideUser() && userSprite) { - userSprite.setVisible(true); + this.user?.getSprite().setVisible(true); // using this.user to fix context loss due to isOppAnim swap (#481) } if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) { - targetSprite.setVisible(true); + this.target?.getSprite().setVisible(true); // using this.target to fix context loss due to isOppAnim swap (#481) } for (const ms of Object.values(spriteCache).flat()) { if (ms) { diff --git a/src/data/battle-stat.ts b/src/data/battle-stat.ts deleted file mode 100644 index a0cb7ca88e1..00000000000 --- a/src/data/battle-stat.ts +++ /dev/null @@ -1,71 +0,0 @@ -import i18next, { ParseKeys } from "i18next"; - -export enum BattleStat { - ATK, - DEF, - SPATK, - SPDEF, - SPD, - ACC, - EVA, - RAND, - HP -} - -export function getBattleStatName(stat: BattleStat) { - switch (stat) { - case BattleStat.ATK: - return i18next.t("pokemonInfo:Stat.ATK"); - case BattleStat.DEF: - return i18next.t("pokemonInfo:Stat.DEF"); - case BattleStat.SPATK: - return i18next.t("pokemonInfo:Stat.SPATK"); - case BattleStat.SPDEF: - return i18next.t("pokemonInfo:Stat.SPDEF"); - case BattleStat.SPD: - return i18next.t("pokemonInfo:Stat.SPD"); - case BattleStat.ACC: - return i18next.t("pokemonInfo:Stat.ACC"); - case BattleStat.EVA: - return i18next.t("pokemonInfo:Stat.EVA"); - case BattleStat.HP: - return i18next.t("pokemonInfo:Stat.HPStat"); - default: - return "???"; - } -} - -export function getBattleStatLevelChangeDescription(pokemonNameWithAffix: string, stats: string, levels: integer, up: boolean, count: number = 1) { - const stringKey = (() => { - if (up) { - switch (levels) { - case 1: - return "battle:statRose"; - case 2: - return "battle:statSharplyRose"; - case 3: - case 4: - case 5: - case 6: - return "battle:statRoseDrastically"; - default: - return "battle:statWontGoAnyHigher"; - } - } else { - switch (levels) { - case 1: - return "battle:statFell"; - case 2: - return "battle:statHarshlyFell"; - case 3: - case 4: - case 5: - case 6: - return "battle:statSeverelyFell"; - default: - return "battle:statWontGoAnyLower"; - } - } - })(); - return i18next.t(stringKey as ParseKeys, { pokemonNameWithAffix, stats, count }); -} diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 8c05d296e76..66b6676a4f5 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,7 +1,6 @@ import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "./battle-anims"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { MoveResult, HitResult } from "../field/pokemon"; -import { Stat, getStatName } from "./pokemon-stat"; import { StatusEffect } from "./status-effect"; import * as Utils from "../utils"; import { ChargeAttr, MoveFlags, allMoves } from "./move"; @@ -9,20 +8,20 @@ import { Type } from "./type"; import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability"; import { TerrainType } from "./terrain"; import { WeatherType } from "./weather"; -import { BattleStat } from "./battle-stat"; import { allAbilities } from "./ability"; import { SpeciesFormChangeManualTrigger } from "./pokemon-forms"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import i18next from "#app/plugins/i18n.js"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; -import { StatChangePhase, StatChangeCallback } from "#app/phases/stat-change-phase.js"; +import i18next from "#app/plugins/i18n"; +import { Stat, type BattleStat, type EffectiveStat, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; +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 { StatStageChangePhase, StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; export enum BattlerTagLapseType { FAINT, @@ -40,13 +39,15 @@ export class BattlerTag { public turnCount: number; public sourceMove: Moves; public sourceId?: number; + public isBatonPassable: boolean; - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number, isBatonPassable: boolean = false) { this.tagType = tagType; this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ]; this.turnCount = turnCount; this.sourceMove = sourceMove!; // TODO: is this bang correct? this.sourceId = sourceId; + this.isBatonPassable = isBatonPassable; } canAdd(pokemon: Pokemon): boolean { @@ -97,6 +98,245 @@ export interface TerrainBattlerTag { terrainTypes: TerrainType[]; } +/** + * Base class for tags that restrict the usage of moves. This effect is generally referred to as "disabling" a move + * in-game. This is not to be confused with {@linkcode Moves.DISABLE}. + * + * Descendants can override {@linkcode isMoveRestricted} to restrict moves that + * match a condition. A restricted move gets cancelled before it is used. Players and enemies should not be allowed + * to select restricted moves. + */ +export abstract class MoveRestrictionBattlerTag extends BattlerTag { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: integer, sourceMove?: Moves, sourceId?: integer) { + super(tagType, lapseType, turnCount, sourceMove, sourceId); + } + + /** @override */ + override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { + if (lapseType === BattlerTagLapseType.PRE_MOVE) { + // Cancel the affected pokemon's selected move + const phase = pokemon.scene.getCurrentPhase() as MovePhase; + const move = phase.move; + + if (this.isMoveRestricted(move.moveId)) { + if (this.interruptedText(pokemon, move.moveId)) { + pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId)); + } + phase.cancel(); + } + + return true; + } + + return super.lapse(pokemon, lapseType); + } + + /** + * Gets whether this tag is restricting a move. + * + * @param {Moves} move {@linkcode Moves} ID to check restriction for. + * @returns {boolean} `true` if the move is restricted by this tag, otherwise `false`. + */ + abstract isMoveRestricted(move: Moves): boolean; + + /** + * Gets the text to display when the player attempts to select a move that is restricted by this tag. + * + * @param {Pokemon} pokemon {@linkcode Pokemon} for which the player is attempting to select the restricted move + * @param {Moves} move {@linkcode Moves} ID of the move that is having its selection denied + * @returns {string} text to display when the player attempts to select the restricted move + */ + abstract selectionDeniedText(pokemon: Pokemon, move: Moves): string; + + /** + * Gets the text to display when a move's execution is prevented as a result of the restriction. + * Because restriction effects also prevent selection of the move, this situation can only arise if a + * pokemon first selects a move, then gets outsped by a pokemon using a move that restricts the selected move. + * + * @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move + * @param {Moves} move {@linkcode Moves} ID of the move being interrupted + * @returns {string} text to display when the move is interrupted + */ + interruptedText(pokemon: Pokemon, move: Moves): string { + return ""; + } +} + +/** + * Tag representing the "Throat Chop" effect. Pokemon with this tag cannot use sound-based moves. + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Throat_Chop_(move) | Throat Chop} + * @extends MoveRestrictionBattlerTag + */ +export class ThroatChoppedTag extends MoveRestrictionBattlerTag { + constructor() { + super(BattlerTagType.THROAT_CHOPPED, [ BattlerTagLapseType.TURN_END, BattlerTagLapseType.PRE_MOVE ], 2, Moves.THROAT_CHOP); + } + + /** + * Checks if a {@linkcode Moves | move} is restricted by Throat Chop. + * @override + * @param {Moves} move the {@linkcode Moves | move} to check for sound-based restriction + * @returns true if the move is sound-based + */ + override isMoveRestricted(move: Moves): boolean { + return allMoves[move].hasFlag(MoveFlags.SOUND_BASED); + } + + /** + * Shows a message when the player attempts to select a move that is restricted by Throat Chop. + * @override + * @param {Pokemon} pokemon the {@linkcode Pokemon} that is attempting to select the restricted move + * @param {Moves} move the {@linkcode Moves | move} that is being restricted + * @returns the message to display when the player attempts to select the restricted move + */ + override selectionDeniedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name }); + } + + /** + * Shows a message when a move is interrupted by Throat Chop. + * @override + * @param {Pokemon} pokemon the interrupted {@linkcode Pokemon} + * @param {Moves} move the {@linkcode Moves | move} that was interrupted + * @returns the message to display when the move is interrupted + */ + override interruptedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:throatChopInterruptedMove", { pokemonName: getPokemonNameWithAffix(pokemon) }); + } +} + +/** + * Tag representing the "disabling" effect performed by {@linkcode Moves.DISABLE} and {@linkcode Abilities.CURSED_BODY}. + * When the tag is added, the last-used move of the tag holder is set as the disabled move. + */ +export class DisabledTag extends MoveRestrictionBattlerTag { + /** The move being disabled. Gets set when {@linkcode onAdd} is called for this tag. */ + private moveId: Moves = Moves.NONE; + + constructor(sourceId: number) { + super(BattlerTagType.DISABLED, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END ], 4, Moves.DISABLE, sourceId); + } + + /** @override */ + override isMoveRestricted(move: Moves): boolean { + return move === this.moveId; + } + + /** + * @override + * + * Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@linkcode moveId} and shows a message. + * Otherwise the move ID will not get assigned and this tag will get removed next turn. + */ + override onAdd(pokemon: Pokemon): void { + super.onAdd(pokemon); + + const move = pokemon.getLastXMoves() + .find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual); + if (move === undefined) { + return; + } + + this.moveId = move.move; + + pokemon.scene.queueMessage(i18next.t("battlerTags:disabledOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name })); + } + + /** @override */ + override onRemove(pokemon: Pokemon): void { + super.onRemove(pokemon); + + pokemon.scene.queueMessage(i18next.t("battlerTags:disabledLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name })); + } + + /** @override */ + override selectionDeniedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name }); + } + + /** + * @override + * @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move + * @param {Moves} move {@linkcode Moves} ID of the move being interrupted + * @returns {string} text to display when the move is interrupted + */ + override interruptedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name }); + } + + /** @override */ + override loadTag(source: BattlerTag | any): void { + super.loadTag(source); + this.moveId = source.moveId; + } +} + +/** + * Tag used by Gorilla Tactics to restrict the user to using only one move. + * @extends MoveRestrictionBattlerTag + */ +export class GorillaTacticsTag extends MoveRestrictionBattlerTag { + private moveId = Moves.NONE; + + constructor() { + super(BattlerTagType.GORILLA_TACTICS, BattlerTagLapseType.CUSTOM, 0); + } + + /** @override */ + override isMoveRestricted(move: Moves): boolean { + return move !== this.moveId; + } + + /** + * @override + * @param {Pokemon} pokemon the {@linkcode Pokemon} to check if the tag can be added + * @returns `true` if the pokemon has a valid move and no existing {@linkcode GorillaTacticsTag}; `false` otherwise + */ + override canAdd(pokemon: Pokemon): boolean { + return (this.getLastValidMove(pokemon) !== undefined) && !pokemon.getTag(GorillaTacticsTag); + } + + /** + * Ensures that move history exists on {@linkcode Pokemon} and has a valid move. + * If so, sets the {@linkcode moveId} and increases the user's Attack by 50%. + * @override + * @param {Pokemon} pokemon the {@linkcode Pokemon} to add the tag to + */ + override onAdd(pokemon: Pokemon): void { + const lastValidMove = this.getLastValidMove(pokemon); + + if (!lastValidMove) { + return; + } + + this.moveId = lastValidMove; + pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false); + } + + /** + * + * @override + * @param {Pokemon} pokemon n/a + * @param {Moves} move {@linkcode Moves} ID of the move being denied + * @returns {string} text to display when the move is denied + */ + override selectionDeniedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:canOnlyUseMove", { moveName: allMoves[this.moveId].name, pokemonName: getPokemonNameWithAffix(pokemon) }); + } + + /** + * Gets the last valid move from the pokemon's move history. + * @param {Pokemon} pokemon {@linkcode Pokemon} to get the last valid move from + * @returns {Moves | undefined} the last valid move from the pokemon's move history + */ + getLastValidMove(pokemon: Pokemon): Moves | undefined { + const move = pokemon.getLastXMoves() + .find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual); + + return move?.move; + } +} + /** * BattlerTag that represents the "recharge" effects of moves like Hyper Beam. */ @@ -207,12 +447,12 @@ export class ShellTrapTag extends BattlerTag { export class TrappedTag extends BattlerTag { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) { - super(tagType, lapseType, turnCount, sourceMove, sourceId); + super(tagType, lapseType, turnCount, sourceMove, sourceId, true); } canAdd(pokemon: Pokemon): boolean { const isGhost = pokemon.isOfType(Type.GHOST); - const isTrapped = pokemon.getTag(BattlerTagType.TRAPPED); + const isTrapped = pokemon.getTag(TrappedTag); return !isTrapped && !isGhost; } @@ -245,6 +485,23 @@ export class TrappedTag extends BattlerTag { } } +/** + * BattlerTag implementing No Retreat's trapping effect. + * This is treated separately from other trapping effects to prevent + * Ghost-type Pokemon from being able to reuse the move. + * @extends TrappedTag + */ +class NoRetreatTag extends TrappedTag { + constructor(sourceId: number) { + super(BattlerTagType.NO_RETREAT, BattlerTagLapseType.CUSTOM, 0, Moves.NO_RETREAT, sourceId); + } + + /** overrides {@linkcode TrappedTag.apply}, removing the Ghost-type condition */ + canAdd(pokemon: Pokemon): boolean { + return !pokemon.getTag(TrappedTag); + } +} + /** * BattlerTag that represents the {@link https://bulbapedia.bulbagarden.net/wiki/Flinch Flinch} status condition */ @@ -310,7 +567,7 @@ export class InterruptedTag extends BattlerTag { */ export class ConfusedTag extends BattlerTag { constructor(turnCount: number, sourceMove: Moves) { - super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove); + super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove, undefined, true); } canAdd(pokemon: Pokemon): boolean { @@ -345,9 +602,9 @@ export class ConfusedTag extends BattlerTag { // 1/3 chance of hitting self with a 40 base power move if (pokemon.randSeedInt(3) === 0) { - const atk = pokemon.getBattleStat(Stat.ATK); - const def = pokemon.getBattleStat(Stat.DEF); - const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); + const atk = pokemon.getEffectiveStat(Stat.ATK); + const def = pokemon.getEffectiveStat(Stat.DEF); + const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedIntRange(85, 100) / 100)); pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); pokemon.damageAndUpdate(damage); pokemon.battleData.hitCount++; @@ -370,7 +627,7 @@ export class ConfusedTag extends BattlerTag { */ export class DestinyBondTag extends BattlerTag { constructor(sourceMove: Moves, sourceId: number) { - super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId); + super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId, true); } /** @@ -489,7 +746,7 @@ export class SeedTag extends BattlerTag { private sourceIndex: number; constructor(sourceId: number) { - super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId); + super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId, true); } /** @@ -750,7 +1007,7 @@ export class OctolockTag extends TrappedTag { const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (shouldLapse) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF, BattleStat.SPDEF], -1)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.DEF, Stat.SPDEF ], -1)); return true; } @@ -760,7 +1017,7 @@ export class OctolockTag extends TrappedTag { export class AquaRingTag extends BattlerTag { constructor() { - super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined); + super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined, true); } onAdd(pokemon: Pokemon): void { @@ -792,7 +1049,7 @@ export class AquaRingTag extends BattlerTag { /** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */ export class MinimizeTag extends BattlerTag { constructor() { - super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE, undefined); + super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE); } canAdd(pokemon: Pokemon): boolean { @@ -864,7 +1121,7 @@ export abstract class DamagingTrapTag extends TrappedTag { } canAdd(pokemon: Pokemon): boolean { - return !pokemon.isOfType(Type.GHOST) && !pokemon.findTag(t => t instanceof DamagingTrapTag); + return !pokemon.getTag(TrappedTag); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1041,6 +1298,13 @@ export class ProtectedTag extends BattlerTag { } } +/** Base class for `BattlerTag`s that block damaging moves but not status moves */ +export class DamageProtectedTag extends ProtectedTag {} + +/** + * `BattlerTag` class for moves that block damaging moves damage the enemy if the enemy's move makes contact + * Used by {@linkcode Moves.SPIKY_SHIELD} + */ export class ContactDamageProtectedTag extends ProtectedTag { private damageRatio: number; @@ -1076,7 +1340,11 @@ export class ContactDamageProtectedTag extends ProtectedTag { } } -export class ContactStatChangeProtectedTag extends ProtectedTag { +/** + * `BattlerTag` class for moves that block damaging moves and lower enemy stats if the enemy's move makes contact + * Used by {@linkcode Moves.KINGS_SHIELD}, {@linkcode Moves.OBSTRUCT}, {@linkcode Moves.SILK_TRAP} + */ +export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { private stat: BattleStat; private levels: number; @@ -1093,7 +1361,7 @@ export class ContactStatChangeProtectedTag extends ProtectedTag { */ loadTag(source: BattlerTag | any): void { super.loadTag(source); - this.stat = source.stat as BattleStat; + this.stat = source.stat; this.levels = source.levels; } @@ -1104,7 +1372,7 @@ export class ContactStatChangeProtectedTag extends ProtectedTag { const effectPhase = pokemon.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels)); } } @@ -1132,7 +1400,11 @@ export class ContactPoisonProtectedTag extends ProtectedTag { } } -export class ContactBurnProtectedTag extends ProtectedTag { +/** + * `BattlerTag` class for moves that block damaging moves and burn the enemy if the enemy's move makes contact + * Used by {@linkcode Moves.BURNING_BULWARK} + */ +export class ContactBurnProtectedTag extends DamageProtectedTag { constructor(sourceMove: Moves) { super(sourceMove, BattlerTagType.BURNING_BULWARK); } @@ -1190,7 +1462,7 @@ export class SturdyTag extends BattlerTag { export class PerishSongTag extends BattlerTag { constructor(turnCount: number) { - super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG); + super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG, undefined, true); } canAdd(pokemon: Pokemon): boolean { @@ -1246,7 +1518,7 @@ export class AbilityBattlerTag extends BattlerTag { public ability: Abilities; constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) { - super(tagType, lapseType, turnCount, undefined); + super(tagType, lapseType, turnCount); this.ability = ability; } @@ -1331,11 +1603,10 @@ export class HighestStatBoostTag extends AbilityBattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - const stats = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; - let highestStat: Stat; - stats.map(s => pokemon.getBattleStat(s)).reduce((highestValue: number, value: number, i: number) => { + let highestStat: EffectiveStat; + EFFECTIVE_STATS.map(s => pokemon.getEffectiveStat(s)).reduce((highestValue: number, value: number, i: number) => { if (value > highestValue) { - highestStat = stats[i]; + highestStat = EFFECTIVE_STATS[i]; return value; } return highestValue; @@ -1353,7 +1624,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag { break; } - pokemon.scene.queueMessage(i18next.t("battlerTags:highestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), statName: getStatName(highestStat) }), null, false, null, true); + pokemon.scene.queueMessage(i18next.t("battlerTags:highestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), statName: i18next.t(getStatKey(highestStat)) }), null, false, null, true); } onRemove(pokemon: Pokemon): void { @@ -1423,7 +1694,7 @@ export class TypeImmuneTag extends BattlerTag { public immuneType: Type; constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) { - super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove); + super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove, undefined, true); this.immuneType = immuneType; } @@ -1487,7 +1758,7 @@ export class TypeBoostTag extends BattlerTag { export class CritBoostTag extends BattlerTag { constructor(tagType: BattlerTagType, sourceMove: Moves) { - super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove); + super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true); } onAdd(pokemon: Pokemon): void { @@ -1507,6 +1778,25 @@ export class CritBoostTag extends BattlerTag { } } +/** + * Tag for the effects of Dragon Cheer, which boosts the critical hit ratio of the user's allies. + * @extends {CritBoostTag} + */ +export class DragonCheerTag extends CritBoostTag { + /** The types of the user's ally when the tag is added */ + public typesOnAdd: Type[]; + + constructor() { + super(BattlerTagType.CRIT_BOOST, Moves.DRAGON_CHEER); + } + + onAdd(pokemon: Pokemon): void { + super.onAdd(pokemon); + + this.typesOnAdd = pokemon.getTypes(true); + } +} + export class SaltCuredTag extends BattlerTag { private sourceIndex: number; @@ -1560,7 +1850,7 @@ export class CursedTag extends BattlerTag { private sourceIndex: number; constructor(sourceId: number) { - super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId); + super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId, true); } /** @@ -1678,25 +1968,25 @@ export class IceFaceBlockDamageTag extends FormBlockDamageTag { */ export class StockpilingTag extends BattlerTag { public stockpiledCount: number = 0; - public statChangeCounts: { [BattleStat.DEF]: number; [BattleStat.SPDEF]: number } = { - [BattleStat.DEF]: 0, - [BattleStat.SPDEF]: 0 + public statChangeCounts: { [Stat.DEF]: number; [Stat.SPDEF]: number } = { + [Stat.DEF]: 0, + [Stat.SPDEF]: 0 }; constructor(sourceMove: Moves = Moves.NONE) { super(BattlerTagType.STOCKPILING, BattlerTagLapseType.CUSTOM, 1, sourceMove); } - private onStatsChanged: StatChangeCallback = (_, statsChanged, statChanges) => { - const defChange = statChanges[statsChanged.indexOf(BattleStat.DEF)] ?? 0; - const spDefChange = statChanges[statsChanged.indexOf(BattleStat.SPDEF)] ?? 0; + private onStatStagesChanged: StatStageChangeCallback = (_, statsChanged, statChanges) => { + const defChange = statChanges[statsChanged.indexOf(Stat.DEF)] ?? 0; + const spDefChange = statChanges[statsChanged.indexOf(Stat.SPDEF)] ?? 0; if (defChange) { - this.statChangeCounts[BattleStat.DEF]++; + this.statChangeCounts[Stat.DEF]++; } if (spDefChange) { - this.statChangeCounts[BattleStat.SPDEF]++; + this.statChangeCounts[Stat.SPDEF]++; } }; @@ -1704,8 +1994,8 @@ export class StockpilingTag extends BattlerTag { super.loadTag(source); this.stockpiledCount = source.stockpiledCount || 0; this.statChangeCounts = { - [ BattleStat.DEF ]: source.statChangeCounts?.[ BattleStat.DEF ] ?? 0, - [ BattleStat.SPDEF ]: source.statChangeCounts?.[ BattleStat.SPDEF ] ?? 0, + [ Stat.DEF ]: source.statChangeCounts?.[ Stat.DEF ] ?? 0, + [ Stat.SPDEF ]: source.statChangeCounts?.[ Stat.SPDEF ] ?? 0, }; } @@ -1725,9 +2015,9 @@ export class StockpilingTag extends BattlerTag { })); // Attempt to increase DEF and SPDEF by one stage, keeping track of successful changes. - pokemon.scene.unshiftPhase(new StatChangePhase( + pokemon.scene.unshiftPhase(new StatStageChangePhase( pokemon.scene, pokemon.getBattlerIndex(), true, - [BattleStat.SPDEF, BattleStat.DEF], 1, true, false, true, this.onStatsChanged + [Stat.SPDEF, Stat.DEF], 1, true, false, true, this.onStatStagesChanged )); } } @@ -1741,15 +2031,15 @@ export class StockpilingTag extends BattlerTag { * one stage for each stack which had successfully changed that particular stat during onAdd. */ onRemove(pokemon: Pokemon): void { - const defChange = this.statChangeCounts[BattleStat.DEF]; - const spDefChange = this.statChangeCounts[BattleStat.SPDEF]; + const defChange = this.statChangeCounts[Stat.DEF]; + const spDefChange = this.statChangeCounts[Stat.SPDEF]; if (defChange) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF], -defChange, true, false, true)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF ], -defChange, true, false, true)); } if (spDefChange) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.SPDEF], -spDefChange, true, false, true)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF ], -spDefChange, true, false, true)); } } } @@ -1827,7 +2117,38 @@ export class ExposedTag extends BattlerTag { } } +/** + * Tag that doubles the type effectiveness of Fire-type moves. + * @extends BattlerTag + */ +export class TarShotTag extends BattlerTag { + constructor() { + super(BattlerTagType.TAR_SHOT, BattlerTagLapseType.CUSTOM, 0); + } + /** + * If the Pokemon is terastallized, the tag cannot be added. + * @param {Pokemon} pokemon the {@linkcode Pokemon} to which the tag is added + * @returns whether the tag is applied + */ + override canAdd(pokemon: Pokemon): boolean { + return !pokemon.isTerastallized(); + } + + override onAdd(pokemon: Pokemon): void { + pokemon.scene.queueMessage(i18next.t("battlerTags:tarShotOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } +} + +/** + * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. + * + * @param {BattlerTagType} tagType the type of the {@linkcode BattlerTagType}. + * @param turnCount the turn count. + * @param {Moves} sourceMove the source {@linkcode Moves}. + * @param sourceId the source ID. + * @returns {BattlerTag} the corresponding {@linkcode BattlerTag} object. + */ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag { switch (tagType) { case BattlerTagType.RECHARGING: @@ -1864,6 +2185,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new DrowsyTag(); case BattlerTagType.TRAPPED: return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); + case BattlerTagType.NO_RETREAT: + return new NoRetreatTag(sourceId); case BattlerTagType.BIND: return new BindTag(turnCount, sourceId); case BattlerTagType.WRAP: @@ -1889,11 +2212,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.SPIKY_SHIELD: return new ContactDamageProtectedTag(sourceMove, 8); case BattlerTagType.KINGS_SHIELD: - return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.ATK, -1); + return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.ATK, -1); case BattlerTagType.OBSTRUCT: - return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.DEF, -2); + return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.DEF, -2); case BattlerTagType.SILK_TRAP: - return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.SPD, -1); + return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.SPD, -1); case BattlerTagType.BANEFUL_BUNKER: return new ContactPoisonProtectedTag(sourceMove); case BattlerTagType.BURNING_BULWARK: @@ -1923,6 +2246,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new TypeBoostTag(tagType, sourceMove, Type.FIRE, 1.5, false); case BattlerTagType.CRIT_BOOST: return new CritBoostTag(tagType, sourceMove); + case BattlerTagType.DRAGON_CHEER: + return new DragonCheerTag(); case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.IGNORE_ACCURACY: return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove); @@ -1955,6 +2280,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new StockpilingTag(sourceMove); case BattlerTagType.OCTOLOCK: return new OctolockTag(sourceId); + case BattlerTagType.DISABLED: + return new DisabledTag(sourceId); case BattlerTagType.IGNORE_GHOST: return new ExposedTag(tagType, sourceMove, Type.GHOST, [Type.NORMAL, Type.FIGHTING]); case BattlerTagType.IGNORE_DARK: @@ -1962,6 +2289,12 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.GULP_MISSILE_ARROKUDA: case BattlerTagType.GULP_MISSILE_PIKACHU: return new GulpMissileTag(tagType, sourceMove); + case BattlerTagType.TAR_SHOT: + return new TarShotTag(); + case BattlerTagType.THROAT_CHOPPED: + return new ThroatChoppedTag(); + case BattlerTagType.GORILLA_TACTICS: + return new GorillaTacticsTag(); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/berry.ts b/src/data/berry.ts index d0c9c311e16..01325ee39dd 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -1,14 +1,14 @@ import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { HitResult } from "../field/pokemon"; -import { BattleStat } from "./battle-stat"; import { getStatusEffectHealText } from "./status-effect"; import * as Utils from "../utils"; import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./ability"; import i18next from "i18next"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; +import { Stat, type BattleStat } from "#app/enums/stat"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; export function getBerryName(berryType: BerryType): string { return i18next.t(`berry:${BerryType[berryType]}.name`); @@ -35,9 +35,10 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { case BerryType.SALAC: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - const battleStat = (berryType - BerryType.LIECHI) as BattleStat; + // Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth + const stat: BattleStat = berryType - BerryType.ENIGMA; applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); - return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6; + return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6; }; case BerryType.LANSAT: return (pokemon: Pokemon) => { @@ -95,10 +96,11 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const battleStat = (berryType - BerryType.LIECHI) as BattleStat; - const statLevels = new Utils.NumberHolder(1); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value)); + // 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); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value)); }; case BerryType.LANSAT: return (pokemon: Pokemon) => { @@ -112,9 +114,10 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const statLevels = new Utils.NumberHolder(2); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value)); + const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK); + const stages = new Utils.NumberHolder(2); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value)); }; case BerryType.LEPPA: return (pokemon: Pokemon) => { diff --git a/src/data/biomes.ts b/src/data/biomes.ts index d9ea22f50f2..0e37cc94ff5 100644 --- a/src/data/biomes.ts +++ b/src/data/biomes.ts @@ -1,6 +1,6 @@ import { Type } from "./type"; import * as Utils from "../utils"; -import {pokemonEvolutions, SpeciesFormEvolution} from "./pokemon-evolutions"; +import { pokemonEvolutions, SpeciesFormEvolution } from "./pokemon-evolutions"; import i18next from "i18next"; import { Biome } from "#enums/biome"; import { Species } from "#enums/species"; @@ -46,7 +46,7 @@ export const biomeLinks: BiomeLinks = { [Biome.SEABED]: [ Biome.CAVE, [ Biome.VOLCANO, 3 ] ], [Biome.MOUNTAIN]: [ Biome.VOLCANO, [ Biome.WASTELAND, 2 ], [ Biome.SPACE, 3 ] ], [Biome.BADLANDS]: [ Biome.DESERT, Biome.MOUNTAIN ], - [Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE [ Biome.LABORATORY, 2 ] ], + [Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE, [ Biome.LABORATORY, 2 ] ], [Biome.DESERT]: [ Biome.RUINS, [ Biome.CONSTRUCTION_SITE, 2 ] ], [Biome.ICE_CAVE]: Biome.SNOWY_FOREST, [Biome.MEADOW]: [ Biome.PLAINS, Biome.FAIRY_CAVE ], @@ -7666,7 +7666,7 @@ export function initBiomes() { if (biome === Biome.END) { const biomeList = Object.keys(Biome).filter(key => !isNaN(Number(key))); biomeList.pop(); // Removes Biome.END from the list - const randIndex = Utils.randInt(biomeList.length, 2); // Will never be Biome.TOWN or Biome.PLAINS + const randIndex = Utils.randInt(biomeList.length, 1); // Will never be Biome.TOWN biome = Biome[biomeList[randIndex]]; } const linkedBiomes: (Biome | [ Biome, integer ])[] = Array.isArray(biomeLinks[biome]) diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 36f696e63c1..2205519c532 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,19 +1,18 @@ import * as Utils from "../utils"; import i18next from "i18next"; -import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data.js"; +import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data"; import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "./pokemon-species"; -import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; -import { BattleType, FixedBattleConfig } from "#app/battle.js"; -import Trainer, { TrainerVariant } from "#app/field/trainer.js"; -import { GameMode } from "#app/game-mode.js"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; +import { BattleType, FixedBattleConfig } from "#app/battle"; +import Trainer, { TrainerVariant } from "#app/field/trainer"; +import { GameMode } from "#app/game-mode"; import { Type } from "./type"; import { Challenges } from "#enums/challenges"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { Nature } from "./nature"; -import { Moves } from "#app/enums/moves.js"; -import { TypeColor, TypeShadow } from "#app/enums/color.js"; -import { Gender } from "./gender"; +import { Moves } from "#app/enums/moves"; +import { TypeColor, TypeShadow } from "#app/enums/color"; import { pokemonEvolutions } from "./pokemon-evolutions"; import { pokemonFormChanges } from "./pokemon-forms"; @@ -659,7 +658,6 @@ export class FreshStartChallenge extends Challenge { pokemon.luck = 0; // No luck pokemon.shiny = false; // Not shiny pokemon.variant = 0; // Not shiny - pokemon.gender = Gender.MALE; // Starters default to male pokemon.formIndex = 0; // Froakie should be base form pokemon.ivs = [10, 10, 10, 10, 10, 10]; // Default IVs of 10 for all stats return true; diff --git a/src/data/dialogue.ts b/src/data/dialogue.ts index 3ef6d30643c..355f05523d1 100644 --- a/src/data/dialogue.ts +++ b/src/data/dialogue.ts @@ -1,6 +1,6 @@ import { BattleSpec } from "#enums/battle-spec"; import { TrainerType } from "#enums/trainer-type"; -import {trainerConfigs} from "./trainer-config"; +import { trainerConfigs } from "./trainer-config"; export interface TrainerTypeMessages { encounter?: string | string[], @@ -707,6 +707,20 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { ] } ], + [TrainerType.ROOD]: [ + { + encounter: [ + "dialogue:rood.encounter.1", + "dialogue:rood.encounter.2", + "dialogue:rood.encounter.3", + ], + victory: [ + "dialogue:rood.victory.1", + "dialogue:rood.victory.2", + "dialogue:rood.victory.3", + ] + } + ], [TrainerType.FLARE_GRUNT]: [ { encounter: [ @@ -1555,8 +1569,7 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { "dialogue:roark.victory.1", "dialogue:roark.victory.2", "dialogue:roark.victory.3", - "dialogue:roark.victory.4", - "dialogue:roark.victory.5" + "dialogue:roark.victory.4" ], defeat: [ "dialogue:roark.defeat.1", diff --git a/src/data/egg-hatch-data.ts b/src/data/egg-hatch-data.ts new file mode 100644 index 00000000000..e754a9205c4 --- /dev/null +++ b/src/data/egg-hatch-data.ts @@ -0,0 +1,98 @@ +import BattleScene from "#app/battle-scene"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { DexEntry, StarterDataEntry } from "#app/system/game-data"; + +/** + * Stores data associated with a specific egg and the hatched pokemon + * Allows hatch info to be stored at hatch then retrieved for display during egg summary + */ +export class EggHatchData { + /** the pokemon that hatched from the file (including shiny, IVs, ability) */ + public pokemon: PlayerPokemon; + /** index of the egg move from the hatched pokemon (not stored in PlayerPokemon) */ + public eggMoveIndex: number; + /** boolean indicating if the egg move for the hatch is new */ + public eggMoveUnlocked: boolean; + /** stored copy of the hatched pokemon's dex entry before it was updated due to hatch */ + public dexEntryBeforeUpdate: DexEntry; + /** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */ + public starterDataEntryBeforeUpdate: StarterDataEntry; + /** reference to the battle scene to get gamedata and update dex */ + private scene: BattleScene; + + constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) { + this.scene = scene; + this.pokemon = pokemon; + this.eggMoveIndex = eggMoveIndex; + } + + /** + * Sets the boolean for if the egg move for the hatch is a new unlock + * @param unlocked True if the EM is new + */ + setEggMoveUnlocked(unlocked: boolean) { + this.eggMoveUnlocked = unlocked; + } + + /** + * Stores a copy of the current DexEntry of the pokemon and StarterDataEntry of its starter + * Used before updating the dex, so comparing the pokemon to these entries will show the new attributes + */ + setDex() { + const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId]; + const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()]; + this.dexEntryBeforeUpdate = { + seenAttr: currDexEntry.seenAttr, + caughtAttr: currDexEntry.caughtAttr, + natureAttr: currDexEntry.natureAttr, + seenCount: currDexEntry.seenCount, + caughtCount: currDexEntry.caughtCount, + hatchedCount: currDexEntry.hatchedCount, + ivs: [...currDexEntry.ivs] + }; + this.starterDataEntryBeforeUpdate = { + moveset: currStarterDataEntry.moveset, + eggMoves: currStarterDataEntry.eggMoves, + candyCount: currStarterDataEntry.candyCount, + friendship: currStarterDataEntry.friendship, + abilityAttr: currStarterDataEntry.abilityAttr, + passiveAttr: currStarterDataEntry.passiveAttr, + valueReduction: currStarterDataEntry.valueReduction, + classicWinCount: currStarterDataEntry.classicWinCount + }; + } + + /** + * Gets the dex entry before update + * @returns Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex + */ + getDex(): DexEntry { + return this.dexEntryBeforeUpdate; + } + + /** + * Gets the starter dex entry before update + * @returns Starter Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex + */ + getStarterEntry(): StarterDataEntry { + return this.starterDataEntryBeforeUpdate; + } + + /** + * Update the pokedex data corresponding with the new hatch's pokemon data + * Also sets whether the egg move is a new unlock or not + * @param showMessage boolean to show messages for the new catches and egg moves (false by default) + * @returns + */ + updatePokemon(showMessage : boolean = false) { + return new Promise(resolve => { + this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => { + this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); + this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => { + this.setEggMoveUnlocked(value); + resolve(); + }); + }); + }); + } +} diff --git a/src/data/egg.ts b/src/data/egg.ts index 3e872d364f3..1cd5c65fc18 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -8,14 +8,14 @@ import { PlayerPokemon } from "#app/field/pokemon"; import i18next from "i18next"; import { EggTier } from "#enums/egg-type"; import { Species } from "#enums/species"; -import { EggSourceType } from "#app/enums/egg-source-types.js"; +import { EggSourceType } from "#app/enums/egg-source-types"; export const EGG_SEED = 1073741824; // Rates for specific random properties in 1/x const DEFAULT_SHINY_RATE = 128; const GACHA_SHINY_UP_SHINY_RATE = 64; -const SAME_SPECIES_EGG_SHINY_RATE = 24; +const SAME_SPECIES_EGG_SHINY_RATE = 12; const SAME_SPECIES_EGG_HA_RATE = 8; const MANAPHY_EGG_MANAPHY_RATE = 8; const GACHA_EGG_HA_RATE = 192; @@ -139,46 +139,57 @@ export class Egg { //// constructor(eggOptions?: IEggOptions) { - //if (eggOptions.tier && eggOptions.species) throw Error("Error egg can't have species and tier as option. only choose one of them.") + const generateEggProperties = (eggOptions?: IEggOptions) => { + //if (eggOptions.tier && eggOptions.species) throw Error("Error egg can't have species and tier as option. only choose one of them.") - this._sourceType = eggOptions?.sourceType!; // TODO: is this bang correct? - // Ensure _sourceType is defined before invoking rollEggTier(), as it is referenced - this._tier = eggOptions?.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); - // If egg was pulled, check if egg pity needs to override the egg tier - if (eggOptions?.pulled) { - // Needs this._tier and this._sourceType to work - this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? - } + this._sourceType = eggOptions?.sourceType!; // TODO: is this bang correct? + // Ensure _sourceType is defined before invoking rollEggTier(), as it is referenced + this._tier = eggOptions?.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); + // If egg was pulled, check if egg pity needs to override the egg tier + if (eggOptions?.pulled) { + // Needs this._tier and this._sourceType to work + this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? + } - this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); + this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); - this._sourceType = eggOptions?.sourceType ?? undefined; - this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); + this._sourceType = eggOptions?.sourceType ?? undefined; + this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); + this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); - // First roll shiny and variant so we can filter if species with an variant exist - this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); - this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); - this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? + // First roll shiny and variant so we can filter if species with an variant exist + this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); + this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); + this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? - this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; + this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; - // Override egg tier and hatchwaves if species was given - if (eggOptions?.species) { - this._tier = this.getEggTierFromSpeciesStarterValue(); - this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - } - // If species has no variant, set variantTier to common. This needs to - // be done because species with no variants get filtered at rollSpecies but if the - // species is set via options or the legendary gacha pokemon gets choosen the check never happens - if (this._species && !getPokemonSpecies(this._species).hasVariants()) { - this._variantTier = VariantTier.COMMON; - } - // Needs this._tier so it needs to be generated afer the tier override if bought from same species - this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); - if (eggOptions?.pulled) { - this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? - this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? + // Override egg tier and hatchwaves if species was given + if (eggOptions?.species) { + this._tier = this.getEggTierFromSpeciesStarterValue(); + this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); + } + // If species has no variant, set variantTier to common. This needs to + // be done because species with no variants get filtered at rollSpecies but if the + // species is set via options or the legendary gacha pokemon gets choosen the check never happens + if (this._species && !getPokemonSpecies(this._species).hasVariants()) { + this._variantTier = VariantTier.COMMON; + } + // Needs this._tier so it needs to be generated afer the tier override if bought from same species + this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); + if (eggOptions?.pulled) { + this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? + this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? + } + }; + + if (eggOptions?.scene) { + const seedOverride = Utils.randomString(24); + eggOptions?.scene.executeWithSeedOffset(() => { + generateEggProperties(eggOptions); + }, 0, seedOverride); + } else { // For legacy eggs without scene + generateEggProperties(eggOptions); } } @@ -200,37 +211,46 @@ export class Egg { // Generates a PlayerPokemon from an egg public generatePlayerPokemon(scene: BattleScene): PlayerPokemon { - // Legacy egg wants to hatch. Generate missing properties - if (!this._species) { - this._isShiny = this.rollShiny(); - this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? - } + let ret: PlayerPokemon; - let pokemonSpecies = getPokemonSpecies(this._species); - // Special condition to have Phione eggs also have a chance of generating Manaphy - if (this._species === Species.PHIONE) { - pokemonSpecies = getPokemonSpecies(Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY); - } + const generatePlayerPokemonHelper = (scene: BattleScene) => { + // Legacy egg wants to hatch. Generate missing properties + if (!this._species) { + this._isShiny = this.rollShiny(); + this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? + } - // 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)); - if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) { - abilityIndex = 2; - } + 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); + } - // This function has way to many optional parameters - const ret: PlayerPokemon = scene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false); - ret.shiny = this._isShiny; - ret.variant = this._variantTier; + // 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)); + if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) { + abilityIndex = 2; + } - const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295)); + // This function has way to many optional parameters + ret = scene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false); + ret.shiny = this._isShiny; + ret.variant = this._variantTier; - for (let s = 0; s < ret.ivs.length; s++) { - ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]); - } + const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295)); + + for (let s = 0; s < ret.ivs.length; s++) { + ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]); + } + }; + + ret = ret!; // Tell TS compiler it's defined now + scene.executeWithSeedOffset(() => { + generatePlayerPokemonHelper(scene); + }, this._id, EGG_SEED.toString()); return ret; } @@ -306,7 +326,8 @@ export class Egg { break; } - return Utils.randSeedInt(baseChance * Math.pow(2, 3 - this.tier)) ? Utils.randSeedInt(3) : 3; + const tierMultiplier = this.isManaphyEgg() ? 2 : Math.pow(2, 3 - this.tier); + return Utils.randSeedInt(baseChance * tierMultiplier) ? Utils.randSeedInt(3) : 3; } private getEggTierDefaultHatchWaves(eggTier?: EggTier): number { @@ -341,7 +362,12 @@ export class Egg { * the species that was the legendary focus at the time */ if (this.isManaphyEgg()) { - const rand = Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE); + /** + * Adding a technicality to make unit tests easier: By making this check pass + * 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); return rand ? Species.PHIONE : Species.MANAPHY; } else if (this.tier === EggTier.MASTER && this._sourceType === EggSourceType.GACHA_LEGENDARY) { diff --git a/src/data/move.ts b/src/data/move.ts index d50dc7e2074..672a1154a0e 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,5 +1,4 @@ import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleStat, getBattleStatName } from "./battle-stat"; import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, TypeBoostTag } from "./battler-tags"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon"; @@ -13,7 +12,6 @@ import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilit import { allAbilities } from "./ability"; import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier"; import { BattlerIndex, BattleType } from "../battle"; -import { Stat } from "./pokemon-stat"; import { TerrainType } from "./terrain"; import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../ui/command-ui-handler"; @@ -27,13 +25,14 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { MoveUsedEvent } from "#app/events/battle-scene"; +import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MovePhase } from "#app/phases/move-phase"; import { NewBattlePhase } from "#app/phases/new-battle-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms"; @@ -82,6 +81,16 @@ export enum MoveFlags { MAKES_CONTACT = 1 << 0, IGNORE_PROTECT = 1 << 1, IGNORE_VIRTUAL = 1 << 2, + /** + * Sound-based moves have the following effects: + * - Pokemon with the {@linkcode Abilities.SOUNDPROOF Soundproof Ability} are unaffected by other Pokemon's sound-based moves. + * - Pokemon affected by {@linkcode Moves.THROAT_CHOP Throat Chop} cannot use sound-based moves for two turns. + * - Sound-based moves used by a Pokemon with {@linkcode Abilities.LIQUID_VOICE Liquid Voice} become Water-type moves. + * - Sound-based moves used by a Pokemon with {@linkcode Abilities.PUNK_ROCK Punk Rock} are boosted by 30%. Pokemon with Punk Rock also take half damage from sound-based moves. + * - All sound-based moves (except Howl) can hit Pokemon behind an active {@linkcode Moves.SUBSTITUTE Substitute}. + * + * cf https://bulbapedia.bulbagarden.net/wiki/Sound-based_move + */ SOUND_BASED = 1 << 3, HIDE_USER = 1 << 4, HIDE_TARGET = 1 << 5, @@ -94,19 +103,20 @@ export enum MoveFlags { * @see {@linkcode Move.recklessMove()} */ RECKLESS_MOVE = 1 << 10, + /** Indicates a move should be affected by {@linkcode Abilities.BULLETPROOF} */ BALLBOMB_MOVE = 1 << 11, + /** Grass types and pokemon with {@linkcode Abilities.OVERCOAT} are immune to powder moves */ POWDER_MOVE = 1 << 12, + /** Indicates a move should trigger {@linkcode Abilities.DANCER} */ DANCE_MOVE = 1 << 13, + /** Indicates a move should trigger {@linkcode Abilities.WIND_RIDER} */ WIND_MOVE = 1 << 14, + /** Indicates a move should trigger {@linkcode Abilities.TRIAGE} */ TRIAGE_MOVE = 1 << 15, IGNORE_ABILITIES = 1 << 16, - /** - * Enables all hits of a multi-hit move to be accuracy checked individually - */ + /** Enables all hits of a multi-hit move to be accuracy checked individually */ CHECK_ALL_HITS = 1 << 17, - /** - * Indicates a move is able to be redirected to allies in a double battle if the attacker faints - */ + /** Indicates a move is able to be redirected to allies in a double battle if the attacker faints */ REDIRECT_COUNTER = 1 << 18, } @@ -119,22 +129,22 @@ export default class Move implements Localizable { private _type: Type; private _category: MoveCategory; public moveTarget: MoveTarget; - public power: integer; - public accuracy: integer; - public pp: integer; + public power: number; + public accuracy: number; + public pp: number; public effect: string; - public chance: integer; - public priority: integer; - public generation: integer; - public attrs: MoveAttr[]; - private conditions: MoveCondition[]; - private flags: integer; - private nameAppend: string; + /** The chance of a move's secondary effects activating */ + public chance: number; + public priority: number; + public generation: number; + public attrs: MoveAttr[] = []; + private conditions: MoveCondition[] = []; + /** The move's {@linkcode MoveFlags} */ + private flags: number = 0; + private nameAppend: string = ""; - constructor(id: Moves, type: Type, category: MoveCategory, defaultMoveTarget: MoveTarget, power: integer, accuracy: integer, pp: integer, chance: integer, priority: integer, generation: integer) { + constructor(id: Moves, type: Type, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) { this.id = id; - - this.nameAppend = ""; this._type = type; this._category = category; this.moveTarget = defaultMoveTarget; @@ -145,10 +155,6 @@ export default class Move implements Localizable { this.priority = priority; this.generation = generation; - this.attrs = []; - this.conditions = []; - - this.flags = 0; if (defaultMoveTarget === MoveTarget.USER) { this.setFlag(MoveFlags.IGNORE_PROTECT, true); } @@ -378,7 +384,7 @@ export default class Move implements Localizable { * @param makesContact The value (boolean) to set the flag to * @returns The {@linkcode Move} that called this function */ - makesContact(makesContact: boolean = true): this { // TODO: is true the correct default? + makesContact(makesContact: boolean = true): this { this.setFlag(MoveFlags.MAKES_CONTACT, makesContact); return this; } @@ -389,7 +395,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.CURSE} * @returns The {@linkcode Move} that called this function */ - ignoresProtect(ignoresProtect: boolean = true): this { // TODO: is `true` the correct default? + ignoresProtect(ignoresProtect: boolean = true): this { this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect); return this; } @@ -400,7 +406,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.NATURE_POWER} * @returns The {@linkcode Move} that called this function */ - ignoresVirtual(ignoresVirtual: boolean = true): this { // TODO: is `true` the correct default? + ignoresVirtual(ignoresVirtual: boolean = true): this { this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual); return this; } @@ -411,7 +417,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.UPROAR} * @returns The {@linkcode Move} that called this function */ - soundBased(soundBased: boolean = true): this { // TODO: is `true` the correct default? + soundBased(soundBased: boolean = true): this { this.setFlag(MoveFlags.SOUND_BASED, soundBased); return this; } @@ -422,7 +428,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TELEPORT} * @returns The {@linkcode Move} that called this function */ - hidesUser(hidesUser: boolean = true): this { // TODO: is `true` the correct default? + hidesUser(hidesUser: boolean = true): this { this.setFlag(MoveFlags.HIDE_USER, hidesUser); return this; } @@ -433,7 +439,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WHIRLWIND} * @returns The {@linkcode Move} that called this function */ - hidesTarget(hidesTarget: boolean = true): this { // TODO: is `true` the correct default? + hidesTarget(hidesTarget: boolean = true): this { this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget); return this; } @@ -444,7 +450,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.BITE} * @returns The {@linkcode Move} that called this function */ - bitingMove(bitingMove: boolean = true): this { // TODO: is `true` the correct default? + bitingMove(bitingMove: boolean = true): this { this.setFlag(MoveFlags.BITING_MOVE, bitingMove); return this; } @@ -455,7 +461,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WATER_PULSE} * @returns The {@linkcode Move} that called this function */ - pulseMove(pulseMove: boolean = true): this { // TODO: is `true` the correct default? + pulseMove(pulseMove: boolean = true): this { this.setFlag(MoveFlags.PULSE_MOVE, pulseMove); return this; } @@ -466,7 +472,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.DRAIN_PUNCH} * @returns The {@linkcode Move} that called this function */ - punchingMove(punchingMove: boolean = true): this { // TODO: is `true` the correct default? + punchingMove(punchingMove: boolean = true): this { this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove); return this; } @@ -477,7 +483,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.X_SCISSOR} * @returns The {@linkcode Move} that called this function */ - slicingMove(slicingMove: boolean = true): this { // TODO: is `true` the correct default? + slicingMove(slicingMove: boolean = true): this { this.setFlag(MoveFlags.SLICING_MOVE, slicingMove); return this; } @@ -488,7 +494,7 @@ export default class Move implements Localizable { * @param recklessMove The value to set the flag to * @returns The {@linkcode Move} that called this function */ - recklessMove(recklessMove: boolean = true): this { // TODO: is `true` the correct default? + recklessMove(recklessMove: boolean = true): this { this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove); return this; } @@ -499,7 +505,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ELECTRO_BALL} * @returns The {@linkcode Move} that called this function */ - ballBombMove(ballBombMove: boolean = true): this { // TODO: is `true` the correct default? + ballBombMove(ballBombMove: boolean = true): this { this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove); return this; } @@ -510,7 +516,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.STUN_SPORE} * @returns The {@linkcode Move} that called this function */ - powderMove(powderMove: boolean = true): this { // TODO: is `true` the correct default? + powderMove(powderMove: boolean = true): this { this.setFlag(MoveFlags.POWDER_MOVE, powderMove); return this; } @@ -521,7 +527,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.PETAL_DANCE} * @returns The {@linkcode Move} that called this function */ - danceMove(danceMove: boolean = true): this { // TODO: is `true` the correct default? + danceMove(danceMove: boolean = true): this { this.setFlag(MoveFlags.DANCE_MOVE, danceMove); return this; } @@ -532,7 +538,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.HURRICANE} * @returns The {@linkcode Move} that called this function */ - windMove(windMove: boolean = true): this { // TODO: is `true` the correct default? + windMove(windMove: boolean = true): this { this.setFlag(MoveFlags.WIND_MOVE, windMove); return this; } @@ -543,7 +549,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ABSORB} * @returns The {@linkcode Move} that called this function */ - triageMove(triageMove: boolean = true): this { // TODO: is `true` the correct default? + triageMove(triageMove: boolean = true): this { this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove); return this; } @@ -554,7 +560,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.SUNSTEEL_STRIKE} * @returns The {@linkcode Move} that called this function */ - ignoresAbilities(ignoresAbilities: boolean = true): this { // TODO: is `true` the correct default? + ignoresAbilities(ignoresAbilities: boolean = true): this { this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities); return this; } @@ -565,7 +571,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TRIPLE_AXEL} * @returns The {@linkcode Move} that called this function */ - checkAllHits(checkAllHits: boolean = true): this { // TODO: is `true` the correct default? + checkAllHits(checkAllHits: boolean = true): this { this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits); return this; } @@ -576,7 +582,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.METAL_BURST} * @returns The {@linkcode Move} that called this function */ - redirectCounter(redirectCounter: boolean = true): this { // TODO: is `true` the correct default? + redirectCounter(redirectCounter: boolean = true): this { this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter); return this; } @@ -758,12 +764,14 @@ export default class Move implements Localizable { const fieldAuras = new Set( source.scene.getField(true) - .map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr) as FieldMoveTypePowerBoostAbAttr[]) + .map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr).filter(attr => { + const condition = attr.getCondition(); + return (!condition || condition(p)); + }) as FieldMoveTypePowerBoostAbAttr[]) .flat(), ); for (const aura of fieldAuras) { - // The only relevant values are `move` and the `power` holder - aura.applyPreAttack(null, null, simulated, null, this, [power]); + aura.applyPreAttack(source, null, simulated, target, this, [power]); } const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField(); @@ -819,10 +827,10 @@ export class AttackMove extends Move { attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2; if (attackScore) { if (this.category === MoveCategory.PHYSICAL) { - const atk = new Utils.IntegerHolder(user.getBattleStat(Stat.ATK, target)); + const atk = new Utils.IntegerHolder(user.getEffectiveStat(Stat.ATK, target)); applyMoveAttrs(VariableAtkAttr, user, target, move, atk); - if (atk.value > user.getBattleStat(Stat.SPATK, target)) { - const statRatio = user.getBattleStat(Stat.SPATK, target) / atk.value; + if (atk.value > user.getEffectiveStat(Stat.SPATK, target)) { + const statRatio = user.getEffectiveStat(Stat.SPATK, target) / atk.value; if (statRatio <= 0.75) { attackScore *= 2; } else if (statRatio <= 0.875) { @@ -830,10 +838,10 @@ export class AttackMove extends Move { } } } else { - const spAtk = new Utils.IntegerHolder(user.getBattleStat(Stat.SPATK, target)); + const spAtk = new Utils.IntegerHolder(user.getEffectiveStat(Stat.SPATK, target)); applyMoveAttrs(VariableAtkAttr, user, target, move, spAtk); - if (spAtk.value > user.getBattleStat(Stat.ATK, target)) { - const statRatio = user.getBattleStat(Stat.ATK, target) / spAtk.value; + if (spAtk.value > user.getEffectiveStat(Stat.ATK, target)) { + const statRatio = user.getEffectiveStat(Stat.ATK, target) / spAtk.value; if (statRatio <= 0.75) { attackScore *= 2; } else if (statRatio <= 0.875) { @@ -1100,9 +1108,9 @@ export class PreMoveMessageAttr extends MoveAttr { */ export class RespectAttackTypeImmunityAttr extends MoveAttr { } -export class IgnoreOpponentStatChangesAttr extends MoveAttr { +export class IgnoreOpponentStatStagesAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = 0; + (args[0] as Utils.BooleanHolder).value = true; return true; } @@ -1730,10 +1738,9 @@ export class HealOnAllyAttr extends HealAttr { */ export class HitHealAttr extends MoveEffectAttr { private healRatio: number; - private message: string; - private healStat: Stat | null; + private healStat: EffectiveStat | null; - constructor(healRatio?: number | null, healStat?: Stat) { + constructor(healRatio?: number | null, healStat?: EffectiveStat) { super(true, MoveEffectTrigger.HIT); this.healRatio = healRatio ?? 0.5; @@ -1755,7 +1762,7 @@ export class HitHealAttr extends MoveEffectAttr { const reverseDrain = target.hasAbilityWithAttr(ReverseDrainAbAttr, false); if (this.healStat !== null) { // Strength Sap formula - healAmount = target.getBattleStat(this.healStat); + healAmount = target.getEffectiveStat(this.healStat); message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)}); } else { // Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc. @@ -1785,7 +1792,7 @@ export class HitHealAttr extends MoveEffectAttr { */ getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { if (this.healStat) { - const healAmount = target.getBattleStat(this.healStat); + const healAmount = target.getEffectiveStat(this.healStat); return Math.floor(Math.max(0, (Math.min(1, (healAmount+user.hp)/user.getMaxHp() - 0.33))) / user.getHpRatio()); } return Math.floor(Math.max((1 - user.getHpRatio()) - 0.33, 0) * (move.power / 4)); @@ -1953,6 +1960,13 @@ export class StatusEffectAttr extends MoveEffectAttr { return false; } } + + if (user !== target && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) { + if (move.category === MoveCategory.STATUS) { + user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)})); + } + return false; + } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); @@ -2509,14 +2523,14 @@ export class ElectroShotChargeAttr extends ChargeAttr { const weatherType = user.scene.arena.weather?.weatherType; if (!user.scene.arena.weather?.isEffectSuppressed(user.scene) && (weatherType === WeatherType.RAIN || weatherType === WeatherType.HEAVY_RAIN)) { // Apply the SPATK increase every call when used in the rain - const statChangeAttr = new StatChangeAttr(BattleStat.SPATK, 1, true); + const statChangeAttr = new StatStageChangeAttr([ Stat.SPATK ], 1, true); statChangeAttr.apply(user, target, move, args); // After the SPATK is raised, execute the move resolution e.g. deal damage resolve(false); } else { if (!this.statIncreaseApplied) { // Apply the SPATK increase only if it hasn't been applied before e.g. on the first turn charge up animation - const statChangeAttr = new StatChangeAttr(BattleStat.SPATK, 1, true); + const statChangeAttr = new StatStageChangeAttr([ Stat.SPATK ], 1, true); statChangeAttr.apply(user, target, move, args); // Set the flag to true so that on the following turn it doesn't raise SPATK a second time this.statIncreaseApplied = true; @@ -2564,18 +2578,16 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { } } -export class StatChangeAttr extends MoveEffectAttr { +export class StatStageChangeAttr extends MoveEffectAttr { public stats: BattleStat[]; - public levels: integer; + public stages: integer; private condition: MoveConditionFunc | null; private showMessage: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { + constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { super(selfTarget, moveEffectTrigger, firstHitOnly, false, firstTargetOnly); - this.stats = typeof(stats) === "number" - ? [ stats as BattleStat ] - : stats as BattleStat[]; - this.levels = levels; + this.stats = stats; + this.stages = stages; this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } @@ -2587,8 +2599,8 @@ export class StatChangeAttr extends MoveEffectAttr { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { - const levels = this.getLevels(user); - user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage)); + const stages = this.getLevels(user); + user.scene.unshiftPhase(new StatStageChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); return true; } @@ -2596,7 +2608,7 @@ export class StatChangeAttr extends MoveEffectAttr { } getLevels(_user: Pokemon): integer { - return this.levels; + return this.stages; } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { @@ -2604,29 +2616,30 @@ export class StatChangeAttr extends MoveEffectAttr { const moveLevels = this.getLevels(user); for (const stat of this.stats) { let levels = moveLevels; + const statStage = target.getStatStage(stat); if (levels > 0) { - levels = Math.min(target.summonData.battleStats[stat] + levels, 6) - target.summonData.battleStats[stat]; + levels = Math.min(statStage + levels, 6) - statStage; } else { - levels = Math.max(target.summonData.battleStats[stat] + levels, -6) - target.summonData.battleStats[stat]; + levels = Math.max(statStage + levels, -6) - statStage; } let noEffect = false; switch (stat) { - case BattleStat.ATK: + case Stat.ATK: if (this.selfTarget) { noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.PHYSICAL); } break; - case BattleStat.DEF: + case Stat.DEF: if (!this.selfTarget) { noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.PHYSICAL); } break; - case BattleStat.SPATK: + case Stat.SPATK: if (this.selfTarget) { noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.SPECIAL); } break; - case BattleStat.SPDEF: + case Stat.SPDEF: if (!this.selfTarget) { noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.SPECIAL); } @@ -2641,18 +2654,16 @@ export class StatChangeAttr extends MoveEffectAttr { } } -export class PostVictoryStatChangeAttr extends MoveAttr { +export class PostVictoryStatStageChangeAttr extends MoveAttr { private stats: BattleStat[]; - private levels: integer; + private stages: number; private condition: MoveConditionFunc | null; private showMessage: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { + constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { super(); - this.stats = typeof(stats) === "number" - ? [ stats as BattleStat ] - : stats as BattleStat[]; - this.levels = levels; + this.stats = stats; + this.stages = stages; this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } @@ -2660,49 +2671,48 @@ export class PostVictoryStatChangeAttr extends MoveAttr { if (this.condition && !this.condition(user, target, move)) { return; } - const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage); + const statChangeAttr = new StatStageChangeAttr(this.stats, this.stages, this.showMessage); statChangeAttr.apply(user, target, move); } } -export class AcupressureStatChangeAttr extends MoveEffectAttr { +export class AcupressureStatStageChangeAttr extends MoveEffectAttr { constructor() { super(); } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { - let randStats = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD, BattleStat.ACC, BattleStat.EVA ]; - randStats = randStats.filter(s => target.summonData.battleStats[s] < 6); + const randStats = BATTLE_STATS.filter(s => target.getStatStage(s) < 6); if (randStats.length > 0) { - const boostStat = [randStats[Utils.randInt(randStats.length)]]; - user.scene.unshiftPhase(new StatChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2)); + const boostStat = [randStats[user.randSeedInt(randStats.length)]]; + user.scene.unshiftPhase(new StatStageChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2)); return true; } return false; } } -export class GrowthStatChangeAttr extends StatChangeAttr { +export class GrowthStatStageChangeAttr extends StatStageChangeAttr { constructor() { - super([ BattleStat.ATK, BattleStat.SPATK ], 1, true); + super([ Stat.ATK, Stat.SPATK ], 1, true); } getLevels(user: Pokemon): number { if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { const weatherType = user.scene.arena.weather?.weatherType; if (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN) { - return this.levels + 1; + return this.stages + 1; } } - return this.levels; + return this.stages; } } -export class CutHpStatBoostAttr extends StatChangeAttr { +export class CutHpStatStageBoostAttr extends StatStageChangeAttr { private cutRatio: integer; private messageCallback: ((user: Pokemon) => void) | undefined; - constructor(stat: BattleStat | BattleStat[], levels: integer, cutRatio: integer, messageCallback?: ((user: Pokemon) => void) | undefined) { + constructor(stat: BattleStat[], levels: integer, cutRatio: integer, messageCallback?: ((user: Pokemon) => void) | undefined) { super(stat, levels, true, null, true); this.cutRatio = cutRatio; @@ -2723,7 +2733,7 @@ export class CutHpStatBoostAttr extends StatChangeAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => user.getHpRatio() > 1 / this.cutRatio && this.stats.some(s => user.summonData.battleStats[s] < 6); + return (user, _target, _move) => user.getHpRatio() > 1 / this.cutRatio && this.stats.some(s => user.getStatStage(s) < 6); } } @@ -2733,9 +2743,11 @@ export class CopyStatsAttr extends MoveEffectAttr { return false; } - for (let s = 0; s < target.summonData.battleStats.length; s++) { - user.summonData.battleStats[s] = target.summonData.battleStats[s]; + // Copy all stat stages + for (const s of BATTLE_STATS) { + user.setStatStage(s, target.getStatStage(s)); } + if (target.getTag(BattlerTagType.CRIT_BOOST)) { user.addTag(BattlerTagType.CRIT_BOOST, 0, move.id); } else { @@ -2755,9 +2767,10 @@ export class InvertStatsAttr extends MoveEffectAttr { return false; } - for (let s = 0; s < target.summonData.battleStats.length; s++) { - target.summonData.battleStats[s] *= -1; + for (const s of BATTLE_STATS) { + target.setStatStage(s, -target.getStatStage(s)); } + target.updateInfo(); user.updateInfo(); @@ -2773,57 +2786,77 @@ export class ResetStatsAttr extends MoveEffectAttr { super(); this.targetAllPokemon = targetAllPokemon; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!super.apply(user, target, move, args)) { - return false; - } - + async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + const promises: Promise[] = []; if (this.targetAllPokemon) { // Target all pokemon on the field when Freezy Frost or Haze are used const activePokemon = user.scene.getField(true); - activePokemon.forEach(p => this.resetStats(p)); + activePokemon.forEach(p => promises.push(this.resetStats(p))); target.scene.queueMessage(i18next.t("moveTriggers:statEliminated")); } else { // Affects only the single target when Clear Smog is used - this.resetStats(target); + promises.push(this.resetStats(target)); target.scene.queueMessage(i18next.t("moveTriggers:resetStats", {pokemonName: getPokemonNameWithAffix(target)})); } + await Promise.all(promises); return true; } - resetStats(pokemon: Pokemon) { - for (let s = 0; s < pokemon.summonData.battleStats.length; s++) { - pokemon.summonData.battleStats[s] = 0; + async resetStats(pokemon: Pokemon): Promise { + for (const s of BATTLE_STATS) { + pokemon.setStatStage(s, 0); } - pokemon.updateInfo(); + return pokemon.updateInfo(); } } /** - * Attribute used for moves which swap the user and the target's stat changes. + * Attribute used for status moves, specifically Heart, Guard, and Power Swap, + * that swaps the user's and target's corresponding stat stages. + * @extends MoveEffectAttr + * @see {@linkcode apply} */ -export class SwapStatsAttr extends MoveEffectAttr { +export class SwapStatStagesAttr extends MoveEffectAttr { + /** The stat stages to be swapped between the user and the target */ + private stats: readonly BattleStat[]; + + constructor(stats: readonly BattleStat[]) { + super(); + + this.stats = stats; + } + /** - * Swaps the user and the target's stat changes. - * @param user Pokemon that used the move - * @param target The target of the move - * @param move Move with this attribute + * For all {@linkcode stats}, swaps the user's and target's corresponding stat + * stage. + * @param user the {@linkcode Pokemon} that used the move + * @param target the {@linkcode Pokemon} that the move was used on + * @param move N/A * @param args N/A - * @returns true if the function succeeds + * @returns true if attribute application succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any []): boolean { - if (!super.apply(user, target, move, args)) { - return false; - } //Exits if the move can't apply - let priorBoost : integer; //For storing a stat boost - for (let s = 0; s < target.summonData.battleStats.length; s++) { - priorBoost = user.summonData.battleStats[s]; //Store user stat boost - user.summonData.battleStats[s] = target.summonData.battleStats[s]; //Applies target boost to self - target.summonData.battleStats[s] = priorBoost; //Applies stored boost to target + if (super.apply(user, target, move, args)) { + for (const s of BATTLE_STATS) { + const temp = user.getStatStage(s); + user.setStatStage(s, target.getStatStage(s)); + target.setStatStage(s, temp); + } + + target.updateInfo(); + user.updateInfo(); + + if (this.stats.length === 7) { + user.scene.queueMessage(i18next.t("moveTriggers:switchedStatChanges", { pokemonName: getPokemonNameWithAffix(user) })); + } else if (this.stats.length === 2) { + user.scene.queueMessage(i18next.t("moveTriggers:switchedTwoStatChanges", { + pokemonName: getPokemonNameWithAffix(user), + firstStat: i18next.t(getStatKey(this.stats[0])), + secondStat: i18next.t(getStatKey(this.stats[1])) + })); + } + return true; } - target.updateInfo(); - user.updateInfo(); - target.scene.queueMessage(i18next.t("moveTriggers:switchedStatChanges", {pokemonName: getPokemonNameWithAffix(user)})); - return true; + return false; } } @@ -3068,7 +3101,7 @@ export class WeightPowerAttr extends VariablePowerAttr { **/ export class ElectroBallPowerAttr extends VariablePowerAttr { /** - * Move that deals more damage the faster {@linkcode BattleStat.SPD} + * Move that deals more damage the faster {@linkcode Stat.SPD} * the user is compared to the target. * @param user Pokemon that used the move * @param target The target of the move @@ -3079,7 +3112,7 @@ export class ElectroBallPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0] as Utils.NumberHolder; - const statRatio = target.getBattleStat(Stat.SPD) / user.getBattleStat(Stat.SPD); + const statRatio = target.getEffectiveStat(Stat.SPD) / user.getEffectiveStat(Stat.SPD); const statThresholds = [ 0.25, 1 / 3, 0.5, 1, -1 ]; const statThresholdPowers = [ 150, 120, 80, 60, 40 ]; @@ -3103,7 +3136,7 @@ export class ElectroBallPowerAttr extends VariablePowerAttr { **/ export class GyroBallPowerAttr extends VariablePowerAttr { /** - * Move that deals more damage the slower {@linkcode BattleStat.SPD} + * Move that deals more damage the slower {@linkcode Stat.SPD} * the user is compared to the target. * @param user Pokemon that used the move * @param target The target of the move @@ -3113,14 +3146,14 @@ export class GyroBallPowerAttr extends VariablePowerAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0] as Utils.NumberHolder; - const userSpeed = user.getBattleStat(Stat.SPD); + const userSpeed = user.getEffectiveStat(Stat.SPD); if (userSpeed < 1) { // Gen 6+ always have 1 base power power.value = 1; return true; } - power.value = Math.floor(Math.min(150, 25 * target.getBattleStat(Stat.SPD) / userSpeed + 1)); + power.value = Math.floor(Math.min(150, 25 * target.getEffectiveStat(Stat.SPD) / userSpeed + 1)); return true; } } @@ -3340,18 +3373,18 @@ export class HitCountPowerAttr extends VariablePowerAttr { } /** - * Turning a once was (StatChangeCountPowerAttr) statement and making it available to call for any attribute. - * @param {Pokemon} pokemon The pokemon that is being used to calculate the count of positive stats - * @returns {number} Returns the amount of positive stats + * Tallies the number of positive stages for a given {@linkcode Pokemon}. + * @param pokemon The {@linkcode Pokemon} that is being used to calculate the count of positive stats + * @returns the amount of positive stats */ -const countPositiveStats = (pokemon: Pokemon): number => { - return pokemon.summonData.battleStats.reduce((total, stat) => (stat && stat > 0) ? total + stat : total, 0); +const countPositiveStatStages = (pokemon: Pokemon): number => { + return pokemon.getStatStages().reduce((total, stat) => (stat && stat > 0) ? total + stat : total, 0); }; /** - * Attribute that increases power based on the amount of positive stat increases. + * Attribute that increases power based on the amount of positive stat stage increases. */ -export class StatChangeCountPowerAttr extends VariablePowerAttr { +export class PositiveStatStagePowerAttr extends VariablePowerAttr { /** * @param {Pokemon} user The pokemon that is being used to calculate the amount of positive stats @@ -3361,9 +3394,9 @@ export class StatChangeCountPowerAttr extends VariablePowerAttr { * @returns {boolean} Returns true if attribute is applied */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const positiveStats: number = countPositiveStats(user); + const positiveStatStages: number = countPositiveStatStages(user); - (args[0] as Utils.NumberHolder).value += positiveStats * 20; + (args[0] as Utils.NumberHolder).value += positiveStatStages * 20; return true; } } @@ -3385,10 +3418,10 @@ export class PunishmentPowerAttr extends VariablePowerAttr { * @returns Returns true if attribute is applied */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const positiveStats: number = countPositiveStats(target); + const positiveStatStages: number = countPositiveStatStages(target); (args[0] as Utils.NumberHolder).value = Math.min( this.PUNISHMENT_MAX_BASE_POWER, - this.PUNISHMENT_MIN_BASE_POWER + positiveStats * 20 + this.PUNISHMENT_MIN_BASE_POWER + positiveStatStages * 20 ); return true; } @@ -3444,7 +3477,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag !== null && stockpilingTag.stockpiledCount > 0) { + if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { const power = args[0] as Utils.IntegerHolder; power.value = this.multiplier * stockpilingTag.stockpiledCount; return true; @@ -3462,7 +3495,7 @@ export class SwallowHealAttr extends HealAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag !== null && stockpilingTag?.stockpiledCount > 0) { + if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { const stockpiled = stockpilingTag.stockpiledCount; let healRatio: number; @@ -3608,7 +3641,7 @@ export class TargetAtkUserAtkAttr extends VariableAtkAttr { super(); } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = target.getBattleStat(Stat.ATK, target); + (args[0] as Utils.IntegerHolder).value = target.getEffectiveStat(Stat.ATK, target); return true; } } @@ -3619,7 +3652,7 @@ export class DefAtkAttr extends VariableAtkAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = user.getBattleStat(Stat.DEF, target); + (args[0] as Utils.IntegerHolder).value = user.getEffectiveStat(Stat.DEF, target); return true; } } @@ -3641,7 +3674,7 @@ export class DefDefAttr extends VariableDefAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = target.getBattleStat(Stat.DEF, user); + (args[0] as Utils.IntegerHolder).value = target.getEffectiveStat(Stat.DEF, user); return true; } } @@ -3763,7 +3796,7 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const category = (args[0] as Utils.NumberHolder); - if (user.getBattleStat(Stat.ATK, target, move) > user.getBattleStat(Stat.SPATK, target, move)) { + if (user.getEffectiveStat(Stat.ATK, target, move) > user.getEffectiveStat(Stat.SPATK, target, move)) { category.value = MoveCategory.PHYSICAL; return true; } @@ -3776,7 +3809,7 @@ export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const category = (args[0] as Utils.NumberHolder); - if (user.isTerastallized() && user.getBattleStat(Stat.ATK, target, move) > user.getBattleStat(Stat.SPATK, target, move)) { + if (user.isTerastallized() && user.getEffectiveStat(Stat.ATK, target, move) > user.getEffectiveStat(Stat.SPATK, target, move)) { category.value = MoveCategory.PHYSICAL; return true; } @@ -3840,8 +3873,8 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const category = (args[0] as Utils.IntegerHolder); - const atkRatio = user.getBattleStat(Stat.ATK, target, move) / target.getBattleStat(Stat.DEF, user, move); - const specialRatio = user.getBattleStat(Stat.SPATK, target, move) / target.getBattleStat(Stat.SPDEF, user, move); + const atkRatio = user.getEffectiveStat(Stat.ATK, target, move) / target.getEffectiveStat(Stat.DEF, user, move); + const specialRatio = user.getEffectiveStat(Stat.SPATK, target, move) / target.getEffectiveStat(Stat.SPDEF, user, move); // Shell Side Arm is much more complicated than it looks, this is a partial implementation to try to achieve something similar to the games if (atkRatio > specialRatio) { @@ -4307,72 +4340,6 @@ export class TypelessAttr extends MoveAttr { } */ export class BypassRedirectAttr extends MoveAttr { } -export class DisableMoveAttr extends MoveEffectAttr { - constructor() { - super(false); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!super.apply(user, target, move, args)) { - return false; - } - - const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove | undefined; - while (moveQueue.length) { - turnMove = moveQueue.shift(); - if (turnMove?.virtual) { - continue; - } - - const moveIndex = target.getMoveset().findIndex(m => m?.moveId === turnMove?.move); - if (moveIndex === -1) { - return false; - } - - const disabledMove = target.getMoveset()[moveIndex]; - target.summonData.disabledMove = disabledMove?.moveId!; // TODO: is this bang correct? - target.summonData.disabledTurns = 4; - - user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove?.getName()})); - - return true; - } - - return false; - } - - getCondition(): MoveConditionFunc { - return (user, target, move): boolean => { // TODO: Not sure what to do here - if (target.summonData.disabledMove || target.isMax()) { - return false; - } - - const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove | undefined; - while (moveQueue.length) { - turnMove = moveQueue.shift(); - if (turnMove?.virtual) { - continue; - } - - const move = target.getMoveset().find(m => m?.moveId === turnMove?.move); - if (!move) { - continue; - } - - return true; - } - - return false; - }; - } - - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { - return -5; - } -} - export class FrenzyAttr extends MoveEffectAttr { constructor() { super(true, MoveEffectTrigger.HIT, false, true); @@ -4441,7 +4408,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { - return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id); + return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id); } return false; @@ -4463,6 +4430,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { case BattlerTagType.INFATUATED: case BattlerTagType.NIGHTMARE: case BattlerTagType.DROWSY: + case BattlerTagType.DISABLED: return -5; case BattlerTagType.SEEDED: case BattlerTagType.SALT_CURED: @@ -4598,8 +4566,8 @@ export class CurseAttr extends MoveEffectAttr { target.addTag(BattlerTagType.CURSED, 0, move.id, user.id); return true; } else { - user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF], 1)); - user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.SPD], -1)); + user.scene.unshiftPhase(new StatStageChangePhase(user.scene, user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF], 1)); + user.scene.unshiftPhase(new StatStageChangePhase(user.scene, user.getBattlerIndex(), true, [ Stat.SPD ], -1)); return true; } } @@ -4659,6 +4627,17 @@ export class ConfuseAttr extends AddBattlerTagAttr { constructor(selfTarget?: boolean) { super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5); } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!this.selfTarget && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) { + if (move.category === MoveCategory.STATUS) { + user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)})); + } + return false; + } + + return super.apply(user, target, move, args); + } } export class RechargeAttr extends AddBattlerTagAttr { @@ -4773,7 +4752,7 @@ export class AddArenaTagAttr extends MoveEffectAttr { return false; } - if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) { + if ((move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) && user.getLastXMoves(1)[0].result === MoveResult.SUCCESS) { user.scene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY); return true; } @@ -5145,8 +5124,8 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } let ret = this.user ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move); if (this.user && this.batonPass) { - const battleStatTotal = user.summonData.battleStats.reduce((bs: integer, total: integer) => total += bs, 0); - ret = ret / 2 + (Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(battleStatTotal), 10) / 10) * (battleStatTotal >= 0 ? 10 : -10)); + const statStageTotal = user.getStatStages().reduce((s: integer, total: integer) => total += s, 0); + ret = ret / 2 + (Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(statStageTotal), 10) / 10) * (statStageTotal >= 0 ? 10 : -10)); } return ret; } @@ -5888,9 +5867,9 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { target.summonData.ability = tempAbilityId; user.scene.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", {pokemonName: getPokemonNameWithAffix(user)})); - // Swaps Forecast from Castform + // Swaps Forecast/Flower Gift from Castform/Cherrim user.scene.arena.triggerWeatherBasedFormChangesToNormal(); - // Swaps Forecast to Castform (edge case) + // Swaps Forecast/Flower Gift to Castform/Cherrim (edge case) user.scene.arena.triggerWeatherBasedFormChanges(); return true; @@ -5971,8 +5950,17 @@ export class TransformAttr extends MoveEffectAttr { user.summonData.ability = target.getAbility().id; user.summonData.gender = target.getGender(); user.summonData.fusionGender = target.getFusionGender(); - user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1)); - user.summonData.battleStats = target.summonData.battleStats.slice(0); + + // Copy all stats (except HP) + for (const s of EFFECTIVE_STATS) { + user.setStat(s, target.getStat(s, false), false); + } + + // Copy all stat stages + for (const s of BATTLE_STATS) { + user.setStatStage(s, target.getStatStage(s)); + } + user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId!, m?.ppUsed, m?.ppUp)); // TODO: is this bang correct? user.summonData.types = target.getTypes(); @@ -5980,12 +5968,157 @@ export class TransformAttr extends MoveEffectAttr { user.loadAssets(false).then(() => { user.playAnim(); + user.updateInfo(); resolve(true); }); }); } } +/** + * Attribute used for status moves, namely Speed Swap, + * that swaps the user's and target's corresponding stats. + * @extends MoveEffectAttr + * @see {@linkcode apply} + */ +export class SwapStatAttr extends MoveEffectAttr { + /** The stat to be swapped between the user and the target */ + private stat: EffectiveStat; + + constructor(stat: EffectiveStat) { + super(); + + this.stat = stat; + } + + /** + * Swaps the user's and target's corresponding current + * {@linkcode EffectiveStat | stat} values + * @param user the {@linkcode Pokemon} that used the move + * @param target the {@linkcode Pokemon} that the move was used on + * @param move N/A + * @param args N/A + * @returns true if attribute application succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (super.apply(user, target, move, args)) { + const temp = user.getStat(this.stat, false); + user.setStat(this.stat, target.getStat(this.stat, false), false); + target.setStat(this.stat, temp, false); + + user.scene.queueMessage(i18next.t("moveTriggers:switchedStat", { + pokemonName: getPokemonNameWithAffix(user), + stat: i18next.t(getStatKey(this.stat)), + })); + + return true; + } + return false; + } +} + +/** + * Attribute used to switch the user's own stats. + * Used by Power Shift. + * @extends MoveEffectAttr + */ +export class ShiftStatAttr extends MoveEffectAttr { + private statToSwitch: EffectiveStat; + private statToSwitchWith: EffectiveStat; + + constructor(statToSwitch: EffectiveStat, statToSwitchWith: EffectiveStat) { + super(); + + this.statToSwitch = statToSwitch; + this.statToSwitchWith = statToSwitchWith; + } + + /** + * Switches the user's stats based on the {@linkcode statToSwitch} and {@linkcode statToSwitchWith} attributes. + * @param {Pokemon} user the {@linkcode Pokemon} that used the move + * @param target n/a + * @param move n/a + * @param args n/a + * @returns whether the effect was applied + */ + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) { + return false; + } + + const firstStat = user.getStat(this.statToSwitch, false); + const secondStat = user.getStat(this.statToSwitchWith, false); + + user.setStat(this.statToSwitch, secondStat, false); + user.setStat(this.statToSwitchWith, firstStat, false); + + user.scene.queueMessage(i18next.t("moveTriggers:shiftedStats", { + pokemonName: getPokemonNameWithAffix(user), + statToSwitch: i18next.t(getStatKey(this.statToSwitch)), + statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith)) + })); + + return true; + } + + /** + * Encourages the user to use the move if the stat to switch with is greater than the stat to switch. + * @param {Pokemon} user the {@linkcode Pokemon} that used the move + * @param target n/a + * @param move n/a + * @returns number of points to add to the user's benefit score + */ + override getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + return user.getStat(this.statToSwitchWith, false) > user.getStat(this.statToSwitch, false) ? 10 : 0; + } +} + +/** + * Attribute used for status moves, namely Power Split and Guard Split, + * that take the average of a user's and target's corresponding + * stats and assign that average back to each corresponding stat. + * @extends MoveEffectAttr + * @see {@linkcode apply} + */ +export class AverageStatsAttr extends MoveEffectAttr { + /** The stats to be averaged individually between the user and the target */ + private stats: readonly EffectiveStat[]; + private msgKey: string; + + constructor(stats: readonly EffectiveStat[], msgKey: string) { + super(); + + this.stats = stats; + this.msgKey = msgKey; + } + + /** + * Takes the average of the user's and target's corresponding {@linkcode stat} + * values and sets those stats to the corresponding average for both + * temporarily. + * @param user the {@linkcode Pokemon} that used the move + * @param target the {@linkcode Pokemon} that the move was used on + * @param move N/A + * @param args N/A + * @returns true if attribute application succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (super.apply(user, target, move, args)) { + for (const s of this.stats) { + const avg = Math.floor((user.getStat(s, false) + target.getStat(s, false)) / 2); + + user.setStat(s, avg, false); + target.setStat(s, avg, false); + } + + user.scene.queueMessage(i18next.t(this.msgKey, { pokemonName: getPokemonNameWithAffix(user) })); + + return true; + } + return false; + } +} + export class DiscourageFrequentUseAttr extends MoveAttr { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { const lastMoves = user.getLastXMoves(4); @@ -6037,6 +6170,57 @@ export class DestinyBondAttr extends MoveEffectAttr { } } +/** + * Attribute to apply a battler tag to the target if they have had their stats boosted this turn. + * @extends AddBattlerTagAttr + */ +export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr { + constructor(tag: BattlerTagType) { + super(tag, false, false, 2, 5); + } + + /** + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param {any[]} args N/A + * @returns true + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (target.turnData.statStagesIncreased) { + super.apply(user, target, move, args); + } + return true; + } +} + +/** + * Attribute to apply a status effect to the target if they have had their stats boosted this turn. + * @extends MoveEffectAttr + */ +export class StatusIfBoostedAttr extends MoveEffectAttr { + public effect: StatusEffect; + + constructor(effect: StatusEffect) { + super(true, MoveEffectTrigger.HIT); + this.effect = effect; + } + + /** + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} N/A + * @param {any[]} args N/A + * @returns true + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (target.turnData.statStagesIncreased) { + target.trySetStatus(this.effect, true, user); + } + return true; + } +} + export class LastResortAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user: Pokemon, target: Pokemon, move: Move) => { @@ -6093,12 +6277,42 @@ export class VariableTargetAttr extends MoveAttr { } } +/** + * Attribute for {@linkcode Moves.AFTER_YOU} + * + * [After You - Move | Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/After_You_(move)) + */ +export class AfterYouAttr extends MoveEffectAttr { + /** + * Allows the target of this move to act right after the user. + * + * @param user {@linkcode Pokemon} that is using the move. + * @param target {@linkcode Pokemon} that will move right after this move is used. + * @param move {@linkcode Move} {@linkcode Moves.AFTER_YOU} + * @param _args N/A + * @returns true + */ + override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + user.scene.queueMessage(i18next.t("moveTriggers:afterYou", {targetName: getPokemonNameWithAffix(target)})); + + //Will find next acting phase of the targeted pokémon, delete it and queue it next on successful delete. + const nextAttackPhase = target.scene.findPhase((phase) => phase.pokemon === target); + if (nextAttackPhase && target.scene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { + target.scene.prependToPhase(new MovePhase(target.scene, target, [...nextAttackPhase.targets], nextAttackPhase.move), MovePhase); + } + + return true; + } +} + const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY); const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune(); const failOnMaxCondition: MoveConditionFunc = (user, target, move) => !target.isMax(); +const failIfSingleBattle: MoveConditionFunc = (user, target, move) => user.scene.currentBattle.double; + const failIfDampCondition: MoveConditionFunc = (user, target, move) => { const cancelled = new Utils.BooleanHolder(false); user.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); @@ -6113,6 +6327,8 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(Abilities.COMATOSE); +const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => user.scene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined; + export type MoveAttrFilter = (attr: MoveAttr) => boolean; function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise { @@ -6388,7 +6604,7 @@ export function initMoves() { .ignoresVirtual() .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(Moves.SWORDS_DANCE, Type.NORMAL, -1, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ATK, 2, true) + .attr(StatStageChangeAttr, [ Stat.ATK ], 2, true) .danceMove(), new AttackMove(Moves.CUT, Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1) .slicingMove(), @@ -6424,7 +6640,7 @@ export function initMoves() { new AttackMove(Moves.ROLLING_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, 30, 0, 1) .attr(FlinchAttr), new StatusMove(Moves.SAND_ATTACK, Type.GROUND, 100, 15, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.HEADBUTT, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 15, 30, 0, 1) .attr(FlinchAttr), new AttackMove(Moves.HORN_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 25, -1, 0, 1), @@ -6452,7 +6668,7 @@ export function initMoves() { .attr(RecoilAttr, false, 0.33) .recklessMove(), new StatusMove(Moves.TAIL_WHIP, Type.NORMAL, 100, 30, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.POISON_STING, Type.POISON, MoveCategory.PHYSICAL, 15, 100, 35, 30, 0, 1) .attr(StatusEffectAttr, StatusEffect.POISON) @@ -6465,13 +6681,13 @@ export function initMoves() { .attr(MultiHitAttr) .makesContact(false), new StatusMove(Moves.LEER, Type.NORMAL, 100, 30, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.BITE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 1) .attr(FlinchAttr) .bitingMove(), new StatusMove(Moves.GROWL, Type.NORMAL, 100, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ATK, -1) + .attr(StatStageChangeAttr, [ Stat.ATK ], -1) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1) @@ -6487,10 +6703,11 @@ export function initMoves() { new AttackMove(Moves.SONIC_BOOM, Type.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, 0, 1) .attr(FixedDamageAttr, 20), new StatusMove(Moves.DISABLE, Type.NORMAL, 100, 20, -1, 0, 1) - .attr(DisableMoveAttr) + .attr(AddBattlerTagAttr, BattlerTagType.DISABLED, false, true) + .condition((user, target, move) => target.getMoveHistory().reverse().find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual) !== undefined) .condition(failOnMaxCondition), new AttackMove(Moves.ACID, Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.SPDEF, -1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.EMBER, Type.FIRE, MoveCategory.SPECIAL, 40, 100, 25, 10, 0, 1) .attr(StatusEffectAttr, StatusEffect.BURN), @@ -6515,9 +6732,9 @@ export function initMoves() { new AttackMove(Moves.PSYBEAM, Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) .attr(ConfuseAttr), new AttackMove(Moves.BUBBLE_BEAM, Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new AttackMove(Moves.AURORA_BEAM, Type.ICE, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.HYPER_BEAM, Type.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 1) .attr(RechargeAttr), new AttackMove(Moves.PECK, Type.FLYING, MoveCategory.PHYSICAL, 35, 100, 35, -1, 0, 1), @@ -6544,7 +6761,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.SEEDED) .condition((user, target, move) => !target.getTag(BattlerTagType.SEEDED) && !target.isOfType(Type.GRASS)), new SelfStatusMove(Moves.GROWTH, Type.NORMAL, -1, 20, -1, 0, 1) - .attr(GrowthStatChangeAttr), + .attr(GrowthStatStageChangeAttr), new AttackMove(Moves.RAZOR_LEAF, Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, 0, 1) .attr(HighCritAttr) .makesContact(false) @@ -6571,7 +6788,7 @@ export function initMoves() { .danceMove() .target(MoveTarget.RANDOM_NEAR_ENEMY), new StatusMove(Moves.STRING_SHOT, Type.BUG, 95, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.SPD, -2) + .attr(StatStageChangeAttr, [ Stat.SPD ], -2) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.DRAGON_RAGE, Type.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 1) .attr(FixedDamageAttr, 40), @@ -6608,13 +6825,13 @@ export function initMoves() { new AttackMove(Moves.CONFUSION, Type.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, 10, 0, 1) .attr(ConfuseAttr), new AttackMove(Moves.PSYCHIC, Type.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.SPDEF, -1), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), new StatusMove(Moves.HYPNOSIS, Type.PSYCHIC, 60, 20, -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.SLEEP), new SelfStatusMove(Moves.MEDITATE, Type.PSYCHIC, -1, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ATK, 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), new SelfStatusMove(Moves.AGILITY, Type.PSYCHIC, -1, 30, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.SPD, 2, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), new AttackMove(Moves.QUICK_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1), new AttackMove(Moves.RAGE, Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1) .partial(), @@ -6627,28 +6844,28 @@ export function initMoves() { .attr(MovesetCopyMoveAttr) .ignoresVirtual(), new StatusMove(Moves.SCREECH, Type.NORMAL, 85, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, -2) + .attr(StatStageChangeAttr, [ Stat.DEF ], -2) .soundBased(), new SelfStatusMove(Moves.DOUBLE_TEAM, Type.NORMAL, -1, 15, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.EVA, 1, true), + .attr(StatStageChangeAttr, [ Stat.EVA ], 1, true), new SelfStatusMove(Moves.RECOVER, Type.NORMAL, -1, 5, -1, 0, 1) .attr(HealAttr, 0.5) .triageMove(), new SelfStatusMove(Moves.HARDEN, Type.NORMAL, -1, 30, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), new SelfStatusMove(Moves.MINIMIZE, Type.NORMAL, -1, 10, -1, 0, 1) .attr(AddBattlerTagAttr, BattlerTagType.MINIMIZED, true, false) - .attr(StatChangeAttr, BattleStat.EVA, 2, true), + .attr(StatStageChangeAttr, [ Stat.EVA ], 2, true), new StatusMove(Moves.SMOKESCREEN, Type.NORMAL, 100, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new StatusMove(Moves.CONFUSE_RAY, Type.GHOST, 100, 10, -1, 0, 1) .attr(ConfuseAttr), new SelfStatusMove(Moves.WITHDRAW, Type.WATER, -1, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), new SelfStatusMove(Moves.DEFENSE_CURL, Type.NORMAL, -1, 40, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), new SelfStatusMove(Moves.BARRIER, Type.PSYCHIC, -1, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, 2, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), new StatusMove(Moves.LIGHT_SCREEN, Type.PSYCHIC, -1, 30, -1, 0, 1) .attr(AddArenaTagAttr, ArenaTagType.LIGHT_SCREEN, 5, true) .target(MoveTarget.USER_SIDE), @@ -6696,17 +6913,17 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.SKULL_BASH, Type.NORMAL, MoveCategory.PHYSICAL, 130, 100, 10, -1, 0, 1) .attr(ChargeAttr, ChargeAnim.SKULL_BASH_CHARGING, i18next.t("moveTriggers:loweredItsHead", {pokemonName: "{USER}"}), null, true) - .attr(StatChangeAttr, BattleStat.DEF, 1, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true) .ignoresVirtual(), new AttackMove(Moves.SPIKE_CANNON, Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 15, -1, 0, 1) .attr(MultiHitAttr) .makesContact(false), new AttackMove(Moves.CONSTRICT, Type.NORMAL, MoveCategory.PHYSICAL, 10, 100, 35, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new SelfStatusMove(Moves.AMNESIA, Type.PSYCHIC, -1, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.SPDEF, 2, true), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], 2, true), new StatusMove(Moves.KINESIS, Type.PSYCHIC, 80, 15, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new SelfStatusMove(Moves.SOFT_BOILED, Type.NORMAL, -1, 5, -1, 0, 1) .attr(HealAttr, 0.5) .triageMove(), @@ -6743,7 +6960,7 @@ export function initMoves() { .attr(TransformAttr) .ignoresProtect(), new AttackMove(Moves.BUBBLE, Type.WATER, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.DIZZY_PUNCH, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, 20, 0, 1) .attr(ConfuseAttr) @@ -6752,13 +6969,13 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.SLEEP) .powderMove(), new StatusMove(Moves.FLASH, Type.NORMAL, 100, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.PSYWAVE, Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) .attr(RandomLevelDamageAttr), new SelfStatusMove(Moves.SPLASH, Type.NORMAL, -1, 40, -1, 0, 1) .condition(failOnGravityCondition), new SelfStatusMove(Moves.ACID_ARMOR, Type.POISON, -1, 20, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.DEF, 2, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), new AttackMove(Moves.CRABHAMMER, Type.WATER, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 1) .attr(HighCritAttr), new AttackMove(Moves.EXPLOSION, Type.NORMAL, MoveCategory.PHYSICAL, 250, 100, 5, -1, 0, 1) @@ -6784,7 +7001,7 @@ export function initMoves() { .attr(FlinchAttr) .bitingMove(), new SelfStatusMove(Moves.SHARPEN, Type.NORMAL, -1, 30, -1, 0, 1) - .attr(StatChangeAttr, BattleStat.ATK, 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), new SelfStatusMove(Moves.CONVERSION, Type.NORMAL, -1, 30, -1, 0, 1) .attr(FirstMoveTypeAttr), new AttackMove(Moves.TRI_ATTACK, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, 20, 0, 1) @@ -6839,7 +7056,7 @@ export function initMoves() { .windMove() .attr(HighCritAttr), new StatusMove(Moves.COTTON_SPORE, Type.GRASS, 100, 40, -1, 0, 2) - .attr(StatChangeAttr, BattleStat.SPD, -2) + .attr(StatStageChangeAttr, [ Stat.SPD ], -2) .powderMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.REVERSAL, Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) @@ -6850,25 +7067,26 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.FREEZE) .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(Moves.PROTECT, Type.NORMAL, -1, 10, -1, 4, 2) - .attr(ProtectAttr), + .attr(ProtectAttr) + .condition(failIfLastCondition), new AttackMove(Moves.MACH_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 2) .punchingMove(), new StatusMove(Moves.SCARY_FACE, Type.NORMAL, 100, 10, -1, 0, 2) - .attr(StatChangeAttr, BattleStat.SPD, -2), + .attr(StatStageChangeAttr, [ Stat.SPD ], -2), new AttackMove(Moves.FEINT_ATTACK, Type.DARK, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 2), new StatusMove(Moves.SWEET_KISS, Type.FAIRY, 75, 10, -1, 0, 2) .attr(ConfuseAttr), new SelfStatusMove(Moves.BELLY_DRUM, Type.NORMAL, -1, 10, -1, 0, 2) - .attr(CutHpStatBoostAttr, [BattleStat.ATK], 12, 2, (user) => { - user.scene.queueMessage(i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", {pokemonName: getPokemonNameWithAffix(user), statName: getBattleStatName(BattleStat.ATK)})); + .attr(CutHpStatStageBoostAttr, [ Stat.ATK ], 12, 2, (user) => { + user.scene.queueMessage(i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", { pokemonName: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) })); }), new AttackMove(Moves.SLUDGE_BOMB, Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 30, 0, 2) .attr(StatusEffectAttr, StatusEffect.POISON) .ballBombMove(), new AttackMove(Moves.MUD_SLAP, Type.GROUND, MoveCategory.SPECIAL, 20, 100, 10, 100, 0, 2) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.OCTAZOOKA, Type.WATER, MoveCategory.SPECIAL, 65, 85, 10, 50, 0, 2) - .attr(StatChangeAttr, BattleStat.ACC, -1) + .attr(StatStageChangeAttr, [ Stat.ACC ], -1) .ballBombMove(), new StatusMove(Moves.SPIKES, Type.GROUND, -1, 20, -1, 0, 2) .attr(AddArenaTrapTagAttr, ArenaTagType.SPIKES) @@ -6897,11 +7115,12 @@ export function initMoves() { .condition(failOnBossCondition) .target(MoveTarget.ALL), new AttackMove(Moves.ICY_WIND, Type.ICE, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 2) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(Moves.DETECT, Type.FIGHTING, -1, 5, -1, 4, 2) - .attr(ProtectAttr), + .attr(ProtectAttr) + .condition(failIfLastCondition), new AttackMove(Moves.BONE_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, 0, 2) .attr(MultiHitAttr) .makesContact(false), @@ -6919,15 +7138,17 @@ export function initMoves() { .attr(HitHealAttr) .triageMove(), new SelfStatusMove(Moves.ENDURE, Type.NORMAL, -1, 10, -1, 4, 2) - .attr(ProtectAttr, BattlerTagType.ENDURING), + .attr(ProtectAttr, BattlerTagType.ENDURING) + .condition(failIfLastCondition), new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2) - .attr(StatChangeAttr, BattleStat.ATK, -2), + .attr(StatStageChangeAttr, [ Stat.ATK ], -2), new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2) + .partial() .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL), new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2) .attr(SurviveDamageAttr), new StatusMove(Moves.SWAGGER, Type.NORMAL, 85, 15, -1, 0, 2) - .attr(StatChangeAttr, BattleStat.ATK, 2) + .attr(StatStageChangeAttr, [ Stat.ATK ], 2) .attr(ConfuseAttr), new SelfStatusMove(Moves.MILK_DRINK, Type.NORMAL, -1, 5, -1, 0, 2) .attr(HealAttr, 0.5) @@ -6938,7 +7159,7 @@ export function initMoves() { .attr(ConsecutiveUseDoublePowerAttr, 3, true) .slicingMove(), new AttackMove(Moves.STEEL_WING, Type.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, 10, 0, 2) - .attr(StatChangeAttr, BattleStat.DEF, 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), new StatusMove(Moves.MEAN_LOOK, Type.NORMAL, -1, 5, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1), new StatusMove(Moves.ATTRACT, Type.NORMAL, 100, 15, -1, 0, 2) @@ -6963,7 +7184,7 @@ export function initMoves() { .attr(FriendshipPowerAttr, true), new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2) .target(MoveTarget.USER_SIDE) - .unimplemented(), + .attr(AddArenaTagAttr, ArenaTagType.SAFEGUARD, 5, true, true), new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2) .attr(HpSplitAttr) .condition(failOnBossCondition), @@ -6992,7 +7213,7 @@ export function initMoves() { new AttackMove(Moves.PURSUIT, Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2) .partial(), new AttackMove(Moves.RAPID_SPIN, Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2) - .attr(StatChangeAttr, BattleStat.SPD, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) .attr(RemoveBattlerTagAttr, [ BattlerTagType.BIND, BattlerTagType.WRAP, @@ -7008,12 +7229,12 @@ export function initMoves() { ], true) .attr(RemoveArenaTrapAttr), new StatusMove(Moves.SWEET_SCENT, Type.NORMAL, 100, 20, -1, 0, 2) - .attr(StatChangeAttr, BattleStat.EVA, -2) + .attr(StatStageChangeAttr, [ Stat.EVA ], -2) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.IRON_TAIL, Type.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, 30, 0, 2) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.METAL_CLAW, Type.STEEL, MoveCategory.PHYSICAL, 50, 95, 35, 10, 0, 2) - .attr(StatChangeAttr, BattleStat.ATK, 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), new AttackMove(Moves.VITAL_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 70, -1, 10, -1, -1, 2), new SelfStatusMove(Moves.MORNING_SUN, Type.NORMAL, -1, 5, -1, 0, 2) .attr(PlantHealAttr) @@ -7040,7 +7261,7 @@ export function initMoves() { .attr(WeatherChangeAttr, WeatherType.SUNNY) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.CRUNCH, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 2) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .bitingMove(), new AttackMove(Moves.MIRROR_COAT, Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, -5, 2) .attr(CounterDamageAttr, (move: Move) => move.category === MoveCategory.SPECIAL, 2) @@ -7049,15 +7270,15 @@ export function initMoves() { .attr(CopyStatsAttr), new AttackMove(Moves.EXTREME_SPEED, Type.NORMAL, MoveCategory.PHYSICAL, 80, 100, 5, -1, 2, 2), new AttackMove(Moves.ANCIENT_POWER, Type.ROCK, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 2) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true), new AttackMove(Moves.SHADOW_BALL, Type.GHOST, MoveCategory.SPECIAL, 80, 100, 15, 20, 0, 2) - .attr(StatChangeAttr, BattleStat.SPDEF, -1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .ballBombMove(), new AttackMove(Moves.FUTURE_SIGHT, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) .partial() .attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, i18next.t("moveTriggers:foresawAnAttack", {pokemonName: "{USER}"})), new AttackMove(Moves.ROCK_SMASH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.WHIRLPOOL, Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2) .attr(TrapAttr, BattlerTagType.WHIRLPOOL) .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), @@ -7096,13 +7317,13 @@ export function initMoves() { new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3) .unimplemented(), new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPATK, 1) + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1) .attr(ConfuseAttr), new StatusMove(Moves.WILL_O_WISP, Type.FIRE, 85, 15, -1, 0, 3) .attr(StatusEffectAttr, StatusEffect.BURN), new StatusMove(Moves.MEMENTO, Type.DARK, 100, 10, -1, 0, 3) .attr(SacrificialAttrOnHit) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -2), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -2), new AttackMove(Moves.FACADE, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => user.status && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1) @@ -7121,7 +7342,7 @@ export function initMoves() { .attr(NaturePowerAttr) .ignoresVirtual(), new SelfStatusMove(Moves.CHARGE, Type.ELECTRIC, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPDEF, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], 1, true) .attr(AddBattlerTagAttr, BattlerTagType.CHARGED, true, false), new StatusMove(Moves.TAUNT, Type.DARK, 100, 20, -1, 0, 3) .unimplemented(), @@ -7141,7 +7362,7 @@ export function initMoves() { new SelfStatusMove(Moves.INGRAIN, Type.GRASS, -1, 20, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true), new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true), new SelfStatusMove(Moves.MAGIC_COAT, Type.PSYCHIC, -1, 15, -1, 4, 3) .unimplemented(), new SelfStatusMove(Moves.RECYCLE, Type.NORMAL, -1, 10, -1, 0, 3) @@ -7152,7 +7373,7 @@ export function initMoves() { .attr(RemoveScreensAttr), new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) - .condition((user, target, move) => !target.status), + .condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)), new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1) .attr(RemoveHeldItemAttr, false), @@ -7185,14 +7406,14 @@ export function initMoves() { new SelfStatusMove(Moves.CAMOUFLAGE, Type.NORMAL, -1, 20, -1, 0, 3) .attr(CopyBiomeTypeAttr), new SelfStatusMove(Moves.TAIL_GLOW, Type.BUG, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPATK, 3, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], 3, true), new AttackMove(Moves.LUSTER_PURGE, Type.PSYCHIC, MoveCategory.SPECIAL, 95, 100, 5, 50, 0, 3) - .attr(StatChangeAttr, BattleStat.SPDEF, -1), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), new AttackMove(Moves.MIST_BALL, Type.PSYCHIC, MoveCategory.SPECIAL, 95, 100, 5, 50, 0, 3) - .attr(StatChangeAttr, BattleStat.SPATK, -1) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .ballBombMove(), new StatusMove(Moves.FEATHER_DANCE, Type.FLYING, 100, 15, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.ATK, -2) + .attr(StatStageChangeAttr, [ Stat.ATK ], -2) .danceMove(), new StatusMove(Moves.TEETER_DANCE, Type.NORMAL, 100, 20, -1, 0, 3) .attr(ConfuseAttr) @@ -7202,9 +7423,11 @@ export function initMoves() { .attr(HighCritAttr) .attr(StatusEffectAttr, StatusEffect.BURN), new StatusMove(Moves.MUD_SPORT, Type.GROUND, -1, 15, -1, 0, 3) + .ignoresProtect() .attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.ICE_BALL, Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 3) + .partial() .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL) .ballBombMove(), new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3) @@ -7219,13 +7442,13 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.TOXIC) .bitingMove(), new AttackMove(Moves.CRUSH_CLAW, Type.NORMAL, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 3) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.BLAST_BURN, Type.FIRE, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) .attr(RechargeAttr), new AttackMove(Moves.HYDRO_CANNON, Type.WATER, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) .attr(RechargeAttr), new AttackMove(Moves.METEOR_MASH, Type.STEEL, MoveCategory.PHYSICAL, 90, 90, 10, 20, 0, 3) - .attr(StatChangeAttr, BattleStat.ATK, 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) .punchingMove(), new AttackMove(Moves.ASTONISH, Type.GHOST, MoveCategory.PHYSICAL, 30, 100, 15, 30, 0, 3) .attr(FlinchAttr), @@ -7237,33 +7460,33 @@ export function initMoves() { .attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), Abilities.SAP_SIPPER) .target(MoveTarget.PARTY), new StatusMove(Moves.FAKE_TEARS, Type.DARK, 100, 20, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPDEF, -2), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), new AttackMove(Moves.AIR_CUTTER, Type.FLYING, MoveCategory.SPECIAL, 60, 95, 25, -1, 0, 3) .attr(HighCritAttr) .slicingMove() .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.OVERHEAT, Type.FIRE, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPATK, -2, true) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE), new StatusMove(Moves.ODOR_SLEUTH, Type.NORMAL, -1, 40, -1, 0, 3) .attr(ExposedMoveAttr, BattlerTagType.IGNORE_GHOST), new AttackMove(Moves.ROCK_TOMB, Type.ROCK, MoveCategory.PHYSICAL, 60, 95, 15, 100, 0, 3) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .makesContact(false), new AttackMove(Moves.SILVER_WIND, Type.BUG, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 3) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .windMove(), new StatusMove(Moves.METAL_SOUND, Type.STEEL, 85, 40, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPDEF, -2) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2) .soundBased(), new StatusMove(Moves.GRASS_WHISTLE, Type.GRASS, 55, 15, -1, 0, 3) .attr(StatusEffectAttr, StatusEffect.SLEEP) .soundBased(), new StatusMove(Moves.TICKLE, Type.NORMAL, 100, 20, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1), new SelfStatusMove(Moves.COSMIC_POWER, Type.PSYCHIC, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), new AttackMove(Moves.WATER_SPOUT, Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 3) .attr(HpPowerAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -7284,7 +7507,7 @@ export function initMoves() { .attr(OneHitKOAttr) .attr(SheerColdAccuracyAttr), new AttackMove(Moves.MUDDY_WATER, Type.WATER, MoveCategory.SPECIAL, 90, 85, 10, 30, 0, 3) - .attr(StatChangeAttr, BattleStat.ACC, -1) + .attr(StatStageChangeAttr, [ Stat.ACC ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.BULLET_SEED, Type.GRASS, MoveCategory.PHYSICAL, 25, 100, 30, -1, 0, 3) .attr(MultiHitAttr) @@ -7296,25 +7519,25 @@ export function initMoves() { .attr(MultiHitAttr) .makesContact(false), new SelfStatusMove(Moves.IRON_DEFENSE, Type.STEEL, -1, 15, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.DEF, 2, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), new StatusMove(Moves.BLOCK, Type.NORMAL, -1, 5, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1), new StatusMove(Moves.HOWL, Type.NORMAL, -1, 40, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.ATK, 1) + .attr(StatStageChangeAttr, [ Stat.ATK ], 1) .soundBased() .target(MoveTarget.USER_AND_ALLIES), new AttackMove(Moves.DRAGON_CLAW, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 3), new AttackMove(Moves.FRENZY_PLANT, Type.GRASS, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) .attr(RechargeAttr), new SelfStatusMove(Moves.BULK_UP, Type.FIGHTING, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1, true), new AttackMove(Moves.BOUNCE, Type.FLYING, MoveCategory.PHYSICAL, 85, 85, 5, 30, 0, 3) .attr(ChargeAttr, ChargeAnim.BOUNCE_CHARGING, i18next.t("moveTriggers:sprangUp", {pokemonName: "{USER}"}), BattlerTagType.FLYING) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .condition(failOnGravityCondition) .ignoresVirtual(), new AttackMove(Moves.MUD_SHOT, Type.GROUND, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 3) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new AttackMove(Moves.POISON_TAIL, Type.POISON, MoveCategory.PHYSICAL, 50, 100, 25, 10, 0, 3) .attr(HighCritAttr) .attr(StatusEffectAttr, StatusEffect.POISON), @@ -7326,15 +7549,16 @@ export function initMoves() { .recklessMove(), new AttackMove(Moves.MAGICAL_LEAF, Type.GRASS, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 3), new StatusMove(Moves.WATER_SPORT, Type.WATER, -1, 15, -1, 0, 3) + .ignoresProtect() .attr(AddArenaTagAttr, ArenaTagType.WATER_SPORT, 5) .target(MoveTarget.BOTH_SIDES), new SelfStatusMove(Moves.CALM_MIND, Type.PSYCHIC, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true), + .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF ], 1, true), new AttackMove(Moves.LEAF_BLADE, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 3) .attr(HighCritAttr) .slicingMove(), new SelfStatusMove(Moves.DRAGON_DANCE, Type.DRAGON, -1, 20, -1, 0, 3) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true) .danceMove(), new AttackMove(Moves.ROCK_BLAST, Type.ROCK, MoveCategory.PHYSICAL, 25, 90, 10, -1, 0, 3) .attr(MultiHitAttr) @@ -7348,12 +7572,13 @@ export function initMoves() { .partial() .attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, i18next.t("moveTriggers:choseDoomDesireAsDestiny", {pokemonName: "{USER}"})), new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) - .attr(StatChangeAttr, BattleStat.SPATK, -2, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), new SelfStatusMove(Moves.ROOST, Type.FLYING, -1, 5, -1, 0, 4) .attr(HealAttr, 0.5) .attr(AddBattlerTagAttr, BattlerTagType.ROOSTED, true, false) .triageMove(), new StatusMove(Moves.GRAVITY, Type.PSYCHIC, -1, 5, -1, 0, 4) + .ignoresProtect() .attr(AddArenaTagAttr, ArenaTagType.GRAVITY, 5) .target(MoveTarget.BOTH_SIDES), new StatusMove(Moves.MIRACLE_EYE, Type.PSYCHIC, -1, 40, -1, 0, 4) @@ -7362,7 +7587,7 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => targetSleptOrComatoseCondition(user, target, move) ? 2 : 1) .attr(HealStatusEffectAttr, false, StatusEffect.SLEEP), new AttackMove(Moves.HAMMER_ARM, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPD, -1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1, true) .punchingMove(), new AttackMove(Moves.GYRO_BALL, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) .attr(GyroBallPowerAttr) @@ -7387,7 +7612,7 @@ export function initMoves() { .attr(AddArenaTagAttr, ArenaTagType.TAILWIND, 4, true) .target(MoveTarget.USER_SIDE), new StatusMove(Moves.ACUPRESSURE, Type.NORMAL, -1, 30, -1, 0, 4) - .attr(AcupressureStatChangeAttr) + .attr(AcupressureStatStageChangeAttr) .target(MoveTarget.USER_OR_NEAR_ALLY), new AttackMove(Moves.METAL_BURST, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 4) .attr(CounterDamageAttr, (move: Move) => (move.category === MoveCategory.PHYSICAL || move.category === MoveCategory.SPECIAL), 1.5) @@ -7397,7 +7622,7 @@ export function initMoves() { new AttackMove(Moves.U_TURN, Type.BUG, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) .attr(ForceSwitchOutAttr, true, false), new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), new AttackMove(Moves.ASSURANCE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) @@ -7441,9 +7666,9 @@ export function initMoves() { .attr(CopyMoveAttr) .ignoresVirtual(), new StatusMove(Moves.POWER_SWAP, Type.PSYCHIC, -1, 10, 100, 0, 4) - .unimplemented(), + .attr(SwapStatStagesAttr, [ Stat.ATK, Stat.SPATK ]), new StatusMove(Moves.GUARD_SWAP, Type.PSYCHIC, -1, 10, 100, 0, 4) - .unimplemented(), + .attr(SwapStatStagesAttr, [ Stat.DEF, Stat.SPDEF ]), new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) .makesContact(true) .attr(PunishmentPowerAttr), @@ -7457,7 +7682,7 @@ export function initMoves() { .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .target(MoveTarget.ENEMY_SIDE), new StatusMove(Moves.HEART_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 4) - .attr(SwapStatsAttr), + .attr(SwapStatStagesAttr, BATTLE_STATS), new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4) .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) @@ -7474,7 +7699,7 @@ export function initMoves() { .pulseMove() .ballBombMove(), new SelfStatusMove(Moves.ROCK_POLISH, Type.ROCK, -1, 20, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPD, 2, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), new AttackMove(Moves.POISON_JAB, Type.POISON, MoveCategory.PHYSICAL, 80, 100, 20, 30, 0, 4) .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(Moves.DARK_PULSE, Type.DARK, MoveCategory.SPECIAL, 80, 100, 15, 20, 0, 4) @@ -7493,7 +7718,7 @@ export function initMoves() { new AttackMove(Moves.X_SCISSOR, Type.BUG, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 4) .slicingMove(), new AttackMove(Moves.BUG_BUZZ, Type.BUG, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .soundBased(), new AttackMove(Moves.DRAGON_PULSE, Type.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4) .pulseMove(), @@ -7508,22 +7733,22 @@ export function initMoves() { .triageMove(), new AttackMove(Moves.VACUUM_WAVE, Type.FIGHTING, MoveCategory.SPECIAL, 40, 100, 30, -1, 1, 4), new AttackMove(Moves.FOCUS_BLAST, Type.FIGHTING, MoveCategory.SPECIAL, 120, 70, 5, 10, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .ballBombMove(), new AttackMove(Moves.ENERGY_BALL, Type.GRASS, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .ballBombMove(), new AttackMove(Moves.BRAVE_BIRD, Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) .attr(RecoilAttr, false, 0.33) .recklessMove(), new AttackMove(Moves.EARTH_POWER, Type.GROUND, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -1), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), new StatusMove(Moves.SWITCHEROO, Type.DARK, 100, 10, -1, 0, 4) .unimplemented(), new AttackMove(Moves.GIGA_IMPACT, Type.NORMAL, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) .attr(RechargeAttr), new SelfStatusMove(Moves.NASTY_PLOT, Type.DARK, -1, 20, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPATK, 2, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], 2, true), new AttackMove(Moves.BULLET_PUNCH, Type.STEEL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4) .punchingMove(), new AttackMove(Moves.AVALANCHE, Type.ICE, MoveCategory.PHYSICAL, 60, 100, 10, -1, -4, 4) @@ -7546,7 +7771,7 @@ export function initMoves() { .bitingMove(), new AttackMove(Moves.SHADOW_SNEAK, Type.GHOST, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4), new AttackMove(Moves.MUD_BOMB, Type.GROUND, MoveCategory.SPECIAL, 65, 85, 10, 30, 0, 4) - .attr(StatChangeAttr, BattleStat.ACC, -1) + .attr(StatStageChangeAttr, [ Stat.ACC ], -1) .ballBombMove(), new AttackMove(Moves.PSYCHO_CUT, Type.PSYCHIC, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) .attr(HighCritAttr) @@ -7555,13 +7780,13 @@ export function initMoves() { new AttackMove(Moves.ZEN_HEADBUTT, Type.PSYCHIC, MoveCategory.PHYSICAL, 80, 90, 15, 20, 0, 4) .attr(FlinchAttr), new AttackMove(Moves.MIRROR_SHOT, Type.STEEL, MoveCategory.SPECIAL, 65, 85, 10, 30, 0, 4) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.FLASH_CANNON, Type.STEEL, MoveCategory.SPECIAL, 80, 100, 10, 10, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -1), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), new AttackMove(Moves.ROCK_CLIMB, Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, 20, 0, 4) .attr(ConfuseAttr), new StatusMove(Moves.DEFOG, Type.FLYING, -1, 15, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.EVA, -1) + .attr(StatStageChangeAttr, [ Stat.EVA ], -1) .attr(ClearWeatherAttr, WeatherType.FOG) .attr(ClearTerrainAttr) .attr(RemoveScreensAttr, false) @@ -7571,7 +7796,7 @@ export function initMoves() { .ignoresProtect() .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.DRACO_METEOR, Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPATK, -2, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), new AttackMove(Moves.DISCHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .target(MoveTarget.ALL_NEAR_OTHERS), @@ -7579,7 +7804,7 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.BURN) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.LEAF_STORM, Type.GRASS, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPATK, -2, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), new AttackMove(Moves.POWER_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 4), new AttackMove(Moves.ROCK_WRECKER, Type.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) .attr(RechargeAttr) @@ -7601,7 +7826,7 @@ export function initMoves() { .attr(HighCritAttr) .makesContact(false), new StatusMove(Moves.CAPTIVATE, Type.NORMAL, 100, 20, -1, 0, 4) - .attr(StatChangeAttr, BattleStat.SPATK, -2) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2) .condition((user, target, move) => target.isOppositeGender(user)) .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(Moves.STEALTH_ROCK, Type.ROCK, -1, 20, -1, 0, 4) @@ -7619,7 +7844,7 @@ export function initMoves() { new AttackMove(Moves.BUG_BITE, Type.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) .attr(StealEatBerryAttr), new AttackMove(Moves.CHARGE_BEAM, Type.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 70, 0, 4) - .attr(StatChangeAttr, BattleStat.SPATK, 1, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true), new AttackMove(Moves.WOOD_HAMMER, Type.GRASS, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) .attr(RecoilAttr, false, 0.33) .recklessMove(), @@ -7628,7 +7853,7 @@ export function initMoves() { .attr(HighCritAttr) .makesContact(false), new SelfStatusMove(Moves.DEFEND_ORDER, Type.BUG, -1, 10, -1, 0, 4) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), new SelfStatusMove(Moves.HEAL_ORDER, Type.BUG, -1, 10, -1, 0, 4) .attr(HealAttr, 0.5) .triageMove(), @@ -7654,23 +7879,24 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.SLEEP) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.SEED_FLARE, Type.GRASS, MoveCategory.SPECIAL, 120, 85, 5, 40, 0, 4) - .attr(StatChangeAttr, BattleStat.SPDEF, -2), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), new AttackMove(Moves.OMINOUS_WIND, Type.GHOST, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 4) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .windMove(), new AttackMove(Moves.SHADOW_FORCE, Type.GHOST, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) .attr(ChargeAttr, ChargeAnim.SHADOW_FORCE_CHARGING, i18next.t("moveTriggers:vanishedInstantly", {pokemonName: "{USER}"}), BattlerTagType.HIDDEN) .ignoresProtect() .ignoresVirtual(), new SelfStatusMove(Moves.HONE_CLAWS, Type.DARK, -1, 15, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.ACC ], 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.ACC ], 1, true), new StatusMove(Moves.WIDE_GUARD, Type.ROCK, -1, 10, -1, 3, 5) .target(MoveTarget.USER_SIDE) - .attr(AddArenaTagAttr, ArenaTagType.WIDE_GUARD, 1, true, true), + .attr(AddArenaTagAttr, ArenaTagType.WIDE_GUARD, 1, true, true) + .condition(failIfLastCondition), new StatusMove(Moves.GUARD_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5) - .unimplemented(), + .attr(AverageStatsAttr, [ Stat.DEF, Stat.SPDEF ], "moveTriggers:sharedGuard"), new StatusMove(Moves.POWER_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5) - .unimplemented(), + .attr(AverageStatsAttr, [ Stat.ATK, Stat.SPATK ], "moveTriggers:sharedPower"), new StatusMove(Moves.WONDER_ROOM, Type.PSYCHIC, -1, 10, -1, 0, 5) .ignoresProtect() .target(MoveTarget.BOTH_SIDES) @@ -7680,7 +7906,7 @@ export function initMoves() { new AttackMove(Moves.VENOSHOCK, Type.POISON, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 5) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1), new SelfStatusMove(Moves.AUTOTOMIZE, Type.STEEL, -1, 15, -1, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, 2, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true) .partial(), new SelfStatusMove(Moves.RAGE_POWDER, Type.BUG, -1, 20, -1, 2, 5) .powderMove() @@ -7706,7 +7932,7 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.POISON) .target(MoveTarget.ALL_NEAR_OTHERS), new SelfStatusMove(Moves.QUIVER_DANCE, Type.BUG, -1, 20, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .danceMove(), new AttackMove(Moves.HEAVY_SLAM, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5) .attr(MinimizeAccuracyAttr) @@ -7723,13 +7949,13 @@ export function initMoves() { new StatusMove(Moves.SOAK, Type.WATER, 100, 20, -1, 0, 5) .attr(ChangeTypeAttr, Type.WATER), new AttackMove(Moves.FLAME_CHARGE, Type.FIRE, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, 1, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), new SelfStatusMove(Moves.COIL, Type.POISON, -1, 20, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.ACC ], 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.ACC ], 1, true), new AttackMove(Moves.LOW_SWEEP, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 20, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new AttackMove(Moves.ACID_SPRAY, Type.POISON, MoveCategory.SPECIAL, 40, 100, 20, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPDEF, -2) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2) .ballBombMove(), new AttackMove(Moves.FOUL_PLAY, Type.DARK, MoveCategory.PHYSICAL, 95, 100, 15, -1, 0, 5) .attr(TargetAtkUserAtkAttr), @@ -7739,7 +7965,10 @@ export function initMoves() { .attr(AbilityGiveAttr), new StatusMove(Moves.AFTER_YOU, Type.NORMAL, -1, 15, -1, 0, 5) .ignoresProtect() - .unimplemented(), + .target(MoveTarget.NEAR_OTHER) + .condition(failIfSingleBattle) + .condition((user, target, move) => !target.turnData.acted) + .attr(AfterYouAttr), new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) .soundBased() .partial(), @@ -7747,14 +7976,15 @@ export function initMoves() { .attr(ConsecutiveUseMultiBasePowerAttr, 5, false) .soundBased(), new AttackMove(Moves.CHIP_AWAY, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 5) - .attr(IgnoreOpponentStatChangesAttr), + .attr(IgnoreOpponentStatStagesAttr), new AttackMove(Moves.CLEAR_SMOG, Type.POISON, MoveCategory.SPECIAL, 50, -1, 15, -1, 0, 5) .attr(ResetStatsAttr, false), new AttackMove(Moves.STORED_POWER, Type.PSYCHIC, MoveCategory.SPECIAL, 20, 100, 10, -1, 0, 5) - .attr(StatChangeCountPowerAttr), + .attr(PositiveStatStagePowerAttr), new StatusMove(Moves.QUICK_GUARD, Type.FIGHTING, -1, 15, -1, 3, 5) .target(MoveTarget.USER_SIDE) - .attr(AddArenaTagAttr, ArenaTagType.QUICK_GUARD, 1, true, true), + .attr(AddArenaTagAttr, ArenaTagType.QUICK_GUARD, 1, true, true) + .condition(failIfLastCondition), new SelfStatusMove(Moves.ALLY_SWITCH, Type.PSYCHIC, -1, 15, -1, 2, 5) .ignoresProtect() .unimplemented(), @@ -7763,8 +7993,8 @@ export function initMoves() { .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.BURN), new SelfStatusMove(Moves.SHELL_SMASH, Type.NORMAL, -1, 15, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 2, true) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 2, true) + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), new StatusMove(Moves.HEAL_PULSE, Type.PSYCHIC, -1, 10, -1, 0, 5) .attr(HealAttr, 0.5, false, false) .pulseMove() @@ -7778,8 +8008,8 @@ export function initMoves() { .condition(failOnGravityCondition) .ignoresVirtual(), new SelfStatusMove(Moves.SHIFT_GEAR, Type.STEEL, -1, 10, -1, 0, 5) - .attr(StatChangeAttr, BattleStat.ATK, 1, true) - .attr(StatChangeAttr, BattleStat.SPD, 2, true), + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) .attr(ForceSwitchOutAttr), new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) @@ -7792,7 +8022,15 @@ export function initMoves() { new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5) .attr(CopyTypeAttr), new AttackMove(Moves.RETALIATE, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, 0, 5) - .partial(), + .attr(MovePowerMultiplierAttr, (user, target, move) => { + const turn = user.scene.currentBattle.turn; + const lastPlayerFaint = user.scene.currentBattle.playerFaintsHistory[user.scene.currentBattle.playerFaintsHistory.length - 1]; + const lastEnemyFaint = user.scene.currentBattle.enemyFaintsHistory[user.scene.currentBattle.enemyFaintsHistory.length - 1]; + return ( + (lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) || + (lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && !user.isPlayer()) + ) ? 2 : 1; + }), new AttackMove(Moves.FINAL_GAMBIT, Type.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 5) .attr(UserHpDamageAttr) .attr(SacrificialAttrOnHit), @@ -7810,10 +8048,10 @@ export function initMoves() { new AttackMove(Moves.VOLT_SWITCH, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5) .attr(ForceSwitchOutAttr, true, false), new AttackMove(Moves.STRUGGLE_BUG, Type.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPATK, -1) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.BULLDOZE, Type.GROUND, MoveCategory.PHYSICAL, 60, 100, 20, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5) @@ -7822,9 +8060,9 @@ export function initMoves() { .attr(ForceSwitchOutAttr) .hidesTarget(), new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, true), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true), new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.WILD_CHARGE, Type.ELECTRIC, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 5) .attr(RecoilAttr) @@ -7839,10 +8077,10 @@ export function initMoves() { .attr(HitHealAttr) .triageMove(), new AttackMove(Moves.SACRED_SWORD, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 5) - .attr(IgnoreOpponentStatChangesAttr) + .attr(IgnoreOpponentStatStagesAttr) .slicingMove(), new AttackMove(Moves.RAZOR_SHELL, Type.WATER, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 5) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .slicingMove(), new AttackMove(Moves.HEAT_CRASH, Type.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5) .attr(MinimizeAccuracyAttr) @@ -7850,13 +8088,13 @@ export function initMoves() { .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) .condition(failOnMaxCondition), new AttackMove(Moves.LEAF_TORNADO, Type.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.STEAMROLLER, Type.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5) .attr(FlinchAttr), new SelfStatusMove(Moves.COTTON_GUARD, Type.GRASS, -1, 10, -1, 0, 5) - .attr(StatChangeAttr, BattleStat.DEF, 3, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 3, true), new AttackMove(Moves.NIGHT_DAZE, Type.DARK, MoveCategory.SPECIAL, 85, 95, 10, 40, 0, 5) - .attr(StatChangeAttr, BattleStat.ACC, -1), + .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.PSYSTRIKE, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 5) .attr(DefDefAttr), new AttackMove(Moves.TAIL_SLAP, Type.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, 0, 5) @@ -7885,14 +8123,14 @@ export function initMoves() { .attr(DefDefAttr) .slicingMove(), new AttackMove(Moves.GLACIATE, Type.ICE, MoveCategory.SPECIAL, 65, 95, 10, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.BOLT_STRIKE, Type.ELECTRIC, MoveCategory.PHYSICAL, 130, 85, 5, 20, 0, 5) .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new AttackMove(Moves.BLUE_FLARE, Type.FIRE, MoveCategory.SPECIAL, 130, 85, 5, 20, 0, 5) .attr(StatusEffectAttr, StatusEffect.BURN), new AttackMove(Moves.FIERY_DANCE, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, 50, 0, 5) - .attr(StatChangeAttr, BattleStat.SPATK, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) .danceMove(), new AttackMove(Moves.FREEZE_SHOCK, Type.ICE, MoveCategory.PHYSICAL, 140, 90, 5, 30, 0, 5) .attr(ChargeAttr, ChargeAnim.FREEZE_SHOCK_CHARGING, i18next.t("moveTriggers:becameCloakedInFreezingLight", {pokemonName: "{USER}"})) @@ -7903,14 +8141,14 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.BURN) .ignoresVirtual(), new AttackMove(Moves.SNARL, Type.DARK, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) - .attr(StatChangeAttr, BattleStat.SPATK, -1) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.ICICLE_CRASH, Type.ICE, MoveCategory.PHYSICAL, 85, 90, 10, 30, 0, 5) .attr(FlinchAttr) .makesContact(false), new AttackMove(Moves.V_CREATE, Type.FIRE, MoveCategory.PHYSICAL, 180, 95, 5, -1, 0, 5) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF, BattleStat.SPD ], -1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF, Stat.SPD ], -1, true), new AttackMove(Moves.FUSION_FLARE, Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 5) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(LastMoveDoublePowerAttr, Moves.FUSION_BOLT), @@ -7925,7 +8163,8 @@ export function initMoves() { new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6) .target(MoveTarget.USER_SIDE) .attr(AddArenaTagAttr, ArenaTagType.MAT_BLOCK, 1, true, true) - .condition(new FirstMoveCondition()), + .condition(new FirstMoveCondition()) + .condition(failIfLastCondition), new AttackMove(Moves.BELCH, Type.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6) .condition((user, target, move) => user.battleData.berriesEaten.length > 0), new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, -1, 0, 6) @@ -7934,12 +8173,12 @@ export function initMoves() { // If any fielded pokémon is grass-type and grounded. return [...user.scene.getEnemyParty(), ...user.scene.getParty()].some((poke) => poke.isOfType(Type.GRASS) && poke.isGrounded()); }) - .attr(StatChangeAttr, [BattleStat.ATK, BattleStat.SPATK], 1, false, (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded()), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded()), new StatusMove(Moves.STICKY_WEB, Type.BUG, -1, 20, -1, 0, 6) .attr(AddArenaTrapTagAttr, ArenaTagType.STICKY_WEB) .target(MoveTarget.ENEMY_SIDE), new AttackMove(Moves.FELL_STINGER, Type.BUG, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 6) - .attr(PostVictoryStatChangeAttr, BattleStat.ATK, 3, true ), + .attr(PostVictoryStatStageChangeAttr, [ Stat.ATK ], 3, true ), new AttackMove(Moves.PHANTOM_FORCE, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) .attr(ChargeAttr, ChargeAnim.PHANTOM_FORCE_CHARGING, i18next.t("moveTriggers:vanishedInstantly", {pokemonName: "{USER}"}), BattlerTagType.HIDDEN) .ignoresProtect() @@ -7948,7 +8187,7 @@ export function initMoves() { .attr(AddTypeAttr, Type.GHOST) .partial(), new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .soundBased(), new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6) .target(MoveTarget.BOTH_SIDES) @@ -7972,7 +8211,7 @@ export function initMoves() { .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY) .attr(ForceSwitchOutAttr, true, false) .soundBased(), new StatusMove(Moves.TOPSY_TURVY, Type.DARK, -1, 20, -1, 0, 6) @@ -7983,10 +8222,11 @@ export function initMoves() { .triageMove(), new StatusMove(Moves.CRAFTY_SHIELD, Type.FAIRY, -1, 10, -1, 3, 6) .target(MoveTarget.USER_SIDE) - .attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true), + .attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true) + .condition(failIfLastCondition), new StatusMove(Moves.FLOWER_SHIELD, Type.FAIRY, -1, 10, -1, 0, 6) .target(MoveTarget.ALL) - .attr(StatChangeAttr, BattleStat.DEF, 1, false, (user, target, move) => target.getTypes().includes(Type.GRASS) && !target.getTag(SemiInvulnerableTag)), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, false, (user, target, move) => target.getTypes().includes(Type.GRASS) && !target.getTag(SemiInvulnerableTag)), new StatusMove(Moves.GRASSY_TERRAIN, Type.GRASS, -1, 10, -1, 0, 6) .attr(TerrainChangeAttr, TerrainType.GRASSY) .target(MoveTarget.BOTH_SIDES), @@ -7996,11 +8236,11 @@ export function initMoves() { new StatusMove(Moves.ELECTRIFY, Type.ELECTRIC, -1, 20, -1, 0, 6) .unimplemented(), new AttackMove(Moves.PLAY_ROUGH, Type.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.FAIRY_WIND, Type.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6) .windMove(), new AttackMove(Moves.MOONBLAST, Type.FAIRY, MoveCategory.SPECIAL, 95, 100, 15, 30, 0, 6) - .attr(StatChangeAttr, BattleStat.SPATK, -1), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), new AttackMove(Moves.BOOMBURST, Type.NORMAL, MoveCategory.SPECIAL, 140, 100, 10, -1, 0, 6) .soundBased() .target(MoveTarget.ALL_NEAR_OTHERS), @@ -8008,14 +8248,15 @@ export function initMoves() { .target(MoveTarget.BOTH_SIDES) .unimplemented(), new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6) - .attr(ProtectAttr, BattlerTagType.KINGS_SHIELD), + .attr(ProtectAttr, BattlerTagType.KINGS_SHIELD) + .condition(failIfLastCondition), new StatusMove(Moves.PLAY_NICE, Type.NORMAL, -1, 20, -1, 0, 6) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new StatusMove(Moves.CONFIDE, Type.NORMAL, -1, 20, -1, 0, 6) - .attr(StatChangeAttr, BattleStat.SPATK, -1) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .soundBased(), new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6) - .attr(StatChangeAttr, BattleStat.DEF, 2, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.STEAM_ERUPTION, Type.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6) @@ -8029,26 +8270,27 @@ export function initMoves() { .attr(WaterShurikenPowerAttr) .attr(WaterShurikenMultiHitTypeAttr), new AttackMove(Moves.MYSTICAL_FIRE, Type.FIRE, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 6) - .attr(StatChangeAttr, BattleStat.SPATK, -1), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), new SelfStatusMove(Moves.SPIKY_SHIELD, Type.GRASS, -1, 10, -1, 4, 6) - .attr(ProtectAttr, BattlerTagType.SPIKY_SHIELD), + .attr(ProtectAttr, BattlerTagType.SPIKY_SHIELD) + .condition(failIfLastCondition), new StatusMove(Moves.AROMATIC_MIST, Type.FAIRY, -1, 20, -1, 0, 6) - .attr(StatChangeAttr, BattleStat.SPDEF, 1) + .attr(StatStageChangeAttr, [ Stat.SPDEF ], 1) .target(MoveTarget.NEAR_ALLY), new StatusMove(Moves.EERIE_IMPULSE, Type.ELECTRIC, 100, 15, -1, 0, 6) - .attr(StatChangeAttr, BattleStat.SPATK, -2), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2), new StatusMove(Moves.VENOM_DRENCH, Type.POISON, 100, 20, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], -1, false, (user, target, move) => target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], -1, false, (user, target, move) => target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC) .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(Moves.POWDER, Type.BUG, 100, 20, -1, 1, 6) .powderMove() .unimplemented(), new SelfStatusMove(Moves.GEOMANCY, Type.FAIRY, -1, 10, -1, 0, 6) .attr(ChargeAttr, ChargeAnim.GEOMANCY_CHARGING, i18next.t("moveTriggers:isChargingPower", {pokemonName: "{USER}"})) - .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true) + .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true) .ignoresVirtual(), new StatusMove(Moves.MAGNETIC_FLUX, Type.ELECTRIC, -1, 20, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new StatusMove(Moves.HAPPY_HOUR, Type.NORMAL, -1, 30, -1, 0, 6) // No animation @@ -8063,7 +8305,7 @@ export function initMoves() { new StatusMove(Moves.HOLD_HANDS, Type.NORMAL, -1, 40, -1, 0, 6) .target(MoveTarget.NEAR_ALLY), new StatusMove(Moves.BABY_DOLL_EYES, Type.FAIRY, 100, 30, -1, 1, 6) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.NUZZLE, Type.ELECTRIC, MoveCategory.PHYSICAL, 20, 100, 20, 100, 0, 6) .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new AttackMove(Moves.HOLD_BACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 6) @@ -8072,7 +8314,7 @@ export function initMoves() { .makesContact() .attr(TrapAttr, BattlerTagType.INFESTATION), new AttackMove(Moves.POWER_UP_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 20, 100, 0, 6) - .attr(StatChangeAttr, BattleStat.ATK, 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) .punchingMove(), new AttackMove(Moves.OBLIVION_WING, Type.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) .attr(HitHealAttr, 0.75) @@ -8103,9 +8345,9 @@ export function initMoves() { .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.DRAGON_ASCENT, Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), new AttackMove(Moves.HYPERSPACE_FURY, Type.DARK, MoveCategory.PHYSICAL, 100, -1, 5, -1, 0, 6) - .attr(StatChangeAttr, BattleStat.DEF, -1, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true) .makesContact(false) .ignoresProtect(), /* Unused */ @@ -8227,18 +8469,19 @@ export function initMoves() { new AttackMove(Moves.FIRST_IMPRESSION, Type.BUG, MoveCategory.PHYSICAL, 90, 100, 10, -1, 2, 7) .condition(new FirstMoveCondition()), new SelfStatusMove(Moves.BANEFUL_BUNKER, Type.POISON, -1, 10, -1, 4, 7) - .attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER), + .attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER) + .condition(failIfLastCondition), new AttackMove(Moves.SPIRIT_SHACKLE, Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true) .makesContact(false), new AttackMove(Moves.DARKEST_LARIAT, Type.DARK, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) - .attr(IgnoreOpponentStatChangesAttr), + .attr(IgnoreOpponentStatStagesAttr), new AttackMove(Moves.SPARKLING_ARIA, Type.WATER, MoveCategory.SPECIAL, 90, 100, 10, 100, 0, 7) .attr(HealStatusEffectAttr, false, StatusEffect.BURN) .soundBased() .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.ICE_HAMMER, Type.ICE, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 7) - .attr(StatChangeAttr, BattleStat.SPD, -1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1, true) .punchingMove(), new StatusMove(Moves.FLORAL_HEALING, Type.FAIRY, -1, 10, -1, 0, 7) .attr(BoostHealAttr, 0.5, 2/3, true, false, (user, target, move) => user.scene.arena.terrain?.terrainType === TerrainType.GRASSY) @@ -8246,8 +8489,8 @@ export function initMoves() { new AttackMove(Moves.HIGH_HORSEPOWER, Type.GROUND, MoveCategory.PHYSICAL, 95, 95, 10, -1, 0, 7), new StatusMove(Moves.STRENGTH_SAP, Type.GRASS, 100, 10, -1, 0, 7) .attr(HitHealAttr, null, Stat.ATK) - .attr(StatChangeAttr, BattleStat.ATK, -1) - .condition((user, target, move) => target.summonData.battleStats[BattleStat.ATK] > -6) + .attr(StatStageChangeAttr, [ Stat.ATK ], -1) + .condition((user, target, move) => target.getStatStage(Stat.ATK) > -6) .triageMove(), new AttackMove(Moves.SOLAR_BLADE, Type.GRASS, MoveCategory.PHYSICAL, 125, 100, 10, -1, 0, 7) .attr(SunlightChargeAttr, ChargeAnim.SOLAR_BLADE_CHARGING, i18next.t("moveTriggers:isGlowing", {pokemonName: "{USER}"})) @@ -8259,15 +8502,15 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false), new StatusMove(Moves.TOXIC_THREAD, Type.POISON, 100, 20, -1, 0, 7) .attr(StatusEffectAttr, StatusEffect.POISON) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new SelfStatusMove(Moves.LASER_FOCUS, Type.NORMAL, -1, 30, -1, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false), new StatusMove(Moves.GEAR_UP, Type.STEEL, -1, 20, -1, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new AttackMove(Moves.THROAT_CHOP, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) - .partial(), + .attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED), new AttackMove(Moves.POLLEN_PUFF, Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7) .attr(StatusCategoryOnAllyAttr) .attr(HealOnAllyAttr, 0.5, true, false) @@ -8278,11 +8521,11 @@ export function initMoves() { .attr(TerrainChangeAttr, TerrainType.PSYCHIC) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.LUNGE, Type.BUG, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.FIRE_LASH, Type.FIRE, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.POWER_TRIP, Type.DARK, MoveCategory.PHYSICAL, 20, 100, 10, -1, 0, 7) - .attr(StatChangeCountPowerAttr), + .attr(PositiveStatStagePowerAttr), new AttackMove(Moves.BURN_UP, Type.FIRE, MoveCategory.SPECIAL, 130, 100, 5, -1, 0, 7) .condition((user) => { const userTypes = user.getTypes(true); @@ -8293,7 +8536,7 @@ export function initMoves() { user.scene.queueMessage(i18next.t("moveTriggers:burnedItselfOut", {pokemonName: getPokemonNameWithAffix(user)})); }), new StatusMove(Moves.SPEED_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 7) - .unimplemented(), + .attr(SwapStatAttr, Stat.SPD), new AttackMove(Moves.SMART_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7), new StatusMove(Moves.PURIFY, Type.POISON, -1, 20, -1, 0, 7) .condition( @@ -8308,7 +8551,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES) .attr(SuppressAbilitiesIfActedAttr), new AttackMove(Moves.TROP_KICK, Type.GRASS, MoveCategory.PHYSICAL, 70, 100, 15, 100, 0, 7) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new StatusMove(Moves.INSTRUCT, Type.PSYCHIC, -1, 15, -1, 0, 7) .unimplemented(), new AttackMove(Moves.BEAK_BLAST, Type.FLYING, MoveCategory.PHYSICAL, 100, 100, 15, -1, -3, 7) @@ -8316,7 +8559,7 @@ export function initMoves() { .ballBombMove() .makesContact(false), new AttackMove(Moves.CLANGING_SCALES, Type.DRAGON, MoveCategory.SPECIAL, 110, 100, 5, -1, 0, 7) - .attr(StatChangeAttr, BattleStat.DEF, -1, true, null, true, false, MoveEffectTrigger.HIT, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true, null, true, false, MoveEffectTrigger.HIT, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.DRAGON_HAMMER, Type.DRAGON, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 7), @@ -8350,7 +8593,7 @@ export function initMoves() { .partial() .ignoresVirtual(), new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, -1, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true) .ignoresVirtual(), new AttackMove(Moves.GENESIS_SUPERNOVA, Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) .attr(TerrainChangeAttr, TerrainType.PSYCHIC) @@ -8362,18 +8605,18 @@ export function initMoves() { // Fails if the user was not hit by a physical attack during the turn .condition((user, target, move) => user.getTag(ShellTrapTag)?.activated === true), new AttackMove(Moves.FLEUR_CANNON, Type.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 7) - .attr(StatChangeAttr, BattleStat.SPATK, -2, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), new AttackMove(Moves.PSYCHIC_FANGS, Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) .bitingMove() .attr(RemoveScreensAttr), new AttackMove(Moves.STOMPING_TANTRUM, Type.GROUND, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 7) .attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1), new AttackMove(Moves.SHADOW_BONE, Type.GHOST, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .makesContact(false), new AttackMove(Moves.ACCELEROCK, Type.ROCK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 1, 7), new AttackMove(Moves.LIQUIDATION, Type.WATER, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.PRISMATIC_LASER, Type.PSYCHIC, MoveCategory.SPECIAL, 160, 100, 10, -1, 0, 7) .attr(RechargeAttr), new AttackMove(Moves.SPECTRAL_THIEF, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7) @@ -8385,7 +8628,7 @@ export function initMoves() { .ignoresAbilities() .partial(), new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1), new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) .attr(FlinchAttr), new AttackMove(Moves.NATURES_MADNESS, Type.FAIRY, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 7) @@ -8427,7 +8670,7 @@ export function initMoves() { .makesContact(false) .ignoresVirtual(), new AttackMove(Moves.CLANGOROUS_SOULBLAZE, Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES) .partial() @@ -8470,6 +8713,7 @@ export function initMoves() { /* Unused */ new SelfStatusMove(Moves.MAX_GUARD, Type.NORMAL, -1, 10, -1, 4, 8) .attr(ProtectAttr) + .condition(failIfLastCondition) .ignoresVirtual(), /* End Unused */ new AttackMove(Moves.DYNAMAX_CANNON, Type.DRAGON, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8) @@ -8494,19 +8738,19 @@ export function initMoves() { .bitingMove(), new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) // TODO: Stuff Cheeks should not be selectable when the user does not have a berry, see wiki .attr(EatBerryAttr) - .attr(StatChangeAttr, BattleStat.DEF, 2, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .condition((user) => { const userBerries = user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()); return userBerries.length > 0; }) .partial(), new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false) .condition((user, target, move) => user.getTag(TrappedTag)?.sourceMove !== Moves.NO_RETREAT), // fails if the user is currently trapped by No Retreat new StatusMove(Moves.TAR_SHOT, Type.ROCK, 100, 15, -1, 0, 8) - .attr(StatChangeAttr, BattleStat.SPD, -1) - .partial(), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(AddBattlerTagAttr, BattlerTagType.TAR_SHOT, false), new StatusMove(Moves.MAGIC_POWDER, Type.PSYCHIC, 100, 20, -1, 0, 8) .attr(ChangeTypeAttr, Type.PSYCHIC) .powderMove(), @@ -8600,16 +8844,16 @@ export function initMoves() { .ignoresVirtual(), /* End Unused */ new SelfStatusMove(Moves.CLANGOROUS_SOUL, Type.DRAGON, 100, 5, -1, 0, 8) - .attr(CutHpStatBoostAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, 3) + .attr(CutHpStatStageBoostAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, 3) .soundBased() .danceMove(), new AttackMove(Moves.BODY_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) .attr(DefAtkAttr), new StatusMove(Moves.DECORATE, Type.FAIRY, -1, 15, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 2) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 2) .ignoresProtect(), new AttackMove(Moves.DRUM_BEATING, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .makesContact(false), new AttackMove(Moves.SNAP_TRAP, Type.GRASS, MoveCategory.PHYSICAL, 35, 100, 15, -1, 0, 8) .attr(TrapAttr, BattlerTagType.SNAP_TRAP), @@ -8622,25 +8866,25 @@ export function initMoves() { .slicingMove(), new AttackMove(Moves.BEHEMOTH_BASH, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 8), new AttackMove(Moves.AURA_WHEEL, Type.ELECTRIC, MoveCategory.PHYSICAL, 110, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPD, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) .makesContact(false) .attr(AuraWheelTypeAttr) .condition((user, target, move) => [user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)), // Missing custom fail message new AttackMove(Moves.BREAKING_SWIPE, Type.DRAGON, MoveCategory.PHYSICAL, 60, 100, 15, 100, 0, 8) .target(MoveTarget.ALL_NEAR_ENEMIES) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.BRANCH_POKE, Type.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 8), new AttackMove(Moves.OVERDRIVE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.APPLE_ACID, Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPDEF, -1), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), new AttackMove(Moves.GRAV_APPLE, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTag(ArenaTagType.GRAVITY) ? 1.5 : 1) .makesContact(false), new AttackMove(Moves.SPIRIT_BREAK, Type.FAIRY, MoveCategory.PHYSICAL, 75, 100, 15, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPATK, -1), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), new AttackMove(Moves.STRANGE_STEAM, Type.FAIRY, MoveCategory.SPECIAL, 90, 95, 10, 20, 0, 8) .attr(ConfuseAttr), new StatusMove(Moves.LIFE_DEW, Type.WATER, -1, 10, -1, 0, 8) @@ -8648,7 +8892,8 @@ export function initMoves() { .target(MoveTarget.USER_AND_ALLIES) .ignoresProtect(), new SelfStatusMove(Moves.OBSTRUCT, Type.DARK, 100, 10, -1, 4, 8) - .attr(ProtectAttr, BattlerTagType.OBSTRUCT), + .attr(ProtectAttr, BattlerTagType.OBSTRUCT) + .condition(failIfLastCondition), new AttackMove(Moves.FALSE_SURRENDER, Type.DARK, MoveCategory.PHYSICAL, 80, -1, 10, -1, 0, 8), new AttackMove(Moves.METEOR_ASSAULT, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 5, -1, 0, 8) .attr(RechargeAttr) @@ -8664,14 +8909,14 @@ export function initMoves() { .attr(ClearTerrainAttr) .condition((user, target, move) => !!user.scene.arena.terrain), new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8) - //.attr(StatChangeAttr, BattleStat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit - //.attr(StatChangeAttr, BattleStat.DEF, -1, true) + //.attr(StatStageChangeAttr, Stat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit + //.attr(StatStageChangeAttr, Stat.DEF, -1, true) .attr(MultiHitAttr) .makesContact(false) .partial(), new AttackMove(Moves.METEOR_BEAM, Type.ROCK, MoveCategory.SPECIAL, 120, 90, 10, 100, 0, 8) .attr(ChargeAttr, ChargeAnim.METEOR_BEAM_CHARGING, i18next.t("moveTriggers:isOverflowingWithSpacePower", {pokemonName: "{USER}"}), null, true) - .attr(StatChangeAttr, BattleStat.SPATK, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) .ignoresVirtual(), new AttackMove(Moves.SHELL_SIDE_ARM, Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8) .attr(ShellSideArmCategoryAttr) @@ -8692,12 +8937,12 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() !== TerrainType.NONE && user.isGrounded() ? 2 : 1) .pulseMove(), new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPATK, -1), + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) - .target(MoveTarget.ALL_NEAR_ENEMIES) - .partial(), + .attr(StatusIfBoostedAttr, StatusEffect.BURN) + .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8) - .partial(), + .attr(MovePowerMultiplierAttr, (user, _target, _move) => user.turnData.statStagesDecreased ? 2 : 1), new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8) .attr(AttackedByItemAttr) .makesContact(false), @@ -8705,7 +8950,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_OTHERS) .unimplemented(), new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1) .target(MoveTarget.NEAR_ALLY), new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8) .attr(ForceSwitchOutAttr, true, false), @@ -8741,7 +8986,7 @@ export function initMoves() { .attr(FlinchAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.THUNDEROUS_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.DEF, -1), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(Moves.GLACIAL_LANCE, Type.ICE, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 8) .target(MoveTarget.ALL_NEAR_ENEMIES) .makesContact(false), @@ -8753,18 +8998,19 @@ export function initMoves() { new AttackMove(Moves.DIRE_CLAW, Type.POISON, MoveCategory.PHYSICAL, 80, 100, 15, 50, 0, 8) .attr(MultiStatusEffectAttr, [StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP]), new AttackMove(Moves.PSYSHIELD_BASH, Type.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.DEF, 1, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), new SelfStatusMove(Moves.POWER_SHIFT, Type.NORMAL, -1, 10, -1, 0, 8) - .unimplemented(), + .target(MoveTarget.USER) + .attr(ShiftStatAttr, Stat.ATK, Stat.DEF), new AttackMove(Moves.STONE_AXE, Type.ROCK, MoveCategory.PHYSICAL, 65, 90, 15, 100, 0, 8) .attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK) .slicingMove(), new AttackMove(Moves.SPRINGTIDE_STORM, Type.FAIRY, MoveCategory.SPECIAL, 100, 80, 5, 30, 0, 8) - .attr(StatChangeAttr, BattleStat.ATK, -1) + .attr(StatStageChangeAttr, [ Stat.ATK ], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.MYSTICAL_POWER, Type.PSYCHIC, MoveCategory.SPECIAL, 70, 90, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.SPATK, 1, true), + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true), new AttackMove(Moves.RAGING_FURY, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 8) .makesContact(false) .attr(FrenzyAttr) @@ -8780,10 +9026,10 @@ export function initMoves() { .makesContact(false) .attr(FlinchAttr), new SelfStatusMove(Moves.VICTORY_DANCE, Type.FIGHTING, -1, 10, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPD ], 1, true) .danceMove(), new AttackMove(Moves.HEADLONG_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true) + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true) .punchingMove(), new AttackMove(Moves.BARB_BARRAGE, Type.POISON, MoveCategory.PHYSICAL, 60, 100, 10, 50, 0, 8) .makesContact(false) @@ -8791,15 +9037,15 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(Moves.ESPER_WING, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) .attr(HighCritAttr) - .attr(StatChangeAttr, BattleStat.SPD, 1, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), new AttackMove(Moves.BITTER_MALICE, Type.GHOST, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new SelfStatusMove(Moves.SHELTER, Type.STEEL, -1, 10, 100, 0, 8) - .attr(StatChangeAttr, BattleStat.DEF, 2, true), + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), new AttackMove(Moves.TRIPLE_ARROWS, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) .makesContact(false) .attr(HighCritAttr) - .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .attr(FlinchAttr) .partial(), new AttackMove(Moves.INFERNAL_PARADE, Type.GHOST, MoveCategory.SPECIAL, 60, 100, 15, 30, 0, 8) @@ -8810,7 +9056,7 @@ export function initMoves() { .slicingMove(), new AttackMove(Moves.BLEAKWIND_STORM, Type.FLYING, MoveCategory.SPECIAL, 100, 80, 10, 30, 0, 8) .attr(StormAccuracyAttr) - .attr(StatChangeAttr, BattleStat.SPD, -1) + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.WILDBOLT_STORM, Type.ELECTRIC, MoveCategory.SPECIAL, 100, 80, 10, 20, 0, 8) @@ -8829,7 +9075,7 @@ export function initMoves() { .target(MoveTarget.USER_AND_ALLIES) .triageMove(), new SelfStatusMove(Moves.TAKE_HEART, Type.PSYCHIC, -1, 10, -1, 0, 8) - .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true) + .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF ], 1, true) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN, StatusEffect.SLEEP), /* Unused new AttackMove(Moves.G_MAX_WILDFIRE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) @@ -8936,10 +9182,10 @@ export function initMoves() { .attr(TeraBlastCategoryAttr) .attr(TeraBlastTypeAttr) .attr(TeraBlastPowerAttr) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)) - .partial(), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)), new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9) - .attr(ProtectAttr, BattlerTagType.SILK_TRAP), + .attr(ProtectAttr, BattlerTagType.SILK_TRAP) + .condition(failIfLastCondition), new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9) .attr(MissEffectAttr, crashDamageFunc) .attr(NoEffectAttr, crashDamageFunc) @@ -8949,17 +9195,17 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => 1 + Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 100)) .makesContact(false), new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.SPDEF, -2), + .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) .makesContact(false) .partial(), new AttackMove(Moves.JET_PUNCH, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) .punchingMove(), new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9) - .attr(StatChangeAttr, BattleStat.ATK, 2) - .attr(StatChangeAttr, BattleStat.DEF, -2), + .attr(StatStageChangeAttr, [ Stat.ATK ], 2) + .attr(StatStageChangeAttr, [ Stat.DEF ], -2), new AttackMove(Moves.SPIN_OUT, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) - .attr(StatChangeAttr, BattleStat.SPD, -2, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], -2, true), new AttackMove(Moves.POPULATION_BOMB, Type.NORMAL, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 9) .attr(MultiHitAttr, MultiHitType._10) .slicingMove() @@ -9001,24 +9247,24 @@ export function initMoves() { new StatusMove(Moves.DOODLE, Type.NORMAL, 100, 10, -1, 0, 9) .attr(AbilityCopyAttr, true), new SelfStatusMove(Moves.FILLET_AWAY, Type.NORMAL, -1, 10, -1, 0, 9) - .attr(CutHpStatBoostAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 2, 2), + .attr(CutHpStatStageBoostAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 2, 2), new AttackMove(Moves.KOWTOW_CLEAVE, Type.DARK, MoveCategory.PHYSICAL, 85, -1, 10, -1, 0, 9) .slicingMove(), new AttackMove(Moves.FLOWER_TRICK, Type.GRASS, MoveCategory.PHYSICAL, 70, -1, 10, 100, 0, 9) .attr(CritOnlyAttr) .makesContact(false), new AttackMove(Moves.TORCH_SONG, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.SPATK, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) .soundBased(), new AttackMove(Moves.AQUA_STEP, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.SPD, 1, true) + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) .danceMove(), new AttackMove(Moves.RAGING_BULL, Type.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 9) .attr(RagingBullTypeAttr) .attr(RemoveScreensAttr), new AttackMove(Moves.MAKE_IT_RAIN, Type.STEEL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) .attr(MoneyAttr) - .attr(StatChangeAttr, BattleStat.SPATK, -1, true, null, true, false, MoveEffectTrigger.HIT, true) + .attr(StatStageChangeAttr, [ Stat.SPATK ], -1, true, null, true, false, MoveEffectTrigger.HIT, true) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.PSYBLADE, Type.PSYCHIC, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) @@ -9040,17 +9286,17 @@ export function initMoves() { .attr(ForceSwitchOutAttr, true, false) .target(MoveTarget.BOTH_SIDES), new SelfStatusMove(Moves.TIDY_UP, Type.NORMAL, -1, 10, -1, 0, 9) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPD ], 1, true, null, true, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true, null, true, true) .attr(RemoveArenaTrapAttr, true), new StatusMove(Moves.SNOWSCAPE, Type.ICE, -1, 10, -1, 0, 9) .attr(WeatherChangeAttr, WeatherType.SNOW) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.POUNCE, Type.BUG, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.SPD, -1), + .attr(StatStageChangeAttr, [ Stat.SPD ], -1), new AttackMove(Moves.TRAILBLAZE, Type.GRASS, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.SPD, 1, true), + .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), new AttackMove(Moves.CHILLING_WATER, Type.WATER, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 9) - .attr(StatChangeAttr, BattleStat.ATK, -1), + .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.HYPER_DRILL, Type.NORMAL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) .ignoresProtect(), new AttackMove(Moves.TWIN_BEAM, Type.PSYCHIC, MoveCategory.SPECIAL, 40, 100, 10, -1, 0, 9) @@ -9059,7 +9305,7 @@ export function initMoves() { .attr(HitCountPowerAttr) .punchingMove(), new AttackMove(Moves.ARMOR_CANNON, Type.FIRE, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), + .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), new AttackMove(Moves.BITTER_BLADE, Type.FIRE, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 9) .attr(HitHealAttr) .slicingMove() @@ -9114,7 +9360,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES) .triageMove(), new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9) - .attr(StatChangeAttr, BattleStat.SPD, -1) //Temporary + .attr(StatStageChangeAttr, [ Stat.SPD ], -1) //Temporary .ballBombMove() .partial(), new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9) @@ -9131,7 +9377,8 @@ export function initMoves() { .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc) .attr(DoublePowerChanceAttr), new SelfStatusMove(Moves.BURNING_BULWARK, Type.FIRE, -1, 10, -1, 4, 9) - .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK), + .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK) + .condition(failIfLastCondition), new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) @@ -9143,12 +9390,11 @@ export function initMoves() { new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) .attr(OpponentHighHpPowerAttr, 100), new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9) - .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true) - .target(MoveTarget.NEAR_ALLY) - .partial(), + .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true) + .target(MoveTarget.NEAR_ALLY), new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) - .soundBased() - .partial(), + .attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED) + .soundBased(), new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9) .attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1), new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9) @@ -9167,7 +9413,7 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.TOXIC) ); allMoves.map(m => { - if (m.getAttrs(StatChangeAttr).some(a => a.selfTarget && a.levels < 0)) { + if (m.getAttrs(StatStageChangeAttr).some(a => a.selfTarget && a.stages < 0)) { selfStatLowerMoves.push(m.id); } }); diff --git a/src/data/nature.ts b/src/data/nature.ts index 72e5bb7863c..c614be465c3 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -1,9 +1,9 @@ -import { Stat, getStatName } from "./pokemon-stat"; import * as Utils from "../utils"; import { TextStyle, getBBCodeFrag } from "../ui/text"; import { Nature } from "#enums/nature"; import { UiTheme } from "#enums/ui-theme"; import i18next from "i18next"; +import { Stat, EFFECTIVE_STATS, getShortenedStatKey } from "#app/enums/stat"; export { Nature }; @@ -14,10 +14,9 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals ret = i18next.t("nature:" + ret as any); } if (includeStatEffects) { - const stats = Utils.getEnumValues(Stat).slice(1); let increasedStat: Stat | null = null; let decreasedStat: Stat | null = null; - for (const stat of stats) { + for (const stat of EFFECTIVE_STATS) { const multiplier = getNatureStatMultiplier(nature, stat); if (multiplier > 1) { increasedStat = stat; @@ -28,7 +27,7 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals const textStyle = forStarterSelect ? TextStyle.SUMMARY_ALT : TextStyle.WINDOW; const getTextFrag = !ignoreBBCode ? (text: string, style: TextStyle) => getBBCodeFrag(text, style, uiTheme) : (text: string, style: TextStyle) => text; if (increasedStat && decreasedStat) { - ret = `${getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(`, textStyle)}${getTextFrag(`+${getStatName(increasedStat, true)}`, TextStyle.SUMMARY_PINK)}${getTextFrag("/", textStyle)}${getTextFrag(`-${getStatName(decreasedStat, true)}`, TextStyle.SUMMARY_BLUE)}${getTextFrag(")", textStyle)}`; + ret = `${getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(`, textStyle)}${getTextFrag(`+${i18next.t(getShortenedStatKey(increasedStat))}`, TextStyle.SUMMARY_PINK)}${getTextFrag("/", textStyle)}${getTextFrag(`-${i18next.t(getShortenedStatKey(decreasedStat))}`, TextStyle.SUMMARY_BLUE)}${getTextFrag(")", textStyle)}`; } else { ret = getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(-)`, textStyle); } diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 315e75e53e1..6479d620182 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -1,7 +1,7 @@ import { Gender } from "./gender"; import { PokeballType } from "./pokeball"; import Pokemon from "../field/pokemon"; -import { Stat } from "./pokemon-stat"; +import { Stat } from "#enums/stat"; import { Type } from "./type"; import * as Utils from "../utils"; import { SpeciesFormKey } from "./pokemon-species"; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index e4417f8e8bb..20703cebe81 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -1,14 +1,15 @@ -import { PokemonFormChangeItemModifier } from "../modifier/modifier"; +import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier"; import Pokemon from "../field/pokemon"; import { SpeciesFormKey } from "./pokemon-species"; import { StatusEffect } from "./status-effect"; import { MoveCategory, allMoves } from "./move"; +import { Type } from "./type"; import { Constructor } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TimeOfDay } from "#enums/time-of-day"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { WeatherType } from "./weather"; @@ -66,34 +67,34 @@ export enum FormChangeItem { BLUE_ORB = 50, RED_ORB, - SHARP_METEORITE, - HARD_METEORITE, - SMOOTH_METEORITE, ADAMANT_CRYSTAL, LUSTROUS_GLOBE, GRISEOUS_CORE, REVEAL_GLASS, - GRACIDEA, MAX_MUSHROOMS, DARK_STONE, LIGHT_STONE, PRISON_BOTTLE, - N_LUNARIZER, - N_SOLARIZER, RUSTED_SWORD, RUSTED_SHIELD, ICY_REINS_OF_UNITY, SHADOW_REINS_OF_UNITY, - WELLSPRING_MASK, - HEARTHFLAME_MASK, - CORNERSTONE_MASK, + ULTRANECROZIUM_Z, + + SHARP_METEORITE = 100, + HARD_METEORITE, + SMOOTH_METEORITE, + GRACIDEA, SHOCK_DRIVE, BURN_DRIVE, CHILL_DRIVE, DOUSE_DRIVE, - ULTRANECROZIUM_Z, - - FIST_PLATE = 100, + N_SOLARIZER, + N_LUNARIZER, + WELLSPRING_MASK, + HEARTHFLAME_MASK, + CORNERSTONE_MASK, + FIST_PLATE, SKY_PLATE, TOXIC_PLATE, EARTH_PLATE, @@ -129,7 +130,7 @@ export enum FormChangeItem { DRAGON_MEMORY, DARK_MEMORY, FAIRY_MEMORY, - BLANK_MEMORY // TODO: Find a potential use for this + NORMAL_MEMORY // TODO: Find a potential use for this } export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; @@ -357,9 +358,44 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger { } } +/** + * Class used for triggering form changes based on the user's Tera type. + * Used by Ogerpon and Terapagos. + * @extends SpeciesFormChangeTrigger + */ +export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger { + /** The Tera type that triggers the form change */ + private teraType: Type; + + constructor(teraType: Type) { + super(); + this.teraType = teraType; + } + + /** + * Checks if the associated Pokémon has the required Tera Shard that matches with the associated Tera type. + * @param {Pokemon} pokemon the Pokémon that is trying to do the form change + * @returns `true` if the Pokémon can change forms, `false` otherwise + */ + canChange(pokemon: Pokemon): boolean { + return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id && m.teraType === this.teraType); + } +} + +/** + * Class used for triggering form changes based on the user's lapsed Tera type. + * Used by Ogerpon and Terapagos. + * @extends SpeciesFormChangeTrigger + */ +export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger { + canChange(pokemon: Pokemon): boolean { + return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id); + } +} + /** * Class used for triggering form changes based on weather. - * Used by Castform. + * Used by Castform and Cherrim. * @extends SpeciesFormChangeTrigger */ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger { @@ -392,7 +428,7 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger { /** * Class used for reverting to the original form when the weather runs out * or when the user loses the ability/is suppressed. - * Used by Castform. + * Used by Castform and Cherrim. * @extends SpeciesFormChangeTrigger */ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger { @@ -592,6 +628,23 @@ export const pokemonFormChanges: PokemonFormChanges = { [Species.ALTARIA]: [ new SpeciesFormChange(Species.ALTARIA, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.ALTARIANITE)) ], + [Species.CASTFORM]: [ + new SpeciesFormChange(Species.CASTFORM, "", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), + new SpeciesFormChange(Species.CASTFORM, "rainy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), + new SpeciesFormChange(Species.CASTFORM, "snowy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), + new SpeciesFormChange(Species.CASTFORM, "", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), + new SpeciesFormChange(Species.CASTFORM, "sunny", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), + new SpeciesFormChange(Species.CASTFORM, "snowy", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), + new SpeciesFormChange(Species.CASTFORM, "", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), + new SpeciesFormChange(Species.CASTFORM, "sunny", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), + new SpeciesFormChange(Species.CASTFORM, "rainy", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), + new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), + new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), + new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), + new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeActiveTrigger(), true), + new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true), + new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true) + ], [Species.BANETTE]: [ new SpeciesFormChange(Species.BANETTE, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.BANETTITE)) ], @@ -627,6 +680,11 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.DEOXYS, "normal", "defense", new SpeciesFormChangeItemTrigger(FormChangeItem.HARD_METEORITE)), new SpeciesFormChange(Species.DEOXYS, "normal", "speed", new SpeciesFormChangeItemTrigger(FormChangeItem.SMOOTH_METEORITE)) ], + [Species.CHERRIM]: [ + new SpeciesFormChange(Species.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(Abilities.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true), + new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true), + new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true) + ], [Species.LOPUNNY]: [ new SpeciesFormChange(Species.LOPUNNY, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.LOPUNNITE)) ], @@ -822,6 +880,14 @@ export const pokemonFormChanges: PokemonFormChanges = { [Species.SANDACONDA]: [ new SpeciesFormChange(Species.SANDACONDA, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)) ], + [Species.CRAMORANT]: [ + new SpeciesFormChange(Species.CRAMORANT, "", "gulping", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() >= .5)), + new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)), + new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true), + new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true), + new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true), + new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true) + ], [Species.TOXTRICITY]: [ new SpeciesFormChange(Species.TOXTRICITY, "amped", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)), new SpeciesFormChange(Species.TOXTRICITY, "lowkey", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)), @@ -848,6 +914,10 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.ALCREMIE, "caramel-swirl", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)), new SpeciesFormChange(Species.ALCREMIE, "rainbow-swirl", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)) ], + [Species.EISCUE]: [ + new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.EISCUE, "no-ice", "", new SpeciesFormChangeManualTrigger(), true) + ], [Species.MORPEKO]: [ new SpeciesFormChange(Species.MORPEKO, "full-belly", "hangry", new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.MORPEKO, "hangry", "full-belly", new SpeciesFormChangeManualTrigger(), true) @@ -883,53 +953,24 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.OGERPON, "teal-mask", "wellspring-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.WELLSPRING_MASK)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "hearthflame-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.HEARTHFLAME_MASK)), new SpeciesFormChange(Species.OGERPON, "teal-mask", "cornerstone-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)), - new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Grass Tera Shard - new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Grass Tera Shard - new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Water Tera Shard - new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Water Tera Shard - new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Fire Tera Shard - new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Fire Tera Shard - new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Rock Tera Shard - new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeManualTrigger(), true) //When no longer holding a Rock Tera Shard + new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeTeraTrigger(Type.GRASS)), + new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.GRASS)), + new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(Type.WATER)), + new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.WATER)), + new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeTeraTrigger(Type.FIRE)), + new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.FIRE)), + new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(Type.ROCK)), + new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.ROCK)) ], [Species.TERAPAGOS]: [ new SpeciesFormChange(Species.TERAPAGOS, "", "terastal", new SpeciesFormChangeManualTrigger(), true), - new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeManualTrigger(), true), //When holding a Stellar Tera Shard - new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeManualTrigger(), true) //When no longer holding a Stellar Tera Shard + new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(Type.STELLAR)), + new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.STELLAR)) ], [Species.GALAR_DARMANITAN]: [ new SpeciesFormChange(Species.GALAR_DARMANITAN, "", "zen", new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.GALAR_DARMANITAN, "zen", "", new SpeciesFormChangeManualTrigger(), true) ], - [Species.EISCUE]: [ - new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true), - new SpeciesFormChange(Species.EISCUE, "no-ice", "", new SpeciesFormChangeManualTrigger(), true), - ], - [Species.CRAMORANT]: [ - new SpeciesFormChange(Species.CRAMORANT, "", "gulping", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() >= .5)), - new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)), - new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true), - new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true), - new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true), - new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true), - ], - [Species.CASTFORM]: [ - new SpeciesFormChange(Species.CASTFORM, "", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), - new SpeciesFormChange(Species.CASTFORM, "rainy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), - new SpeciesFormChange(Species.CASTFORM, "snowy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true), - new SpeciesFormChange(Species.CASTFORM, "", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), - new SpeciesFormChange(Species.CASTFORM, "sunny", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), - new SpeciesFormChange(Species.CASTFORM, "snowy", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true), - new SpeciesFormChange(Species.CASTFORM, "", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), - new SpeciesFormChange(Species.CASTFORM, "sunny", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), - new SpeciesFormChange(Species.CASTFORM, "rainy", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true), - new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), - new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), - new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true), - new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeActiveTrigger(), true), - new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true), - new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true), - ], }; export function initPokemonForms() { diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 17f2de794ae..09448b332e4 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -14,7 +14,7 @@ import { GrowthRate } from "./exp"; import { EvolutionLevel, SpeciesWildEvolutionDelay, pokemonEvolutions, pokemonPrevolutions } from "./pokemon-evolutions"; import { Type } from "./type"; import { LevelMoves, pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves } from "./pokemon-level-moves"; -import { Stat } from "./pokemon-stat"; +import { Stat } from "#enums/stat"; import { Variant, VariantSet, variantColorCache, variantData } from "./variant"; export enum Region { @@ -944,7 +944,7 @@ export function initSpecies() { new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true, new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 32, Abilities.TINTED_LENS, Abilities.TINTED_LENS, Abilities.TINTED_LENS, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 32, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true), ), new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false), diff --git a/src/data/pokemon-stat.ts b/src/data/pokemon-stat.ts deleted file mode 100644 index 16570785a62..00000000000 --- a/src/data/pokemon-stat.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Stat } from "#enums/stat"; -import i18next from "i18next"; - -export { Stat }; - -export function getStatName(stat: Stat, shorten: boolean = false) { - let ret: string = ""; - switch (stat) { - case Stat.HP: - ret = !shorten ? i18next.t("pokemonInfo:Stat.HP") : i18next.t("pokemonInfo:Stat.HPshortened"); - break; - case Stat.ATK: - ret = !shorten ? i18next.t("pokemonInfo:Stat.ATK") : i18next.t("pokemonInfo:Stat.ATKshortened"); - break; - case Stat.DEF: - ret = !shorten ? i18next.t("pokemonInfo:Stat.DEF") : i18next.t("pokemonInfo:Stat.DEFshortened"); - break; - case Stat.SPATK: - ret = !shorten ? i18next.t("pokemonInfo:Stat.SPATK") : i18next.t("pokemonInfo:Stat.SPATKshortened"); - break; - case Stat.SPDEF: - ret = !shorten ? i18next.t("pokemonInfo:Stat.SPDEF") : i18next.t("pokemonInfo:Stat.SPDEFshortened"); - break; - case Stat.SPD: - ret = !shorten ? i18next.t("pokemonInfo:Stat.SPD") : i18next.t("pokemonInfo:Stat.SPDshortened"); - break; - } - return ret; -} diff --git a/src/data/temp-battle-stat.ts b/src/data/temp-battle-stat.ts deleted file mode 100644 index 2d461a1d647..00000000000 --- a/src/data/temp-battle-stat.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { BattleStat, getBattleStatName } from "./battle-stat"; -import i18next from "i18next"; - -export enum TempBattleStat { - ATK, - DEF, - SPATK, - SPDEF, - SPD, - ACC, - CRIT -} - -export function getTempBattleStatName(tempBattleStat: TempBattleStat) { - if (tempBattleStat === TempBattleStat.CRIT) { - return i18next.t("modifierType:TempBattleStatBoosterStatName.CRIT"); - } - return getBattleStatName(tempBattleStat as integer as BattleStat); -} - -export function getTempBattleStatBoosterItemName(tempBattleStat: TempBattleStat) { - switch (tempBattleStat) { - case TempBattleStat.ATK: - return "X Attack"; - case TempBattleStat.DEF: - return "X Defense"; - case TempBattleStat.SPATK: - return "X Sp. Atk"; - case TempBattleStat.SPDEF: - return "X Sp. Def"; - case TempBattleStat.SPD: - return "X Speed"; - case TempBattleStat.ACC: - return "X Accuracy"; - case TempBattleStat.CRIT: - return "Dire Hit"; - } -} diff --git a/src/data/terrain.ts b/src/data/terrain.ts index d4789078af7..6db68b92239 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -4,7 +4,7 @@ import { Type } from "./type"; import * as Utils from "../utils"; import { ChangeMovePriorityAbAttr, applyAbAttrs } from "./ability"; import { ProtectAttr } from "./move"; -import { BattlerIndex } from "#app/battle.js"; +import { BattlerIndex } from "#app/battle"; import i18next from "i18next"; export enum TerrainType { diff --git a/src/data/variant.ts b/src/data/variant.ts index 48369d112db..b7a01a4be89 100644 --- a/src/data/variant.ts +++ b/src/data/variant.ts @@ -1,4 +1,4 @@ -import { VariantTier } from "#app/enums/variant-tier.js"; +import { VariantTier } from "#app/enums/variant-tier"; export type Variant = 0 | 1 | 2; diff --git a/src/data/weather.ts b/src/data/weather.ts index 2421f719e6e..afdd0a958cf 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -88,12 +88,14 @@ export class Weather { return 1; } - isMoveWeatherCancelled(move: Move): boolean { + isMoveWeatherCancelled(user: Pokemon, move: Move): boolean { + const moveType = user.getMoveType(move); + switch (this.weatherType) { case WeatherType.HARSH_SUN: - return move instanceof AttackMove && move.type === Type.WATER; + return move instanceof AttackMove && moveType === Type.WATER; case WeatherType.HEAVY_RAIN: - return move instanceof AttackMove && move.type === Type.FIRE; + return move instanceof AttackMove && moveType === Type.FIRE; } return false; diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index 1265b815bf4..1c79750c91a 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -22,5 +22,6 @@ export enum ArenaTagType { CRAFTY_SHIELD = "CRAFTY_SHIELD", TAILWIND = "TAILWIND", HAPPY_HOUR = "HAPPY_HOUR", + SAFEGUARD = "SAFEGUARD", NO_CRIT = "NO_CRIT" } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index b133b442801..cb83ebf4882 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -64,10 +64,16 @@ export enum BattlerTagType { STOCKPILING = "STOCKPILING", RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE", ALWAYS_GET_HIT = "ALWAYS_GET_HIT", + DISABLED = "DISABLED", IGNORE_GHOST = "IGNORE_GHOST", IGNORE_DARK = "IGNORE_DARK", GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA", GULP_MISSILE_PIKACHU = "GULP_MISSILE_PIKACHU", BEAK_BLAST_CHARGING = "BEAK_BLAST_CHARGING", - SHELL_TRAP = "SHELL_TRAP" + SHELL_TRAP = "SHELL_TRAP", + DRAGON_CHEER = "DRAGON_CHEER", + NO_RETREAT = "NO_RETREAT", + GORILLA_TACTICS = "GORILLA_TACTICS", + THROAT_CHOPPED = "THROAT_CHOPPED", + TAR_SHOT = "TAR_SHOT", } diff --git a/src/enums/shop-cursor-target.ts b/src/enums/shop-cursor-target.ts index d2f72fed0d6..11f524399b2 100644 --- a/src/enums/shop-cursor-target.ts +++ b/src/enums/shop-cursor-target.ts @@ -1,13 +1,13 @@ /** - * Determines the cursor target when entering the shop phase. + * Determines the row cursor target when entering the shop phase. */ export enum ShopCursorTarget { - /** Cursor points to Reroll */ + /** Cursor points to Reroll row */ REROLL, - /** Cursor points to Items */ - ITEMS, - /** Cursor points to Shop */ + /** Cursor points to Rewards row */ + REWARDS, + /** Cursor points to Shop row */ SHOP, - /** Cursor points to Check Team */ + /** Cursor points to Check Team row */ CHECK_TEAM } diff --git a/src/enums/stat.ts b/src/enums/stat.ts index a40319664d6..a12d53e8559 100644 --- a/src/enums/stat.ts +++ b/src/enums/stat.ts @@ -1,8 +1,75 @@ +/** Enum that comprises all possible stat-related attributes, in-battle and permanent, of a Pokemon. */ export enum Stat { + /** Hit Points */ HP = 0, + /** Attack */ ATK, + /** Defense */ DEF, + /** Special Attack */ SPATK, + /** Special Defense */ SPDEF, + /** Speed */ SPD, + /** Accuracy */ + ACC, + /** Evasiveness */ + EVA +} + +/** A constant array comprised of the {@linkcode Stat} values that make up {@linkcode PermanentStat}. */ +export const PERMANENT_STATS = [ Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const; +/** Type used to describe the core, permanent stats of a Pokemon. */ +export type PermanentStat = typeof PERMANENT_STATS[number]; + +/** A constant array comprised of the {@linkcode Stat} values that make up {@linkcode EFfectiveStat}. */ +export const EFFECTIVE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const; +/** Type used to describe the intersection of core stats and stats that have stages in battle. */ +export type EffectiveStat = typeof EFFECTIVE_STATS[number]; + +/** A constant array comprised of {@linkcode Stat} the values that make up {@linkcode BattleStat}. */ +export const BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC, Stat.EVA ] as const; +/** Type used to describe the stats that have stages which can be incremented and decremented in battle. */ +export type BattleStat = typeof BATTLE_STATS[number]; + +/** A constant array comprised of {@linkcode Stat} the values that make up {@linkcode TempBattleStat}. */ +export const TEMP_BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC ] as const; +/** Type used to describe the stats that have X item (`TEMP_STAT_STAGE_BOOSTER`) equivalents. */ +export type TempBattleStat = typeof TEMP_BATTLE_STATS[number]; + +/** + * Provides the translation key corresponding to the amount of stat stages and whether those stat stages + * are positive or negative. + * @param stages the amount of stages + * @param isIncrease dictates a negative (`false`) or a positive (`true`) stat stage change + * @returns the translation key fitting the conditions described by {@linkcode stages} and {@linkcode isIncrease} + */ +export function getStatStageChangeDescriptionKey(stages: number, isIncrease: boolean) { + if (stages === 1) { + return isIncrease ? "battle:statRose" : "battle:statFell"; + } else if (stages === 2) { + return isIncrease ? "battle:statSharplyRose" : "battle:statHarshlyFell"; + } else if (stages <= 6) { + return isIncrease ? "battle:statRoseDrastically" : "battle:statSeverelyFell"; + } + return isIncrease ? "battle:statWontGoAnyHigher" : "battle:statWontGoAnyLower"; +} + +/** + * Provides the translation key corresponding to a given stat which can be translated into its full name. + * @param stat the {@linkcode Stat} to be translated + * @returns the translation key corresponding to the given {@linkcode Stat} + */ +export function getStatKey(stat: Stat) { + return `pokemonInfo:Stat.${Stat[stat]}`; +} + +/** + * Provides the translation key corresponding to a given stat which can be translated into its shortened name. + * @param stat the {@linkcode Stat} to be translated + * @returns the translation key corresponding to the given {@linkcode Stat} + */ +export function getShortenedStatKey(stat: PermanentStat) { + return `pokemonInfo:Stat.${Stat[stat]}shortened`; } diff --git a/src/events/arena.ts b/src/events/arena.ts index 9fbbe572601..c05e67d353c 100644 --- a/src/events/arena.ts +++ b/src/events/arena.ts @@ -1,7 +1,7 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; import { ArenaTagType } from "#enums/arena-tag-type"; -import { TerrainType } from "#app/data/terrain.js"; -import { WeatherType } from "#app/data/weather.js"; +import { TerrainType } from "#app/data/terrain"; +import { WeatherType } from "#app/data/weather"; /** Alias for all {@linkcode ArenaEvent} type strings */ export enum ArenaEventType { diff --git a/src/field/arena.ts b/src/field/arena.ts index 7622b9a014f..9f0a9691dee 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -60,7 +60,7 @@ export class Arena { this.scene.arenaBg.setTexture(`${biomeKey}_bg`); this.scene.arenaBgTransition.setTexture(`${biomeKey}_bg`); - // Redo this on initialise because during save/load the current wave isn't always + // Redo this on initialize because during save/load the current wave isn't always // set correctly during construction this.updatePoolsForTimeOfDay(); } @@ -289,7 +289,7 @@ export class Arena { /** * Sets weather to the override specified in overrides.ts - * @param weather new weather to set of type WeatherType + * @param weather new {@linkcode WeatherType} to set * @returns true to force trySetWeather to return true */ trySetWeatherOverride(weather: WeatherType): boolean { @@ -301,8 +301,8 @@ export class Arena { /** * Attempts to set a new weather to the battle - * @param weather new weather to set of type WeatherType - * @param hasPokemonSource is the new weather from a pokemon + * @param weather {@linkcode WeatherType} new {@linkcode WeatherType} to set + * @param hasPokemonSource boolean if the new weather is from a pokemon * @returns true if new weather set, false if no weather provided or attempting to set the same weather as currently in use */ trySetWeather(weather: WeatherType, hasPokemonSource: boolean): boolean { @@ -339,7 +339,10 @@ export class Arena { */ triggerWeatherBasedFormChanges(): void { this.scene.getField(true).forEach( p => { - if (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM) { + const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM); + const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { new ShowAbilityPhase(this.scene, p.getBattlerIndex()); this.scene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger); } @@ -351,7 +354,10 @@ export class Arena { */ triggerWeatherBasedFormChangesToNormal(): void { this.scene.getField(true).forEach( p => { - if (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM) { + const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM); + const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { new ShowAbilityPhase(this.scene, p.getBattlerIndex()); return this.scene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger); } @@ -385,8 +391,8 @@ export class Arena { return true; } - isMoveWeatherCancelled(move: Move) { - return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(move); + isMoveWeatherCancelled(user: Pokemon, move: Move) { + return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move); } isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move) { @@ -567,6 +573,12 @@ export class Arena { this.ignoreAbilities = ignoreAbilities; } + /** + * Applies each `ArenaTag` in this Arena, based on which side (self, enemy, or both) is passed in as a parameter + * @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply + * @param side {@linkcode ArenaTagSide} which side's arena tags to apply + * @param args array of parameters that the called upon tags may need + */ applyTagsForSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide, ...args: unknown[]): void { let tags = typeof tagType === "string" ? this.tags.filter(t => t.tagType === tagType) @@ -577,11 +589,28 @@ export class Arena { tags.forEach(t => t.apply(this, args)); } + /** + * Applies the specified tag to both sides (ie: both user and trainer's tag that match the Tag specified) + * by calling {@linkcode applyTagsForSide()} + * @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply + * @param args array of parameters that the called upon tags may need + */ applyTags(tagType: ArenaTagType | Constructor, ...args: unknown[]): void { this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); } - addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { + /** + * Adds a new tag to the arena + * @param tagType {@linkcode ArenaTagType} the tag being added + * @param turnCount How many turns the tag lasts + * @param sourceMove {@linkcode Moves} the move the tag came from, or `undefined` if not from a move + * @param sourceId The ID of the pokemon in play the tag came from (see {@linkcode BattleScene.getPokemonById}) + * @param side {@linkcode ArenaTagSide} which side(s) the tag applies to + * @param quiet If a message should be queued on screen to announce the tag being added + * @param targetIndex The {@linkcode BattlerIndex} of the target pokemon + * @returns `false` if there already exists a tag of this type in the Arena + */ + addTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { const existingTag = this.getTagOnSide(tagType, side); if (existingTag) { existingTag.onOverlap(this); @@ -594,6 +623,7 @@ export class Arena { return false; } + // creates a new tag object const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); if (newTag) { this.tags.push(newTag); @@ -607,6 +637,11 @@ export class Arena { return true; } + /** + * Attempts to get a tag from the Arena via {@linkcode getTagOnSide} that applies to both sides + * @param tagType The {@linkcode ArenaTagType} or {@linkcode ArenaTag} to get + * @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there + */ getTag(tagType: ArenaTagType | Constructor): ArenaTag | undefined { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } @@ -615,16 +650,35 @@ export class Arena { return !!this.getTag(tagType); } + /** + * Attempts to get a tag from the Arena from a specific side (the tag passed in has to either apply to both sides, or the specific side only) + * + * eg: `MIST` only applies to the user's side, while `MUD_SPORT` applies to both user and enemy side + * @param tagType The {@linkcode ArenaTagType} or {@linkcode ArenaTag} to get + * @param side The {@linkcode ArenaTagSide} to look at + * @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there + */ getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { return typeof(tagType) === "string" ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) : this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); } + /** + * Uses {@linkcode findTagsOnSide} to filter (using the parameter function) for specific tags that apply to both sides + * @param tagPredicate a function mapping {@linkcode ArenaTag}s to `boolean`s + * @returns array of {@linkcode ArenaTag}s from which the Arena's tags return true and apply to both sides + */ findTags(tagPredicate: (t: ArenaTag) => boolean): ArenaTag[] { return this.findTagsOnSide(tagPredicate, ArenaTagSide.BOTH); } + /** + * Returns specific tags from the arena that pass the `tagPredicate` function passed in as a parameter, and apply to the given side + * @param tagPredicate a function mapping {@linkcode ArenaTag}s to `boolean`s + * @param side The {@linkcode ArenaTagSide} to look at + * @returns array of {@linkcode ArenaTag}s from which the Arena's tags return `true` and apply to the given side + */ findTagsOnSide(tagPredicate: (t: ArenaTag) => boolean, side: ArenaTagSide): ArenaTag[] { return this.tags.filter(t => tagPredicate(t) && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 9fcbdd8dc6c..566eecbfeb6 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3,26 +3,24 @@ import BattleScene, { AnySound } from "../battle-scene"; import { Variant, VariantSet, variantColorCache } from "#app/data/variant"; import { variantData } from "#app/data/variant"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info"; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type"; import { getLevelTotalExp } from "../data/exp"; -import { Stat } from "../data/pokemon-stat"; -import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier } from "../modifier/modifier"; +import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; +import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier } from "../modifier/modifier"; import { PokeballType } from "../data/pokeball"; import { Gender } from "../data/gender"; import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims"; import { Status, StatusEffect, getRandomStatus } from "../data/status-effect"; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions"; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms"; -import { BattleStat } from "../data/battle-stat"; -import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag } from "../data/battler-tags"; +import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag } from "../data/battler-tags"; import { WeatherType } from "../data/weather"; -import { TempBattleStat } from "../data/temp-battle-stat"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr } from "../data/ability"; +import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "../data/ability"; import PokemonData from "../system/pokemon-data"; import { BattlerIndex } from "../battle"; import { Mode } from "../ui/ui"; @@ -40,7 +38,7 @@ import Overrides from "#app/overrides"; import i18next from "i18next"; import { speciesEggMoves } from "../data/egg-moves"; import { ModifierTier } from "../modifier/modifier-tier"; -import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; +import { applyChallenges, ChallengeType } from "#app/data/challenge"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; @@ -49,17 +47,18 @@ import { BerryType } from "#enums/berry-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { Challenges } from "#enums/challenges"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { FaintPhase } from "#app/phases/faint-phase.js"; -import { LearnMovePhase } from "#app/phases/learn-move-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase.js"; +import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; export enum FieldPosition { CENTER, @@ -119,6 +118,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public maskEnabled: boolean; public maskSprite: Phaser.GameObjects.Sprite | null; + public usedTMs: Moves[]; + private shinySparkle: Phaser.GameObjects.Sprite; constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) { @@ -133,9 +134,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } - const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); - const randAbilityIndex = Utils.randSeedInt(2); - this.species = species; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; @@ -146,6 +144,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined } else { // If abilityIndex is not provided, determine it based on species and hidden ability + const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); + const randAbilityIndex = Utils.randSeedInt(2); if (species.abilityHidden && hasHiddenAbility) { // If the species has a hidden ability and the hidden ability is present this.abilityIndex = 2; @@ -196,6 +196,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.fusionVariant = dataSource.fusionVariant || 0; this.fusionGender = dataSource.fusionGender; this.fusionLuck = dataSource.fusionLuck; + this.usedTMs = dataSource.usedTMs ?? []; } else { this.id = Utils.randSeedInt(4294967296); this.ivs = ivs || Utils.getIvsFromId(this.id); @@ -674,49 +675,139 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); } - getStat(stat: Stat): integer { + /** + * Retrieves the entire set of stats of the {@linkcode Pokemon}. + * @param bypassSummonData prefer actual stats (`true` by default) or in-battle overriden stats (`false`) + * @returns the numeric values of the {@linkcode Pokemon}'s stats + */ + getStats(bypassSummonData: boolean = true): number[] { + if (!bypassSummonData && this.summonData?.stats) { + return this.summonData.stats; + } + return this.stats; + } + + /** + * Retrieves the corresponding {@linkcode PermanentStat} of the {@linkcode Pokemon}. + * @param stat the desired {@linkcode PermanentStat} + * @param bypassSummonData prefer actual stats (`true` by default) or in-battle overridden stats (`false`) + * @returns the numeric value of the desired {@linkcode Stat} + */ + getStat(stat: PermanentStat, bypassSummonData: boolean = true): number { + if (!bypassSummonData && this.summonData && (this.summonData.stats[stat] !== 0)) { + return this.summonData.stats[stat]; + } return this.stats[stat]; } - getBattleStat(stat: Stat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer { - if (stat === Stat.HP) { - return this.getStat(Stat.HP); - } - const battleStat = (stat - 1) as BattleStat; - const statLevel = new Utils.IntegerHolder(this.summonData.battleStats[battleStat]); - if (opponent) { - if (isCritical) { - switch (stat) { - case Stat.ATK: - case Stat.SPATK: - statLevel.value = Math.max(statLevel.value, 0); - break; - case Stat.DEF: - case Stat.SPDEF: - statLevel.value = Math.min(statLevel.value, 0); - break; - } - } - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, false, statLevel); - if (move) { - applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel); + /** + * Writes the value to the corrseponding {@linkcode PermanentStat} of the {@linkcode Pokemon}. + * + * Note that this does nothing if {@linkcode value} is less than 0. + * @param stat the desired {@linkcode PermanentStat} to be overwritten + * @param value the desired numeric value + * @param bypassSummonData write to actual stats (`true` by default) or in-battle overridden stats (`false`) + */ + setStat(stat: PermanentStat, value: number, bypassSummonData: boolean = true): void { + if (value >= 0) { + if (!bypassSummonData && this.summonData) { + this.summonData.stats[stat] = value; + } else { + this.stats[stat] = value; } } - if (this.isPlayer()) { - this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), battleStat as integer as TempBattleStat, statLevel); + } + + /** + * Retrieves the entire set of in-battle stat stages of the {@linkcode Pokemon}. + * @returns the numeric values of the {@linkcode Pokemon}'s in-battle stat stages if available, a fresh stat stage array otherwise + */ + getStatStages(): number[] { + return this.summonData ? this.summonData.statStages : [ 0, 0, 0, 0, 0, 0, 0 ]; + } + + /** + * Retrieves the in-battle stage of the specified {@linkcode BattleStat}. + * @param stat the {@linkcode BattleStat} whose stage is desired + * @returns the stage of the desired {@linkcode BattleStat} if available, 0 otherwise + */ + getStatStage(stat: BattleStat): number { + return this.summonData ? this.summonData.statStages[stat - 1] : 0; + } + + /** + * Writes the value to the in-battle stage of the corresponding {@linkcode BattleStat} of the {@linkcode Pokemon}. + * + * Note that, if the value is not within a range of [-6, 6], it will be forced to the closest range bound. + * @param stat the {@linkcode BattleStat} whose stage is to be overwritten + * @param value the desired numeric value + */ + setStatStage(stat: BattleStat, value: number): void { + if (this.summonData) { + if (value >= -6) { + this.summonData.statStages[stat - 1] = Math.min(value, 6); + } else { + this.summonData.statStages[stat - 1] = Math.max(value, -6); + } } - const statValue = new Utils.NumberHolder(this.getStat(stat)); + } + + /** + * Retrieves the critical-hit stage considering the move used and the Pokemon + * who used it. + * @param source the {@linkcode Pokemon} who using the move + * @param move the {@linkcode Move} being used + * @returns the final critical-hit stage value + */ + getCritStage(source: Pokemon, move: Move): number { + const critStage = new Utils.IntegerHolder(0); + applyMoveAttrs(HighCritAttr, source, this, move, critStage); + this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage); + this.scene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage); + const bonusCrit = new Utils.BooleanHolder(false); + //@ts-ignore + if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. + if (bonusCrit.value) { + critStage.value += 1; + } + } + const critBoostTag = source.getTag(CritBoostTag); + if (critBoostTag) { + if (critBoostTag instanceof DragonCheerTag) { + critStage.value += critBoostTag.typesOnAdd.includes(Type.DRAGON) ? 2 : 1; + } else { + critStage.value += 2; + } + } + + console.log(`crit stage: +${critStage.value}`); + return critStage.value; + } + + /** + * Calculates and retrieves the final value of a stat considering any held + * items, move effects, opponent abilities, and whether there was a critical + * hit. + * @param stat the desired {@linkcode EffectiveStat} + * @param opponent the target {@linkcode Pokemon} + * @param move the {@linkcode Move} being used + * @param isCritical determines whether a critical hit has occurred or not (`false` by default) + * @returns the final in-battle value of a stat + */ + getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer { + const statValue = new Utils.NumberHolder(this.getStat(stat, false)); this.scene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue); const fieldApplied = new Utils.BooleanHolder(false); for (const pokemon of this.scene.getField(true)) { - applyFieldBattleStatMultiplierAbAttrs(FieldMultiplyBattleStatAbAttr, pokemon, stat, statValue, this, fieldApplied); + applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied); if (fieldApplied.value) { break; } } - applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, battleStat, statValue); - let ret = statValue.value * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value)); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue); + let ret = statValue.value * this.getStatStageMultiplier(stat, opponent, move, isCritical); + switch (stat) { case Stat.ATK: if (this.getTag(BattlerTagType.SLOW_START)) { @@ -763,24 +854,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!this.stats) { this.stats = [ 0, 0, 0, 0, 0, 0 ]; } - const baseStats = this.getSpeciesForm().baseStats.slice(0); - if (this.fusionSpecies) { - const fusionBaseStats = this.getFusionSpeciesForm().baseStats; - for (let s = 0; s < this.stats.length; s++) { + + // Get and manipulate base stats + const baseStats = this.getSpeciesForm(true).baseStats.slice(); + if (this.isFusion()) { + const fusionBaseStats = this.getFusionSpeciesForm(true).baseStats; + for (const s of PERMANENT_STATS) { baseStats[s] = Math.ceil((baseStats[s] + fusionBaseStats[s]) / 2); } } else if (this.scene.gameMode.isSplicedOnly) { - for (let s = 0; s < this.stats.length; s++) { + for (const s of PERMANENT_STATS) { baseStats[s] = Math.ceil(baseStats[s] / 2); } } - this.scene.applyModifiers(PokemonBaseStatModifier, this.isPlayer(), this, baseStats); - const stats = Utils.getEnumValues(Stat); - for (const s of stats) { - const isHp = s === Stat.HP; - const baseStat = baseStats[s]; - let value = Math.floor(((2 * baseStat + this.ivs[s]) * this.level) * 0.01); - if (isHp) { + this.scene.applyModifiers(BaseStatModifier, this.isPlayer(), this, baseStats); + + // Using base stats, calculate and store stats one by one + for (const s of PERMANENT_STATS) { + let value = Math.floor(((2 * baseStats[s] + this.ivs[s]) * this.level) * 0.01); + if (s === Stat.HP) { value = value + this.level + 10; if (this.hasAbility(Abilities.WONDER_GUARD, false, true)) { value = 1; @@ -801,7 +893,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { value = Math.max(Math[natureStatMultiplier.value > 1 ? "ceil" : "floor"](value * natureStatMultiplier.value), 1); } } - this.stats[s] = value; + + this.setStat(s, value); } } @@ -891,10 +984,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : this.moveset; // Overrides moveset based on arrays specified in overrides.ts - const overrideArray: Array = this.isPlayer() ? Overrides.MOVESET_OVERRIDE : Overrides.OPP_MOVESET_OVERRIDE; + let overrideArray: Moves | Array = this.isPlayer() ? Overrides.MOVESET_OVERRIDE : Overrides.OPP_MOVESET_OVERRIDE; + if (!Array.isArray(overrideArray)) { + overrideArray = [overrideArray]; + } if (overrideArray.length > 0) { + if (!this.isPlayer()) { + this.moveset = []; + } overrideArray.forEach((move: Moves, index: number) => { - const ppUsed = this.moveset[index]?.ppUsed || 0; + const ppUsed = this.moveset[index]?.ppUsed ?? 0; this.moveset[index] = new PokemonMove(move, Math.min(ppUsed, allMoves[move].pp)); }); } @@ -936,7 +1035,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) { levelMoves = this.getUnlockedEggMoves().concat(levelMoves); } - return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); + if (Array.isArray(this.usedTMs) && this.usedTMs.length > 0) { + levelMoves = this.usedTMs.filter(m => !levelMoves.includes(m)).concat(levelMoves); + } + levelMoves = levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); + return levelMoves; } /** @@ -953,6 +1056,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const teraType = this.getTeraType(); if (teraType !== Type.UNKNOWN) { types.push(teraType); + if (forDefend) { + return types; + } } } @@ -1210,6 +1316,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); } + /** + * Determines whether this Pokemon is prevented from running or switching due + * to effects from moves and/or abilities. + * @param trappedAbMessages `string[]` If defined, ability trigger messages + * (e.g. from Shadow Tag) are forwarded through this array. + * @param simulated `boolean` if `true`, applies abilities via simulated calls. + * @returns + */ + isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean { + if (this.isOfType(Type.GHOST)) { + return false; + } + + const trappedByAbility = new Utils.BooleanHolder(false); + const opposingField = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); + + opposingField.forEach(opponent => + applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated) + ); + + return (trappedByAbility.value || !!this.getTag(TrappedTag)); + } + /** * Calculates the type of a move when used by this Pokemon after * type-changing move and ability attributes have applied. @@ -1230,7 +1359,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Calculates the effectiveness of a move against the Pokémon. - * + * This includes modifiers from move and ability attributes. * @param source {@linkcode Pokemon} The attacking Pokémon. * @param move {@linkcode Move} The move being used by the attacking Pokémon. * @param ignoreAbility Whether to ignore abilities that might affect type effectiveness or immunity (defaults to `false`). @@ -1250,10 +1379,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 1); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); - if (this.getTypes().find(t => move.isTypeImmune(source, this, t))) { + if (this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t))) { typeMultiplier.value = 0; } + if (this.getTag(TarShotTag) && (this.getMoveType(move) === Type.FIRE)) { + typeMultiplier.value *= 2; + } + const cancelledHolder = cancelled ?? new Utils.BooleanHolder(false); if (!ignoreAbility) { applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); @@ -1276,11 +1409,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + // Apply Tera Shell's effect to attacks after all immunities are accounted for + if (!ignoreAbility && move.category !== MoveCategory.STATUS) { + applyPreDefendAbAttrs(FullHpResistTypeAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); + } + return (!cancelledHolder.value ? typeMultiplier.value : 0) as TypeDamageMultiplier; } /** - * Calculates the type effectiveness multiplier for an attack type + * Calculates the move's type effectiveness multiplier based on the target's type/s. * @param moveType {@linkcode Type} the type of the move being used * @param source {@linkcode Pokemon} the Pokemon using the move * @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks) @@ -1304,22 +1442,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } let multiplier = types.map(defType => { + const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType)); + applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier); if (source) { const ignoreImmunity = new Utils.BooleanHolder(false); if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) { applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType); } if (ignoreImmunity.value) { - return 1; + if (multiplier.value === 0) { + return 1; + } } const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { - return 1; + if (multiplier.value === 0) { + return 1; + } } } - const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType)); - applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier); return multiplier.value; }).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; @@ -1345,7 +1487,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const types = this.getTypes(true); const enemyTypes = opponent.getTypes(true, true); /** Is this Pokemon faster than the opponent? */ - const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, opponent) : this.getStat(Stat.SPD)) >= opponent.getBattleStat(Stat.SPD, this); + const outspeed = (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false)) >= opponent.getEffectiveStat(Stat.SPD, this); /** * Based on how effective this Pokemon's types are offensively against the opponent's types. * This score is increased by 25 percent if this Pokemon is faster than the opponent. @@ -1501,13 +1643,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Function that tries to set a Pokemon shiny based on the trainer's trainer ID and secret ID + * Function that tries to set a Pokemon shiny based on the trainer's trainer ID and secret ID. * Endless Pokemon in the end biome are unable to be set to shiny * - * The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID - * F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits - * The XOR of E and F are then compared to the thresholdOverride (default case 32) to see whether or not to generate a shiny - * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance + * The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID. + * F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits. + * The XOR of E and F are then compared to the {@linkcode shinyThreshold} (or {@linkcode thresholdOverride} if set) to see whether or not to generate a shiny. + * The base shiny odds are {@linkcode baseShinyChance} / 65536 + * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) * @returns true if the Pokemon has been set as a shiny, false otherwise */ trySetShiny(thresholdOverride?: integer): boolean { @@ -1522,7 +1665,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId; const F = rand1 ^ rand2; - const shinyThreshold = new Utils.IntegerHolder(32); + /** `64/65536 -> 1/1024` */ + const baseShinyChance = 64; + const shinyThreshold = new Utils.IntegerHolder(baseShinyChance); if (thresholdOverride === undefined) { if (this.scene.eventManager.isEventActive()) { shinyThreshold.value *= this.scene.eventManager.getShinyMultiplier(); @@ -1535,9 +1680,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.shiny = (E ^ F) < shinyThreshold.value; - if ((E ^ F) < 32) { - console.log("REAL SHINY!!"); - } if (this.shiny) { this.initShinySparkle(); @@ -1597,7 +1739,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }; this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true); - this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? this.fusionSpecies.ability2 ? 2 : 1 : this.fusionSpecies.ability2 ? randAbilityIndex : 0); + this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 : this.fusionSpecies.ability2 !== this.fusionSpecies.ability1 ? randAbilityIndex : 0); this.fusionShiny = this.shiny; this.fusionVariant = this.variant; @@ -1724,7 +1866,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]); movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttrOnHit) ? 0.5 : 1)]); // Trainers get a weight bump to stat buffing moves - movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => a.levels > 1 && a.selfTarget) ? 1.25 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatStageChangeAttr).some(a => a.stages > 1 && a.selfTarget) ? 1.25 : 1)]); // Trainers get a weight decrease to multiturn moves movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(ChargeAttr) || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1)]); } @@ -1736,8 +1878,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].category === MoveCategory.STATUS ? 1 : Math.max(Math.min(allMoves[m[0]].power/maxPower, 1), 0.5))]); // Weight damaging moves against the lower stat - const worseCategory: MoveCategory = this.stats[Stat.ATK] > this.stats[Stat.SPATK] ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL; - const statRatio = worseCategory === MoveCategory.PHYSICAL ? this.stats[Stat.ATK]/this.stats[Stat.SPATK] : this.stats[Stat.SPATK]/this.stats[Stat.ATK]; + const atk = this.getStat(Stat.ATK); + const spAtk = this.getStat(Stat.SPATK); + const worseCategory: MoveCategory = atk > spAtk ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL; + const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk; movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1)]); let weightMultiplier = 0.9; // The higher this is the more the game weights towards higher level moves. At 0 all moves are equal weight. @@ -1923,6 +2067,48 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this instanceof PlayerPokemon ? this.scene.getPlayerField() : this.scene.getEnemyField(); } + /** + * Calculates the stat stage multiplier of the user against an opponent. + * + * Note that this does not apply to evasion or accuracy + * @see {@linkcode getAccuracyMultiplier} + * @param stat the desired {@linkcode EffectiveStat} + * @param opponent the target {@linkcode Pokemon} + * @param move the {@linkcode Move} being used + * @param isCritical determines whether a critical hit has occurred or not (`false` by default) + * @return the stat stage multiplier to be used for effective stat calculation + */ + getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): number { + const statStage = new Utils.IntegerHolder(this.getStatStage(stat)); + const ignoreStatStage = new Utils.BooleanHolder(false); + + if (opponent) { + if (isCritical) { + switch (stat) { + case Stat.ATK: + case Stat.SPATK: + statStage.value = Math.max(statStage.value, 0); + break; + case Stat.DEF: + case Stat.SPDEF: + statStage.value = Math.min(statStage.value, 0); + break; + } + } + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, false, stat, ignoreStatStage); + if (move) { + applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage); + } + } + + if (!ignoreStatStage.value) { + const statStageMultiplier = new Utils.NumberHolder(Math.max(2, 2 + statStage.value) / Math.max(2, 2 - statStage.value)); + this.scene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), stat, statStageMultiplier); + return Math.min(statStageMultiplier.value, 4); + } + return 1; + } + /** * Calculates the accuracy multiplier of the user against a target. * @@ -1939,34 +2125,38 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return 1; } - const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]); - const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); + const userAccStage = new Utils.IntegerHolder(this.getStatStage(Stat.ACC)); + const targetEvaStage = new Utils.IntegerHolder(target.getStatStage(Stat.EVA)); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, false, userAccuracyLevel); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, false, targetEvasionLevel); - applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, false, targetEvasionLevel); - applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel); - this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel); + const ignoreAccStatStage = new Utils.BooleanHolder(false); + const ignoreEvaStatStage = new Utils.BooleanHolder(false); + + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage); + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, this, null, false, Stat.EVA, ignoreEvaStatStage); + applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, target, sourceMove, ignoreEvaStatStage); + + this.scene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage); + + userAccStage.value = ignoreAccStatStage.value ? 0 : Math.min(userAccStage.value, 6); + targetEvaStage.value = ignoreEvaStatStage.value ? 0 : targetEvaStage.value; if (target.findTag(t => t instanceof ExposedTag)) { - targetEvasionLevel.value = Math.min(0, targetEvasionLevel.value); + targetEvaStage.value = Math.min(0, targetEvaStage.value); } const accuracyMultiplier = new Utils.NumberHolder(1); - if (userAccuracyLevel.value !== targetEvasionLevel.value) { - accuracyMultiplier.value = userAccuracyLevel.value > targetEvasionLevel.value - ? (3 + Math.min(userAccuracyLevel.value - targetEvasionLevel.value, 6)) / 3 - : 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6)); + if (userAccStage.value !== targetEvaStage.value) { + accuracyMultiplier.value = userAccStage.value > targetEvaStage.value + ? (3 + Math.min(userAccStage.value - targetEvaStage.value, 6)) / 3 + : 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6)); } - applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, false, sourceMove); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, Stat.ACC, accuracyMultiplier, false, sourceMove); const evasionMultiplier = new Utils.NumberHolder(1); - applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, target, Stat.EVA, evasionMultiplier); - accuracyMultiplier.value /= evasionMultiplier.value; - - return accuracyMultiplier.value; + return accuracyMultiplier.value / evasionMultiplier.value; } /** @@ -2053,22 +2243,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (critOnly.value || critAlways) { isCritical = true; } else { - const critLevel = new Utils.IntegerHolder(0); - applyMoveAttrs(HighCritAttr, source, this, move, critLevel); - this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critLevel); - this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); - const bonusCrit = new Utils.BooleanHolder(false); - //@ts-ignore - if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. - if (bonusCrit.value) { - critLevel.value += 1; - } - } - if (source.getTag(BattlerTagType.CRIT_BOOST)) { - critLevel.value += 2; - } - console.log(`crit stage: +${critLevel.value}`); - const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))]; + const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))]; isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance); if (Overrides.NEVER_CRIT_OVERRIDE) { isCritical = false; @@ -2082,8 +2257,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false; } } - const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); - const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); + const sourceAtk = new Utils.IntegerHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); + const targetDef = new Utils.IntegerHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier); const screenMultiplier = new Utils.NumberHolder(1); @@ -2122,7 +2297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!isTypeImmune) { const levelMultiplier = (2 * source.level / 5 + 2); - const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100); + const randomMultiplier = (this.randSeedIntRange(85, 100) / 100); damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier @@ -2494,27 +2669,53 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param source {@linkcode Pokemon} the pokemon whose stats/Tags are to be passed on from, ie: the Pokemon using Baton Pass */ transferSummon(source: Pokemon): void { - const battleStats = Utils.getEnumValues(BattleStat); - for (const stat of battleStats) { - this.summonData.battleStats[stat] = source.summonData.battleStats[stat]; + // Copy all stat stages + for (const s of BATTLE_STATS) { + const sourceStage = source.getStatStage(s); + if ((this instanceof PlayerPokemon) && (sourceStage === 6)) { + this.scene.validateAchv(achvs.TRANSFER_MAX_STAT_STAGE); + } + this.setStatStage(s, sourceStage); } + for (const tag of source.summonData.tags) { - - // bypass those can not be passed via Baton Pass - const excludeTagTypes = new Set([BattlerTagType.DROWSY, BattlerTagType.INFATUATED, BattlerTagType.FIRE_BOOST]); - - if (excludeTagTypes.has(tag.tagType)) { + if (!tag.isBatonPassable) { continue; } this.summonData.tags.push(tag); } - if (this instanceof PlayerPokemon && source.summonData.battleStats.find(bs => bs === 6)) { - this.scene.validateAchv(achvs.TRANSFER_MAX_BATTLE_STAT); - } + this.updateInfo(); } + /** + * Gets whether the given move is currently disabled for this Pokemon. + * + * @param {Moves} moveId {@linkcode Moves} ID of the move to check + * @returns {boolean} `true` if the move is disabled for this Pokemon, otherwise `false` + * + * @see {@linkcode MoveRestrictionBattlerTag} + */ + isMoveRestricted(moveId: Moves): boolean { + return this.getRestrictingTag(moveId) !== null; + } + + /** + * Gets the {@link MoveRestrictionBattlerTag} that is restricting a move, if it exists. + * + * @param {Moves} moveId {@linkcode Moves} ID of the move to check + * @returns {MoveRestrictionBattlerTag | null} the first tag on this Pokemon that restricts the move, or `null` if the move is not restricted. + */ + getRestrictingTag(moveId: Moves): MoveRestrictionBattlerTag | null { + for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { + if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId)) { + return tag as MoveRestrictionBattlerTag; + } + } + return null; + } + getMoveHistory(): TurnMove[] { return this.battleSummonData.moveHistory; } @@ -2589,7 +2790,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.fusionFaintCry(callback); } - const key = `cry/${this.getSpeciesForm().getCryKey(this.formIndex)}`; + const key = `cry/${this.species.getCryKey(this.formIndex)}`; //eslint-disable-next-line @typescript-eslint/no-unused-vars let i = 0; let rate = 0.85; @@ -2647,7 +2848,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } private fusionFaintCry(callback: Function): void { - const key = `cry/${this.getSpeciesForm().getCryKey(this.formIndex)}`; + const key = `cry/${this.species.getCryKey(this.formIndex)}`; let i = 0; let rate = 0.85; const cry = this.scene.playSound(key, { rate: rate }) as AnySound; @@ -2655,7 +2856,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const tintSprite = this.getTintSprite(); let duration = cry.totalDuration * 1000; - const fusionCryKey = `cry/${this.getFusionSpeciesForm().getCryKey(this.fusionFormIndex)}`; + const fusionCryKey = `cry/${this.fusionSpecies?.getCryKey(this.fusionFormIndex)}`; let fusionCry = this.scene.playSound(fusionCryKey, { rate: rate }) as AnySound; fusionCry.stop(); duration = Math.min(duration, fusionCry.totalDuration * 1000); @@ -2754,6 +2955,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const types = this.getTypes(true, true); + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + if (sourcePokemon && sourcePokemon !== this && this.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { + return false; + } + switch (effect) { case StatusEffect.POISON: case StatusEffect.TOXIC: @@ -3261,12 +3467,30 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { fusionCanvas.remove(); } + /** + * Generates a random number using the current battle's seed, or the global seed if `this.scene.currentBattle` is falsy + * + * This calls either {@linkcode BattleScene.randBattleSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle-scene.ts` + * which calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts` + * which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`, + * or it directly calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` if there is no current battle + * + * @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) + */ randSeedInt(range: integer, min: integer = 0): integer { return this.scene.currentBattle ? this.scene.randBattleSeedInt(range, min) : Utils.randSeedInt(range, min); } + /** + * Generates a random number using the current battle's seed, or the global seed if `this.scene.currentBattle` is falsy + * @param min The minimum integer to generate + * @param max The maximum integer to generate + * @returns a random integer between {@linkcode min} and {@linkcode max} inclusive + */ randSeedIntRange(min: integer, max: integer): integer { return this.randSeedInt((max - min) + 1, min); } @@ -3600,6 +3824,9 @@ export class PlayerPokemon extends Pokemon { newPokemon.moveset = this.moveset.slice(); newPokemon.moveset = this.copyMoveset(); newPokemon.luck = this.luck; + newPokemon.metLevel = this.metLevel; + newPokemon.metBiome = this.metBiome; + newPokemon.metSpecies = this.metSpecies; newPokemon.fusionSpecies = this.fusionSpecies; newPokemon.fusionFormIndex = this.fusionFormIndex; newPokemon.fusionAbilityIndex = this.fusionAbilityIndex; @@ -3679,16 +3906,17 @@ export class PlayerPokemon extends Pokemon { this.scene.gameData.gameStats.pokemonFused++; // Store the average HP% that each Pokemon has - const newHpPercent = ((pokemon.hp / pokemon.stats[Stat.HP]) + (this.hp / this.stats[Stat.HP])) / 2; + const maxHp = this.getMaxHp(); + const newHpPercent = ((pokemon.hp / pokemon.getMaxHp()) + (this.hp / maxHp)) / 2; this.generateName(); this.calculateStats(); // Set this Pokemon's HP to the average % of both fusion components - this.hp = Math.round(this.stats[Stat.HP] * newHpPercent); + this.hp = Math.round(maxHp * newHpPercent); if (!this.isFainted()) { // If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum - this.hp = Math.min(this.hp, this.stats[Stat.HP]); + this.hp = Math.min(this.hp, maxHp); this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two } else if (!pokemon.isFainted()) { // If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero @@ -4138,7 +4366,7 @@ export class EnemyPokemon extends Pokemon { //console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))); } - damage = hpRemainder + Math.round(segmentSize * segmentsBypassed); + damage = Utils.toDmgValue(this.hp - hpThreshold + segmentSize * segmentsBypassed); clearedBossSegmentIndex = s - segmentsBypassed; } break; @@ -4181,43 +4409,40 @@ export class EnemyPokemon extends Pokemon { handleBossSegmentCleared(segmentIndex: integer): void { while (segmentIndex - 1 < this.bossSegmentIndex) { - let boostedStat = BattleStat.RAND; + // Filter out already maxed out stat stages and weigh the rest based on existing stats + const leftoverStats = EFFECTIVE_STATS.filter((s: EffectiveStat) => this.getStatStage(s) < 6); + const statWeights = leftoverStats.map((s: EffectiveStat) => this.getStat(s, false)); - const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3); - const statWeights = new Array().fill(battleStats.length).filter((bs: BattleStat) => this.summonData.battleStats[bs] < 6).map((bs: BattleStat) => this.getStat(bs + 1)); - const statThresholds: integer[] = []; + let boostedStat: EffectiveStat; + const statThresholds: number[] = []; let totalWeight = 0; - for (const bs of battleStats) { - totalWeight += statWeights[bs]; + + for (const i in statWeights) { + totalWeight += statWeights[i]; statThresholds.push(totalWeight); } + // Pick a random stat from the leftover stats to increase its stages const randInt = Utils.randSeedInt(totalWeight); - - for (const bs of battleStats) { - if (randInt < statThresholds[bs]) { - boostedStat = bs; + for (const i in statThresholds) { + if (randInt < statThresholds[i]) { + boostedStat = leftoverStats[i]; break; } } - let statLevels = 1; + let stages = 1; - switch (segmentIndex) { - case 1: - if (this.bossSegments >= 3) { - statLevels++; - } - break; - case 2: - if (this.bossSegments >= 5) { - statLevels++; - } - break; + // increase the boost if the boss has at least 3 segments and we passed last shield + if (this.bossSegments >= 3 && this.bossSegmentIndex === 1) { + stages++; + } + // increase the boost if the boss has at least 5 segments and we passed the second to last shield + if (this.bossSegments >= 5 && this.bossSegmentIndex === 2) { + stages++; } - this.scene.unshiftPhase(new StatChangePhase(this.scene, this.getBattlerIndex(), true, [ boostedStat ], statLevels, true, true)); - + this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.getBattlerIndex(), true, [ boostedStat! ], stages, true, true)); this.bossSegmentIndex--; } } @@ -4250,17 +4475,29 @@ export class EnemyPokemon extends Pokemon { return BattlerIndex.ENEMY + this.getFieldIndex(); } - addToParty(pokeballType: PokeballType) { + /** + * Add a new pokemon to the player's party (at `slotIndex` if set). + * @param pokeballType the type of pokeball the pokemon was caught with + * @param slotIndex an optional index to place the pokemon in the party + * @returns the pokemon that was added or null if the pokemon could not be added + */ + addToParty(pokeballType: PokeballType, slotIndex: number = -1) { const party = this.scene.getParty(); let ret: PlayerPokemon | null = null; - if (party.length < 6) { + if (party.length < PLAYER_PARTY_MAX_SIZE) { this.pokeball = pokeballType; this.metLevel = this.level; this.metBiome = this.scene.arena.biomeType; this.metSpecies = this.species.speciesId; const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.variant, this.ivs, this.nature, this); - party.push(newPokemon); + + if (Utils.isBetween(slotIndex, 0, PLAYER_PARTY_MAX_SIZE - 1)) { + party.splice(slotIndex, 0, newPokemon); + } else { + party.push(newPokemon); + } + ret = newPokemon; this.scene.triggerPokemonFormChange(newPokemon, SpeciesFormChangeActiveTrigger, true); } @@ -4274,7 +4511,7 @@ export interface TurnMove { targets?: BattlerIndex[]; result: MoveResult; virtual?: boolean; - turn?: integer; + turn?: number; } export interface QueuedMove { @@ -4286,17 +4523,16 @@ export interface QueuedMove { export interface AttackMoveResult { move: Moves; result: DamageResult; - damage: integer; + damage: number; critical: boolean; - sourceId: integer; + sourceId: number; sourceBattlerIndex: BattlerIndex; } export class PokemonSummonData { - public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ]; + /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ + public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ]; public moveQueue: QueuedMove[] = []; - public disabledMove: Moves = Moves.NONE; - public disabledTurns: integer = 0; public tags: BattlerTag[] = []; public abilitySuppressed: boolean = false; public abilitiesApplied: Abilities[] = []; @@ -4306,14 +4542,14 @@ export class PokemonSummonData { public ability: Abilities = Abilities.NONE; public gender: Gender; public fusionGender: Gender; - public stats: integer[]; + public stats: number[] = [ 0, 0, 0, 0, 0, 0 ]; public moveset: (PokemonMove | null)[]; // If not initialized this value will not be populated from save data. public types: Type[] = []; } export class PokemonBattleData { - public hitCount: integer = 0; + public hitCount: number = 0; public endured: boolean = false; public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; @@ -4322,21 +4558,23 @@ export class PokemonBattleData { export class PokemonBattleSummonData { /** The number of turns the pokemon has passed since entering the battle */ - public turnCount: integer = 1; + public turnCount: number = 1; /** The list of moves the pokemon has used since entering the battle */ public moveHistory: TurnMove[] = []; } export class PokemonTurnData { - public flinched: boolean; - public acted: boolean; - public hitCount: integer; - public hitsLeft: integer; - public damageDealt: integer = 0; - public currDamageDealt: integer = 0; - public damageTaken: integer = 0; + public flinched: boolean = false; + public acted: boolean = false; + public hitCount: number; + public hitsLeft: number; + public damageDealt: number = 0; + public currDamageDealt: number = 0; + public damageTaken: number = 0; public attacksReceived: AttackMoveResult[] = []; public order: number; + public statStagesIncreased: boolean = false; + public statStagesDecreased: boolean = false; } export enum AiType { @@ -4375,7 +4613,7 @@ export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | Hit * It links to {@linkcode Move} class via the move ID. * Compared to {@linkcode Move}, this class also tracks if a move has received. * PP Ups, amount of PP used, and things like that. - * @see {@linkcode isUsable} - checks if move is disabled, out of PP, or not implemented. + * @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented. * @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID. * @see {@linkcode usePp} - removes a point of PP from the move. * @see {@linkcode getMovePp} - returns amount of PP a move currently has. @@ -4395,11 +4633,25 @@ export class PokemonMove { this.virtual = !!virtual; } - isUsable(pokemon: Pokemon, ignorePp?: boolean): boolean { - if (this.moveId && pokemon.summonData?.disabledMove === this.moveId) { + /** + * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. + * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. + * + * @param {Pokemon} pokemon {@linkcode Pokemon} that would be using this move + * @param {boolean} ignorePp If `true`, skips the PP check + * @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) + * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. + */ + isUsable(pokemon: Pokemon, ignorePp?: boolean, ignoreRestrictionTags?: boolean): boolean { + if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) { return false; } - return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1) && !this.getMove().name.endsWith(" (N)"); + + if (this.getMove().name.endsWith(" (N)")) { + return false; + } + + return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1); } getMove(): Move { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 02827d0d69d..326ef0edefb 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -357,7 +357,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(Math.random() * newSpeciesPool.length)]) + ? getPokemonSpecies(newSpeciesPool[Math.floor(Utils.randSeedInt(newSpeciesPool.length))]) : template.isSameSpecies(index) && index > offset ? getPokemonSpecies(battle.enemyParty[offset].species.getTrainerSpeciesForLevel(level, false, template.getStrength(offset), this.scene.currentBattle.waveIndex)) : this.genNewPartyMemberSpecies(level, strength); diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 652e3d58062..bb3cfcbeb3b 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -16,7 +16,7 @@ import { getIconForLatestInput, swap, } from "#app/configs/inputs/configHandler"; import BattleScene from "./battle-scene"; -import {SettingGamepad} from "#app/system/settings/settings-gamepad.js"; +import {SettingGamepad} from "#app/system/settings/settings-gamepad"; import {SettingKeyboard} from "#app/system/settings/settings-keyboard"; import TouchControl from "#app/touch-controls"; import { Button } from "#enums/buttons"; diff --git a/src/interfaces/locales.ts b/src/interfaces/locales.ts index 5f7c52100c1..4405095e0fe 100644 --- a/src/interfaces/locales.ts +++ b/src/interfaces/locales.ts @@ -37,8 +37,7 @@ export interface ModifierTypeTranslationEntries { ModifierType: { [key: string]: ModifierTypeTranslationEntry }, SpeciesBoosterItem: { [key: string]: ModifierTypeTranslationEntry }, AttackTypeBoosterItem: SimpleTranslationEntries, - TempBattleStatBoosterItem: SimpleTranslationEntries, - TempBattleStatBoosterStatName: SimpleTranslationEntries, + TempStatStageBoosterItem: SimpleTranslationEntries, BaseStatBoosterItem: SimpleTranslationEntries, EvolutionItem: SimpleTranslationEntries, FormChangeItem: SimpleTranslationEntries, diff --git a/src/loading-scene.ts b/src/loading-scene.ts index b086b0cb002..6de441fb162 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -7,15 +7,15 @@ import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme"; import { isMobile } from "./touch-controls"; import * as Utils from "./utils"; import { initI18n } from "./plugins/i18n"; -import {initPokemonPrevolutions} from "#app/data/pokemon-evolutions"; -import {initBiomes} from "#app/data/biomes"; -import {initEggMoves} from "#app/data/egg-moves"; -import {initPokemonForms} from "#app/data/pokemon-forms"; -import {initSpecies} from "#app/data/pokemon-species"; -import {initMoves} from "#app/data/move"; -import {initAbilities} from "#app/data/ability"; -import {initAchievements} from "#app/system/achv"; -import {initTrainerTypeDialogue} from "#app/data/dialogue"; +import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions"; +import { initBiomes } from "#app/data/biomes"; +import { initEggMoves } from "#app/data/egg-moves"; +import { initPokemonForms } from "#app/data/pokemon-forms"; +import { initSpecies } from "#app/data/pokemon-species"; +import { initMoves } from "#app/data/move"; +import { initAbilities } from "#app/data/ability"; +import { initAchievements } from "#app/system/achv"; +import { initTrainerTypeDialogue } from "#app/data/dialogue"; import { initChallenges } from "./data/challenge"; import i18next from "i18next"; import { initStatsKeys } from "./ui/game-stats-ui-handler"; @@ -41,8 +41,6 @@ export class LoadingScene extends SceneBase { this.loadImage("loading_bg", "arenas"); this.loadImage("logo", ""); - // this.loadImage("pride-update", "events"); - this.loadImage("august-variant-update", "events"); // Load menu images this.loadAtlas("bg", "ui"); @@ -80,6 +78,7 @@ export class LoadingScene extends SceneBase { this.loadAtlas("overlay_hp_boss", "ui"); this.loadImage("overlay_exp", "ui"); this.loadImage("icon_owned", "ui"); + this.loadImage("icon_egg_move", "ui"); this.loadImage("ability_bar_left", "ui"); this.loadImage("bgm_bar", "ui"); this.loadImage("party_exp_bar", "ui"); @@ -100,6 +99,8 @@ export class LoadingScene extends SceneBase { this.loadImage("ha_capsule", "ui", "ha_capsule.png"); this.loadImage("champion_ribbon", "ui", "champion_ribbon.png"); this.loadImage("icon_spliced", "ui"); + this.loadImage("icon_lock", "ui", "icon_lock.png"); + this.loadImage("icon_stop", "ui", "icon_stop.png"); this.loadImage("icon_tera", "ui"); this.loadImage("type_tera", "ui"); this.loadAtlas("type_bgs", "ui"); @@ -164,6 +165,7 @@ export class LoadingScene extends SceneBase { this.loadImage("saving_icon", "ui"); this.loadImage("discord", "ui"); this.loadImage("google", "ui"); + this.loadImage("settings_icon", "ui"); this.loadImage("default_bg", "arenas"); // Load arena images @@ -246,7 +248,12 @@ export class LoadingScene extends SceneBase { } else { this.loadAtlas("types", ""); } - + const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es", "pt-BR", "zh-CN"]; + if (lang && availableLangs.includes(lang)) { + this.loadImage("egg-update_"+lang, "events"); + } else { + this.loadImage("egg-update_en", "events"); + } this.loadAtlas("statuses", ""); this.loadAtlas("categories", ""); @@ -267,6 +274,7 @@ export class LoadingScene extends SceneBase { this.loadImage("gacha_knob", "egg"); this.loadImage("egg_list_bg", "ui"); + this.loadImage("egg_summary_bg", "ui"); this.loadImage("end_m", "cg"); this.loadImage("end_f", "cg"); diff --git a/src/locales/ca_ES/arena-flyout.json b/src/locales/ca_ES/arena-flyout.json index 9e26dfeeb6e..21a02c789e8 100644 --- a/src/locales/ca_ES/arena-flyout.json +++ b/src/locales/ca_ES/arena-flyout.json @@ -1 +1,40 @@ -{} \ No newline at end of file +{ + "activeBattleEffects": "Efectes d'Arena Activa", + "player": "Jugador", + "neutral": "Neutre", + "enemy": "Enemic", + "sunny": "Assolellat", + "rain": "Plujós", + "sandstorm": "Tempesta Sorra", + "hail": "Calamarsa", + "snow": "Neu", + "fog": "Boira", + "heavyRain": "Diluvi", + "harshSun": "Sol Abrasador", + "strongWinds": "Vents Forts", + "misty": "Camp de Boira", + "electric": "Camp Elèctric", + "grassy": "Camp d'Herba", + "psychic": "Camp Psíquic", + "mudSport": "Xipollejo Fang", + "waterSport": "Hidrorraig", + "spikes": "Pues", + "toxicSpikes": "Pues Tòxiques", + "mist": "Boirina", + "futureSight": "Premonició", + "doomDesire": "Desig Ocult", + "wish": "Desig", + "stealthRock": "Trampa Roques", + "stickyWeb": "Xarxa Viscosa", + "trickRoom": "Espai Rar", + "gravity": "Gravetat", + "reflect": "Reflex", + "lightScreen": "Pantalla de Llum", + "auroraVeil": "Vel Aurora", + "quickGuard": "Anticipi", + "wideGuard": "Vasta Guàrdia", + "matBlock": "Escut Tatami", + "craftyShield": "Truc Defensa", + "tailwind": "Vent Afí", + "happyHour": "Paga Extra" +} diff --git a/src/locales/ca_ES/biome.json b/src/locales/ca_ES/biome.json index 9e26dfeeb6e..e570195e4c8 100644 --- a/src/locales/ca_ES/biome.json +++ b/src/locales/ca_ES/biome.json @@ -1 +1,38 @@ -{} \ No newline at end of file +{ + "unknownLocation": "En algun lloc que no recordes", + "TOWN": "Poble", + "PLAINS": "Vall", + "GRASS": "Camp", + "TALL_GRASS": "Herba Alta", + "METROPOLIS": "Metròpoli", + "FOREST": "Bosc", + "SEA": "Mar", + "SWAMP": "Pantà", + "BEACH": "Platja", + "LAKE": "Llac", + "SEABED": "Fons Marí", + "MOUNTAIN": "Muntanya", + "BADLANDS": "Badlands", + "CAVE": "Cova", + "DESERT": "Desert", + "ICE_CAVE": "Cova Gelada", + "MEADOW": "Prat", + "POWER_PLANT": "Planta d'Energia", + "VOLCANO": "Volcà", + "GRAVEYARD": "Cementiri", + "DOJO": "Dojo", + "FACTORY": "Fàbrica", + "RUINS": "Ruïnes Antigues", + "WASTELAND": "Terra Erma", + "ABYSS": "Avenc", + "SPACE": "Espai", + "CONSTRUCTION_SITE": "Obra", + "JUNGLE": "Jungla", + "FAIRY_CAVE": "Cova de Fades", + "TEMPLE": "Temple", + "SLUM": "Suburbi", + "SNOWY_FOREST": "Bosc Nevat", + "ISLAND": "Illa", + "LABORATORY": "Laboratori", + "END": "???" +} diff --git a/src/locales/ca_ES/common.json b/src/locales/ca_ES/common.json index 9e26dfeeb6e..8fd27cf486f 100644 --- a/src/locales/ca_ES/common.json +++ b/src/locales/ca_ES/common.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "start": "Començar", + "luckIndicator": "Sort:", + "shinyOnHover": "Variocolor", + "commonShiny": "Comú", + "rareShiny": "Rar", + "epicShiny": "Èpica" +} diff --git a/src/locales/ca_ES/menu.json b/src/locales/ca_ES/menu.json index 9e26dfeeb6e..42b0847b2f2 100644 --- a/src/locales/ca_ES/menu.json +++ b/src/locales/ca_ES/menu.json @@ -1 +1,55 @@ -{} \ No newline at end of file +{ + "cancel": "Cancel-la", + "continue": "Continuar", + "dailyRun": "Repte Diari (Beta)", + "loadGame": "Carregar Partida", + "newGame": "Nova Partida", + "settings": "Opcions", + "selectGameMode": "Trieu un mode de joc", + "logInOrCreateAccount": "Inicieu sessió o creeu un compte per començar. No cal correu electrònic!", + "username": "Usuari", + "password": "Contrasenya", + "login": "Iniciar Sessió", + "orUse": "O Usa", + "register": "Registrar-se", + "emptyUsername": "L'usuari no pot estar buit", + "invalidLoginUsername": "L'usuari no és vàlid", + "invalidRegisterUsername": "L'usuari només pot contenir lletres, números i guions baixos", + "invalidLoginPassword": "La contrasenya no és vàlida", + "invalidRegisterPassword": "La Contrasenya ha de tenir 6 o més caràcters", + "usernameAlreadyUsed": "L'usuari ja està en ús", + "accountNonExistent": "L'usuari no existeix", + "unmatchingPassword": "La contrasenya no coincideix", + "passwordNotMatchingConfirmPassword": "La contrasenya ha de coincidir amb la contrasenya de confirmació", + "confirmPassword": "Confirmeu la Contrasenya", + "registrationAgeWarning": "En registrar-te, confirmes que tens 13 anys o més.", + "backToLogin": "Torna a Iniciar Sessió", + "failedToLoadSaveData": "No s'han pogut carregar les dades desades. Torneu a carregar la pàgina.\nSi això continua, comproveu #announcements a Discord.", + "sessionSuccess": "Sessió carregada amb èxit.", + "failedToLoadSession": "No s'han pogut carregar les dades de la sessió.\nÉs possible que estiguin malmeses.", + "boyOrGirl": "Ets Nen o Nena?", + "evolving": "Que?\n{{pokemonName}} està evolucionant!", + "stoppedEvolving": "Prou?\nL'evolució de {{pokemonName}} s'ha aturat!", + "pauseEvolutionsQuestion": "Vols aturar les evolucions de {{pokémon Name}}?\nSempre poden ser activades des de la pantalla del teu equip.", + "evolutionsPaused": "L'evolució s'ha posat en pausa per a ", + "evolutionDone": "Enhorabona!\n{{pokemonName}} ha evolucionat a {{evolvedPokemonName}}!", + "dailyRankings": "Rànquings Diaris", + "weeklyRankings": "Rànquings Setmanals", + "noRankings": "Sense Rànquings", + "positionIcon": "#", + "usernameScoreboard": "Usuari", + "score": "Puntuació", + "wave": "Onada", + "loading": "Carregant…", + "loadingAsset": "Carregant actius: {{assetName}}", + "playersOnline": "Jugadors en Línia", + "yes":"sí", + "no":"No", + "disclaimer": "AVÍS", + "disclaimerDescription": "Aquest joc encara no s'ha completat; podríeu tenir problemes de joc (inclosa la possible pèrdua de dades desades),\n el joc pot canviar sense previ avís, i el joc es pot actualitzar o completar o no.", + "choosePokemon": "Elegir un Pokémon.", + "renamePokemon": "Rebatejar Pokémon", + "rename": "Rebatejar", + "nickname": "Sobrenom", + "errorServerDown": "Vaja! S'ha produït un problema en contactar amb el servidor.\n\nPots deixar aquesta pestanya oberta,\nel joc es tornarà a connectar automàticament." +} diff --git a/src/locales/ca_ES/nature.json b/src/locales/ca_ES/nature.json index 9e26dfeeb6e..b07413683db 100644 --- a/src/locales/ca_ES/nature.json +++ b/src/locales/ca_ES/nature.json @@ -1 +1,27 @@ -{} \ No newline at end of file +{ + "Hardy": "Forta", + "Lonely": "Esquerpa", + "Brave": "Audaç", + "Adamant": "Ferma", + "Naughty": "Múrria", + "Bold": "Agosarada", + "Docile": "Dòcil", + "Relaxed": "Relaxat", + "Impish": "Frenètic", + "Lax": "Despreocupat", + "Timid": "Poruc", + "Hasty": "Àvid", + "Serious": "Seriós", + "Jolly": "Jovial", + "Naive": "Ingenu", + "Modest": "Modesta", + "Mild": "Suau", + "Quiet": "Tranquil", + "Bashful": "Vergonyós", + "Rash": "Imprudent", + "Calm": "Serena", + "Gentle": "Amable", + "Sassy": "Descarat", + "Careful": "Cautelós", + "Quirky": "Estrany" +} diff --git a/src/locales/ca_ES/pokemon-info.json b/src/locales/ca_ES/pokemon-info.json index 9e26dfeeb6e..421ea6a88f7 100644 --- a/src/locales/ca_ES/pokemon-info.json +++ b/src/locales/ca_ES/pokemon-info.json @@ -1 +1,40 @@ -{} \ No newline at end of file +{ + "Stat": { + "HP": "PS", + "HPshortened": "PS", + "ATK": "Atac", + "ATKshortened": "Ata", + "DEF": "Defensa", + "DEFshortened": "Def", + "SPATK": "At. Esp.", + "SPATKshortened": "AtEsp", + "SPDEF": "Def. Esp.", + "SPDEFshortened": "DefEsp", + "SPD": "Velocitat", + "SPDshortened": "Veloc.", + "ACC": "Precisió", + "EVA": "Evació" + }, + "Type": { + "UNKNOWN": "???", + "NORMAL": "Normal", + "FIGHTING": "Lluita", + "FLYING": "Volador", + "POISON": "Verí", + "GROUND": "Terra", + "ROCK": "Roca", + "BUG": "Bestiola", + "GHOST": "Fantasma", + "STEEL": "Acer", + "FIRE": "Foc", + "WATER": "Aigua", + "GRASS": "Planta", + "ELECTRIC": "Elèctric", + "PSYCHIC": "Psíquic", + "ICE": "Gel", + "DRAGON": "Drac", + "DARK": "Sinistre", + "FAIRY": "Fada", + "STELLAR": "Astral" + } +} diff --git a/src/locales/de/ability-trigger.json b/src/locales/de/ability-trigger.json index 60f3432df98..379692c45ae 100644 --- a/src/locales/de/ability-trigger.json +++ b/src/locales/de/ability-trigger.json @@ -12,6 +12,7 @@ "typeImmunityHeal": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} vermeidet Schaden mit {{abilityName}}!", "disguiseAvoidedDamage": "Die Tarnung von {{pokemonNameWithAffix}} ist aufgeflogen!!", + "fullHpResistType": "Der Panzer von {{pokemonNameWithAffix}} funkelt und verzerrt die Wechselwirkungen zwischen den Typen!", "moveImmunity": "Es hat keine Wirkung auf {{pokemonNameWithAffix}}...", "reverseDrain": "{{pokemonNameWithAffix}} saugt Kloakensoße auf!", "postDefendTypeChange": "{{abilityName}} von {{pokemonNameWithAffix}} macht es zu einem {{typeName}}-Typ!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}} strahlt eine knisternde Aura aus!", "postSummonDarkAura": "{{pokemonNameWithAffix}} strahlt eine dunkle Aura aus!", "postSummonFairyAura": "{{pokemonNameWithAffix}} strahlt eine Feenaura aus!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}} kehrt die Wirkung aller Aura-Fähigkeiten um!", "postSummonNeutralizingGas": "Reaktionsgas von {{pokemonNameWithAffix}} hat sich in der Umgebung ausgebreitet!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} verfügt über zwei Fähigkeiten!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} verfügt über zwei Fähigkeiten!", @@ -59,4 +61,4 @@ "postSummonTabletsOfRuin": "Unheilstafeln von {{pokemonNameWithAffix}} schwächt {{statName}} aller Pokémon im Umkreis!", "postSummonBeadsOfRuin": "Unheilsjuwelen von {{pokemonNameWithAffix}} schwächt {{statName}} aller Pokémon im Umkreis!", "preventBerryUse": "{{pokemonNameWithAffix}} kriegt vor Anspannung keine Beeren mehr runter!" -} \ No newline at end of file +} diff --git a/src/locales/de/achv.json b/src/locales/de/achv.json index d2e56089720..21a1d89f9d6 100644 --- a/src/locales/de/achv.json +++ b/src/locales/de/achv.json @@ -89,7 +89,7 @@ "name": "Bänder-Meister", "name_female": "Bänder-Meisterin" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Teamwork", "description": "Nutze Staffette, während der Anwender mindestens eines Statuswertes maximiert hat." }, @@ -274,4 +274,4 @@ "name": "Spieglein, Spieglein an der Wand", "description": "Schließe die 'Umkehrkampf' Herausforderung ab" } -} \ No newline at end of file +} diff --git a/src/locales/de/arena-flyout.json b/src/locales/de/arena-flyout.json index 30d3e7febb3..a1f2254c642 100644 --- a/src/locales/de/arena-flyout.json +++ b/src/locales/de/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "Tatami-Schild", "craftyShield": "Trickschutz", "tailwind": "Rückenwind", - "happyHour": "Goldene Zeiten" -} \ No newline at end of file + "happyHour": "Goldene Zeiten", + "safeguard": "Bodyguard" +} diff --git a/src/locales/de/arena-tag.json b/src/locales/de/arena-tag.json index 454effae60c..3bed4fefbd0 100644 --- a/src/locales/de/arena-tag.json +++ b/src/locales/de/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "Der Rückenwind auf deiner Seite hat sich gelegt!", "tailwindOnRemoveEnemy": "Der Rückenwind auf gegnerischer Seite hat sich gelegt!", "happyHourOnAdd": "Goldene Zeiten sind angebrochen!", - "happyHourOnRemove": "Die goldenen Zeiten sind vorbei!" + "happyHourOnRemove": "Die goldenen Zeiten sind vorbei!", + "safeguardOnAdd": "Das ganze Feld wird von einem Schleier umhüllt!", + "safeguardOnAddPlayer": "Das Team des Anwenders wird von einem Schleier umhüllt!", + "safeguardOnAddEnemy": "Das gegnerische Team wird von einem Schleier umhüllt!", + "safeguardOnRemove": "Der mystische Schleier, der das ganze Feld umgab, hat sich gelüftet!", + "safeguardOnRemovePlayer": "Der mystische Schleier, der dein Team umgab, hat sich gelüftet!", + "safeguardOnRemoveEnemy": "Der mystische Schleier, der das gegnerische Team umgab, hat sich gelüftet!" } \ No newline at end of file diff --git a/src/locales/de/battle.json b/src/locales/de/battle.json index 762b5848439..38e36d4b2da 100644 --- a/src/locales/de/battle.json +++ b/src/locales/de/battle.json @@ -44,6 +44,7 @@ "moveNotImplemented": "{{moveName}} ist noch nicht implementiert und kann nicht ausgewählt werden.", "moveNoPP": "Es sind keine AP für diese Attacke mehr übrig!", "moveDisabled": "{{moveName}} ist deaktiviert!", + "disableInterruptedMove": "{{moveName}} von {{pokemonNameWithAffix}} ist blockiert!", "noPokeballForce": "Eine unsichtbare Kraft verhindert die Nutzung von Pokébällen.", "noPokeballTrainer": "Du kannst das Pokémon eines anderen Trainers nicht fangen!", "noPokeballMulti": "Du kannst erst einen Pokéball werfen, wenn nur noch ein Pokémon übrig ist!", @@ -94,5 +95,6 @@ "retryBattle": "Möchtest du vom Beginn des Kampfes neustarten?", "unlockedSomething": "{{unlockedThing}} wurde freigeschaltet.", "congratulations": "Glückwunsch!", - "beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!" -} \ No newline at end of file + "beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!", + "eggSkipPrompt": "Zur Ei-Zusammenfassung springen?" +} diff --git a/src/locales/de/battler-tags.json b/src/locales/de/battler-tags.json index 5509cc11b0a..1a04d3d4486 100644 --- a/src/locales/de/battler-tags.json +++ b/src/locales/de/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}} wurde durch {{moveName}} verletzt!", "cursedOnAdd": "{{pokemonNameWithAffix}} nimmt einen Teil seiner KP und legt einen Fluch auf {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} wurde durch den Fluch verletzt!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} hortet {{stockpiledCount}}!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}} hortet {{stockpiledCount}}!", + "disabledOnAdd": " {{moveName}} von {{pokemonNameWithAffix}} wurde blockiert!", + "disabledLapse": "{{moveName}} von {{pokemonNameWithAffix}} ist nicht länger blockiert!", + "tarShotOnAdd": "{{pokemonNameWithAffix}} ist nun schwach gegenüber Feuer-Attacken!" +} diff --git a/src/locales/de/challenges.json b/src/locales/de/challenges.json index 17c33353bc6..c8836c50549 100644 --- a/src/locales/de/challenges.json +++ b/src/locales/de/challenges.json @@ -1,10 +1,11 @@ { + "noneSelected": "Keine ausgewählt", "title": "Herausforderungsmodifikatoren", "illegalEvolution": "{{pokemon}} hat sich in ein Pokémon verwandelt, dass für diese Herausforderung nicht zulässig ist!", "singleGeneration": { "name": "Mono-Generation", "desc": "Du kannst nur Pokémon aus der {{gen}} Generation verwenden.", - "desc_default": "Du kannst nur Pokémon gewählten Generation verwenden.", + "desc_default": "Du kannst nur Pokémon aus der gewählten Generation verwenden.", "gen_1": "ersten", "gen_2": "zweiten", "gen_3": "dritten", diff --git a/src/locales/de/dialogue-misc.json b/src/locales/de/dialogue-misc.json index 1529831d7c5..69c704c66c6 100644 --- a/src/locales/de/dialogue-misc.json +++ b/src/locales/de/dialogue-misc.json @@ -1,6 +1,6 @@ { - "ending": "@c{smile}Oh? Du hast gewonnen?@d{96} @c{smile_eclosed}Ich schätze, das hätte ich wissen sollen.\n$Aber, du bist jetzt zurück.\n$@c{smile}Es ist vorbei.@d{64} Du hast die Schleife beendet.\n$@c{serious_smile_fists}Du hast auch deinen Traum erfüllt, nicht wahr?\nDu hast nicht einmal verloren.\n$@c{neutral}Ich bin der Einzige, der sich daran erinnern wird, was du getan hast.@d{96}\n$Ich schätze, das ist in Ordnung, oder?\n$@c{serious_smile_fists}Deine Legende wird immer in unseren Herzen weiterleben.\n$@c{smile_eclosed}Wie auch immer, ich habe genug von diesem Ort, oder nicht? Lass uns nach Hause gehen.\n$@c{serious_smile_fists}Vielleicht können wir, wenn wir zurück sind, noch einen Kampf haben?\n$Wenn du dazu bereit bist.", - "ending_female": "@c{shock}Du bist zurück?@d{32} Bedeutet das…@d{96} du hast gewonnen?!\n$@c{smile_ehalf}Ich hätte wissen sollen, dass du es in dir hast.\n$@c{smile_eclosed}Natürlich… ich hatte immer dieses Gefühl.\n$@c{smile}Es ist jetzt vorbei, richtig? Du hast die Schleife beendet.\n$@c{smile_ehalf}Du hast auch deinen Traum erfüllt, nicht wahr?\n$Du hast nicht einmal verloren.\n$Ich werde die Einzige sein, die sich daran erinnert, was du getan hast.\n$@c{angry_mopen}Ich werde versuchen, es nicht zu vergessen!\n$@c{smile_wave_wink}Nur ein Scherz!@d{64} @c{smile}Ich würde es nie vergessen.@d{32}\n$Deine Legende wird in unseren Herzen weiterleben.\n$@c{smile_wave}Wie auch immer,@d{64} es wird spät…@d{96} denke ich?\nEs ist schwer zu sagen an diesem Ort.\n$Lass uns nach Hause gehen. \n$@c{smile_wave_wink}Vielleicht können wir morgen noch einen Kampf haben, der alten Zeiten willen?", + "ending": "@c{shock}Du bist zurück?@d{32} Bedeutet das…@d{96} du hast gewonnen?!\n$@c{smile_ehalf}Ich hätte wissen sollen, dass du es in dir hast.\n$@c{smile_eclosed}Natürlich… ich hatte immer dieses Gefühl.\n$@c{smile}Es ist jetzt vorbei, richtig? Du hast die Schleife beendet.\n$@c{smile_ehalf}Du hast auch deinen Traum erfüllt, nicht wahr?\n$Du hast nicht einmal verloren.\n$Ich werde die Einzige sein, die sich daran erinnert, was du getan hast.\n$@c{angry_mopen}Ich werde versuchen, es nicht zu vergessen!\n$@c{smile_wave_wink}Nur ein Scherz!@d{64} @c{smile}Ich würde es nie vergessen.@d{32}\n$Deine Legende wird in unseren Herzen weiterleben.\n$@c{smile_wave}Wie auch immer,@d{64} es wird spät…@d{96} denke ich?\nEs ist schwer zu sagen an diesem Ort.\n$Lass uns nach Hause gehen. \n$@c{smile_wave_wink}Vielleicht können wir morgen noch einen Kampf haben, der alten Zeiten willen?", + "ending_female": "@c{smile}Oh? Du hast gewonnen?@d{96} @c{smile_eclosed}Ich schätze, das hätte ich wissen sollen.\n$Aber, du bist jetzt zurück.\n$@c{smile}Es ist vorbei.@d{64} Du hast die Schleife beendet.\n$@c{serious_smile_fists}Du hast auch deinen Traum erfüllt, nicht wahr?\nDu hast nicht einmal verloren.\n$@c{neutral}Ich bin der Einzige, der sich daran erinnern wird, was du getan hast.@d{96}\n$Ich schätze, das ist in Ordnung, oder?\n$@c{serious_smile_fists}Deine Legende wird immer in unseren Herzen weiterleben.\n$@c{smile_eclosed}Wie auch immer, ich habe genug von diesem Ort, oder nicht? Lass uns nach Hause gehen.\n$@c{serious_smile_fists}Vielleicht können wir, wenn wir zurück sind, noch einen Kampf haben?\n$Wenn du dazu bereit bist.", "ending_endless": "Glückwunsch! Du hast das aktuelle Ende erreicht!\nWir arbeiten an mehr Spielinhalten.", "ending_name": "Entwickler" } diff --git a/src/locales/de/dialogue.json b/src/locales/de/dialogue.json index e5bcb81ce52..8a3dbb8880e 100644 --- a/src/locales/de/dialogue.json +++ b/src/locales/de/dialogue.json @@ -1403,19 +1403,19 @@ "1": "Ich muss dein Potenzial als Trainer und die Stärke der Pokémon sehen, die mit dir kämpfen!", "2": "Los geht's! Dies sind meine Gesteins-Pokémon, mein ganzer Stolz!", "3": "Gesteins-Pokémon sind einfach die besten!", - "4": "Ich muss dein Potenzial als Trainer und die Stärke der Pokémon sehen, die mit dir kämpfen!" + "4": "Tag für Tag grabe ich hier nach Fossilien.\n$Die viele Arbeit hat meine Pokémon felsenfest gemacht\nund das wirst du jetzt im Kampf zu spüren bekommen!" }, "victory": { "1": "W-was? Das kann nicht sein! Meine total tranierten Pokémon!", "2": "…Wir haben die Kontrolle verloren. Beim nächsten Mal fordere ich dich\n$zu einem Fossilien-Ausgrabungswettbewerb heraus.", "3": "Mit deinem Können ist es nur natürlich, dass du gewinnst.", - "4": "W-was?! Das kann nicht sein! Selbst das war nicht genug?", - "5": "Ich habe es vermasselt." + "4": "W-was?! Das kann nicht sein! Selbst das war nicht genug?" }, "defeat": { "1": "Siehst du? Ich bin stolz auf meinen steinigen Kampfstil!", "2": "Danke! Der Kampf hat mir Vertrauen gegeben, dass ich vielleicht meinen Vater besiegen kann!", - "3": "Ich fühle mich, als hätte ich gerade einen wirklich hartnäckigen Felsen durchbrochen!" + "3": "Na, was sagst du jetzt? Meine felsenfesten Pokémon waren hart genug für dich, was?", + "4": "Ich wusste, dass ich gewinnen würde!" } }, "morty": { diff --git a/src/locales/de/fight-ui-handler.json b/src/locales/de/fight-ui-handler.json index 6965540c703..f803375e8de 100644 --- a/src/locales/de/fight-ui-handler.json +++ b/src/locales/de/fight-ui-handler.json @@ -3,5 +3,6 @@ "power": "Stärke", "accuracy": "Genauigkeit", "abilityFlyInText": "{{passive}}{{abilityName}} von {{pokemonName}} wirkt!", - "passive": "Passive Fähigkeit " + "passive": "Passive Fähigkeit ", + "teraHover": "Tera-Typ {{type}}" } \ No newline at end of file diff --git a/src/locales/de/menu-ui-handler.json b/src/locales/de/menu-ui-handler.json index 56c03102b9c..93c3f4c38e8 100644 --- a/src/locales/de/menu-ui-handler.json +++ b/src/locales/de/menu-ui-handler.json @@ -25,5 +25,6 @@ "unlinkGoogle": "Google trennen", "cancel": "Abbrechen", "losingProgressionWarning": "Du wirst jeglichen Fortschritt seit Anfang dieses Kampfes verlieren. Fortfahren?", - "noEggs": "Du brütest aktuell keine Eier aus!" + "noEggs": "Du brütest aktuell keine Eier aus!", + "donate": "Spenden" } \ No newline at end of file diff --git a/src/locales/de/menu.json b/src/locales/de/menu.json index e5c1c700425..0e2ba4dc0b3 100644 --- a/src/locales/de/menu.json +++ b/src/locales/de/menu.json @@ -51,5 +51,7 @@ "renamePokemon": "Pokémon umbennenen", "rename": "Umbenennen", "nickname": "Spitzname", - "errorServerDown": "Ups! Es gab einen Fehler beim Versuch\nden Server zu kontaktieren\nLasse dieses Fenster offen\nDu wirst automatisch neu verbunden." -} \ No newline at end of file + "errorServerDown": "Ups! Es gab einen Fehler beim Versuch\nden Server zu kontaktieren\nLasse dieses Fenster offen\nDu wirst automatisch neu verbunden.", + "noSaves": "Du hast keine gespeicherten Dateien!", + "tooManySaves": "Du hast zu viele gespeicherte Dateien!" +} diff --git a/src/locales/de/modifier-type.json b/src/locales/de/modifier-type.json index c9927636c2a..7c7972343d6 100644 --- a/src/locales/de/modifier-type.json +++ b/src/locales/de/modifier-type.json @@ -47,10 +47,14 @@ "description": "Ändert das Wesen zu {{natureName}}. Schaltet dieses Wesen permanent für diesen Starter frei." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Verdoppelt die Wahrscheinlichkeit, dass die nächsten {{battleCount}} Begegnungen mit wilden Pokémon ein Doppelkampf sind." + "description": "Vervierfacht die Chance, dass ein Kampf ein Doppelkampf wird, für bis zu {{battleCount}} Kämpfe." }, - "TempBattleStatBoosterModifierType": { - "description": "Erhöht die {{tempBattleStatName}} aller Teammitglieder für 5 Kämpfe um eine Stufe." + "TempStatStageBoosterModifierType": { + "description": "Erhöht {{stat}} aller Teammitglieder um {{amount}} für bis zu 5 Kämpfe.", + "extra": { + "stage": "eine Stufe", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "Erhöht die Stärke aller {{moveType}}-Attacken eines Pokémon um 20%." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "Erhöht das Level aller Teammitglieder um {{levels}}." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Erhöht den {{statName}} Basiswert des Trägers um 10%. Das Stapellimit erhöht sich, je höher dein IS-Wert ist." + "BaseStatBoosterModifierType": { + "description": "Erhöht den {{stat}} Basiswert des Trägers um 10%. Das Stapellimit erhöht sich, je höher dein IS-Wert ist." }, "AllPokemonFullHpRestoreModifierType": { "description": "Stellt 100% der KP aller Pokémon her." @@ -248,6 +252,12 @@ "name": "Scope-Linse", "description": "Ein Item zum Tragen. Es erhöht die Volltrefferquote." }, + "DIRE_HIT": { + "name": "X-Volltreffer", + "extra": { + "raises": "Volltrefferquote" + } + }, "LEEK": { "name": "Lauchstange", "description": "Ein Item, das von Porenta getragen werden kann. Diese lange Lauchstange erhöht die Volltrefferquote stark." @@ -411,25 +421,13 @@ "description": "Ein Item, das Ditto zum Tragen gegeben werden kann. Fein und doch hart, erhöht dieses sonderbare Pulver die Initiative." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "X-Angriff", "x_defense": "X-Verteidigung", "x_sp_atk": "X-Sp.-Ang.", "x_sp_def": "X-Sp.-Vert.", "x_speed": "X-Tempo", - "x_accuracy": "X-Treffer", - "dire_hit": "X-Volltreffer" - }, - "TempBattleStatBoosterStatName": { - "ATK": "Angriff", - "DEF": "Verteidigung", - "SPATK": "Sp. Ang", - "SPDEF": "Sp. Vert", - "SPD": "Initiative", - "ACC": "Genauigkeit", - "CRIT": "Volltrefferquote", - "EVA": "Fluchtwert", - "DEFAULT": "???" + "x_accuracy": "X-Treffer" }, "AttackTypeBoosterItem": { "silk_scarf": "Seidenschal", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "Drachen-Disc", "DARK_MEMORY": "Unlicht-Disc", "FAIRY_MEMORY": "Feen-Disc", - "BLANK_MEMORY": "Leere-Disc" + "NORMAL_MEMORY": "Normal-Disc" } -} \ No newline at end of file +} diff --git a/src/locales/de/modifier.json b/src/locales/de/modifier.json index 22053b1da63..37227973410 100644 --- a/src/locales/de/modifier.json +++ b/src/locales/de/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{typeName}} von {{pokemonNameWithAffix}} füllt einige KP auf!", "hitHealApply": "{{typeName}} von {{pokemonNameWithAffix}} füllt einige KP auf!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} wurde durch {{typeName}} wiederbelebt!", - "pokemonResetNegativeStatStageApply": "Die negative Statuswertveränderung von {{pokemonNameWithAffix}} wurde durch {{typeName}} aufgehoben!", + "resetNegativeStatStageApply": "Die negative Statuswertveränderung von {{pokemonNameWithAffix}} wurde durch {{typeName}} aufgehoben!", "moneyInterestApply": "Du erhählst {{moneyAmount}} ₽ durch das Item {{typeName}}!", "turnHeldItemTransferApply": "{{itemName}} von {{pokemonNameWithAffix}} wurde durch {{typeName}} von {{pokemonName}} absorbiert!", "contactHeldItemTransferApply": "{{itemName}} von {{pokemonNameWithAffix}} wurde durch {{typeName}} von {{pokemonName}} geklaut!", diff --git a/src/locales/de/move-trigger.json b/src/locales/de/move-trigger.json index 5b2b2471df9..01b22429fb3 100644 --- a/src/locales/de/move-trigger.json +++ b/src/locales/de/move-trigger.json @@ -3,6 +3,10 @@ "cutHpPowerUpMove": "{{pokemonName}} nutzt seine KP um seine Attacke zu verstärken!", "absorbedElectricity": "{{pokemonName}} absorbiert elektrische Energie!", "switchedStatChanges": "{{pokemonName}} tauschte die Statuswerteveränderungen mit dem Ziel!", + "switchedTwoStatChanges": "{{pokemonName}} tauscht Veränderungen an {{firstStat}} und {{secondStat}} mit dem Ziel!", + "switchedStat": "{{pokemonName}} tauscht seinen {{stat}}-Wert mit dem des Zieles!", + "sharedGuard": "{{pokemonName}} addiert seine Schutzkräfte mit jenen des Zieles und teilt sie gerecht auf!", + "sharedPower": "{{pokemonName}} addiert seine Kräfte mit jenen des Zieles und teilt sie gerecht auf!", "goingAllOutForAttack": "{{pokemonName}} legt sich ins Zeug!", "regainedHealth": "{{pokemonName}} erholt sich!", "keptGoingAndCrashed": "{{pokemonName}} springt daneben und verletzt sich!", @@ -61,5 +65,7 @@ "suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!", "revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!", "swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!", - "exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!", + "safeguard": "{{targetName}} wird durch Bodyguard geschützt!", + "afterYou": "{{targetName}} lässt sich auf Galanterie ein!" +} diff --git a/src/locales/de/pokemon-form-battle.json b/src/locales/de/pokemon-form-battle.json index 8651b3d1318..35060c33d0b 100644 --- a/src/locales/de/pokemon-form-battle.json +++ b/src/locales/de/pokemon-form-battle.json @@ -10,5 +10,5 @@ "eternamaxChange": "{{preName}} hat sich zu {{pokemonName}} unendynamaximiert!", "revertChange": "{{pokemonName}} hat seine ursprüngliche Form zurückerlangt!", "formChange": "{{preName}} hat seine Form geändert!", - "disguiseChange": "Its disguise served it as a decoy!" + "disguiseChange": "Sein Kostüm hat die Attacke absorbiert!" } \ No newline at end of file diff --git a/src/locales/de/pokemon-info.json b/src/locales/de/pokemon-info.json index a559001f663..2d625d52ba7 100644 --- a/src/locales/de/pokemon-info.json +++ b/src/locales/de/pokemon-info.json @@ -1,7 +1,6 @@ { "Stat": { "HP": "KP", - "HPStat": "KP", "HPshortened": "KP", "ATK": "Angriff", "ATKshortened": "Ang", diff --git a/src/locales/de/settings.json b/src/locales/de/settings.json index d72a026cf5a..31406f28d17 100644 --- a/src/locales/de/settings.json +++ b/src/locales/de/settings.json @@ -100,7 +100,7 @@ "moveTouchControls": "Bewegung Touch Steuerung", "shopOverlayOpacity": "Shop Overlay Deckkraft", "shopCursorTarget": "Shop-Cursor Ziel", - "items": "Items", + "rewards": "Items", "reroll": "Neu rollen", "shop": "Shop", "checkTeam": "Team überprüfen" diff --git a/src/locales/en/ability-trigger.json b/src/locales/en/ability-trigger.json index 307ab70b85c..da21d80e3c7 100644 --- a/src/locales/en/ability-trigger.json +++ b/src/locales/en/ability-trigger.json @@ -3,7 +3,7 @@ "badDreams": "{{pokemonName}} is tormented!", "costar": "{{pokemonName}} copied {{allyName}}'s stat changes!", "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} avoided\ndamage with {{abilityName}}!", - "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!", + "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both Pokémon in 3 turns!", "poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!", "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", "windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!", @@ -12,6 +12,7 @@ "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", + "fullHpResistType": "{{pokemonNameWithAffix}} made its shell gleam!\nIt's distorting type matchups!", "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!", "postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!", "postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}} reversed all other Pokémon's auras!", "postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!", @@ -59,4 +61,4 @@ "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}'s Tablets of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}'s Beads of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", "preventBerryUse": "{{pokemonNameWithAffix}} is too\nnervous to eat berries!" -} \ No newline at end of file +} diff --git a/src/locales/en/achv-female.json b/src/locales/en/achv-female.json deleted file mode 100644 index edcd8c53fb7..00000000000 --- a/src/locales/en/achv-female.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "Achievements": { - "name": "Achievements" - }, - "Locked": { - "name": "Locked" - }, - "MoneyAchv": { - "description": "Accumulate a total of ₽{{moneyAmount}}" - }, - "10K_MONEY": { - "name": "Money Haver" - }, - "100K_MONEY": { - "name": "Rich" - }, - "1M_MONEY": { - "name": "Millionaire" - }, - "10M_MONEY": { - "name": "One Percenter" - }, - "DamageAchv": { - "description": "Inflict {{damageAmount}} damage in one hit" - }, - "250_DMG": { - "name": "Hard Hitter" - }, - "1000_DMG": { - "name": "Harder Hitter" - }, - "2500_DMG": { - "name": "That's a Lotta Damage!" - }, - "10000_DMG": { - "name": "One Punch Man" - }, - "HealAchv": { - "description": "Heal {{healAmount}} {{HP}} at once with a move, ability, or held item" - }, - "250_HEAL": { - "name": "Novice Healer" - }, - "1000_HEAL": { - "name": "Big Healer" - }, - "2500_HEAL": { - "name": "Cleric" - }, - "10000_HEAL": { - "name": "Recovery Master" - }, - "LevelAchv": { - "description": "Level up a Pokémon to Lv{{level}}" - }, - "LV_100": { - "name": "But Wait, There's More!" - }, - "LV_250": { - "name": "Elite" - }, - "LV_1000": { - "name": "To Go Even Further Beyond" - }, - "RibbonAchv": { - "description": "Accumulate a total of {{ribbonAmount}} Ribbons" - }, - "10_RIBBONS": { - "name": "Pokémon League Champion" - }, - "25_RIBBONS": { - "name": "Great League Champion" - }, - "50_RIBBONS": { - "name": "Ultra League Champion" - }, - "75_RIBBONS": { - "name": "Rogue League Champion" - }, - "100_RIBBONS": { - "name": "Master League Champion" - }, - "TRANSFER_MAX_BATTLE_STAT": { - "name": "Teamwork", - "description": "Baton pass to another party member with at least one stat maxed out" - }, - "MAX_FRIENDSHIP": { - "name": "Friendmaxxing", - "description": "Reach max friendship on a Pokémon" - }, - "MEGA_EVOLVE": { - "name": "Megamorph", - "description": "Mega evolve a Pokémon" - }, - "GIGANTAMAX": { - "name": "Absolute Unit", - "description": "Gigantamax a Pokémon" - }, - "TERASTALLIZE": { - "name": "STAB Enthusiast", - "description": "Terastallize a Pokémon" - }, - "STELLAR_TERASTALLIZE": { - "name": "The Hidden Type", - "description": "Stellar Terastallize a Pokémon" - }, - "SPLICE": { - "name": "Infinite Fusion", - "description": "Splice two Pokémon together with DNA Splicers" - }, - "MINI_BLACK_HOLE": { - "name": "A Hole Lot of Items", - "description": "Acquire a Mini Black Hole" - }, - "CATCH_MYTHICAL": { - "name": "Mythical", - "description": "Catch a mythical Pokémon" - }, - "CATCH_SUB_LEGENDARY": { - "name": "(Sub-)Legendary", - "description": "Catch a sub-legendary Pokémon" - }, - "CATCH_LEGENDARY": { - "name": "Legendary", - "description": "Catch a legendary Pokémon" - }, - "SEE_SHINY": { - "name": "Shiny", - "description": "Find a shiny Pokémon in the wild" - }, - "SHINY_PARTY": { - "name": "That's Dedication", - "description": "Have a full party of shiny Pokémon" - }, - "HATCH_MYTHICAL": { - "name": "Mythical Egg", - "description": "Hatch a mythical Pokémon from an egg" - }, - "HATCH_SUB_LEGENDARY": { - "name": "Sub-Legendary Egg", - "description": "Hatch a sub-legendary Pokémon from an egg" - }, - "HATCH_LEGENDARY": { - "name": "Legendary Egg", - "description": "Hatch a legendary Pokémon from an egg" - }, - "HATCH_SHINY": { - "name": "Shiny Egg", - "description": "Hatch a shiny Pokémon from an egg" - }, - "HIDDEN_ABILITY": { - "name": "Hidden Potential", - "description": "Catch a Pokémon with a hidden ability" - }, - "PERFECT_IVS": { - "name": "Certificate of Authenticity", - "description": "Get perfect IVs on a Pokémon" - }, - "CLASSIC_VICTORY": { - "name": "Undefeated", - "description": "Beat the game in classic mode" - }, - "UNEVOLVED_CLASSIC_VICTORY": { - "name": "Bring Your Child To Work Day", - "description": "Beat the game in Classic Mode with at least one unevolved party member." - }, - "MONO_GEN_ONE": { - "name": "The Original Rival", - "description": "Complete the generation one only challenge." - }, - "MONO_GEN_TWO": { - "name": "Generation 1.5", - "description": "Complete the generation two only challenge." - }, - "MONO_GEN_THREE": { - "name": "Too much water?", - "description": "Complete the generation three only challenge." - }, - "MONO_GEN_FOUR": { - "name": "Is she really the hardest?", - "description": "Complete the generation four only challenge." - }, - "MONO_GEN_FIVE": { - "name": "All Original", - "description": "Complete the generation five only challenge." - }, - "MONO_GEN_SIX": { - "name": "Almost Royalty", - "description": "Complete the generation six only challenge." - }, - "MONO_GEN_SEVEN": { - "name": "Only Technically", - "description": "Complete the generation seven only challenge." - }, - "MONO_GEN_EIGHT": { - "name": "A Champion Time!", - "description": "Complete the generation eight only challenge." - }, - "MONO_GEN_NINE": { - "name": "She was going easy on you", - "description": "Complete the generation nine only challenge." - }, - "MonoType": { - "description": "Complete the {{type}} monotype challenge." - }, - "MONO_NORMAL": { - "name": "Extra Ordinary" - }, - "MONO_FIGHTING": { - "name": "I Know Kung Fu" - }, - "MONO_FLYING": { - "name": "Angry Birds" - }, - "MONO_POISON": { - "name": "Kanto's Favourite" - }, - "MONO_GROUND": { - "name": "Forecast: Earthquakes" - }, - "MONO_ROCK": { - "name": "Brock Hard" - }, - "MONO_BUG": { - "name": "You Like Jazz?" - }, - "MONO_GHOST": { - "name": "Who You Gonna Call?" - }, - "MONO_STEEL": { - "name": "Iron Giant" - }, - "MONO_FIRE": { - "name": "I Cast Fireball!" - }, - "MONO_WATER": { - "name": "When It Rains, It Pours" - }, - "MONO_GRASS": { - "name": "Can't Touch This" - }, - "MONO_ELECTRIC": { - "name": "Aim For The Horn!" - }, - "MONO_PSYCHIC": { - "name": "Big Brain Energy" - }, - "MONO_ICE": { - "name": "Walking On Thin Ice" - }, - "MONO_DRAGON": { - "name": "Pseudo-Legend Club" - }, - "MONO_DARK": { - "name": "It's Just A Phase" - }, - "MONO_FAIRY": { - "name": "Hey! Listen!" - }, - "FRESH_START": { - "name": "First Try!", - "description": "Complete the Fresh Start challenge." - }, - "INVERSE_BATTLE": { - "name": "Mirror rorriM", - "description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC" - } -} \ No newline at end of file diff --git a/src/locales/en/achv.json b/src/locales/en/achv.json index 0ed746c77b3..32d519fbf78 100644 --- a/src/locales/en/achv.json +++ b/src/locales/en/achv.json @@ -10,19 +10,19 @@ }, "10K_MONEY": { "name": "Money Haver", - "name_female": null + "name_female": "Money Haver" }, "100K_MONEY": { "name": "Rich", - "name_female": null + "name_female": "Rich" }, "1M_MONEY": { "name": "Millionaire", - "name_female": null + "name_female": "Millionaire" }, "10M_MONEY": { "name": "One Percenter", - "name_female": null + "name_female": "One Percenter" }, "DamageAchv": { "description": "Inflict {{damageAmount}} damage in one hit" @@ -32,11 +32,11 @@ }, "1000_DMG": { "name": "Harder Hitter", - "name_female": null + "name_female": "Harder Hitter" }, "2500_DMG": { "name": "That's a Lotta Damage!", - "name_female": null + "name_female": "That's a Lotta Damage!" }, "10000_DMG": { "name": "One Punch Man", @@ -47,19 +47,19 @@ }, "250_HEAL": { "name": "Novice Healer", - "name_female": null + "name_female": "Novice Healer" }, "1000_HEAL": { "name": "Big Healer", - "name_female": null + "name_female": "Big Healer" }, "2500_HEAL": { "name": "Cleric", - "name_female": null + "name_female": "Cleric" }, "10000_HEAL": { "name": "Recovery Master", - "name_female": null + "name_female": "Recovery Master" }, "LevelAchv": { "description": "Level up a Pokémon to Lv{{level}}" @@ -69,7 +69,7 @@ }, "LV_250": { "name": "Elite", - "name_female": null + "name_female": "Elite" }, "LV_1000": { "name": "To Go Even Further Beyond" @@ -79,27 +79,27 @@ }, "10_RIBBONS": { "name": "Pokémon League Champion", - "name_female": null + "name_female": "Pokémon League Champion" }, "25_RIBBONS": { "name": "Great League Champion", - "name_female": null + "name_female": "Great League Champion" }, "50_RIBBONS": { "name": "Ultra League Champion", - "name_female": null + "name_female": "Ultra League Champion" }, "75_RIBBONS": { "name": "Rogue League Champion", - "name_female": null + "name_female": "Rogue League Champion" }, "100_RIBBONS": { "name": "Master League Champion", - "name_female": null + "name_female": "Master League Champion" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Teamwork", - "description": "Baton pass to another party member with at least one stat maxed out" + "description": "Baton pass to another party member with at least one stat stage maxed out" }, "MAX_FRIENDSHIP": { "name": "Friendmaxxing", @@ -147,7 +147,7 @@ }, "SHINY_PARTY": { "name": "That's Dedication", - "name_female": null, + "name_female": "That's Dedication", "description": "Have a full party of shiny Pokémon" }, "HATCH_MYTHICAL": { @@ -176,7 +176,7 @@ }, "CLASSIC_VICTORY": { "name": "Undefeated", - "name_female": null, + "name_female": "Undefeated", "description": "Beat the game in classic mode" }, "UNEVOLVED_CLASSIC_VICTORY": { @@ -284,4 +284,4 @@ "name": "Mirror rorriM", "description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC" } -} \ No newline at end of file +} diff --git a/src/locales/en/arena-flyout.json b/src/locales/en/arena-flyout.json index 141ed4f743d..043d4127eb8 100644 --- a/src/locales/en/arena-flyout.json +++ b/src/locales/en/arena-flyout.json @@ -39,5 +39,6 @@ "matBlock": "Mat Block", "craftyShield": "Crafty Shield", "tailwind": "Tailwind", - "happyHour": "Happy Hour" -} + "happyHour": "Happy Hour", + "safeguard": "Safeguard" +} \ No newline at end of file diff --git a/src/locales/en/arena-tag.json b/src/locales/en/arena-tag.json index ef0b55b691b..d8fed386b24 100644 --- a/src/locales/en/arena-tag.json +++ b/src/locales/en/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "Your team's Tailwind petered out!", "tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!", "happyHourOnAdd": "Everyone is caught up in the happy atmosphere!", - "happyHourOnRemove": "The atmosphere returned to normal." + "happyHourOnRemove": "The atmosphere returned to normal.", + "safeguardOnAdd": "The whole field is cloaked in a mystical veil!", + "safeguardOnAddPlayer": "Your team cloaked itself in a mystical veil!", + "safeguardOnAddEnemy": "The opposing team cloaked itself in a mystical veil!", + "safeguardOnRemove": "The field is no longer protected by Safeguard!", + "safeguardOnRemovePlayer": "Your team is no longer protected by Safeguard!", + "safeguardOnRemoveEnemy": "The opposing team is no longer protected by Safeguard!" } \ No newline at end of file diff --git a/src/locales/en/battle.json b/src/locales/en/battle.json index 662678e7673..217c77422d1 100644 --- a/src/locales/en/battle.json +++ b/src/locales/en/battle.json @@ -44,6 +44,10 @@ "moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.", "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "{{moveName}} is disabled!", + "canOnlyUseMove": "{{pokemonName}} can only use {{moveName}}!", + "moveCannotBeSelected": "{{moveName}} cannot be selected!", + "disableInterruptedMove": "{{pokemonNameWithAffix}}'s {{moveName}}\nis disabled!", + "throatChopInterruptedMove": "The effects of Throat Chop prevent\n{{pokemonName}} from using certain moves!", "noPokeballForce": "An unseen force\nprevents using Poké Balls.", "noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!", "noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!", @@ -61,6 +65,7 @@ "skipItemQuestion": "Are you sure you want to skip taking an item?", "itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.", "eggHatching": "Oh?", + "eggSkipPrompt": "Skip to egg summary?", "ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?", "wildPokemonWithAffix": "Wild {{pokemonName}}", "foePokemonWithAffix": "Foe {{pokemonName}}", diff --git a/src/locales/en/battler-tags.json b/src/locales/en/battler-tags.json index 94ea3b14958..5c351fc6961 100644 --- a/src/locales/en/battler-tags.json +++ b/src/locales/en/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}} is hurt by {{moveName}}!", "cursedOnAdd": "{{pokemonNameWithAffix}} cut its own HP and put a curse on the {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} is afflicted by the Curse!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!", + "disabledOnAdd": "{{pokemonNameWithAffix}}'s {{moveName}}\nwas disabled!", + "disabledLapse": "{{pokemonNameWithAffix}}'s {{moveName}}\nis no longer disabled.", + "tarShotOnAdd": "{{pokemonNameWithAffix}} became weaker to fire!" +} diff --git a/src/locales/en/challenges.json b/src/locales/en/challenges.json index f189266cea2..7792755d626 100644 --- a/src/locales/en/challenges.json +++ b/src/locales/en/challenges.json @@ -1,6 +1,7 @@ { "title": "Challenge Modifiers", - "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "illegalEvolution": "{{pokemon}} changed into an ineligible Pokémon\nfor this challenge!", + "noneSelected": "None Selected", "singleGeneration": { "name": "Mono Gen", "desc": "You can only use Pokémon from Generation {{gen}}.", @@ -33,4 +34,4 @@ "value.0": "Off", "value.1": "On" } -} \ No newline at end of file +} diff --git a/src/locales/en/dialogue-double-battle.json b/src/locales/en/dialogue-double-battle.json index 9484aa2edcc..4190af49d15 100644 --- a/src/locales/en/dialogue-double-battle.json +++ b/src/locales/en/dialogue-double-battle.json @@ -58,7 +58,7 @@ "iris_alder_double": { "encounter": { "1": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?", - "1_female": null + "1_female": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?" }, "victory": { "1": "Iris: A loss like this is not easy to take...\n$Alder: But we will only get stronger with every loss!" @@ -75,7 +75,7 @@ "marnie_piers_double": { "encounter": { "1": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing...", - "1_female": null + "1_female": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing..." }, "victory": { "1": "Piers: Now that was a great concert!\n$Marnie: Brother..." diff --git a/src/locales/en/dialogue-final-boss.json b/src/locales/en/dialogue-final-boss.json index 3abe4cd8831..6f99aae3e0c 100644 --- a/src/locales/en/dialogue-final-boss.json +++ b/src/locales/en/dialogue-final-boss.json @@ -1,6 +1,6 @@ { "encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", - "encounter_female": null, + "encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", "firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.", "secondStageWin": "…Magnificent.", "key_ordinal_one": "st", diff --git a/src/locales/en/dialogue-misc.json b/src/locales/en/dialogue-misc.json index f5c63a85410..2f333b5f383 100644 --- a/src/locales/en/dialogue-misc.json +++ b/src/locales/en/dialogue-misc.json @@ -1,6 +1,6 @@ { - "ending": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.", - "ending_female": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?", + "ending": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?", + "ending_female": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.", "ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.", "ending_name": "Devs" -} \ No newline at end of file +} diff --git a/src/locales/en/dialogue.json b/src/locales/en/dialogue.json index e96a42daf1d..5565d2258c2 100644 --- a/src/locales/en/dialogue.json +++ b/src/locales/en/dialogue.json @@ -3,31 +3,31 @@ "encounter": { "1": "Hey, wanna battle?", "2": "Are you a new trainer too?", - "2_female": null, + "2_female": "Are you a new trainer too?", "3": "Hey, I haven't seen you before. Let's battle!", "4": "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", - "4_female": null, + "4_female": "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", "5": "Have we met or not? I don't really remember. Well, I guess it's nice to meet you anyway!", "6": "All right! Let's go!", "7": "All right! Here I come! I'll show you my power!", "8": "Haw haw haw... I'll show you how hawesome my Pokémon are!", "9": "No need to waste time saying hello. Bring it on whenever you're ready!", - "9_female": null, + "9_female": "No need to waste time saying hello. Bring it on whenever you're ready!", "10": "Don't let your guard down, or you may be crying when a kid beats you.", "11": "I've raised my Pokémon with great care. You're not allowed to hurt them!", "12": "Glad you made it! It won't be an easy job from here.", - "12_female": null, + "12_female": "Glad you made it! It won't be an easy job from here.", "13": "The battles continue forever! Welcome to the world with no end!", - "13_female": null + "13_female": "The battles continue forever! Welcome to the world with no end!" }, "victory": { "1": "Wow! You're strong!", - "1_female": null, + "1_female": "Wow! You're strong!", "2": "I didn't stand a chance, huh?", "3": "I'll find you again when I'm older and beat you!", "4": "Ugh. I don't have any more Pokémon.", "5": "No way… NO WAY! How could I lose again…", - "5_female": null, + "5_female": "No way… NO WAY! How could I lose again…", "6": "No! I lost!", "7": "Whoa! You are incredible! I'm amazed and surprised!", "8": "Could it be… How… My Pokémon and I are the strongest, though…", @@ -42,12 +42,12 @@ "encounter": { "1": "Let's have a battle, shall we?", "2": "You look like a new trainer. Let's have a battle!", - "2_female": null, + "2_female": "You look like a new trainer. Let's have a battle!", "3": "I don't recognize you. How about a battle?", "4": "Let's have a fun Pokémon battle!", "5": "I'll show you the ropes of how to really use Pokémon!", "6": "A serious battle starts from a serious beginning! Are you sure you're ready?", - "6_female": null, + "6_female": "A serious battle starts from a serious beginning! Are you sure you're ready?", "7": "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", "8": "You'd better go easy on me, OK? Though I'll be seriously fighting!", "9": "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." @@ -55,15 +55,15 @@ "victory": { "1": "That was impressive! I've got a lot to learn.", "2": "I didn't think you'd beat me that bad…", - "2_female": null, + "2_female": "I didn't think you'd beat me that bad…", "3": "I hope we get to have a rematch some day.", "4": "That was pretty amazingly fun! You've totally exhausted me…", "5": "You actually taught me a lesson! You're pretty amazing!", "6": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", - "6_female": null, + "6_female": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", "7": "I don't need memories like this. Deleting memory…", "8": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", - "8_female": null, + "8_female": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", "9": "I'm actually getting tired of battling… There's gotta be something new to do…" } }, @@ -154,7 +154,7 @@ "ace_trainer": { "encounter": { "1": "You seem quite confident.", - "1_female": null, + "1_female": "You seem quite confident.", "2": "Your Pokémon… Show them to me…", "3": "Because I'm an Ace Trainer, people think I'm strong.", "4": "Are you aware of what it takes to be an Ace Trainer?" @@ -163,9 +163,9 @@ "1": "Yes… You have good Pokémon…", "2": "What?! But I'm a battling genius!", "3": "Of course, you are the main character!", - "3_female": null, + "3_female": "Of course, you are the main character!", "4": "OK! OK! You could be an Ace Trainer!", - "4_female": null + "4_female": "OK! OK! You could be an Ace Trainer!" }, "defeat": { "1": "I am devoting my body and soul to Pokémon battles!", @@ -187,7 +187,7 @@ "1": "Get ready, because when we team up, it's double the trouble!", "2": "Two hearts, one strategy – let's see if you can keep up with our twin power!", "3": "Hope you're ready for double trouble, because we're about to bring the heat!", - "3_female": null + "3_female": "Hope you're ready for double trouble, because we're about to bring the heat!" }, "victory": { "1": "We may have lost this round, but our bond remains unbreakable!", @@ -216,7 +216,7 @@ "encounter": { "1": "I praise your courage in challenging me! For I am the one with the strongest kick!", "2": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?", - "2_female": null + "2_female": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?" }, "victory": { "1": "Oh. The Pokémon did the fighting. My strong kick didn't help a bit.", @@ -328,7 +328,7 @@ "defeat": { "1": "New age simply refers to twentieth century classical composers, right?", "2": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself.", - "2_female": null + "2_female": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself." } }, "psychic": { @@ -360,7 +360,7 @@ "baker": { "encounter": { "1": "Hope you're ready to taste defeat!", - "1_female": null + "1_female": "Hope you're ready to taste defeat!" }, "victory": { "1": "I'll bake a comeback." @@ -391,7 +391,7 @@ "1": "Matey, you're walking the plank if you lose!", "2": "Come on then! My sailor's pride is at stake!", "3": "Ahoy there! Are you seasick?", - "3_female": null + "3_female": "Ahoy there! Are you seasick?" }, "victory": { "1": "Argh! Beaten by a kid!", @@ -413,13 +413,13 @@ }, "ariana": { "encounter": { - "1": "Hold it right there! We can't someone on the loose.\n$It's harmful to Team Rocket's pride, you see.", + "1": "Hold it right there!\nWe can't have someone on the loose.\n$It's harmful to Team Rocket's pride, you see.", "2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told", "3": "Your trip ends here. I'm going to take you down!" }, "victory": { "1": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", - "1_female": null, + "1_female": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", "2": "I... I'm shattered...", "3": "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" } @@ -458,7 +458,7 @@ "1": "Hehehe! You might have beaten me, but you don't stand a chance against the boss!\n$If you get lost now, you won't have to face a sound whipping!", "2": "Hehehe... So, I lost, too...", "3": "Ahya! How could this be? For an Admin like me to lose to some random trainer...", - "3_female": null + "3_female": "Ahya! How could this be? For an Admin like me to lose to some random trainer..." } }, "courtney": { @@ -478,13 +478,13 @@ "1": "Ahahahaha! You're going to meddle in Team Aqua's affairs?\n$You're either absolutely fearless, simply ignorant, or both!\n$You're so cute, you're disgusting! I'll put you down", "2": "What's this? Who's this spoiled brat?", "3": "Cool your jets. Be patient. I'll crush you shortly.", - "3_female": null + "3_female": "Cool your jets. Be patient. I'll crush you shortly." }, "victory": { "1": "Ahahahaha! We got meddled with unexpectedly! We're out of options.\n$We'll have to pull out. But this isn't the last you'll see of Team Aqua!\n$We have other plans! Don't you forget it!", "2": "Ahhh?! Did I go too easy on you?!", "3": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie.", - "3_female": null + "3_female": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie." } }, "matt": { @@ -497,7 +497,7 @@ "1": "Muwuhahaha! That battle was fun even though I lost!", "2": "I can feel it! I can feel it, all right! The strength coming offa you!\n$More! I still want more! But looks like we're outta time...", "3": "Oho! That's a loss I can be proud of!", - "3_female": null + "3_female": "Oho! That's a loss I can be proud of!" } }, "mars": { @@ -505,7 +505,7 @@ "1": "I'm Mars, one of Team Galactic's top Commanders.", "2": "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", "3": "Feeling nervous? You should be!", - "3_female": null + "3_female": "Feeling nervous? You should be!" }, "victory": { "1": "This can't be happening! How did I lose?!", @@ -540,25 +540,25 @@ "zinzolin": { "encounter": { "1": "You could become a threat to Team Plasma, so we will eliminate you here and now!", - "1_female": null, + "1_female": "You could become a threat to Team Plasma, so we will eliminate you here and now!", "2": "You don't have the sense to know when to quit, it seems. It's an act of mercy on my part to bring an end to this now!", "3": "You're an impressive Trainer to have made it this far. But it ends here.", - "3_female": null + "3_female": "You're an impressive Trainer to have made it this far. But it ends here." }, "victory": { "1": "Ghetsis... I have failed you...", "2": "It's bitter cold. I'm shivering. I'm suffering. Yet, we will stand victorious.", "3": "Hmph. You're a smarter Trainer than I expected, but not smart enough.", - "3_female": null + "3_female": "Hmph. You're a smarter Trainer than I expected, but not smart enough." } }, "rood": { "encounter": { "1": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", - "1_female": null, + "1_female": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", "2": "It seems you don't know when to give up. I'll make sure no one interferes with our plans!", "3": "You are a remarkable Trainer to have made it this far. But this is where it ends.", - "3_female": null + "3_female": "You are a remarkable Trainer to have made it this far. But this is where it ends." }, "victory": { "1": "Ghetsis... I have failed my mission...", @@ -569,15 +569,15 @@ "xerosic": { "encounter": { "1": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", - "1_female": null, + "1_female": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", "2": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", - "2_female": null, + "2_female": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", "3": "I've been waiting for you! I need to do a little research on you! Come, let us begin!" }, "victory": { "1": "Ah, you're quite strong. Oh yes—very strong, indeed.", "2": "Ding-ding-ding! You did it! To the victor go the spoils!", - "2_female": null, + "2_female": "Ding-ding-ding! You did it! To the victor go the spoils!", "3": "Wonderful! Amazing! You have tremendous skill and bravery!" } }, @@ -585,7 +585,7 @@ "encounter": { "1": "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", "2": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", - "2_female": null, + "2_female": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", "3": "I've anticipated your arrival. It's time for a little test. Shall we begin?" }, "victory": { @@ -598,11 +598,11 @@ "encounter": { "1": "Prepare for trouble!", "2": "We're pulling a big job here! Get lost, kid!", - "2_female": null, + "2_female": "We're pulling a big job here! Get lost, kid!", "3": "Hand over your Pokémon, or face the wrath of Team Rocket!", "4": "You're about to experience the true terror of Team Rocket!", "5": "Hey, kid! Me am a Team Rocket member kind of guy!", - "5_female": null + "5_female": "Hey, kid! Me am a Team Rocket member kind of guy!" }, "victory": { "1": "Team Rocket blasting off again!", @@ -624,7 +624,7 @@ "1": "Huh? I lost?!", "2": "I can't believe I lost! I even skipped lunch for this", "3": "No way! You're just a kid!", - "3_female": null, + "3_female": "No way! You're just a kid!", "4": "Urrrgh... I should've ducked into our hideout right away...", "5": "You beat me... Do you think the boss will dock my pay for this?" } @@ -652,7 +652,7 @@ "3": "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", "4": "Get ready to lose!", "5": "Hope you're ready for a cosmic beatdown!", - "5_female": null + "5_female": "Hope you're ready for a cosmic beatdown!" }, "victory": { "1": "Shut down...", @@ -682,7 +682,7 @@ "encounter": { "1": "Your Pokémon are no match for the elegance of Team Flare.", "2": "Hope you brought your sunglasses, because things are about to get bright!", - "2_female": null, + "2_female": "Hope you brought your sunglasses, because things are about to get bright!", "3": "Team Flare will cleanse the world of imperfection!", "4": "Prepare to face the brilliance of Team Flare!", "5": "Fashion is most important to us!" @@ -699,6 +699,7 @@ "encounter": { "1": "I'll fight you with all I have to wipe you out!", "2": "I don't care if you're a kid or what. I'll send you flying if you threaten us!", + "2_female": "I don't care if you're a kid or what. I'll send you flying if you threaten us!", "3": "I was told to turn away Trainers, whomever they might be!", "4": "I'll show you the power of Aether Paradise!", "5": "Now that you've learned of the darkness at the heart of Aether Paradise, we'll need you to conveniently disappear!" @@ -715,11 +716,13 @@ "encounter": { "1": "I, Branch Chief Faba, shall show you the harshness of the real world!", "2": "The man who is called Aether Paradise's last line of defense is to battle a mere child?", + "2_female": "The man who is called Aether Paradise's last line of defense is to battle a mere child?", "3": "I, Faba, am the Aether Branch Chief. The only one in the world, I'm irreplaceable." }, "victory": { "1": "Aiyee!", "2": "H-h-how can this be?! How could this child...", + "2_female": "H-h-how can this be?! How could this child...", "3": "This is why... This is why I can't bring myself to like children." } }, @@ -727,9 +730,12 @@ "encounter": { "1": "We're not bad-we're just hard!", "2": "You want some? That's how we say hello! Nice knowing you, punks!", + "2_female": "You want some? That's how we say hello! Nice knowing you, punks!", "3": "We're just a bunch of guys and gals with a great interest in other people's Pokémon!", "4": "Why you trying to act hard when we're already hard as bones out here, homie?", - "5": "Team Skull represent! We can't pay the rent! Had a lot of fun, but our youth was misspent!" + "4_female": "Why you trying to act hard when we're already hard as bones out here, homie?", + "5": "Team Skull represent! We can't pay the rent! Had a lot of fun, but our youth was misspent!", + "5_female": "Team Skull represent! We can't pay the rent! Had a lot of fun, but our youth was misspent!" }, "victory": { "1": "Huh? Is it over already?", @@ -742,11 +748,13 @@ "plumeria": { "encounter": { "1": " ...Hmph. You don't look like anything special to me.", - "2": "It takes these dumb Grunts way too long to deal with you kids..", + "1_female": " ...Hmph. You don't look like anything special to me.", + "2": "It takes these dumb Grunts way too long to deal with you kids...", "3": "Mess with anyone in Team Skull, and I'll show you how serious I can get." }, "victory": { "1": "Hmmph! You're pretty strong. I'll give you that.", + "1_female": "Hmmph! You're pretty strong. I'll give you that.", "2": "Hmmph. Guess you are pretty tough. Now I understand why my Grunts waste so much time battling kids.", "3": "Hmmph! I guess I just have to hold that loss." } @@ -755,6 +763,7 @@ "encounter": { "1": "It looks like this is the end of the line for you!", "2": "You are a trainer aren't you? I'm afraid that doesn't give you the right to interfere in our work.", + "2_female": "You are a trainer aren't you? I'm afraid that doesn't give you the right to interfere in our work.", "3": "I'm from Macro Cosmos Insurance! Do you have a life insurance policy?" }, "victory": { @@ -772,6 +781,7 @@ "victory": { "1": "*sigh* I wasn't able to win... Oleana...you really are a hopeless woman.", "2": "Arghhh! This is inexcusable... What was I thinking... Any trainer who's made it this far would be no pushover..", + "2_female": "Arghhh! This is inexcusable... What was I thinking... Any trainer who's made it this far would be no pushover..", "3": "*sigh* I am one tired Oleana..." } }, @@ -784,7 +794,7 @@ }, "defeat": { "1": "Mark my words. Not being able to measure your own strength shows that you are still a child.", - "1_female": null + "1_female": "Mark my words. Not being able to measure your own strength shows that you are still a child." } }, "rocket_boss_giovanni_2": { @@ -845,7 +855,7 @@ "galactic_boss_cyrus_1": { "encounter": { "1": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!", - "1_female": null + "1_female": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!" }, "victory": { "1": "Interesting. And quite curious." @@ -995,7 +1005,7 @@ "misty": { "encounter": { "1": "My policy is an all out offensive with Water-type Pokémon!", - "1_female": null, + "1_female": "My policy is an all out offensive with Water-type Pokémon!", "2": "Hiya, I'll show you the strength of my aquatic Pokémon!", "3": "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" }, @@ -1013,14 +1023,14 @@ "lt_surge": { "encounter": { "1": "My Electric Pokémon saved me during the war! I'll show you how!", - "1_female": null, + "1_female": "My Electric Pokémon saved me during the war! I'll show you how!", "2": "Ten-hut! I'll shock you into surrender!", "3": "I'll zap you just like I do to all my enemies in battle!" }, "victory": { "1": "Whoa! Your team's the real deal, kid!", "2": "Aaargh, you're strong! Even my electric tricks lost against you.", - "2_female": null, + "2_female": "Aaargh, you're strong! Even my electric tricks lost against you.", "3": "That was an absolutely shocking loss!" }, "defeat": { @@ -1045,7 +1055,7 @@ "defeat": { "1": "I was afraid I would doze off…", "2": "Oh my, it seems my Grass Pokémon overwhelmed you.", - "2_female": null, + "2_female": "Oh my, it seems my Grass Pokémon overwhelmed you.", "3": "That battle was such a soothing experience.", "4": "Oh… Is that all?" } @@ -1106,7 +1116,7 @@ "1": "I, the leader of Team Rocket, will make you feel a world of pain!", "2": "My training here will be vital before I am to face my old associates again.", "3": "I do not think you are prepared for the level of failure you are about to experience!", - "3_female": null + "3_female": "I do not think you are prepared for the level of failure you are about to experience!" }, "victory": { "1": "WHAT! Me, lose?! There is nothing I wish to say to you!", @@ -1139,7 +1149,7 @@ "brawly": { "encounter": { "1": "Oh man, a challenger!\nLet's see what you can do!", - "1_female": null, + "1_female": "Oh man, a challenger!\nLet's see what you can do!", "2": "You seem like a big splash.\nLet's battle!", "3": "Time to create a storm!\nLet's go!" }, @@ -1167,7 +1177,7 @@ }, "defeat": { "1": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", - "1_female": null, + "1_female": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", "2": "I hope you found our battle electrifying!\nWahahahaha!", "3": "Aren't you shocked I won?\nWahahahaha!" } @@ -1214,7 +1224,7 @@ }, "victory": { "1": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", - "1_female": null, + "1_female": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", "2": "Oh, my Flying Pokémon have plummeted!\nVery well.", "3": "Though I may have fallen, my Pokémon will continue to fly!" }, @@ -1227,7 +1237,7 @@ "tate": { "encounter": { "1": "Hehehe…\nWere you surprised to see me without my sister?", - "1_female": null, + "1_female": "Hehehe…\nWere you surprised to see me without my sister?", "2": "I can see what you're thinking…\nYou want to battle!", "3": "How can you defeat someone…\nWho knows your every move?" }, @@ -1245,7 +1255,7 @@ "liza": { "encounter": { "1": "Fufufu…\nWere you surprised to see me without my brother?", - "1_female": null, + "1_female": "Fufufu…\nWere you surprised to see me without my brother?", "2": "I can determine what you desire…\nYou want to battle, don't you?", "3": "How can you defeat someone…\nWho's one with their Pokémon?" }, @@ -1317,10 +1327,10 @@ "nessa": { "encounter": { "1": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", - "1_female": null, + "1_female": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", "2": "I'm not here to chat. I'm here to win!", "3": "This is a little gift from my Pokémon… I hope you can take it!", - "3_female": null + "3_female": "This is a little gift from my Pokémon… I hope you can take it!" }, "victory": { "1": "You and your Pokémon are just too much…", @@ -1341,7 +1351,7 @@ }, "victory": { "1": "You… You're pretty good, huh?", - "1_female": null, + "1_female": "You… You're pretty good, huh?", "2": "If you find Gordie around, be sure to give him a right trashing, would you?", "3": "I think you took breaking the ice a little too literally…" }, @@ -1355,12 +1365,12 @@ "encounter": { "1": "You look strong! Shoots! Let's start!", "2": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", - "2_female": null, + "2_female": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", "3": "Oh ho, so I'm facing you! That's off the wall." }, "victory": { "1": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", - "1_female": null, + "1_female": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", "2": "You don't just look strong, you're strong fo' reals! Eh, I was swept away, too!", "3": "You're strong as a gnarly wave!" }, @@ -1373,7 +1383,7 @@ "shauntal": { "encounter": { "1": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", - "1_female": null, + "1_female": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", "2": "I absolutely love writing about Trainers who come here and the Pokémon they train.\nCould I use you and your Pokémon as a subject?", "3": "Every person who works with Pokémon has a story to tell.\nWhat story is about to be told?" }, @@ -1391,7 +1401,7 @@ "marshal": { "encounter": { "1": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", - "1_female": null, + "1_female": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", "2": "Victory, decisive victory, is my intention! Challenger, here I come!", "3": "In myself, I seek to develop the strength of a fighter and shatter any weakness in myself!\nPrevailing with the force of my convictions!" }, @@ -1411,7 +1421,7 @@ "1": "You remind me of an old friend. That makes me excited about this Pokémon battle!", "2": "Pokémon battles have no meaning if you don't think why you battle.\n$Or better said, it makes battling together with Pokémon meaningless.", "3": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you.", - "3_female": null + "3_female": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you." }, "victory": { "1": "Thank you! I saw what was missing in me.", @@ -1427,73 +1437,73 @@ "chili": { "encounter": { "1": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", - "1_female": null, + "1_female": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", "2": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", - "2_female": null, + "2_female": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", "3": "I'm going to show you what me and my blazing Fire types can do!", - "3_female": null + "3_female": "I'm going to show you what me and my blazing Fire types can do!" }, "victory": { "1": "You got me. I am… burned… out…", - "1_female": null, + "1_female": "You got me. I am… burned… out…", "2": "Whoa ho! You're on fire!", - "2_female": null, + "2_female": "Whoa ho! You're on fire!", "3": "Augh! You got me!" }, "defeat": { "1": "I'm on fire! Play with me, and you'll get burned!", - "1_female": null, + "1_female": "I'm on fire! Play with me, and you'll get burned!", "2": "When you play with fire, you get burned!", "3": "I mean, c'mon, your opponent was me! You didn't have a chance!", - "3_female": null + "3_female": "I mean, c'mon, your opponent was me! You didn't have a chance!" } }, "cilan": { "encounter": { "1": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", - "1_female": null, + "1_female": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", "2": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", - "2_female": null, + "2_female": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", "3": "OK… So, um, I'm Cilan, I like Grass-type Pokémon.", - "3_female": null + "3_female": "OK… So, um, I'm Cilan, I like Grass-type Pokémon." }, "victory": { "1": "Er… Is it over now?", - "1_female": null, + "1_female": "Er… Is it over now?", "2": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", - "2_female": null, + "2_female": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", "3": "…Huh. Looks like my timing was, um, off?" }, "defeat": { "1": "Huh? Did I win?", - "1_female": null, + "1_female": "Huh? Did I win?", "2": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", - "2_female": null, + "2_female": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", "3": "It…it was quite a thrilling experience…", - "3_female": null + "3_female": "It…it was quite a thrilling experience…" } }, "roark": { "encounter": { "1": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", - "1_female": null, + "1_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", "2": "Here goes! These are my rocking Pokémon, my pride and joy!", "3": "Rock-type Pokémon are simply the best!", - "4": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", - "4_female": null + "4": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?", + "4_female": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?" }, "victory": { "1": "W-what? That can't be! My buffed-up Pokémon!", "2": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", - "2_female": null, + "2_female": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", "3": "With skill like yours, it's natural for you to win.", - "4": "Wh-what?! It can't be! Even that wasn't enough?", - "5": "I blew it." + "4": "Wh-what?! It can't be! Even that wasn't enough?" }, "defeat": { "1": "See? I'm proud of my rocking battle style!", "2": "Thanks! The battle gave me confidence that I may be able to beat my dad!", - "3": "I feel like I just smashed through a really stubborn boulder!" + "3": "See? These are my rocking Pokémon, my pride and joy!", + "4": "I knew I would win!" } }, "morty": { @@ -1508,7 +1518,7 @@ "victory": { "1": "I'm not good enough yet…", "2": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", - "2_female": null, + "2_female": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", "3": "How is this possible…", "4": "I don't think our potentials are so different.\n$But you seem to have something more than that… So be it.", "5": "Guess I need more training.", @@ -1568,13 +1578,13 @@ }, "defeat": { "1": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?", - "1_female": null + "1_female": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?" } }, "ramos": { "encounter": { "1": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?", - "1_female": null + "1_female": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?" }, "victory": { "1": "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." @@ -1605,7 +1615,7 @@ "victory": { "1": "I must say, I'm warmed up to you! I might even admire you a little.", "2": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. ", - "2_female": null + "2_female": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. " }, "defeat": { "1": "I sensed your will to win, but I don't lose!", @@ -1618,7 +1628,7 @@ }, "victory": { "1": "Amazing! You're very good, aren't you?", - "1_female": null + "1_female": "Amazing! You're very good, aren't you?" }, "defeat": { "1": "Yes! My Pokémon and I are perfectly good!" @@ -1660,7 +1670,7 @@ "clay": { "encounter": { "1": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!", - "1_female": null + "1_female": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!" }, "victory": { "1": "Man oh man… It feels good to go all out and still be defeated!" @@ -1675,7 +1685,7 @@ }, "victory": { "1": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!", - "1_female": null + "1_female": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" }, "defeat": { "1": "You come back to see me again now, ya hear?" @@ -1742,7 +1752,7 @@ }, "victory": { "1": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy.", - "1_female": null + "1_female": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy." }, "defeat": { "1": "A grand illusion!" @@ -1751,14 +1761,14 @@ "lorelei": { "encounter": { "1": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?", - "1_female": null + "1_female": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?" }, "victory": { "1": "How dare you!" }, "defeat": { "1": "There's nothing you can do once you're frozen.", - "1_female": null + "1_female": "There's nothing you can do once you're frozen." } }, "will": { @@ -1775,11 +1785,11 @@ "malva": { "encounter": { "1": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!", - "1_female": null + "1_female": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!" }, "victory": { "1": "What news… So a new challenger has defeated Malva!", - "1_female": null + "1_female": "What news… So a new challenger has defeated Malva!" }, "defeat": { "1": "I am delighted! Yes, delighted that I could squash you beneath my heel." @@ -1802,7 +1812,7 @@ }, "victory": { "1": "I certainly found an interesting Trainer to face!", - "1_female": null + "1_female": "I certainly found an interesting Trainer to face!" }, "defeat": { "1": "Ahaha. What an interesting battle." @@ -1814,11 +1824,11 @@ }, "victory": { "1": "Not bad, kiddo.", - "1_female": null + "1_female": "Not bad, kiddo." }, "defeat": { "1": "Nahahaha! You really are something else, kiddo!", - "1_female": null + "1_female": "Nahahaha! You really are something else, kiddo!" } }, "bruno": { @@ -1838,7 +1848,7 @@ }, "victory": { "1": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win.", - "1_female": null + "1_female": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win." }, "defeat": { "1": "Thanks! Thanks to our battle, I was also able to make progress in my research!" @@ -1869,11 +1879,11 @@ "lenora": { "encounter": { "1": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!", - "1_female": null + "1_female": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!" }, "victory": { "1": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!", - "1_female": null + "1_female": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!" }, "defeat": { "1": "Ah ha ha! If you lose, make sure to analyze why, and use that knowledge in your next battle!" @@ -1899,7 +1909,7 @@ }, "defeat": { "1": "Hey, c'mon! Get serious! You gotta put more out there!", - "1_female": null + "1_female": "Hey, c'mon! Get serious! You gotta put more out there!" } }, "olivia": { @@ -1938,7 +1948,7 @@ "flint": { "encounter": { "1": "Hope you're warmed up, cause here comes the Big Bang!", - "1_female": null + "1_female": "Hope you're warmed up, cause here comes the Big Bang!" }, "victory": { "1": "Incredible! Your moves are so hot, they make mine look lukewarm!" @@ -1961,7 +1971,7 @@ "caitlin": { "encounter": { "1": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!", - "1_female": null + "1_female": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!" }, "victory": { "1": "My Pokémon and I learned so much! I offer you my thanks." @@ -1984,15 +1994,15 @@ "wikstrom": { "encounter": { "1": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!", - "1_female": null + "1_female": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!" }, "victory": { "1": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!", - "1_female": null + "1_female": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!" }, "defeat": { "1": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!", - "1_female": null + "1_female": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!" } }, "acerola": { @@ -2024,14 +2034,14 @@ }, "victory": { "1": "You got me. You are magnificent!", - "1_female": null, + "1_female": "You got me. You are magnificent!", "2": "I never expected another trainer to beat me… I'm surprised.", - "2_female": null + "2_female": "I never expected another trainer to beat me… I'm surprised." }, "defeat": { "1": "That was close. Want to try again?", "2": "It's not that you are weak. Don't let it bother you.", - "2_female": null + "2_female": "It's not that you are weak. Don't let it bother you." } }, "karen": { @@ -2057,7 +2067,7 @@ }, "victory": { "1": "The power of Grass has wilted… What an incredible Challenger!", - "1_female": null + "1_female": "The power of Grass has wilted… What an incredible Challenger!" }, "defeat": { "1": "This'll really leave you in shock and awe." @@ -2077,7 +2087,7 @@ "drasna": { "encounter": { "1": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!", - "1_female": null + "1_female": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!" }, "victory": { "1": "Oh, dear me. That sure was a quick battle… I do hope you'll come back again sometime!" @@ -2111,7 +2121,7 @@ "blue": { "encounter": { "1": "You must be pretty good to get this far.", - "1_female": null + "1_female": "You must be pretty good to get this far." }, "victory": { "1": "I've only lost to him and now to you… Him? Hee, hee…" @@ -2159,7 +2169,7 @@ }, "victory": { "1": "This is the emergence of a new Champion.", - "1_female": null + "1_female": "This is the emergence of a new Champion." }, "defeat": { "1": "I successfully defended my Championship." @@ -2248,7 +2258,7 @@ }, "victory": { "1": "Waaah! Waaah! You're so mean!", - "1_female": null + "1_female": "Waaah! Waaah! You're so mean!" }, "defeat": { "1": "And that's that!" @@ -2257,7 +2267,7 @@ "chuck": { "encounter": { "1": "Hah! You want to challenge me? Are you brave or just ignorant?", - "1_female": null + "1_female": "Hah! You want to challenge me? Are you brave or just ignorant?" }, "victory": { "1": "You're strong! Would you please make me your apprentice?" @@ -2269,7 +2279,7 @@ "katy": { "encounter": { "1": "Don't let your guard down unless you would like to find yourself knocked off your feet!", - "1_female": null + "1_female": "Don't let your guard down unless you would like to find yourself knocked off your feet!" }, "victory": { "1": "All of my sweet little Pokémon dropped like flies!" @@ -2303,7 +2313,7 @@ "maylene": { "encounter": { "1": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!", - "1_female": null + "1_female": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!" }, "victory": { "1": "I admit defeat…" @@ -2326,7 +2336,7 @@ "byron": { "encounter": { "1": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!", - "1_female": null + "1_female": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!" }, "victory": { "1": "Hmm! My sturdy Pokémon--defeated!" @@ -2349,7 +2359,7 @@ "volkner": { "encounter": { "1": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!", - "1_female": null + "1_female": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!" }, "victory": { "1": "You've got me beat…\n$Your desire and the noble way your Pokémon battled for you… \n$I even felt thrilled during our match. That was a very good battle." @@ -2452,7 +2462,7 @@ "valerie": { "encounter": { "1": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong.", - "1_female": null + "1_female": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong." }, "victory": { "1": "I hope that you will find things worth smiling about tomorrow…" @@ -2500,7 +2510,7 @@ }, "victory": { "1": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon.", - "1_female": null + "1_female": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon." }, "defeat": { "1": "Too bad for you, I guess." @@ -2509,7 +2519,7 @@ "bede": { "encounter": { "1": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am.", - "1_female": null + "1_female": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am." }, "victory": { "1": "I see… Well, that's fine. I wasn't really trying all that hard anyway." @@ -2554,7 +2564,7 @@ "brassius": { "encounter": { "1": "I assume you are ready? Let our collaborative work of art begin!", - "1_female": null + "1_female": "I assume you are ready? Let our collaborative work of art begin!" }, "victory": { "1": "Ahhh…vant-garde!" @@ -2566,11 +2576,11 @@ "iono": { "encounter": { "1": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!", - "1_female": null + "1_female": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!" }, "victory": { "1": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!", - "1_female": null + "1_female": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" }, "defeat": { "1": "Your eyeballs are MINE!" @@ -2593,7 +2603,7 @@ }, "victory": { "1": "You're cool, my friend—you move my SOUL!", - "1_female": null + "1_female": "You're cool, my friend—you move my SOUL!" }, "defeat": { "1": "Later, baby!" @@ -2627,9 +2637,9 @@ "nessa_elite": { "encounter": { "1": "The tides are turning in my favor. Ready to get swept away?", - "1_female": null, + "1_female": "The tides are turning in my favor. Ready to get swept away?", "2": "Let's make some waves with this battle! I hope you're prepared!", - "2_female": null + "2_female": "Let's make some waves with this battle! I hope you're prepared!" }, "victory": { "1": "You navigated those waters perfectly... Well done!", @@ -2657,7 +2667,7 @@ "allister_elite": { "encounter": { "1": "Shadows fall... Are you ready to face your fears?", - "1_female": null, + "1_female": "Shadows fall... Are you ready to face your fears?", "2": "Let's see if you can handle the darkness that I command." }, "victory": { @@ -2681,7 +2691,7 @@ "defeat": { "1": "Another storm weathered, another victory claimed! Well fought!", "2": "You got caught in my storm! Better luck next time!", - "2_female": null + "2_female": "You got caught in my storm! Better luck next time!" } }, "alder": { diff --git a/src/locales/en/fight-ui-handler.json b/src/locales/en/fight-ui-handler.json index 35b7f42772a..bd6bec878c8 100644 --- a/src/locales/en/fight-ui-handler.json +++ b/src/locales/en/fight-ui-handler.json @@ -2,6 +2,7 @@ "pp": "PP", "power": "Power", "accuracy": "Accuracy", - "abilityFlyInText": " {{pokemonName}}'s {{passive}}{{abilityName}}", - "passive": "Passive " -} \ No newline at end of file + "abilityFlyInText": " {{pokemonName}}’s\n{{passive}}{{abilityName}}", + "passive": "Passive ", + "teraHover": "{{type}} Terastallized" +} diff --git a/src/locales/en/menu-ui-handler.json b/src/locales/en/menu-ui-handler.json index fccf9cd3002..0536fa12c2e 100644 --- a/src/locales/en/menu-ui-handler.json +++ b/src/locales/en/menu-ui-handler.json @@ -24,6 +24,7 @@ "linkGoogle": "Link Google", "unlinkGoogle": "Unlink Google", "cancel": "Cancel", + "donate": "Donate", "losingProgressionWarning": "You will lose any progress since the beginning of the battle. Proceed?", "noEggs": "You are not hatching\nany eggs at the moment!" } \ No newline at end of file diff --git a/src/locales/en/menu.json b/src/locales/en/menu.json index 97cfc0b019d..91888e9db31 100644 --- a/src/locales/en/menu.json +++ b/src/locales/en/menu.json @@ -51,5 +51,7 @@ "renamePokemon": "Rename Pokémon", "rename": "Rename", "nickname": "Nickname", - "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect." + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", + "noSaves": "You don't have any save files on record!", + "tooManySaves": "You have too many save files on record!" } \ No newline at end of file diff --git a/src/locales/en/modifier-type.json b/src/locales/en/modifier-type.json index ed1ef900878..babad57b81b 100644 --- a/src/locales/en/modifier-type.json +++ b/src/locales/en/modifier-type.json @@ -47,10 +47,14 @@ "description": "Changes a Pokémon's nature to {{natureName}} and permanently unlocks the nature for the starter." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Doubles the chance of an encounter being a double battle for {{battleCount}} battles." + "description": "Quadruples the chance of an encounter being a double battle for up to {{battleCount}} battles." }, - "TempBattleStatBoosterModifierType": { - "description": "Increases the {{tempBattleStatName}} of all party members by 1 stage for 5 battles." + "TempStatStageBoosterModifierType": { + "description": "Increases the {{stat}} of all party members by {{amount}} for up to 5 battles.", + "extra": { + "stage": "1 stage", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "Increases the power of a Pokémon's {{moveType}}-type moves by 20%." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "Increases all party members' level by {{levels}}." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Increases the holder's base {{statName}} by 10%. The higher your IVs, the higher the stack limit." + "BaseStatBoosterModifierType": { + "description": "Increases the holder's base {{stat}} by 10%. The higher your IVs, the higher the stack limit." }, "AllPokemonFullHpRestoreModifierType": { "description": "Restores 100% HP for all Pokémon." @@ -183,6 +187,7 @@ "SOOTHE_BELL": { "name": "Soothe Bell" }, "SCOPE_LENS": { "name": "Scope Lens", "description": "It's a lens for scoping out weak points. It boosts the holder's critical-hit ratio."}, + "DIRE_HIT": { "name": "Dire Hit", "extra": { "raises": "Critical Hit Ratio" } }, "LEEK": { "name": "Leek", "description": "This very long and stiff stalk of leek boosts the critical-hit ratio of Farfetch'd's moves."}, "EVIOLITE": { "name": "Eviolite", "description": "This mysterious evolutionary lump boosts the Defense and Sp. Def stats when held by a Pokémon that can still evolve." }, @@ -250,28 +255,14 @@ "METAL_POWDER": { "name": "Metal Powder", "description": "Extremely fine yet hard, this odd powder boosts Ditto's Defense stat." }, "QUICK_POWDER": { "name": "Quick Powder", "description": "Extremely fine yet hard, this odd powder boosts Ditto's Speed stat." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "X Attack", "x_defense": "X Defense", "x_sp_atk": "X Sp. Atk", "x_sp_def": "X Sp. Def", "x_speed": "X Speed", - "x_accuracy": "X Accuracy", - "dire_hit": "Dire Hit" + "x_accuracy": "X Accuracy" }, - - "TempBattleStatBoosterStatName": { - "ATK": "Attack", - "DEF": "Defense", - "SPATK": "Sp. Atk", - "SPDEF": "Sp. Def", - "SPD": "Speed", - "ACC": "Accuracy", - "CRIT": "Critical Hit Ratio", - "EVA": "Evasiveness", - "DEFAULT": "???" - }, - "AttackTypeBoosterItem": { "silk_scarf": "Silk Scarf", "black_belt": "Black Belt", @@ -450,6 +441,6 @@ "DRAGON_MEMORY": "Dragon Memory", "DARK_MEMORY": "Dark Memory", "FAIRY_MEMORY": "Fairy Memory", - "BLANK_MEMORY": "Blank Memory" + "NORMAL_MEMORY": "Normal Memory" } } diff --git a/src/locales/en/modifier.json b/src/locales/en/modifier.json index 473be0e8bfa..47944c8adb7 100644 --- a/src/locales/en/modifier.json +++ b/src/locales/en/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", "hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", + "resetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", "moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!", "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!", "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!", diff --git a/src/locales/en/move-trigger.json b/src/locales/en/move-trigger.json index baddbaa34bf..375ea354d33 100644 --- a/src/locales/en/move-trigger.json +++ b/src/locales/en/move-trigger.json @@ -3,6 +3,11 @@ "cutHpPowerUpMove": "{{pokemonName}} cut its own HP to power up its move!", "absorbedElectricity": "{{pokemonName}} absorbed electricity!", "switchedStatChanges": "{{pokemonName}} switched stat changes with the target!", + "switchedTwoStatChanges": "{{pokemonName}} switched all changes to its {{firstStat}}\nand {{secondStat}} with its target!", + "switchedStat": "{{pokemonName}} switched {{stat}} with its target!", + "sharedGuard": "{{pokemonName}} shared its guard with the target!", + "sharedPower": "{{pokemonName}} shared its power with the target!", + "shiftedStats": "{{pokemonName}} switched its {{statToSwitch}} and {{statToSwitchWith}}!", "goingAllOutForAttack": "{{pokemonName}} is going all out for this attack!", "regainedHealth": "{{pokemonName}} regained\nhealth!", "keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!", @@ -61,5 +66,7 @@ "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", "revivalBlessing": "{{pokemonName}} was revived!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", - "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!" + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", + "safeguard": "{{targetName}} is protected by Safeguard!", + "afterYou": "{{pokemonName}} took the kind offer!" } diff --git a/src/locales/en/pokemon-info.json b/src/locales/en/pokemon-info.json index 87d2f7ad17b..b79daaed621 100644 --- a/src/locales/en/pokemon-info.json +++ b/src/locales/en/pokemon-info.json @@ -1,7 +1,7 @@ { "Stat": { "HP": "Max. HP", - "HPshortened": "MaxHP", + "HPshortened": "HP", "ATK": "Attack", "ATKshortened": "Atk", "DEF": "Defense", @@ -13,8 +13,7 @@ "SPD": "Speed", "SPDshortened": "Spd", "ACC": "Accuracy", - "EVA": "Evasiveness", - "HPStat": "HP" + "EVA": "Evasiveness" }, "Type": { "UNKNOWN": "Unknown", @@ -38,4 +37,4 @@ "FAIRY": "Fairy", "STELLAR": "Stellar" } -} \ No newline at end of file +} diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 6528f0368fe..ee8a43d7510 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "EXP Gains Speed", "expPartyDisplay": "Show EXP Party", "skipSeenDialogues": "Skip Seen Dialogues", + "eggSkip": "Egg Skip", + "never": "Never", + "always": "Always", + "ask": "Ask", "battleStyle": "Battle Style", "enableRetries": "Enable Retries", "hideIvs": "Hide IV scanner", @@ -100,7 +104,7 @@ "moveTouchControls": "Move Touch Controls", "shopOverlayOpacity": "Shop Overlay Opacity", "shopCursorTarget": "Shop Cursor Target", - "items": "Items", + "rewards": "Rewards", "reroll": "Reroll", "shop": "Shop", "checkTeam": "Check Team" diff --git a/src/locales/es/ability-trigger.json b/src/locales/es/ability-trigger.json index 8bbcc80662c..4380c84b8e9 100644 --- a/src/locales/es/ability-trigger.json +++ b/src/locales/es/ability-trigger.json @@ -12,6 +12,7 @@ "blockItemTheft": "¡{{pokemonNameWithAffix}} evitó el robo gracias a {{abilityName}}!", "typeImmunityHeal": "¡{{pokemonNameWithAffix}} restauró algunos de sus PS gracias a {{abilityName}}!", "nonSuperEffectiveImmunity": "¡{{pokemonNameWithAffix}} evitó el daño gracias a {{abilityName}}!", + "fullHpResistType": "¡{{pokemonNameWithAffix}} ha hecho brillar su caparazón\ny ha alterado su compatibilidad entre tipos!", "moveImmunity": "¡No afecta a {{pokemonNameWithAffix}}!", "reverseDrain": "¡{{pokemonNameWithAffix}} absorbió lodo líquido!", "postDefendTypeChange": "¡{{abilityName}} de {{pokemonNameWithAffix}} cambió a tipo {{typeName}}!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "¡{{pokemonNameWithAffix}} irradia un aura chisporroteante!", "postSummonDarkAura": "¡{{pokemonNameWithAffix}} irradia un aura oscura!", "postSummonFairyAura": "¡{{pokemonNameWithAffix}} irradia un aura feérica!", + "postSummonAuraBreak": "¡{{pokemonNameWithAffix}} ha invertido todas las auras!", "postSummonNeutralizingGas": "¡El Gas Reactivo de {{pokemonNameWithAffix}} se propaga por toda la zona!", "postSummonAsOneGlastrier": "¡{{pokemonNameWithAffix}} tiene dos Habilidades!", "postSummonAsOneSpectrier": "¡{{pokemonNameWithAffix}} tiene dos Habilidades!", diff --git a/src/locales/es/ability.json b/src/locales/es/ability.json index 807cc755c94..598694f441e 100644 --- a/src/locales/es/ability.json +++ b/src/locales/es/ability.json @@ -1,1242 +1,1242 @@ { "stench": { - "name": "Hedor", + "name": "Hedor", "description": "Puede amedrentar a un Pokémon al atacarlo debido al mal olor que emana." }, "drizzle": { - "name": "Llovizna", + "name": "Llovizna", "description": "Hace que llueva al entrar en combate." }, "speedBoost": { - "name": "Impulso", + "name": "Impulso", "description": "Aumenta su Velocidad en cada turno." }, "battleArmor": { - "name": "Armadura Batalla", + "name": "Armadura Batalla", "description": "La robusta coraza que lo protege bloquea los golpes críticos." }, "sturdy": { - "name": "Robustez", + "name": "Robustez", "description": "El Pokémon no puede debilitarse de un solo golpe cuando tiene los PS al máximo. También evita los movimientos fulminantes." }, "damp": { - "name": "Humedad", + "name": "Humedad", "description": "Aumenta la humedad del entorno y evita que se puedan utilizar movimientos explosivos, tales como Autodestrucción." }, "limber": { - "name": "Flexibilidad", + "name": "Flexibilidad", "description": "Evita ser paralizado gracias a la flexibilidad de su cuerpo." }, "sandVeil": { - "name": "Velo Arena", + "name": "Velo Arena", "description": "Aumenta su Evasión durante las tormentas de arena." }, "static": { - "name": "Elec. Estática", + "name": "Elec. Estática", "description": "La electricidad estática que lo envuelve puede paralizar al Pokémon que lo ataque con un movimiento de contacto." }, "voltAbsorb": { - "name": "Absorbe Elec", + "name": "Absorbe Elec", "description": "Si lo alcanza un movimiento de tipo Eléctrico, recupera PS en vez de sufrir daño." }, "waterAbsorb": { - "name": "Absorbe Agua", + "name": "Absorbe Agua", "description": "Si lo alcanza un movimiento de tipo Agua, recupera PS en vez de sufrir daño." }, "oblivious": { - "name": "Despiste", + "name": "Despiste", "description": "Su indiferencia evita que sea provocado, caiga presa del enamoramiento o sufra los efectos de Intimidación." }, "cloudNine": { - "name": "Aclimatación", + "name": "Aclimatación", "description": "Anula todos los efectos del tiempo atmosférico." }, "compoundEyes": { - "name": "Ojo Compuesto", + "name": "Ojo Compuesto", "description": "Aumenta la precisión de sus movimientos." }, "insomnia": { - "name": "Insomnio", + "name": "Insomnio", "description": "Su resistencia al sueño le impide quedarse dormido." }, "colorChange": { - "name": "Cambio Color", + "name": "Cambio Color", "description": "Adopta el tipo del último movimiento del que es blanco." }, "immunity": { - "name": "Inmunidad", + "name": "Inmunidad", "description": "Su sistema inmunitario evita el envenenamiento." }, "flashFire": { - "name": "Absorbe Fuego", + "name": "Absorbe Fuego", "description": "Si lo alcanza algún movimiento de tipo Fuego, potencia sus propios movimientos de dicho tipo." }, "shieldDust": { - "name": "Polvo Escudo", + "name": "Polvo Escudo", "description": "El polvo de escamas que lo envuelve lo protege de los efectos secundarios de los ataques recibidos." }, "ownTempo": { - "name": "Ritmo Propio", + "name": "Ritmo Propio", "description": "Como le gusta hacer las cosas a su manera, no le afecta la confusión ni sufre los efectos de Intimidación." }, "suctionCups": { - "name": "Ventosas", + "name": "Ventosas", "description": "Sus ventosas se aferran al suelo, con lo cual anula movimientos y objetos que fuercen el cambio de Pokémon." }, "intimidate": { - "name": "Intimidación", + "name": "Intimidación", "description": "Al entrar en combate, amilana al rival de tal manera que reduce su Ataque." }, "shadowTag": { - "name": "Sombra Trampa", + "name": "Sombra Trampa", "description": "Pisa la sombra del rival para impedir que huya o lo cambien por otro." }, "roughSkin": { - "name": "Piel Tosca", + "name": "Piel Tosca", "description": "Hiere con su piel áspera al Pokémon que lo ataque con un movimiento de contacto." }, "wonderGuard": { - "name": "Superguarda", + "name": "Superguarda", "description": "Gracias a un poder misterioso, solo le hacen daño los movimientos supereficaces." }, "levitate": { - "name": "Levitación", + "name": "Levitación", "description": "Su capacidad de flotar sobre el suelo le proporciona inmunidad frente a los movimientos de tipo Tierra." }, "effectSpore": { - "name": "Efecto Espora", + "name": "Efecto Espora", "description": "Puede dormir, envenenar o paralizar al Pokémon que lo ataque con un movimiento de contacto." }, "synchronize": { - "name": "Sincronía", + "name": "Sincronía", "description": "Contagia el envenenamiento, las quemaduras o la parálisis al Pokémon que le cause ese estado." }, "clearBody": { - "name": "Cuerpo Puro", + "name": "Cuerpo Puro", "description": "Evita que se reduzcan sus características a causa de movimientos o habilidades de otros Pokémon." }, "naturalCure": { - "name": "Cura Natural", + "name": "Cura Natural", "description": "Sus problemas de estado desaparecen cuando se retira del combate." }, "lightningRod": { - "name": "Pararrayos", + "name": "Pararrayos", "description": "Atrae y neutraliza los movimientos de tipo Eléctrico, que además le aumentan el Ataque Especial." }, "sereneGrace": { - "name": "Dicha", + "name": "Dicha", "description": "Aumenta la probabilidad de que los movimientos causen efectos secundarios." }, "swiftSwim": { - "name": "Nado Rápido", + "name": "Nado Rápido", "description": "Aumenta su Velocidad cuando llueve." }, "chlorophyll": { - "name": "Clorofila", + "name": "Clorofila", "description": "Aumenta su Velocidad cuando hace sol." }, "illuminate": { - "name": "Iluminación", + "name": "Iluminación", "description": "Al iluminar el entorno, evita que su Precisión se reduzca." }, "trace": { - "name": "Calco", + "name": "Calco", "description": "Copia la habilidad del rival al entrar en combate." }, "hugePower": { - "name": "Potencia", + "name": "Potencia", "description": "Duplica la potencia de sus ataques físicos." }, "poisonPoint": { - "name": "Punto Tóxico", + "name": "Punto Tóxico", "description": "Puede envenenar al Pokémon que lo ataque con un movimiento de contacto." }, "innerFocus": { - "name": "Fuerza Mental", + "name": "Fuerza Mental", "description": "Gracias a su profunda concentración, no se amedrenta ante los ataques de otros Pokémon ni sufre los efectos de Intimidación." }, "magmaArmor": { - "name": "Escudo Magma", + "name": "Escudo Magma", "description": "Gracias al magma candente que lo envuelve, no puede ser congelado." }, "waterVeil": { - "name": "Velo Agua", + "name": "Velo Agua", "description": "Evita las quemaduras gracias a la capa de agua que lo envuelve." }, "magnetPull": { - "name": "Imán", + "name": "Imán", "description": "Su magnetismo atrae a los Pokémon de tipo Acero y les impide huir o ser cambiados por otros." }, "soundproof": { - "name": "Insonorizar", + "name": "Insonorizar", "description": "Su aislamiento acústico lo protege de movimientos que usan sonido." }, "rainDish": { - "name": "Cura Lluvia", + "name": "Cura Lluvia", "description": "Recupera PS de forma gradual cuando llueve." }, "sandStream": { - "name": "Chorro Arena", + "name": "Chorro Arena", "description": "Crea una tormenta de arena al entrar en combate." }, "pressure": { - "name": "Presión", + "name": "Presión", "description": "Presiona al rival de tal manera que este consume más PP al usar sus movimientos." }, "thickFat": { - "name": "Sebo", + "name": "Sebo", "description": "Gracias a la gruesa capa de grasa que lo protege, reduce a la mitad el daño que recibe de ataques de tipo Fuego o Hielo." }, "earlyBird": { - "name": "Madrugar", + "name": "Madrugar", "description": "Si se duerme, tardará la mitad de tiempo en despertarse." }, "flameBody": { - "name": "Cuerpo Llama", + "name": "Cuerpo Llama", "description": "Puede quemar al Pokémon que lo ataque con un movimiento de contacto." }, "runAway": { - "name": "Fuga", + "name": "Fuga", "description": "Puede escapar de cualquier Pokémon salvaje." }, "keenEye": { - "name": "Vista Lince", + "name": "Vista Lince", "description": "Su aguda vista evita que su Precisión se reduzca." }, "hyperCutter": { - "name": "Corte Fuerte", + "name": "Corte Fuerte", "description": "Evita que otros Pokémon le reduzcan el Ataque." }, "pickup": { - "name": "Recogida", - "description": "Puede recoger objetos que otros Pokémon hayan usado, o bien aquellos que encuentre en plena aventura." + "name": "Recogida", + "description": "Puede que recoja un objeto del enemigo tras cada batalla, al azar." }, "truant": { - "name": "Pereza", + "name": "Pereza", "description": "Al ejecutar un movimiento, descansará en el turno siguiente." }, "hustle": { - "name": "Entusiasmo", - "description": "Aumenta su Ataque, pero reduce su Precisión." + "name": "Entusiasmo", + "description": "Aumenta su ataque, pero reduce su precisión." }, "cuteCharm": { - "name": "Gran Encanto", + "name": "Gran Encanto", "description": "Puede causar enamoramiento al Pokémon que lo ataque con un movimiento de contacto." }, "plus": { - "name": "Más", - "description": "Aumenta su Ataque Especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." + "name": "Más", + "description": "Aumenta su ataque especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." }, "minus": { - "name": "Menos", - "description": "Aumenta su Ataque Especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." + "name": "Menos", + "description": "Aumenta su ataque especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." }, "forecast": { - "name": "Predicción", + "name": "Predicción", "description": "Cambia a tipo Agua, Fuego o Hielo en función del tiempo atmosférico." }, "stickyHold": { - "name": "Viscosidad", + "name": "Viscosidad", "description": "Los objetos se quedan pegados a su cuerpo, por lo que no pueden robárselos." }, "shedSkin": { - "name": "Mudar", + "name": "Mudar", "description": "Puede curar sus problemas de estado al mudar la piel." }, "guts": { - "name": "Agallas", - "description": "Si sufre un problema de estado, se arma de valor y aumenta su Ataque." + "name": "Agallas", + "description": "Si sufre un problema de estado, se arma de valor y aumenta su ataque." }, "marvelScale": { - "name": "Escama Especial", - "description": "Si sufre un problema de estado, sus escamas especiales reaccionan y aumenta su Defensa." + "name": "Escama Especial", + "description": "Si sufre un problema de estado, sus escamas especiales reaccionan y aumenta su defensa." }, "liquidOoze": { - "name": "Viscosecreción", + "name": "Viscosecreción", "description": "Exuda una secreción viscosa y tóxica de intenso hedor que hiere a quienes intentan drenarle PS." }, "overgrow": { - "name": "Espesura", + "name": "Espesura", "description": "Potencia sus movimientos de tipo Planta cuando le quedan pocos PS." }, "blaze": { - "name": "Mar Llamas", + "name": "Mar Llamas", "description": "Potencia sus movimientos de tipo Fuego cuando le quedan pocos PS." }, "torrent": { - "name": "Torrente", + "name": "Torrente", "description": "Potencia sus movimientos de tipo Agua cuando le quedan pocos PS." }, "swarm": { - "name": "Enjambre", + "name": "Enjambre", "description": "Potencia sus movimientos de tipo Bicho cuando le quedan pocos PS." }, "rockHead": { - "name": "Cabeza Roca", + "name": "Cabeza Roca", "description": "No pierde PS al usar movimientos que también hieren al usuario." }, "drought": { - "name": "Sequía", + "name": "Sequía", "description": "El tiempo pasa a ser soleado al entrar en combate." }, "arenaTrap": { - "name": "Trampa Arena", + "name": "Trampa Arena", "description": "Evita que el rival huya o sea cambiado por otro." }, "vitalSpirit": { - "name": "Espíritu Vital", + "name": "Espíritu Vital", "description": "Su determinación le impide quedarse dormido." }, "whiteSmoke": { - "name": "Humo Blanco", + "name": "Humo Blanco", "description": "El humo blanco que lo protege evita que otros Pokémon le reduzcan las características." }, "purePower": { - "name": "Energía Pura", + "name": "Energía Pura", "description": "Duplica la potencia de sus ataques físicos gracias al yoga." }, "shellArmor": { - "name": "Caparazón", + "name": "Caparazón", "description": "La robusta coraza que lo protege bloquea los golpes críticos." }, "airLock": { - "name": "Esclusa de Aire", + "name": "Esclusa de Aire", "description": "Neutraliza todos los efectos del tiempo atmosférico." }, "tangledFeet": { - "name": "Tumbos", - "description": "Aumenta su Evasión si está confuso." + "name": "Tumbos", + "description": "Aumenta su evasión si está confuso." }, "motorDrive": { - "name": "Electromotor", - "description": "Si lo alcanza un movimiento de tipo Eléctrico, aumenta su Velocidad en vez de sufrir daño." + "name": "Electromotor", + "description": "Si lo alcanza un movimiento de tipo Eléctrico, aumenta su velocidad en vez de sufrir daño." }, "rivalry": { - "name": "Rivalidad", + "name": "Rivalidad", "description": "Si el objetivo es del mismo sexo, su competitividad le lleva a infligir más daño. Si es del sexo contrario, en cambio, el daño será menor." }, "steadfast": { - "name": "Impasible", - "description": "Cada vez que se amedrenta, aumenta su Velocidad debido a su voluntad inquebrantable." + "name": "Impasible", + "description": "Cada vez que se amedrenta, aumenta su velocidad debido a su voluntad inquebrantable." }, "snowCloak": { - "name": "Manto Níveo", - "description": "Aumenta su Evasión cuando nieva." + "name": "Manto Níveo", + "description": "Aumenta su evasión cuando nieva." }, "gluttony": { - "name": "Gula", + "name": "Gula", "description": "Cuando sus PS se ven reducidos a la mitad, engulle la baya que normalmente solo se comería cuando le quedasen pocos PS." }, "angerPoint": { - "name": "Irascible", - "description": "Si recibe un golpe crítico, monta en cólera y su Ataque aumenta al máximo." + "name": "Irascible", + "description": "Si recibe un golpe crítico, monta en cólera y su ataque aumenta al máximo." }, "unburden": { - "name": "Liviano", - "description": "Aumenta su Velocidad si usa o pierde el objeto que lleva." + "name": "Liviano", + "description": "Aumenta su velocidad si usa o pierde el objeto que lleva." }, "heatproof": { - "name": "Ignífugo", + "name": "Ignífugo", "description": "Su cuerpo, resistente al calor, reduce a la mitad el daño recibido por movimientos de tipo Fuego." }, "simple": { - "name": "Simple", + "name": "Simple", "description": "Duplica los cambios en las características." }, "drySkin": { - "name": "Piel Seca", + "name": "Piel Seca", "description": "Pierde PS si hace sol y los recupera si llueve o recibe un movimiento de tipo Agua. Los movimientos de tipo Fuego, por su parte, le hacen más daño de lo normal." }, "download": { - "name": "Descarga", - "description": "Compara la Defensa y la Defensa Especial del rival para ver cuál es inferior y aumenta su propio Ataque o Ataque Especial según sea lo más eficaz." + "name": "Descarga", + "description": "Compara la defensa y la defensa especial del rival para ver cuál es inferior y aumenta su propio ataque o ataque especial según sea lo más eficaz." }, "ironFist": { - "name": "Puño Férreo", + "name": "Puño Férreo", "description": "Aumenta la potencia de los movimientos con los puños." }, "poisonHeal": { - "name": "Antídoto", + "name": "Antídoto", "description": "Si resulta envenenado, recupera PS en vez de perderlos." }, "adaptability": { - "name": "Adaptable", + "name": "Adaptable", "description": "Potencia aún más los movimientos cuyo tipo coincida con el suyo." }, "skillLink": { - "name": "Encadenado", + "name": "Encadenado", "description": "Ejecuta siempre los movimientos de ataque múltiple con el número máximo de golpes." }, "hydration": { - "name": "Hidratación", + "name": "Hidratación", "description": "Cura los problemas de estado si está lloviendo." }, "solarPower": { - "name": "Poder Solar", - "description": "Si hace sol, aumenta su Ataque Especial, pero pierde PS en cada turno." + "name": "Poder Solar", + "description": "Si hace sol, aumenta su ataque especial, pero pierde PS en cada turno." }, "quickFeet": { - "name": "Pies Rápidos", - "description": "Aumenta su Velocidad si sufre problemas de estado." + "name": "Pies Rápidos", + "description": "Aumenta su velocidad si sufre problemas de estado." }, "normalize": { - "name": "Normalidad", + "name": "Normalidad", "description": "Hace que todos sus movimientos se vuelvan de tipo Normal y aumenta ligeramente su potencia." }, "sniper": { - "name": "Francotirador", + "name": "Francotirador", "description": "Potencia los golpes críticos que asesta aún más de lo normal." }, "magicGuard": { - "name": "Muro Mágico", + "name": "Muro Mágico", "description": "Solo recibe daño de ataques." }, "noGuard": { - "name": "Indefenso", + "name": "Indefenso", "description": "Al quedar ambos expuestos, tanto sus movimientos como los del Pokémon que lo ataque acertarán siempre." }, "stall": { - "name": "Rezagado", + "name": "Rezagado", "description": "Ejecuta su movimiento tras todos los demás." }, "technician": { - "name": "Experto", + "name": "Experto", "description": "Aumenta la potencia de sus movimientos débiles." }, "leafGuard": { - "name": "Defensa Hoja", + "name": "Defensa Hoja", "description": "Evita los problemas de estado si hace sol." }, "klutz": { - "name": "Zoquete", + "name": "Zoquete", "description": "No puede usar objetos equipados." }, "moldBreaker": { - "name": "Rompemoldes", + "name": "Rompemoldes", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "superLuck": { - "name": "Afortunado", + "name": "Afortunado", "description": "Su buena suerte aumenta la probabilidad de asestar golpes críticos." }, "aftermath": { - "name": "Detonación", + "name": "Detonación", "description": "Daña al Pokémon que le ha dado el golpe de gracia con un movimiento de contacto." }, "anticipation": { - "name": "Anticipación", + "name": "Anticipación", "description": "Prevé los movimientos peligrosos del rival." }, "forewarn": { - "name": "Alerta", + "name": "Alerta", "description": "Revela uno de los movimientos del rival al entrar en combate." }, "unaware": { - "name": "Ignorante", + "name": "Ignorante", "description": "Pasa por alto los cambios en las características de un Pokémon al atacarlo o recibir daño." }, "tintedLens": { - "name": "Cromolente", + "name": "Cromolente", "description": "Potencia los movimientos que no son muy eficaces, que infligen ahora un daño normal." }, "filter": { - "name": "Filtro", + "name": "Filtro", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "slowStart": { - "name": "Inicio Lento", - "description": "Reduce a la mitad su Ataque y su Velocidad durante cinco turnos." + "name": "Inicio Lento", + "description": "Reduce a la mitad su ataque y su velocidad durante cinco turnos." }, "scrappy": { - "name": "Intrépido", + "name": "Intrépido", "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Además, no sufre los efectos de Intimidación." }, "stormDrain": { - "name": "Colector", - "description": "Atrae y neutraliza los movimientos de tipo Agua, que además le aumentan el Ataque Especial." + "name": "Colector", + "description": "Atrae y neutraliza los movimientos de tipo Agua, que además le aumentan el ataque especial." }, "iceBody": { - "name": "Gélido", + "name": "Gélido", "description": "Recupera PS de forma gradual cuando nieva." }, "solidRock": { - "name": "Roca Sólida", + "name": "Roca Sólida", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "snowWarning": { - "name": "Nevada", + "name": "Nevada", "description": "Invoca una nevada al entrar en combate." }, "honeyGather": { - "name": "Recogemiel", - "description": "The Pokémon gathers Honey after a battle. The Honey is then sold for money." + "name": "Recogemiel", + "description": "El Pokémon recoge miel tras cada batalla. La miel se vende inmediatamente por ₽." }, "frisk": { - "name": "Cacheo", + "name": "Cacheo", "description": "Cuando entra en combate, el Pokémon puede comprobar la habilidad de un Pokémon rival." }, "reckless": { - "name": "Audaz", + "name": "Audaz", "description": "Potencia los movimientos que también dañan al usuario." }, "multitype": { - "name": "Multitipo", + "name": "Multitipo", "description": "Cambia su tipo al de la tabla que lleve." }, "flowerGift": { - "name": "Don Floral", - "description": "Si hace sol, aumenta su Ataque y su Defensa Especial, así como los de sus aliados." + "name": "Don Floral", + "description": "Si hace sol, aumenta su ataque y su defensa Especial, así como los de sus aliados." }, "badDreams": { - "name": "Mal Sueño", + "name": "Mal Sueño", "description": "Inflige daño a cualquier rival que esté dormido." }, "pickpocket": { - "name": "Hurto", + "name": "Hurto", "description": "Roba el objeto del Pokémon que lo ataque con un movimiento de contacto." }, "sheerForce": { - "name": "Potencia Bruta", + "name": "Potencia Bruta", "description": "Aumenta la potencia de sus movimientos en detrimento de los efectos secundarios, que se ven anulados." }, "contrary": { - "name": "Respondón", + "name": "Respondón", "description": "Invierte los cambios en las características: bajan cuando les toca subir y suben cuando les toca bajar." }, "unnerve": { - "name": "Nerviosismo", + "name": "Nerviosismo", "description": "Pone nervioso al rival y le impide comer bayas." }, "defiant": { - "name": "Competitivo", - "description": "Aumenta mucho su Ataque cuando el rival le reduce cualquiera de sus características." + "name": "Competitivo", + "description": "Aumenta mucho su ataque cuando el rival le reduce cualquiera de sus características." }, "defeatist": { - "name": "Flaqueza", - "description": "Cuando sus PS se ven reducidos a la mitad, se cansa tanto que su Ataque y su Ataque Especial también se ven reducidos a la mitad." + "name": "Flaqueza", + "description": "Cuando sus PS se ven reducidos a la mitad, se cansa tanto que su ataque y su ataque Especial también se ven reducidos a la mitad." }, "cursedBody": { - "name": "Cuerpo Maldito", + "name": "Cuerpo Maldito", "description": "Puede anular el movimiento usado en su contra." }, "healer": { - "name": "Alma Cura", + "name": "Alma Cura", "description": "A veces cura los problemas de estado de un aliado." }, "friendGuard": { - "name": "Compiescolta", + "name": "Compiescolta", "description": "Reduce el daño que sufren los aliados." }, "weakArmor": { - "name": "Armadura Frágil", - "description": "Al recibir daño de un ataque físico, se reduce su Defensa, pero aumenta mucho su Velocidad." + "name": "Armadura Frágil", + "description": "Al recibir daño de un ataque físico, se reduce su defensa, pero aumenta mucho su velocidad." }, "heavyMetal": { - "name": "Metal Pesado", + "name": "Metal Pesado", "description": "Duplica su peso." }, "lightMetal": { - "name": "Metal Liviano", + "name": "Metal Liviano", "description": "Reduce a la mitad su peso." }, "multiscale": { - "name": "Multiescamas", + "name": "Multiescamas", "description": "Reduce el daño que sufre si sus PS están al máximo." }, "toxicBoost": { - "name": "Ímpetu Tóxico", + "name": "Ímpetu Tóxico", "description": "Aumenta la potencia de sus ataques físicos cuando está envenenado." }, "flareBoost": { - "name": "Ímpetu Ardiente", + "name": "Ímpetu Ardiente", "description": "Aumenta la potencia de sus ataques especiales cuando sufre quemaduras." }, "harvest": { - "name": "Cosecha", + "name": "Cosecha", "description": "Puede reutilizar varias veces una misma baya." }, "telepathy": { - "name": "Telepatía", + "name": "Telepatía", "description": "Elude los ataques de los aliados durante el combate." }, "moody": { - "name": "Veleta", + "name": "Veleta", "description": "Aumenta mucho una característica en cada turno, pero reduce otra." }, "overcoat": { - "name": "Funda", + "name": "Funda", "description": "No recibe daño de las tormentas de arena ni sufre los efectos causados por polvos o esporas." }, "poisonTouch": { - "name": "Toque Tóxico", + "name": "Toque Tóxico", "description": "Puede envenenar al Pokémon al que ataque con un movimiento de contacto." }, "regenerator": { - "name": "Regeneración", + "name": "Regeneración", "description": "Recupera unos pocos PS cuando se retira del combate." }, "bigPecks": { - "name": "Sacapecho", - "description": "Impide que otros Pokémon le reduzcan la Defensa." + "name": "Sacapecho", + "description": "Impide que otros Pokémon le reduzcan la defensa." }, "sandRush": { - "name": "Ímpetu Arena", - "description": "Aumenta su Velocidad durante las tormentas de arena." + "name": "Ímpetu Arena", + "description": "Aumenta su velocidad durante las tormentas de arena." }, "wonderSkin": { - "name": "Piel Milagro", + "name": "Piel Milagro", "description": "Presenta una mayor resistencia ante los movimientos de estado." }, "analytic": { - "name": "Cálculo Final", + "name": "Cálculo Final", "description": "Aumenta la potencia de su movimiento si es el último en atacar." }, "illusion": { - "name": "Ilusión", + "name": "Ilusión", "description": "Adopta el aspecto del último Pokémon del equipo al entrar en combate para desconcertar al rival." }, "imposter": { - "name": "Impostor", + "name": "Impostor", "description": "Se transforma en el Pokémon que tiene enfrente." }, "infiltrator": { - "name": "Allanamiento", + "name": "Allanamiento", "description": "Ataca sorteando las barreras o el sustituto del objetivo." }, "mummy": { - "name": "Momia", + "name": "Momia", "description": "Contagia la habilidad Momia al Pokémon que lo ataque con un movimiento de contacto." }, "moxie": { - "name": "Autoestima", - "description": "Al debilitar a un objetivo, su confianza se refuerza de tal manera que aumenta su Ataque." + "name": "Autoestima", + "description": "Al debilitar a un objetivo, su confianza se refuerza de tal manera que aumenta su ataque." }, "justified": { - "name": "Justiciero", - "description": "Si lo alcanza un movimiento de tipo Siniestro, aumenta el Ataque debido a su integridad." + "name": "Justiciero", + "description": "Si lo alcanza un movimiento de tipo Siniestro, aumenta el ataque debido a su integridad." }, "rattled": { - "name": "Cobardía", - "description": "Si lo alcanza un ataque de tipo Siniestro, Bicho o Fantasma, o si sufre los efectos de Intimidación, el miedo hace que aumente su Velocidad." + "name": "Cobardía", + "description": "Si lo alcanza un ataque de tipo Siniestro, Bicho o Fantasma, o si sufre los efectos de Intimidación, el miedo hace que aumente su velocidad." }, "magicBounce": { - "name": "Espejo Mágico", + "name": "Espejo Mágico", "description": "Puede devolver los movimientos de estado sin verse afectado por ellos." }, "sapSipper": { - "name": "Herbívoro", - "description": "Si lo alcanza un movimiento de tipo Planta, aumenta su Ataque en vez de sufrir daño." + "name": "Herbívoro", + "description": "Si lo alcanza un movimiento de tipo Planta, aumenta su ataque en vez de sufrir daño." }, "prankster": { - "name": "Bromista", + "name": "Bromista", "description": "Sus movimientos de estado tienen prioridad alta." }, "sandForce": { - "name": "Poder Arena", + "name": "Poder Arena", "description": "Potencia los movimientos de tipo Tierra, Acero y Roca durante las tormentas de arena." }, "ironBarbs": { - "name": "Punta Acero", + "name": "Punta Acero", "description": "Inflige daño con sus púas de acero al Pokémon que lo ataque con un movimiento de contacto." }, "zenMode": { - "name": "Modo Daruma", + "name": "Modo Daruma", "description": "Cambia de forma si sus PS se ven reducidos a la mitad o menos." }, "victoryStar": { - "name": "Tinovictoria", - "description": "Aumenta su Precisión y la de sus aliados." + "name": "Tinovictoria", + "description": "Aumenta su precisión y la de sus aliados." }, "turboblaze": { - "name": "Turbollama", + "name": "Turbollama", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "teravolt": { - "name": "Terravoltaje", + "name": "Terravoltaje", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "aromaVeil": { - "name": "Velo Aroma", + "name": "Velo Aroma", "description": "Se protege a sí mismo y a sus aliados de efectos que impiden usar movimientos." }, "flowerVeil": { - "name": "Velo Flor", + "name": "Velo Flor", "description": "Evita que los Pokémon de tipo Planta aliados sufran problemas de estado o que les reduzcan sus características." }, "cheekPouch": { - "name": "Carrillo", + "name": "Carrillo", "description": "Recupera PS al comer cualquier baya." }, "protean": { - "name": "Mutatipo", + "name": "Mutatipo", "description": "Al entrar en combate, cambia su tipo al del primer movimiento que va a usar." }, "furCoat": { - "name": "Pelaje Recio", + "name": "Pelaje Recio", "description": "Reduce a la mitad el daño que recibe de ataques físicos." }, "magician": { - "name": "Prestidigitador", + "name": "Prestidigitador", "description": "Roba el objeto del Pokémon al que alcance con un movimiento." }, "bulletproof": { - "name": "Antibalas", + "name": "Antibalas", "description": "No le afectan las bombas ni algunos proyectiles." }, "competitive": { - "name": "Tenacidad", - "description": "Aumenta mucho su Ataque Especial cuando el rival le reduce cualquiera de sus características." + "name": "Tenacidad", + "description": "Aumenta mucho su ataque especial cuando el rival le reduce cualquiera de sus características." }, "strongJaw": { - "name": "Mandíbula Fuerte", + "name": "Mandíbula Fuerte", "description": "Su robusta mandíbula le confiere una mordedura mucho más potente." }, "refrigerate": { - "name": "Piel Helada", + "name": "Piel Helada", "description": "Convierte los movimientos de tipo Normal en tipo Hielo y aumenta ligeramente su potencia." }, "sweetVeil": { - "name": "Velo Dulce", + "name": "Velo Dulce", "description": "No cae dormido y evita también que sus aliados se duerman." }, "stanceChange": { - "name": "Cambio Táctico", + "name": "Cambio Táctico", "description": "Adopta la Forma Filo al lanzar un ataque, o bien la Forma Escudo si usa el movimiento Escudo Real." }, "galeWings": { - "name": "Alas Vendaval", + "name": "Alas Vendaval", "description": "Da prioridad a los movimientos de tipo Volador si sus PS están al máximo." }, "megaLauncher": { - "name": "Megadisparador", + "name": "Megadisparador", "description": "Aumenta la potencia de algunos movimientos de pulsos y auras." }, "grassPelt": { - "name": "Manto Frondoso", - "description": "Aumenta su Defensa si hay un campo de hierba en el terreno de combate." + "name": "Manto Frondoso", + "description": "Aumenta su defensa si hay un campo de hierba en el terreno de combate." }, "symbiosis": { - "name": "Simbiosis", + "name": "Simbiosis", "description": "Pasa su objeto a un aliado cuando este use el suyo." }, "toughClaws": { - "name": "Garra Dura", + "name": "Garra Dura", "description": "Aumenta la potencia de los movimientos de contacto." }, "pixilate": { - "name": "Piel Feérica", + "name": "Piel Feérica", "description": "Convierte los movimientos de tipo Normal en tipo Hada y aumenta ligeramente su potencia." }, "gooey": { - "name": "Baba", - "description": "Reduce la Velocidad del Pokémon que lo ataque con un movimiento de contacto." + "name": "Baba", + "description": "Reduce la velocidad del Pokémon que lo ataque con un movimiento de contacto." }, "aerilate": { - "name": "Piel Celeste", + "name": "Piel Celeste", "description": "Convierte los movimientos de tipo Normal en tipo Volador y aumenta ligeramente su potencia." }, "parentalBond": { - "name": "Amor Filial", + "name": "Amor Filial", "description": "Une fuerzas con su cría y ataca dos veces." }, "darkAura": { - "name": "Aura Oscura", + "name": "Aura Oscura", "description": "Aumenta la potencia de los movimientos de tipo Siniestro de todos los Pokémon." }, "fairyAura": { - "name": "Aura Feérica", + "name": "Aura Feérica", "description": "Aumenta la potencia de los movimientos de tipo Hada de todos los Pokémon." }, "auraBreak": { - "name": "Rompeaura", + "name": "Rompeaura", "description": "Invierte los efectos de las habilidades de auras, por lo que reduce la potencia de ciertos movimientos en vez de aumentarla." }, "primordialSea": { - "name": "Mar del Albor", + "name": "Mar del Albor", "description": "Altera el clima para anular los ataques de tipo Fuego." }, "desolateLand": { - "name": "Tierra del Ocaso", + "name": "Tierra del Ocaso", "description": "Altera el clima para anular los ataques de tipo Agua." }, "deltaStream": { - "name": "Ráfaga Delta", + "name": "Ráfaga Delta", "description": "Altera el clima para anular las vulnerabilidades del tipo Volador." }, "stamina": { - "name": "Firmeza", - "description": "Aumenta su Defensa al recibir un ataque." + "name": "Firmeza", + "description": "Aumenta su defensa al recibir un ataque." }, "wimpOut": { - "name": "Huida", + "name": "Huida", "description": "Se asusta y abandona el terreno de combate cuando sus PS se ven reducidos a la mitad." }, "emergencyExit": { - "name": "Retirada", + "name": "Retirada", "description": "Abandona el terreno de combate cuando sus PS se ven reducidos a la mitad para evitar males mayores." }, "waterCompaction": { - "name": "Hidrorrefuerzo", - "description": "Aumenta mucho su Defensa si lo alcanza un movimiento de tipo Agua." + "name": "Hidrorrefuerzo", + "description": "Aumenta mucho su defensa si lo alcanza un movimiento de tipo Agua." }, "merciless": { - "name": "Ensañamiento", + "name": "Ensañamiento", "description": "Hace que sus movimientos asesten siempre un golpe crítico si el objetivo está envenenado." }, "shieldsDown": { - "name": "Escudo Limitado", + "name": "Escudo Limitado", "description": "Rompe su coraza cuando sus PS se ven reducidos a la mitad y adopta una forma ofensiva." }, "stakeout": { - "name": "Vigilante", + "name": "Vigilante", "description": "Si el objetivo de su ataque es sustituido por otro, duplica el daño que infligirá." }, "waterBubble": { - "name": "Pompa", + "name": "Pompa", "description": "Reduce el daño que le provocan los movimientos de tipo Fuego y es inmune a las quemaduras." }, "steelworker": { - "name": "Acero Templado", + "name": "Acero Templado", "description": "Potencia los movimientos de tipo Acero." }, "berserk": { - "name": "Cólera", - "description": "Aumenta su Ataque Especial si sus PS se ven reducidos a la mitad debido a algún ataque." + "name": "Cólera", + "description": "Aumenta su ataque especial si sus PS se ven reducidos a la mitad debido a algún ataque." }, "slushRush": { - "name": "Quitanieves", - "description": "Aumenta su Velocidad cuando nieva." + "name": "Quitanieves", + "description": "Aumenta su velocidad cuando nieva." }, "longReach": { - "name": "Remoto", + "name": "Remoto", "description": "Puede usar cualquier movimiento sin entrar en contacto con su objetivo." }, "liquidVoice": { - "name": "Voz Fluida", + "name": "Voz Fluida", "description": "Hace que todos sus movimientos que usan sonido pasen a ser de tipo Agua." }, "triage": { - "name": "Primer Auxilio", + "name": "Primer Auxilio", "description": "Da prioridad a los movimientos que restauran PS." }, "galvanize": { - "name": "Piel Eléctrica", + "name": "Piel Eléctrica", "description": "Convierte los movimientos de tipo Normal en tipo Eléctrico y aumenta ligeramente su potencia." }, "surgeSurfer": { - "name": "Cola Surf", - "description": "Duplica su Velocidad si hay un campo eléctrico en el terreno de combate." + "name": "Cola Surf", + "description": "Duplica su velocidad si hay un campo eléctrico en el terreno de combate." }, "schooling": { - "name": "Banco", + "name": "Banco", "description": "Forma bancos con sus congéneres cuando tiene muchos PS, lo cual le otorga más fuerza. Cuando le quedan pocos PS, el banco se dispersa." }, "disguise": { - "name": "Disfraz", + "name": "Disfraz", "description": "Puede eludir un ataque valiéndose de la tela que le cubre el cuerpo una vez por combate." }, "battleBond": { - "name": "Fuerte Afecto", - "description": "Al derrotar a un Pokémon, los vínculos con su Entrenador se refuerzan y aumentan su Ataque, su Ataque Especial y su Velocidad." + "name": "Fuerte Afecto", + "description": "Al derrotar a un Pokémon, los vínculos con su Entrenador se refuerzan y aumentan su ataque, su ataque especial y su velocidad." }, "powerConstruct": { - "name": "Agrupamiento", + "name": "Agrupamiento", "description": "Cuando sus PS se ven reducidos a la mitad, las células se reagrupan y adopta su Forma Completa." }, "corrosion": { - "name": "Corrosión", + "name": "Corrosión", "description": "Puede envenenar incluso a Pokémon de tipo Acero o Veneno." }, "comatose": { - "name": "Letargo Perenne", + "name": "Letargo Perenne", "description": "No despierta jamás de su profundo letargo e incluso ataca dormido." }, "queenlyMajesty": { - "name": "Regia Presencia", + "name": "Regia Presencia", "description": "Intimida al rival y le impide usar movimientos con prioridad contra él y sus aliados." }, "innardsOut": { - "name": "Revés", + "name": "Revés", "description": "Al caer debilitado, inflige al atacante un daño equivalente a los PS que le quedaran antes de recibir el golpe de gracia." }, "dancer": { - "name": "Pareja de Baile", + "name": "Pareja de Baile", "description": "Puede copiar inmediatamente cualquier movimiento de baile que haya usado otro Pokémon presente en el combate." }, "battery": { - "name": "Batería", + "name": "Batería", "description": "Potencia los ataques especiales de los aliados." }, "fluffy": { - "name": "Peluche", + "name": "Peluche", "description": "Reduce a la mitad el daño recibido por los movimientos de contacto, pero duplica el que le infligen los de tipo Fuego." }, "dazzling": { - "name": "Cuerpo Vívido", + "name": "Cuerpo Vívido", "description": "Desconcierta al rival y le impide usar movimientos con prioridad contra él y sus aliados." }, "soulHeart": { - "name": "Coránima", - "description": "Aumenta su Ataque Especial cada vez que un Pokémon cae debilitado." + "name": "Coránima", + "description": "Aumenta su ataque especial cada vez que un Pokémon cae debilitado." }, "tanglingHair": { - "name": "Rizos Rebeldes", - "description": "Reduce la Velocidad del Pokémon que lo ataque con un movimiento de contacto." + "name": "Rizos Rebeldes", + "description": "Reduce la velocidad del Pokémon que lo ataque con un movimiento de contacto." }, "receiver": { - "name": "Receptor", + "name": "Receptor", "description": "Adquiere la habilidad de un aliado debilitado." }, "powerOfAlchemy": { - "name": "Reacción Química", + "name": "Reacción Química", "description": "Reacciona copiando la habilidad de un aliado debilitado." }, "beastBoost": { - "name": "Ultraimpulso", + "name": "Ultraimpulso", "description": "Al derrotar a un Pokémon, aumenta su característica más fuerte." }, "rksSystem": { - "name": "Sistema Alfa", + "name": "Sistema Alfa", "description": "Cambia su tipo según el disco que lleve instalado." }, "electricSurge": { - "name": "Electrogénesis", + "name": "Electrogénesis", "description": "Crea un campo eléctrico al entrar en combate." }, "psychicSurge": { - "name": "Psicogénesis", + "name": "Psicogénesis", "description": "Crea un campo psíquico al entrar en combate." }, "mistySurge": { - "name": "Nebulogénesis", + "name": "Nebulogénesis", "description": "Crea un campo de niebla al entrar en combate." }, "grassySurge": { - "name": "Herbogénesis", + "name": "Herbogénesis", "description": "Crea un campo de hierba al entrar en combate." }, "fullMetalBody": { - "name": "Guardia Metálica", + "name": "Guardia Metálica", "description": "Evita que se reduzcan sus características a causa de movimientos o habilidades de otros Pokémon." }, "shadowShield": { - "name": "Guardia Espectro", + "name": "Guardia Espectro", "description": "Reduce el daño que sufre si sus PS están al máximo." }, "prismArmor": { - "name": "Armadura Prisma", + "name": "Armadura Prisma", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "neuroforce": { - "name": "Fuerza Cerebral", + "name": "Fuerza Cerebral", "description": "Potencia los ataques supereficaces." }, "intrepidSword": { - "name": "Espada Indómita", - "description": "Aumenta su Ataque al entrar en combate por primera vez." + "name": "Espada Indómita", + "description": "Aumenta su ataque al entrar en combate por primera vez." }, "dauntlessShield": { - "name": "Escudo Recio", - "description": "Aumenta su Defensa al entrar en combate por primera vez." + "name": "Escudo Recio", + "description": "Aumenta su defensa al entrar en combate por primera vez." }, "libero": { - "name": "Líbero", + "name": "Líbero", "description": "Al entrar en combate, cambia su tipo al del primer movimiento que va a usar." }, "ballFetch": { - "name": "Recogebolas", - "description": "Si no lleva equipado ningún objeto, recupera la Poké Ball del primer intento de captura fallido." + "name": "Recogebolas", + "description": "Recupera la Poké Ball del primer intento de captura fallido." }, "cottonDown": { - "name": "Pelusa", - "description": "Al ser alcanzado por un ataque, suelta una pelusa de algodón que reduce la Velocidad de todos los demás Pokémon." + "name": "Pelusa", + "description": "Al ser alcanzado por un ataque, suelta una pelusa de algodón que reduce la velocidad de todos los demás Pokémon." }, "propellerTail": { - "name": "Hélice Caudal", + "name": "Hélice Caudal", "description": "Ignora los efectos de las habilidades o los movimientos que permiten a un Pokémon centrar la atención sobre sí." }, "mirrorArmor": { - "name": "Coraza Reflejo", + "name": "Coraza Reflejo", "description": "Refleja los efectos que reducen las características." }, "gulpMissile": { - "name": "Tragamisil", + "name": "Tragamisil", "description": "Tras usar Surf o Buceo, emerge con una presa en la boca. Al recibir daño, ataca escupiéndola." }, "stalwart": { - "name": "Acérrimo", + "name": "Acérrimo", "description": "Ignora los efectos de las habilidades o los movimientos que permiten a un Pokémon centrar la atención sobre sí." }, "steamEngine": { - "name": "Combustible", - "description": "Si lo alcanza un movimiento de tipo Fuego o Agua, aumenta muchísimo su Velocidad." + "name": "Combustible", + "description": "Si lo alcanza un movimiento de tipo Fuego o Agua, aumenta muchísimo su velocidad." }, "punkRock": { - "name": "Punk Rock", + "name": "Punk Rock", "description": "Potencia los movimientos que usan sonido y reduce a la mitad el daño que le infligen dichos movimientos." }, "sandSpit": { - "name": "Expulsarena", + "name": "Expulsarena", "description": "Provoca una tormenta de arena al recibir un ataque." }, "iceScales": { - "name": "Escama de Hielo", + "name": "Escama de Hielo", "description": "Las gélidas escamas que protegen su cuerpo reducen a la mitad el daño que le infligen los ataques especiales." }, "ripen": { - "name": "Maduración", + "name": "Maduración", "description": "Hace madurar las bayas, por lo que duplica sus efectos." }, "iceFace": { - "name": "Cara de Hielo", + "name": "Cara de Hielo", "description": "Absorbe el daño de un ataque físico con el hielo de la cabeza, tras lo cual cambia de forma. El hielo se regenerará la próxima vez que nieve." }, "powerSpot": { - "name": "Fuente Energía", + "name": "Fuente Energía", "description": "Potencia los movimientos de los Pokémon adyacentes." }, "mimicry": { - "name": "Mimetismo", + "name": "Mimetismo", "description": "Cambia su tipo según el campo que haya en el terreno de combate." }, "screenCleaner": { - "name": "Antibarrera", + "name": "Antibarrera", "description": "Anula los efectos de Pantalla de Luz, Reflejo y Velo Aurora tanto de rivales como de aliados al entrar en combate." }, "steelySpirit": { - "name": "Alma Acerada", + "name": "Alma Acerada", "description": "Potencia los movimientos de tipo Acero del Pokémon y sus aliados." }, "perishBody": { - "name": "Cuerpo Mortal", + "name": "Cuerpo Mortal", "description": "Si lo alcanza un movimiento de contacto, se debilitará al cabo de 3 turnos, así como el atacante, a menos que abandonen el terreno de combate." }, "wanderingSpirit": { - "name": "Alma Errante", + "name": "Alma Errante", "description": "Si lo alcanza un movimiento de contacto, intercambia su habilidad con la del atacante." }, "gorillaTactics": { - "name": "Monotema", - "description": "Aumenta su Ataque, pero solo puede usar el primer movimiento escogido." + "name": "Monotema", + "description": "Aumenta su ataque, pero solo puede usar el primer movimiento escogido." }, "neutralizingGas": { - "name": "Gas Reactivo", + "name": "Gas Reactivo", "description": "Anula los efectos de las habilidades de los demás Pokémon presentes mientras esté en el terreno de combate." }, "pastelVeil": { - "name": "Velo Pastel", + "name": "Velo Pastel", "description": "Se protege a sí mismo y a sus aliados del envenenamiento." }, "hungerSwitch": { - "name": "Mutapetito", + "name": "Mutapetito", "description": "Alterna entre su Forma Saciada y Forma Voraz al final de cada turno." }, "quickDraw": { - "name": "Mano Rápida", + "name": "Mano Rápida", "description": "A veces, puede atacar el primero." }, "unseenFist": { - "name": "Puño Invisible", + "name": "Puño Invisible", "description": "Si usa un movimiento de contacto, puede infligir daño al objetivo aunque este se proteja." }, "curiousMedicine": { - "name": "Medicina Extraña", + "name": "Medicina Extraña", "description": "Al entrar en combate, rezuma una substancia medicinal por la caracola que revierte los cambios en las características de los aliados." }, "transistor": { - "name": "Transistor", + "name": "Transistor", "description": "Potencia los movimientos de tipo Eléctrico." }, "dragonsMaw": { - "name": "Mandíbula Dragón", + "name": "Mandíbula Dragón", "description": "Potencia los movimientos de tipo Dragón." }, "chillingNeigh": { - "name": "Relincho Blanco", - "description": "Al derrotar a un objetivo, emite un relincho gélido y aumenta su Ataque." + "name": "Relincho Blanco", + "description": "Al derrotar a un objetivo, emite un relincho gélido y aumenta su ataque." }, "grimNeigh": { - "name": "Relincho Negro", - "description": "Al derrotar a un objetivo, emite un relincho aterrador y aumenta su Ataque Especial." + "name": "Relincho Negro", + "description": "Al derrotar a un objetivo, emite un relincho aterrador y aumenta su ataque especial." }, "asOneGlastrier": { - "name": "Unidad Ecuestre", + "name": "Unidad Ecuestre", "description": "El Pokémon tiene dos habilidades: Relincho Negro de Spectrier y Nerviosismo de Calyrex." }, "asOneSpectrier": { - "name": "Unidad Ecuestre", + "name": "Unidad Ecuestre", "description": "El Pokémon tiene dos habilidades: Relincho Negro de Spectrier y Nerviosismo de Calyrex." }, "lingeringAroma": { - "name": "Olor Persistente", + "name": "Olor Persistente", "description": "Contagia la habilidad Olor Persistente al Pokémon que lo ataque con un movimiento de contacto." }, "seedSower": { - "name": "Disemillar", + "name": "Disemillar", "description": "Crea un campo de hierba al recibir un ataque." }, "thermalExchange": { - "name": "Termoconversión", - "description": "Evita las quemaduras y, si lo alcanza un movimiento de tipo Fuego, aumenta su Ataque." + "name": "Termoconversión", + "description": "Evita las quemaduras y, si lo alcanza un movimiento de tipo Fuego, aumenta su ataque." }, "angerShell": { - "name": "Coraza Ira", - "description": "Cuando un ataque reduce sus PS a la mitad, un arrebato de cólera reduce su Defensa y su Defensa Especial, pero aumenta su Ataque, su Ataque Especial y su Velocidad." + "name": "Coraza Ira", + "description": "Cuando un ataque reduce sus PS a la mitad, un arrebato de cólera reduce su defensa y su defensa especial, pero aumenta su ataque, su ataque especial y su velocidad." }, "purifyingSalt": { - "name": "Sal Purificadora", + "name": "Sal Purificadora", "description": "Su sal pura lo protege de los problemas de estado y reduce a la mitad el daño que recibe de ataques de tipo Fantasma." }, "wellBakedBody": { - "name": "Cuerpo Horneado", - "description": "Si lo alcanza un movimiento de tipo Fuego, aumenta mucho su Defensa en vez de sufrir daño." + "name": "Cuerpo Horneado", + "description": "Si lo alcanza un movimiento de tipo Fuego, aumenta mucho su defensa en vez de sufrir daño." }, "windRider": { - "name": "Surcavientos", - "description": "Si sopla un Viento Afín o lo alcanza un movimiento que usa viento, aumenta su Ataque. Tampoco recibe daño de este último." + "name": "Surcavientos", + "description": "Si sopla un Viento Afín o lo alcanza un movimiento que usa viento, aumenta su ataque. Tampoco recibe daño de este último." }, "guardDog": { - "name": "Perro Guardián", - "description": "Aumenta su Ataque si sufre los efectos de Intimidación. También anula movimientos y objetos que fuercen el cambio de Pokémon." + "name": "Perro Guardián", + "description": "Aumenta su ataque si sufre los efectos de Intimidación. También anula movimientos y objetos que fuercen el cambio de Pokémon." }, "rockyPayload": { - "name": "Transportarrocas", + "name": "Transportarrocas", "description": "Potencia los movimientos de tipo Roca." }, "windPower": { - "name": "Energía Eólica", + "name": "Energía Eólica", "description": "Su cuerpo se carga de electricidad si lo alcanza un movimiento que usa viento, lo que potencia su siguiente movimiento de tipo Eléctrico." }, "zeroToHero": { - "name": "Cambio Heroico", + "name": "Cambio Heroico", "description": "Adopta la Forma Heroica cuando se retira del combate." }, "commander": { - "name": "Comandar", + "name": "Comandar", "description": "Si al entrar en combate coincide con un Dondozo aliado, se cuela en el interior de su boca para tomar el control." }, "electromorphosis": { - "name": "Dinamo", + "name": "Dinamo", "description": "Su cuerpo se carga de electricidad al recibir daño, lo que potencia su siguiente movimiento de tipo Eléctrico." }, "protosynthesis": { - "name": "Paleosíntesis", + "name": "Paleosíntesis", "description": "Si hace sol o lleva un tanque de Energía Potenciadora, aumenta su característica más alta." }, "quarkDrive": { - "name": "Carga Cuark", + "name": "Carga Cuark", "description": "Si hay un campo eléctrico en el terreno de combate o lleva un tanque de Energía Potenciadora, aumenta su característica más alta." }, "goodAsGold": { - "name": "Cuerpo Áureo", + "name": "Cuerpo Áureo", "description": "Su robusto cuerpo de oro inoxidable lo hace inmune frente a movimientos de estado de otros Pokémon." }, "vesselOfRuin": { - "name": "Caldero Debacle", - "description": "Reduce el Ataque Especial de todos los demás Pokémon con el poder de su caldero maldito." + "name": "Caldero Debacle", + "description": "Reduce el ataque especial de todos los demás Pokémon con el poder de su caldero maldito." }, "swordOfRuin": { - "name": "Espada Debacle", - "description": "Reduce la Defensa de todos los demás Pokémon con el poder de su espada maldita." + "name": "Espada Debacle", + "description": "Reduce la defensa de todos los demás Pokémon con el poder de su espada maldita." }, "tabletsOfRuin": { - "name": "Tablilla Debacle", - "description": "Reduce el Ataque de todos los demás Pokémon con el poder de sus tablillas malditas." + "name": "Tablilla Debacle", + "description": "Reduce el ataque de todos los demás Pokémon con el poder de sus tablillas malditas." }, "beadsOfRuin": { - "name": "Abalorio Debacle", - "description": "Reduce la Defensa Especial de todos los demás Pokémon con el poder de sus abalorios malditos." + "name": "Abalorio Debacle", + "description": "Reduce la defensa especial de todos los demás Pokémon con el poder de sus abalorios malditos." }, "orichalcumPulse": { - "name": "Latido Oricalco", - "description": "El tiempo pasa a ser soleado cuando entra en combate. Si hace mucho sol, su Ataque aumenta gracias a su pulso primigenio." + "name": "Latido Oricalco", + "description": "El tiempo pasa a ser soleado cuando entra en combate. Si hace mucho sol, su ataque aumenta gracias a su pulso primigenio." }, "hadronEngine": { - "name": "Motor Hadrónico", - "description": "Crea un campo eléctrico al entrar en combate. Si hay un campo eléctrico, su Ataque Especial aumenta gracias a su motor futurista." + "name": "Motor Hadrónico", + "description": "Crea un campo eléctrico al entrar en combate. Si hay un campo eléctrico, su ataque especial aumenta gracias a su motor futurista." }, "opportunist": { - "name": "Oportunista", + "name": "Oportunista", "description": "Copia las mejoras en las características del rival, aprovechándose de la situación." }, "cudChew": { - "name": "Rumia", + "name": "Rumia", "description": "Cuando ingiere una baya, la regurgita al final del siguiente turno y se la come por segunda vez." }, "sharpness": { - "name": "Cortante", + "name": "Cortante", "description": "Aumenta la potencia de los movimientos cortantes." }, "supremeOverlord": { - "name": "General Supremo", - "description": "Al entrar en combate, su Ataque y su Ataque Especial aumentan un poco por cada miembro del equipo que haya sido derrotado hasta el momento." + "name": "General Supremo", + "description": "Al entrar en combate, su ataque y su ataque especial aumentan un poco por cada miembro del equipo que haya sido derrotado hasta el momento." }, "costar": { - "name": "Unísono", + "name": "Unísono", "description": "Al entrar en combate, copia los cambios en las características de su aliado." }, "toxicDebris": { - "name": "Capa Tóxica", + "name": "Capa Tóxica", "description": "Al recibir daño de un ataque físico, lanza una trampa de púas tóxicas a los pies del rival." }, "armorTail": { - "name": "Cola Armadura", + "name": "Cola Armadura", "description": "La extraña cola que le envuelve la cabeza impide al rival usar movimientos con prioridad contra él y sus aliados." }, "earthEater": { - "name": "Geofagia", + "name": "Geofagia", "description": "Si lo alcanza un movimiento de tipo Tierra, recupera PS en vez de sufrir daño." }, "myceliumMight": { - "name": "Poder Fúngico", + "name": "Poder Fúngico", "description": "El Pokémon siempre actúa con lentitud cuando usa movimientos de estado, pero estos no se ven afectados por la habilidad del objetivo." }, "mindsEye": { - "name": "Ojo Mental", - "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Su Precisión no se puede reducir e ignora los cambios en la Evasión del objetivo." + "name": "Ojo Mental", + "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Su precisión no se puede reducir e ignora los cambios en la evasión del objetivo." }, "supersweetSyrup": { - "name": "Néctar Dulce", - "description": "Al entrar en combate por primera vez, esparce un aroma dulzón a néctar que reduce la Evasión del rival." + "name": "Néctar Dulce", + "description": "Al entrar en combate por primera vez, esparce un aroma dulzón a néctar que reduce la evasión del rival." }, "hospitality": { - "name": "Hospitalidad", + "name": "Hospitalidad", "description": "Al entrar en combate, restaura algunos PS de su aliado como muestra de hospitalidad." }, "toxicChain": { - "name": "Cadena Tóxica", + "name": "Cadena Tóxica", "description": "Gracias al poder de su cadena impregnada de toxinas, puede envenenar gravemente al Pokémon al que ataque." }, "embodyAspectTeal": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara turquesa y aumenta su velocidad." }, "embodyAspectWellspring": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara fuente y aumenta su defensa especial." }, "embodyAspectHearthflame": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara horno y aumenta su ataque." }, "embodyAspectCornerstone": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara cimiento y aumenta su defensa." }, "teraShift": { - "name": "Teracambio", + "name": "Teracambio", "description": "Al entrar en combate, adopta la Forma Teracristal tras absorber la energía de su alrededor." }, "teraShell": { - "name": "Teracaparazón", + "name": "Teracaparazón", "description": "Su caparazón encierra energía de todos los tipos. Gracias a ello, si sus PS están al máximo, el movimiento que lo alcance no será muy eficaz." }, "teraformZero": { - "name": "Teraformación 0", + "name": "Teraformación 0", "description": "Cuando Terapagos adopta la Forma Astral, anula todos los efectos del tiempo atmosférico y de los campos que haya en el terreno gracias a su poder oculto." }, "poisonPuppeteer": { - "name": "Títere Tóxico", + "name": "Títere Tóxico", "description": "Los rivales que Pecharunt envenene con sus movimientos también sufrirán confusión." } } diff --git a/src/locales/es/achv.json b/src/locales/es/achv.json index c94b8858233..14501dbdb6b 100644 --- a/src/locales/es/achv.json +++ b/src/locales/es/achv.json @@ -91,7 +91,7 @@ "name": "Campeón Liga Master", "name_female": "Campeona Liga Master" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Trabajo en Equipo", "description": "Haz relevo a otro miembro del equipo con al menos una estadística al máximo." }, @@ -175,4 +175,4 @@ "name": "Espejo ojepsE", "description": "Completa el reto de Combate Inverso.\n.osrevnI etabmoC ed oter le atelpmoC" } -} \ No newline at end of file +} diff --git a/src/locales/es/arena-flyout.json b/src/locales/es/arena-flyout.json index b2881b5de76..64c9a489d73 100644 --- a/src/locales/es/arena-flyout.json +++ b/src/locales/es/arena-flyout.json @@ -1,21 +1,21 @@ { - "activeBattleEffects": "Efectos de Terreno Activos", + "activeBattleEffects": "Efectos de terreno activos", "player": "Jugador", "neutral": "Neutral", "enemy": "Enemigo", "sunny": "Sol", "rain": "Lluvia", - "sandstorm": "Tormenta de Arena", + "sandstorm": "Tormenta de arena", "hail": "Granizo", "snow": "Nieve", "fog": "Niebla", "heavyRain": "Diluvio", - "harshSun": "Sol Abrasador", + "harshSun": "Sol abrasador", "strongWinds": "Turbulencias", - "misty": "Campo de Niebla", - "electric": "Campo Eléctrico", - "grassy": "Campo de Hierba", - "psychic": "Campo Psíquico", + "misty": "Campo de niebla", + "electric": "Campo eléctrico", + "grassy": "Campo de hierba", + "psychic": "Campo psíquico", "mudSport": "Chapoteo Lodo", "waterSport": "Hidrochorro", "spikes": "Púas", @@ -36,5 +36,6 @@ "matBlock": "Escudo Tatami", "craftyShield": "Truco Defensa", "tailwind": "Viento Afín", - "happyHour": "Paga Extra" -} \ No newline at end of file + "happyHour": "Paga Extra", + "safeguard": "Velo Sagrado" +} diff --git a/src/locales/es/arena-tag.json b/src/locales/es/arena-tag.json index 9e26dfeeb6e..0f63b62e784 100644 --- a/src/locales/es/arena-tag.json +++ b/src/locales/es/arena-tag.json @@ -1 +1,57 @@ -{} \ No newline at end of file +{ + "yourTeam": "tu equipo", + "opposingTeam": "el equipo rival", + "arenaOnRemove": "Los efectos de {{moveName}} desaparecieron.", + "arenaOnRemovePlayer": "Los efectos de {{moveName}}\ndesaparecieron en tu bando.", + "arenaOnRemoveEnemy": "Los efectos de {{moveName}}\ndesaparecieron en el bando rival.", + "mistOnAdd": "¡Neblina de {{pokemonNameWithAffix}}\nha cubierto a su equipo!", + "mistApply": "¡La neblina evita los cambios de estadísticas!", + "reflectOnAdd": "¡Reflejo redujo el daño físico!", + "reflectOnAddPlayer": "¡Reflejo redujo el daño físico en tu bando!", + "reflectOnAddEnemy": "Reflejo redujo el daño físico en el bando rival.", + "lightScreenOnAdd": "¡Pantalla de Luz redujo el daño físico!", + "lightScreenOnAddPlayer": "¡Pantalla de Luz redujo el daño físico en tu bando!", + "lightScreenOnAddEnemy": "¡Pantalla de Luz redujo el daño físico en el bando enemigo!", + "auroraVeilOnAdd": "¡Velo Aurora redujo el daño físico!", + "auroraVeilOnAddPlayer": "¡Velo Aurora redujo el daño físico en tu bando!", + "auroraVeilOnAddEnemy": "¡Velo Aurora redujo el daño físico en el bando rival!", + "conditionalProtectOnAdd": "¡{{moveName}} protege a su bando!", + "conditionalProtectOnAddPlayer": "¡{{moveName}} protege a tu bando!", + "conditionalProtectOnAddEnemy": "¡{{moveName}} protege al bando rival!", + "conditionalProtectApply": "¡{{pokemonNameWithAffix}} ha sido protegido por {{moveName}}!", + "matBlockOnAdd": "¡{{pokemonNameWithAffix}} va a usar un tatami para bloquear ataques!", + "noCritOnAddPlayer": "¡{{moveName}} protege a tu bando de golpes críticos!", + "noCritOnAddEnemy": "¡{{moveName}} protege al bando rival de golpes críticos!", + "noCritOnRemove": "¡Los efectos de {{moveName}} de {{pokemonNameWithAffix}} se han disipado!", + "wishTagOnAdd": "¡El deseo de {{pokemonNameWithAffix}} se ha hecho realidad!", + "mudSportOnAdd": "¡Se han debilitado los ataques de tipo Eléctrico!", + "mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.", + "waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!", + "waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.", + "spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!", + "spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!", + "toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!", + "toxicSpikesActivateTrapPoison": "¡{{pokemonNameWithAffix}} ha sido herido por {{moveName}}!", + "stealthRockOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por piedras puntiagudas!", + "stealthRockActivateTrap": "¡Unas piedras puntiagudas han dañado a {{pokemonNameWithAffix}}!", + "stickyWebOnAdd": "¡Una {{moveName}} se extiende a los pies del bando rival!", + "stickyWebActivateTrap": "¡{{pokemonName}} ha caído en una red viscosa!", + "trickRoomOnAdd": "¡{{pokemonNameWithAffix}} ha alterado las dimensiones!", + "trickRoomOnRemove": "Se han restaurado las dimensiones alteradas.", + "gravityOnAdd": "¡La gravedad se ha incrementado!", + "gravityOnRemove": "La gravedad ha vuelto a su estado normal.", + "tailwindOnAdd": "¡Sopla un viento afín!", + "tailwindOnAddPlayer": "¡El viento sopla a favor de tu bando!", + "tailwindOnAddEnemy": "¡El viento sopla a favor del bando rival!", + "tailwindOnRemove": "Ha dejado de soplar el viento afín.", + "tailwindOnRemovePlayer": "Ha dejado de soplar el viento que favorecía a tu equipo.", + "tailwindOnRemoveEnemy": "Ha dejado de soplar el viento que favorecía al bando rival.", + "happyHourOnAdd": "¡La felicidad se respira en el aire!", + "happyHourOnRemove": "La felicidad ya no se respira en el aire.", + "safeguardOnAdd": "¡Todos los Pokémon están protegidos por Velo Sagrado!", + "safeguardOnAddPlayer": "¡Tu equipo se ha protegido con Velo Sagrado!", + "safeguardOnAddEnemy": "¡El equipo enemigo se ha protegido con Velo Sagrado!", + "safeguardOnRemove": "¡Velo Sagrado dejó de hacer efecto!", + "safeguardOnRemovePlayer": "El efecto de Velo Sagrado en tu equipo se ha disipado.", + "safeguardOnRemoveEnemy": "El efecto de Velo Sagrado en el equipo enemigo se ha disipado." +} \ No newline at end of file diff --git a/src/locales/es/battle.json b/src/locales/es/battle.json index 8573f74a94e..c79315f297b 100644 --- a/src/locales/es/battle.json +++ b/src/locales/es/battle.json @@ -42,6 +42,7 @@ "moveNotImplemented": "{{moveName}} aún no está implementado y no se puede seleccionar.", "moveNoPP": "¡No hay suficientes PP\npara este movimiento!", "moveDisabled": "!No puede usar {{moveName}} porque ha sido anulado!", + "disableInterruptedMove": "¡Se ha anulado el movimiento {{moveName}}\nde {{pokemonNameWithAffix}}!", "noPokeballForce": "Una fuerza misteriosa\nte impide usar Poké Balls.", "noPokeballTrainer": "¡No puedes atrapar a los\nPokémon de los demás!", "noPokeballMulti": "¡No se pueden lanzar Poké Balls\ncuando hay más de un Pokémon!", @@ -85,4 +86,4 @@ "statSeverelyFell_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han bajado muchísimo!", "statWontGoAnyLower_one": "¡El {{stats}} de {{pokemonNameWithAffix}} no puede bajar más!", "statWontGoAnyLower_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} no pueden bajar más!" -} \ No newline at end of file +} diff --git a/src/locales/es/battler-tags.json b/src/locales/es/battler-tags.json index 9e26dfeeb6e..49efed6e8b4 100644 --- a/src/locales/es/battler-tags.json +++ b/src/locales/es/battler-tags.json @@ -1 +1,74 @@ -{} \ No newline at end of file +{ + "trappedDesc": "trampa", + "flinchedDesc": "retroceso", + "confusedDesc": "confusión", + "infatuatedDesc": "enamoramiento", + "seedDesc": "drenado", + "nightmareDesc": "pesadillas", + "ingrainDesc": "raíces", + "drowsyDesc": "sueño", + "rechargingLapse": "¡{{pokemonNameWithAffix}} necesita\nrecuperarse de su ataque!", + "trappedOnAdd": "¡{{pokemonNameWithAffix}} no puede escapar!", + "trappedOnRemove": "¡{{pokemonNameWithAffix}} se ha\nliberado de {{moveName}}!", + "flinchedLapse": "¡{{pokemonNameWithAffix}} se amedrentó!", + "confusedOnAdd": "¡{{pokemonNameWithAffix}} se encuentra confuso!", + "confusedOnRemove": "¡{{pokemonNameWithAffix}} ya no está confuso!", + "confusedOnOverlap": "¡{{pokemonNameWithAffix}} ya está confuso!", + "confusedLapse": "¡{{pokemonNameWithAffix}} está confuso!", + "confusedLapseHurtItself": "¡Está tan confuso que se ha herido a sí mismo!", + "destinyBondLapseIsBoss": "Mismo Destino no afecta a {{pokemonNameWithAffix}}.", + "destinyBondLapse": "¡{{pokemonNameWithAffix2}} ha sufrido\nel mismo destino que {{pokemonNameWithAffix}}!", + "infatuatedOnAdd": "¡{{pokemonNameWithAffix}} se ha enamorado\nde {{sourcePokemonName}}!", + "infatuatedOnOverlap": "¡{{pokemonNameWithAffix}} ya está enamorado!", + "infatuatedLapse": "¡{{pokemonNameWithAffix}} se ha enamorado\ndebido a {{sourcePokemonName}}!", + "infatuatedLapseImmobilize": "¡El enamoramiento impide que\n{{pokemonNameWithAffix}} reaccione!", + "infatuatedOnRemove": "{{pokemonNameWithAffix}} ya no está enamorado.", + "seededOnAdd": "¡{{pokemonNameWithAffix}} ha sido infectado!", + "seededLapse": "¡Las drenadoras han restado salud a {{pokemonNameWithAffix}}!", + "seededLapseShed": "¡{{pokemonNameWithAffix}} ha absorbido el lodo líquido!", + "nightmareOnAdd": "¡{{pokemonNameWithAffix}} se ha sumido en una pesadilla!", + "nightmareOnOverlap": "¡{{pokemonNameWithAffix}} ya está teniendo pesadillas!", + "nightmareLapse": "¡{{pokemonNameWithAffix}} sufre pesadillas!", + "encoreOnAdd": "¡{{pokemonNameWithAffix}} sufre los efectos de Otra Vez!", + "encoreOnRemove": "¡{{pokemonNameWithAffix}} ya no sufre los efectos de Otra Vez!", + "helpingHandOnAdd": "¡{{pokemonNameWithAffix}} se prepara\npara ayudar a {{pokemonName}}!", + "ingrainLapse": "¡{{pokemonNameWithAffix}} ha absorbido\nnutrientes a través de sus raíces!", + "ingrainOnTrap": "¡{{pokemonNameWithAffix}} ha echado raíces!", + "aquaRingOnAdd": "¡{{pokemonNameWithAffix}} se ha rodeado de un manto de agua!", + "aquaRingLapse": "¡{{pokemonName}} restauró sus PS con {{moveName}}!", + "drowsyOnAdd": "¡{{pokemonNameWithAffix}} empieza a tener sueño!", + "damagingTrapLapse": "¡{{moveName}} hiere a {{pokemonNameWithAffix}}!", + "bindOnTrap": "¡{{moveName}} de {{sourcePokemonName}} oprime a {{pokemonNameWithAffix}}!", + "wrapOnTrap": "¡{{sourcePokemonName}} ha atrapado a {{pokemonNameWithAffix}} con una constricción!", + "vortexOnTrap": "¡{{pokemonNameWithAffix}} no puede salir del torbellino!", + "clampOnTrap": "¡{{sourcePokemonNameWithAffix}} ha atenazado a \n{{pokemonName}}!", + "sandTombOnTrap": "¡{{pokemonNameWithAffix}} ha sido atrapado por {{moveName}}!", + "magmaStormOnTrap": "¡La lluvia ígnea cae sobre {{pokemonNameWithAffix}}!", + "snapTrapOnTrap": "¡{{pokemonNameWithAffix}} cayó en un cepo!", + "thunderCageOnTrap": "¡{{sourcePokemonNameWithAffix}} ha enjaulado a {{pokemonNameWithAffix}}!", + "infestationOnTrap": "¡{{pokemonNameWithAffix}} es presa del acoso de {{sourcePokemonNameWithAffix}}!", + "protectedOnAdd": "{{pokemonNameWithAffix}}\nse está protegiendo.", + "protectedLapse": "¡{{pokemonNameWithAffix}}\nse ha protegido!", + "enduringOnAdd": "{{pokemonNameWithAffix}} se prepara para resistir los ataques...", + "enduringLapse": "¡{{pokemonNameWithAffix}} ha encajado el golpe!", + "sturdyLapse": "¡{{pokemonNameWithAffix}} ha encajado el golpe!", + "perishSongLapse": "La cuenta atrás de Canto Mortal de\n{{pokemonNameWithAffix}} ha bajado a {{turnCount}}.", + "centerOfAttentionOnAdd": "¡{{pokemonNameWithAffix}} es el centro de atención!", + "truantLapse": "{{pokemonNameWithAffix}} está holgazaneando...", + "slowStartOnAdd": "¡{{pokemonNameWithAffix}} no está dando todo de sí!", + "slowStartOnRemove": "¡{{pokemonNameWithAffix}} ya puede darlo todo!", + "highestStatBoostOnAdd": "¡{{pokemonNameWithAffix}} ha reforzado su {{statName}}!", + "highestStatBoostOnRemove": "¡Los efectos de {{abilityName}}\nde {{pokemonNameWithAffix}} han desaparecido!", + "magnetRisenOnAdd": "¡{{pokemonNameWithAffix}} levita gracias a un campo electromagnético!", + "magnetRisenOnRemove": "¡El campo electromagnético de {{pokemonNameWithAffix}} se ha disipado!", + "critBoostOnAdd": "¡{{pokemonNameWithAffix}} se está preparando para luchar!", + "critBoostOnRemove": "{{pokemonNameWithAffix}} se ha relajado.", + "saltCuredOnAdd": "¡{{pokemonNameWithAffix}} está en salazón!", + "saltCuredLapse": "¡{{moveName}} ha herido a {{pokemonNameWithAffix}}!", + "cursedOnAdd": "¡{{pokemonNameWithAffix}} sacrifica algunos PS y maldice a {{pokemonName}}!", + "cursedLapse": "¡{{pokemonNameWithAffix}} es víctima de una maldición!", + "stockpilingOnAdd": "¡{{pokemonNameWithAffix}} ha reservado energía por {{stockpiledCount}}ª vez!", + "disabledOnAdd": "¡Se ha anulado el movimiento {{moveName}}\nde {{pokemonNameWithAffix}}!", + "disabledLapse": "¡El movimiento {{moveName}} de {{pokemonNameWithAffix}} ya no está anulado!", + "tarShotOnAdd": "¡{{pokemonNameWithAffix}} se ha vuelto débil ante el fuego!" +} diff --git a/src/locales/es/bgm-name.json b/src/locales/es/bgm-name.json index be617b79567..f0e0ab7e852 100644 --- a/src/locales/es/bgm-name.json +++ b/src/locales/es/bgm-name.json @@ -1,95 +1,99 @@ { "music": "Música: ", "missing_entries": "{{name}}", - "battle_kanto_champion": "B2W2 - ¡Vs Campeón de Kanto!", - "battle_johto_champion": "B2W2 - ¡Vs Campeón de Johto!", - "battle_hoenn_champion_g5": "B2W2 - ¡Vs Campeón de Hoenn!", - "battle_hoenn_champion_g6": "ORAS - ¡Vs Campeón de Hoenn!", - "battle_sinnoh_champion": "B2W2 - ¡Vs Campeón de Sinnoh!", - "battle_champion_alder": "BW - ¡Vs Campeón de Teselia!", - "battle_champion_iris": "B2W2 - ¡Vs Campeón de Teselia!", - "battle_kalos_champion": "XY - ¡Vs Campeón de Kalos!", - "battle_alola_champion": "USUM - ¡Vs Campeón de Alola!", - "battle_galar_champion": "SWSH - ¡Vs Campeón de Galar!", - "battle_champion_geeta": "SV - ¡Vs Campeona Ságita!", - "battle_champion_nemona": "SV - ¡Vs Campeona Mencía!", - "battle_champion_kieran": "SV - ¡Vs Campeón Cass!", - "battle_hoenn_elite": "ORAS - ¡Vs Alto Mando!", - "battle_unova_elite": "BW - ¡Vs Alto Mando!", - "battle_kalos_elite": "XY - ¡Vs Alto Mando!", - "battle_alola_elite": "SM - ¡Vs Alto Mando!", - "battle_galar_elite": "SWSH - Torneo de Finalistas", - "battle_paldea_elite": "SV - ¡Vs Alto Mando!", - "battle_bb_elite": "SV - ¡Vs Alto Mando de la Academia Arándano!", + "battle_kanto_champion": "B2W2 - ¡Vs. Campeón de Kanto!", + "battle_johto_champion": "B2W2 - ¡Vs. Campeón de Johto!", + "battle_hoenn_champion_g5": "B2W2 - ¡Vs. Campeón de Hoenn!", + "battle_hoenn_champion_g6": "ORAS - ¡Vs. Campeón de Hoenn!", + "battle_sinnoh_champion": "B2W2 - ¡Vs. Campeón de Sinnoh!", + "battle_champion_alder": "BW - ¡Vs. Campeón de Teselia!", + "battle_champion_iris": "B2W2 - ¡Vs. Campeón de Teselia!", + "battle_kalos_champion": "XY - ¡Vs. Campeón de Kalos!", + "battle_alola_champion": "USUM - ¡Vs. Campeón de Alola!", + "battle_galar_champion": "SWSH - ¡Vs. Campeón de Galar!", + "battle_champion_geeta": "SV - ¡Vs. Campeona Ságita!", + "battle_champion_nemona": "SV - ¡Vs. Campeona Mencía!", + "battle_champion_kieran": "SV - ¡Vs. Campeón Cass!", + "battle_hoenn_elite": "ORAS - ¡Vs. Alto Mando!", + "battle_unova_elite": "BW - ¡Vs. Alto Mando!", + "battle_kalos_elite": "XY - ¡Vs. Alto Mando!", + "battle_alola_elite": "SM - ¡Vs. Alto Mando!", + "battle_galar_elite": "SWSH - Torneo de finalistas", + "battle_paldea_elite": "SV - ¡Vs. Alto Mando!", + "battle_bb_elite": "SV - ¡Vs. Alto Mando de la Academia Arándano!", "battle_final_encounter": "PMD RTDX - Dominio de Rayquaza", - "battle_final": "BW - ¡Vs Ghechis!", - "battle_kanto_gym": "B2W2 - ¡Vs Líder de Kanto!", - "battle_johto_gym": "B2W2 - ¡Vs Líder de Johto!", - "battle_hoenn_gym": "B2W2 - ¡Vs Líder de Hoenn!", - "battle_sinnoh_gym": "B2W2 - ¡Vs Líder de Sinnoh!", - "battle_unova_gym": "BW - ¡Vs Líder de Teselia!", - "battle_kalos_gym": "XY - ¡Vs Líder de Kalos!", - "battle_galar_gym": "SWSH - ¡Vs Líder de Galar!", - "battle_paldea_gym": "SV - ¡Vs Líder de Paldea!", - "battle_legendary_kanto": "XY - ¡Vs Legendarios de Kanto!", - "battle_legendary_raikou": "HGSS - ¡Vs Raikou!", - "battle_legendary_entei": "HGSS - ¡Vs Entei!", - "battle_legendary_suicune": "HGSS - ¡Vs Suicune!", - "battle_legendary_lugia": "HGSS - ¡Vs Lugia!", - "battle_legendary_ho_oh": "HGSS - ¡Vs Ho-oh!", - "battle_legendary_regis_g5": "B2W2 - ¡Vs Regis!", - "battle_legendary_regis_g6": "ORAS - ¡Vs Regis!", - "battle_legendary_gro_kyo": "ORAS - ¡Vs Groudon/Kyogre!", - "battle_legendary_rayquaza": "ORAS - ¡Vs Rayquaza!", - "battle_legendary_deoxys": "ORAS - ¡Vs Deoxys!", - "battle_legendary_lake_trio": "ORAS - ¡Vs Trío del Lago!", - "battle_legendary_sinnoh": "ORAS - ¡Vs Legendarios de Sinnoh!", - "battle_legendary_dia_pal": "ORAS - ¡Vs Dialga/Palkia!", - "battle_legendary_giratina": "ORAS - ¡Vs Giratina!", - "battle_legendary_arceus": "HGSS - ¡Vs Arceus!", - "battle_legendary_unova": "BW - ¡Vs Legendarios de Teselia!", - "battle_legendary_kyurem": "BW - ¡Vs Kyurem!", - "battle_legendary_res_zek": "BW - ¡Vs Reshiram/Zekrom!", - "battle_legendary_xern_yvel": "XY - ¡Vs Xerneas/Yveltal!", - "battle_legendary_tapu": "SM - ¡Vs Tapus!", - "battle_legendary_sol_lun": "SM - ¡Vs Solgaleo/Lunala!", - "battle_legendary_ub": "SM - ¡Vs Ultraentes!", - "battle_legendary_dusk_dawn": "USUM - ¡Vs Necrozma Melena Crepuscular/Alas del Alba!", - "battle_legendary_ultra_nec": "USUM - ¡Vs Ultra-Necrozma!", - "battle_legendary_zac_zam": "SWSH - ¡Vs Zacian/Zamazenta!", - "battle_legendary_glas_spec": "SWSH - ¡Vs Glastrier/Spectrier!", - "battle_legendary_calyrex": "SWSH - ¡Vs Calyrex!", - "battle_legendary_birds_galar": "SWSH - ¡Vs Aves Legendarias de Galar!", - "battle_legendary_ruinous": "SV - ¡Vs Tesoros Funestos!", - "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", - "battle_legendary_loyal_three": "SV - ¡Vs Compatrones!", - "battle_legendary_ogerpon": "SV - ¡Vs Ogerpon!", - "battle_legendary_terapagos": "SV - ¡Vs Terapagos!", - "battle_legendary_pecharunt": "SV - ¡Vs Pecharunt!", - "battle_rival": "BW - ¡Vs Rival!", + "battle_final": "BW - ¡Vs. Ghechis!", + "battle_kanto_gym": "B2W2 - ¡Vs. Líder de Kanto!", + "battle_johto_gym": "B2W2 - ¡Vs. Líder de Johto!", + "battle_hoenn_gym": "B2W2 - ¡Vs. Líder de Hoenn!", + "battle_sinnoh_gym": "B2W2 - ¡Vs. Líder de Sinnoh!", + "battle_unova_gym": "BW - ¡Vs. Líder de Teselia!", + "battle_kalos_gym": "XY - ¡Vs. Líder de Kalos!", + "battle_galar_gym": "SWSH - ¡Vs. Líder de Galar!", + "battle_paldea_gym": "SV - ¡Vs. Líder de Paldea!", + "battle_legendary_kanto": "XY - ¡Vs. Legendarios de Kanto!", + "battle_legendary_raikou": "HGSS - ¡Vs. Raikou!", + "battle_legendary_entei": "HGSS - ¡Vs. Entei!", + "battle_legendary_suicune": "HGSS - ¡Vs. Suicune!", + "battle_legendary_lugia": "HGSS - ¡Vs. Lugia!", + "battle_legendary_ho_oh": "HGSS - ¡Vs. Ho-oh!", + "battle_legendary_regis_g5": "B2W2 - ¡Vs. Regis!", + "battle_legendary_regis_g6": "ORAS - ¡Vs. Regis!", + "battle_legendary_gro_kyo": "ORAS - ¡Vs. Groudon/Kyogre!", + "battle_legendary_rayquaza": "ORAS - ¡Vs. Rayquaza!", + "battle_legendary_deoxys": "ORAS - ¡Vs. Deoxys!", + "battle_legendary_lake_trio": "ORAS - ¡Vs. trío del Lago!", + "battle_legendary_sinnoh": "ORAS - ¡Vs. legendarios de Sinnoh!", + "battle_legendary_dia_pal": "ORAS - ¡Vs. Dialga/Palkia!", + "battle_legendary_origin_forme": "LA - ¡Vs. Dialga & Palkia, Forma Origen!", + "battle_legendary_giratina": "ORAS - ¡Vs. Giratina!", + "battle_legendary_arceus": "HGSS - ¡Vs. Arceus!", + "battle_legendary_unova": "BW - ¡Vs. legendarios de Teselia!", + "battle_legendary_kyurem": "BW - ¡Vs. Kyurem!", + "battle_legendary_res_zek": "BW - ¡Vs. Reshiram/Zekrom!", + "battle_legendary_xern_yvel": "XY - ¡Vs. Xerneas/Yveltal!", + "battle_legendary_tapu": "SM - ¡Vs. Tapus!", + "battle_legendary_sol_lun": "SM - ¡Vs. Solgaleo/Lunala!", + "battle_legendary_ub": "SM - ¡Vs. Ultraentes!", + "battle_legendary_dusk_dawn": "USUM - ¡Vs. Necrozma Melena Crepuscular/Alas del Alba!", + "battle_legendary_ultra_nec": "USUM - ¡Vs. Ultra-Necrozma!", + "battle_legendary_zac_zam": "SWSH - ¡Vs. Zacian/Zamazenta!", + "battle_legendary_glas_spec": "SWSH - ¡Vs. Glastrier/Spectrier!", + "battle_legendary_calyrex": "SWSH - ¡Vs. Calyrex!", + "battle_legendary_riders": "SWSH - ¡Vs. Calyrex Jinete!", + "battle_legendary_birds_galar": "SWSH - ¡Vs. Aves Legendarias de Galar!", + "battle_legendary_ruinous": "SV - ¡Vs. Tesoros Funestos!", + "battle_legendary_kor_mir": "SV - ¡Batalla en el área Zero!", + "battle_legendary_loyal_three": "SV - ¡Vs. Compatrones!", + "battle_legendary_ogerpon": "SV - ¡Vs. Ogerpon!", + "battle_legendary_terapagos": "SV - ¡Vs. Terapagos!", + "battle_legendary_pecharunt": "SV - ¡Vs. Pecharunt!", + "battle_rival": "BW - ¡Vs. Rival!", "battle_rival_2": "BW - ¡Vs N!", - "battle_rival_3": "BW - ¡Vs N (Liga Pokémon)!", - "battle_trainer": "BW - ¡Vs Entrenador!", - "battle_wild": "BW - ¡Vs Pokémon Salvaje!", - "battle_wild_strong": "BW - ¡Vs Pokémon Salvaje Raro!", - "end_summit": "PMD RTDX - Techo del Cielo", - "battle_rocket_grunt": "HGSS Team Rocket Battle", - "battle_aqua_magma_grunt": "ORAS Team Aqua & Magma Battle", - "battle_galactic_grunt": "BDSP Team Galactic Battle", + "battle_rival_3": "BW - ¡Vs. N (Liga Pokémon)!", + "battle_trainer": "BW - ¡Vs. entrenador!", + "battle_wild": "BW - ¡Vs. Pokémon salvaje!", + "battle_wild_strong": "BW - ¡Vs. Pokémon salvaje raro!", + "end_summit": "PMD RTDX - Techo del cielo", + "battle_rocket_grunt": "HGSS - ¡Vs. Team Rocket!", + "battle_aqua_magma_grunt": "ORAS - ¡Vs. Equipo Aqua & Magma!", + "battle_galactic_grunt": "BDSP - ¡Vs. Equipo Galaxia!", "battle_plasma_grunt": "BW - ¡Vs Equipo Plasma!", - "battle_flare_grunt": "XY Team Flare Battle", - "battle_aether_grunt": "SM Aether Foundation Battle", - "battle_skull_grunt": "SM Team Skull Battle", - "battle_macro_grunt": "SWSH Trainer Battle", - "battle_galactic_admin": "BDSP Team Galactic Admin Battle", - "battle_skull_admin": "SM Team Skull Admin Battle", - "battle_oleana": "SWSH Oleana Battle", - "battle_rocket_boss": "USUM Giovanni Battle", - "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", - "battle_galactic_boss": "BDSP Cyrus Battle", - "battle_plasma_boss": "B2W2 Ghetsis Battle", - "battle_flare_boss": "XY Lysandre Battle", - + "battle_flare_grunt": "XY - ¡Vs. Team Flare!", + "battle_aether_grunt": "SM - ¡Vs. Fundación Æther!", + "battle_skull_grunt": "SM - ¡Vs. Team Skull!", + "battle_macro_grunt": "SWSH - ¡Vs. entrenador!", + "battle_galactic_admin": "BDSP - ¡Vs. Comandante del Equipo Galaxia!", + "battle_skull_admin": "SM - ¡Vs. Comandante del Team Skull!", + "battle_oleana": "SWSH - ¡Vs. Olivia!", + "battle_rocket_boss": "USUM - ¡Vs. Giovanni!", + "battle_aqua_magma_boss": "ORAS - ¡Vs. Aquiles & Magno!", + "battle_galactic_boss": "BDSP - ¡Vs. Helio!", + "battle_plasma_boss": "B2W2 - ¡Vs. Ghechis Armonia!", + "battle_flare_boss": "XY - ¡Vs. Lysson!", + "battle_aether_boss": "SM - ¡Vs. Samina!", + "battle_skull_boss": "SM - ¡Vs. Guzmán!", + "battle_macro_boss": "SWSH - ¡Vs. Rose!", "abyss": "PMD EoS - Cráter Oscuro", "badlands": "PMD EoS - Valle Desolado", "beach": "PMD EoS - Risco Calado", @@ -105,40 +109,40 @@ "graveyard": "PMD EoS - Bosque Misterio", "ice_cave": "PMD EoS - Gran Iceberg", "island": "PMD EoS - Costa Escarpada", - "jungle": "Lmz - Jungle", - "laboratory": "Firel - Laboratory", + "jungle": "Lmz - Jungla", + "laboratory": "Firel - Laboratorio", "lake": "PMD EoS - Cueva Cristal", "meadow": "PMD EoS - Bosque de la Cumbre del Cielo", - "metropolis": "Firel - Metropolis", + "metropolis": "Firel - Metrópolis", "mountain": "PMD EoS - Monte Cuerno", "plains": "PMD EoS - Pradera de la Cumbre del Cielo", "power_plant": "PMD EoS - Pradera Destello", "ruins": "PMD EoS - Sima Hermética", - "sea": "Andr06 - Marine Mystique", - "seabed": "Firel - Seabed", - "slum": "Andr06 - Sneaky Snom", + "sea": "Andr06 - Misticismo marino", + "seabed": "Firel - Lecho del mar", + "slum": "Andr06 - Snom sigiloso", "snowy_forest": "PMD EoS - Campo nevado de la Cumbre del Cielo", - "space": "Firel - Aether", + "space": "Firel - Æther ", "swamp": "PMD EoS - Mar Circundante", "tall_grass": "PMD EoS - Bosque Niebla", "temple": "PMD EoS - Cueva Regia", "town": "PMD EoS - Tema del territorio aleatorio 3", "volcano": "PMD EoS - Cueva Vapor", "wasteland": "PMD EoS - Corazón Tierra Oculta", - "encounter_ace_trainer": "BW - Desafío Combate (Entrenador Guay)", - "encounter_backpacker": "BW - Desafío Combate (Mochilero)", - "encounter_clerk": "BW - Desafío Combate (Empresario)", - "encounter_cyclist": "BW - Desafío Combate (Ciclista)", - "encounter_lass": "BW - Desafío Combate (Chica)", - "encounter_parasol_lady": "BW - Desafío Combate (Dama parasol)", - "encounter_pokefan": "BW - Desafío Combate (Pokéfan)", - "encounter_psychic": "BW - Desafío Combate (Médium)", - "encounter_rich": "BW - Desafío Combate (Aristócrata)", - "encounter_rival": "BW - Desafío Combate (Cheren)", - "encounter_roughneck": "BW - Desafío Combate (Calvo)", - "encounter_scientist": "BW - Desafío Combate (Científico)", - "encounter_twins": "BW - Desafío Combate (Gemelas)", - "encounter_youngster": "BW - Desafío Combate (Joven)", + "encounter_ace_trainer": "BW - ¡Vs. entrenador guay!", + "encounter_backpacker": "BW - ¡Vs. mochilero!", + "encounter_clerk": "BW - ¡Vs. empresario!", + "encounter_cyclist": "BW - ¡Vs. ciclista!", + "encounter_lass": "BW - ¡Vs. chica joven!", + "encounter_parasol_lady": "BW - ¡Vs. dama parasol!", + "encounter_pokefan": "BW - ¡Vs. poké-fan!", + "encounter_psychic": "BW -¡Vs. médium!", + "encounter_rich": "BW - ¡Vs. aristócrata!", + "encounter_rival": "BW - ¡Vs. Cheren!", + "encounter_roughneck": "BW - ¡Vs. tío chungo!", + "encounter_scientist": "BW - ¡Vs. científico!", + "encounter_twins": "BW - ¡Vs. gemelas!", + "encounter_youngster": "BW - ¡Vs. chico joven!", "heal": "BW - Cura Pokémon", "menu": "PMD EoS - ¡Bienvenidos al mundo de los Pokémon!", "title": "PMD EoS - Tema del menú principal" diff --git a/src/locales/es/fight-ui-handler.json b/src/locales/es/fight-ui-handler.json index dc8140b61fc..fa13f2e0e77 100644 --- a/src/locales/es/fight-ui-handler.json +++ b/src/locales/es/fight-ui-handler.json @@ -3,5 +3,6 @@ "power": "Potencia", "accuracy": "Precisión", "abilityFlyInText": " {{passive}}{{pokemonName}}\n{{abilityName}}", - "passive": "Pasiva de " + "passive": "Pasiva de ", + "teraHover": "Tera-tipo {{type}}" } diff --git a/src/locales/es/game-mode.json b/src/locales/es/game-mode.json index e7925900253..0dbccb45e1f 100644 --- a/src/locales/es/game-mode.json +++ b/src/locales/es/game-mode.json @@ -1,8 +1,8 @@ { - "classic": "Clásica", - "endless": "Infinita", - "endlessSpliced": "Infinita (Fusión)", - "dailyRun": "Diaria", - "unknown": "Desconicido", + "classic": "Clásico", + "endless": "Infinito", + "endlessSpliced": "Infinito (Fusión)", + "dailyRun": "Diario", + "unknown": "Desconocido", "challenge": "Desafío" -} \ No newline at end of file +} diff --git a/src/locales/es/menu.json b/src/locales/es/menu.json index bd2479a02df..ef1ae93dd82 100644 --- a/src/locales/es/menu.json +++ b/src/locales/es/menu.json @@ -14,14 +14,14 @@ "register": "Registrarse", "emptyUsername": "El usuario no puede estar vacío", "invalidLoginUsername": "El usuario no es válido", - "invalidRegisterUsername": "El usuario solo puede contener letras, números y guiones bajos", + "invalidRegisterUsername": "El usuario solo puede contener letras, números y guiones bajos.", "invalidLoginPassword": "La contraseña no es válida", "invalidRegisterPassword": "La contraseña debe tener 6 o más caracteres.", "usernameAlreadyUsed": "El usuario ya está en uso", "accountNonExistent": "El usuario no existe", "unmatchingPassword": "La contraseña no coincide", "passwordNotMatchingConfirmPassword": "Las contraseñas deben coincidir", - "confirmPassword": "Confirmar Contra.", + "confirmPassword": "Confirmar contraseña", "registrationAgeWarning": "Al registrarte, confirmas tener 13 o más años de edad.", "backToLogin": "Volver al Login", "failedToLoadSaveData": "No se han podido cargar los datos guardados. Por favor, recarga la página.\nSi el fallo continúa, por favor comprueba #announcements en nuestro Discord.", @@ -51,5 +51,7 @@ "renamePokemon": "Renombrar Pokémon.", "rename": "Renombrar", "nickname": "Apodo", - "errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta, el juego se reconectará automáticamente." + "errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta, el juego se reconectará automáticamente.", + "noSaves": "No tienes ninguna partida guardada registrada!", + "tooManySaves": "¡Tienes demasiadas partidas guardadas registradas!" } diff --git a/src/locales/es/modifier-type.json b/src/locales/es/modifier-type.json index 95325788bf4..3ac4d85f793 100644 --- a/src/locales/es/modifier-type.json +++ b/src/locales/es/modifier-type.json @@ -47,10 +47,14 @@ "description": "Cambia la naturaleza de un Pokémon a {{natureName}} y desbloquea permanentemente dicha naturaleza para el inicial." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Duplica la posibilidad de que un encuentro sea una combate doble durante {{battleCount}} combates." + "description": "Cuadruplica la posibilidad de que un encuentro sea una combate doble durante {{battleCount}} combates." }, - "TempBattleStatBoosterModifierType": { - "description": "Aumenta la est. {{tempBattleStatName}} de todos los miembros del equipo en 1 nivel durante 5 combates." + "TempStatStageBoosterModifierType": { + "description": "Aumenta la est. {{stat}} de todos los miembros del equipo en {{amount}} durante 5 combates.", + "extra": { + "stage": "1 nivel", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "Aumenta la potencia de los movimientos de tipo {{moveType}} de un Pokémon en un 20%." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "Aumenta el nivel de todos los miembros del equipo en {{levels}}." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Aumenta la est. {{statName}} base del portador en un 10%.\nCuanto mayores sean tus IVs, mayor será el límite de acumulación." + "BaseStatBoosterModifierType": { + "description": "Aumenta la est. {{stat}} base del portador en un 10%.\nCuanto mayores sean tus IVs, mayor será el límite de acumulación." }, "AllPokemonFullHpRestoreModifierType": { "description": "Restaura el 100% de los PS de todos los Pokémon." @@ -248,6 +252,12 @@ "name": "Periscopio", "description": "Aumenta la probabilidad de asestar un golpe crítico." }, + "DIRE_HIT": { + "name": "Crítico X", + "extra": { + "raises": "Critical Hit Ratio" + } + }, "LEEK": { "name": "Puerro", "description": "Puerro muy largo y duro que aumenta la probabilidad de asestar un golpe crítico. Debe llevarlo Farfetch'd." @@ -411,25 +421,13 @@ "description": "Polvo muy fino, pero a la vez poderoso, que aumenta la Velocidad. Debe llevarlo Ditto." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "Ataque X", "x_defense": "Defensa X", "x_sp_atk": "Ataq. Esp. X", "x_sp_def": "Def. Esp. X", "x_speed": "Velocidad X", - "x_accuracy": "Precisión X", - "dire_hit": "Crítico X" - }, - "TempBattleStatBoosterStatName": { - "ATK": "Ataque", - "DEF": "Defensa", - "SPATK": "Ataq. Esp.", - "SPDEF": "Def. Esp.", - "SPD": "Velocidad", - "ACC": "Precisión", - "CRIT": "Tasa de crítico", - "EVA": "Evasión", - "DEFAULT": "???" + "x_accuracy": "Precisión X" }, "AttackTypeBoosterItem": { "silk_scarf": "Pañuelo seda", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "Disco dragón", "DARK_MEMORY": "Disco siniestro", "FAIRY_MEMORY": "Disco hada", - "BLANK_MEMORY": "Disco en blanco" + "NORMAL_MEMORY": "Disco normal" } } diff --git a/src/locales/es/modifier.json b/src/locales/es/modifier.json index 593b3df2f0f..a94e41a4574 100644 --- a/src/locales/es/modifier.json +++ b/src/locales/es/modifier.json @@ -1,3 +1,12 @@ { - "bypassSpeedChanceApply": "¡Gracias {{itemName}} {{pokemonName}} puede tener prioridad!" -} \ No newline at end of file + "surviveDamageApply": "{{pokemonNameWithAffix}} ha usado {{typeName}} y ha logrado resistir!", + "turnHealApply": "{{pokemonNameWithAffix}} ha recuperado unos pocos PS gracias a {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} ha recuperado unos pocos PS gracias a {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} ha sido revivido gracias a su {{typeName}}!", + "pokemonResetNegativeStatStageApply": "Las estadísticas bajadas de {{pokemonNameWithAffix}} fueron restauradas gracias a {{typeName}}!", + "moneyInterestApply": "Recibiste intereses de ₽{{moneyAmount}}\ngracias a {{typeName}}!", + "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} fue absorbido\npor {{pokemonName}}'s {{typeName}}!", + "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} fue robado por {{pokemonName}}'s {{typeName}}!", + "enemyTurnHealApply": "¡{{pokemonNameWithAffix}}\nrecuperó algunos PS!", + "bypassSpeedChanceApply": "¡Gracias a su {{itemName}}, {{pokemonName}} puede tener prioridad!" +} diff --git a/src/locales/es/move-trigger.json b/src/locales/es/move-trigger.json index b570f029377..2322a49056f 100644 --- a/src/locales/es/move-trigger.json +++ b/src/locales/es/move-trigger.json @@ -1,4 +1,8 @@ { + "switchedTwoStatChanges": "{{pokemonName}} ha intercambiado los cambios en {{firstStat}} y {{secondStat}} con los del objetivo!", + "switchedStat": "{{pokemonName}} cambia su {{stat}} por la de su objetivo!", + "sharedGuard": "{{pokemonName}} suma su capacidad defensiva a la del objetivo y la reparte equitativamente!", + "sharedPower": "{{pokemonName}} suma su capacidad ofensiva a la del objetivo y la reparte equitativamente!", "isChargingPower": "¡{{pokemonName}} está acumulando energía!", "burnedItselfOut": "¡El fuego interior de {{pokemonName}} se ha extinguido!", "startedHeatingUpBeak": "¡{{pokemonName}} empieza\na calentar su pico!", @@ -7,5 +11,7 @@ "usedUpAllElectricity": "¡{{pokemonName}} ha descargado toda su electricidad!", "stoleItem": "¡{{pokemonName}} robó el objeto\n{{itemName}} de {{targetName}}!", "statEliminated": "¡Los cambios en estadísticas fueron eliminados!", - "revivalBlessing": "¡{{pokemonName}} ha revivido!" -} \ No newline at end of file + "revivalBlessing": "¡{{pokemonName}} ha revivido!", + "safeguard": "¡{{targetName}} está protegido por Velo Sagrado!", + "afterYou": "¡{{pokemonName}} ha decidido aprovechar la oportunidad!" +} diff --git a/src/locales/es/party-ui-handler.json b/src/locales/es/party-ui-handler.json index 65552a1e1d5..0e59aee6fd1 100644 --- a/src/locales/es/party-ui-handler.json +++ b/src/locales/es/party-ui-handler.json @@ -1,4 +1,9 @@ { + "SEND_OUT": "Enviar", + "SUMMARY": "Resumen", + "CANCEL": "Cancelar", + "RELEASE": "Liberar", + "APPLY": "Aplicar", "TEACH": "Enseñar", "SPLICE": "Fusionar", "UNSPLICE": "Separar", @@ -7,23 +12,23 @@ "TRANSFER": "Transferir", "ALL": "Todo", "PASS_BATON": "Relevo", - "UNPAUSE_EVOLUTION": "Reanudar Evolución", + "UNPAUSE_EVOLUTION": "Reanudar evolución", "REVIVE": "Revivir", "RENAME": "Rename", "choosePokemon": "Elige a un Pokémon.", "doWhatWithThisPokemon": "¿Que quieres hacer con este Pokémon?", - "noEnergy": "¡A {{pokemonName}} no le quedan\nfuerzas para luchar!", - "hasEnergy": "¡A {{pokemonName}} le quedan\nfuerzas para luchar!", - "cantBeUsed": "¡{{pokemonName}} no puede usarse\nen este desafío!", - "tooManyItems": "¡{{pokemonName}} tiene demasiados\nde este objeto!", + "noEnergy": "¡A {{pokemonName}} no le\nquedan fuerzas para luchar!", + "hasEnergy": "¡A {{pokemonName}} le\nquedan fuerzas para luchar!", + "cantBeUsed": "¡{{pokemonName}} no puede usarse en este desafío!", + "tooManyItems": "¡{{pokemonName}} tiene\ndemasiado de este objeto!", "anyEffect": "No tendría ningún efecto.", - "unpausedEvolutions": "Se reanudó las evoluciones de {{pokemonName}}.", + "unpausedEvolutions": "Se reanudaron las evoluciones de {{pokemonName}}.", "unspliceConfirmation": "¿Seguro que quiere separar a {{fusionName}}\nde {{pokemonName}}? {{fusionName}} se perderá.", "wasReverted": "{{fusionName}} se revirtió a {{pokemonName}}.", "releaseConfirmation": "¿Quieres liberar a {{pokemonName}}?", "releaseInBattle": "¡No puedes liberar un Pokémon que está en batalla!", "selectAMove": "Selecciona un movimiento.", - "changeQuantity": "Selecciona un objeto equipado para transferir.\nUsa < y > para cambiar la cantidad.", + "changeQuantity": "Selecciona un ítem para transferir.\nUsa < y > para calibrar.", "selectAnotherPokemonToSplice": "Selecciona otro Pokémon para fusionar.", "cancel": "Salir", "able": "Apto", @@ -36,7 +41,7 @@ "thisIsWhereWePart": "¡Aquí es donde nos despedimos, {{pokemonName}}!", "illMissYou": "¡Te echaré de menos, {{pokemonName}}!", "illNeverForgetYou": "¡Nunca te olvidaré, {{pokemonName}}!", - "untilWeMeetAgain": "¡Hasta que nos volvamos a encontrar, {{pokemonName}}!", + "untilWeMeetAgain": "¡Hasta que nos volvamos a\nencontrar, {{pokemonName}}!", "sayonara": "¡Sayonara, {{pokemonName}}!", "smellYaLater": "¡Nos vemos luego, {{pokemonName}}!" -} \ No newline at end of file +} diff --git a/src/locales/es/pokemon-form-battle.json b/src/locales/es/pokemon-form-battle.json index 7af01f8f093..d6eed9e93cc 100644 --- a/src/locales/es/pokemon-form-battle.json +++ b/src/locales/es/pokemon-form-battle.json @@ -4,5 +4,11 @@ "mega-y": "Mega {{pokemonName}} Y", "primal": "{{pokemonName}} Primigenio", "gigantamax": "G-Max {{pokemonName}}", - "eternamax": "E-Max {{pokemonName}}" -} \ No newline at end of file + "eternamax": "E-Max {{pokemonName}}", + "megaChange": "¡{{preName}} ha mega-evolucionado a {{pokemonName}}!", + "gigantamaxChange": "¡{{preName}} ha gigamaxizado a {{pokemonName}}!", + "eternamaxChange": "¡{{preName}} ha eternamaxizado a {{pokemonName}}!", + "revertChange": "¡{{pokemonName}} ha revertido a su forma original!", + "formChange": "¡{{preName}} ha cambiado de forma!", + "disguiseChange": "¡El disfraz ha actuado como señuelo!" +} diff --git a/src/locales/es/pokemon-form.json b/src/locales/es/pokemon-form.json index c46521d78da..2f70038ad2d 100644 --- a/src/locales/es/pokemon-form.json +++ b/src/locales/es/pokemon-form.json @@ -7,6 +7,7 @@ "pikachuToughCosplay": "Enmascarada", "pikachuPartner": "Compañero", "eeveePartner": "Compañero", + "pichuSpiky": "Picoreja", "unownA": "A", "unownB": "B", "unownC": "C", @@ -49,6 +50,8 @@ "rotomFrost": "Frío", "rotomFan": "Ventilador", "rotomMow": "Corte", + "giratinaAltered": "Modificada", + "shayminLand": "Tierra", "basculinRedStriped": "Raya Roja", "basculinBlueStriped": "Raya Azul", "basculinWhiteStriped": "Raya Blanca", @@ -56,6 +59,10 @@ "deerlingSummer": "Verano", "deerlingAutumn": "Otoño", "deerlingWinter": "Invierno", + "tornadusIncarnate": "Avatar", + "thundurusIncarnate": "Avatar", + "landorusIncarnate": "Avatar", + "keldeoOrdinary": "Habitual", "meloettaAria": "Lírica", "meloettaPirouette": "Danza", "froakieBattleBond": "Fuerte Afecto", @@ -87,12 +94,12 @@ "furfrouHeart": "Corazón", "furfrouStar": "Estrella", "furfrouDiamond": "Diamante", - "furfrouDebutante": "Debutante", - "furfrouMatron": "Matrón", - "furfrouDandy": "Dandi", - "furfrouLaReine": "La Reine", + "furfrouDebutante": "Señorita", + "furfrouMatron": "Dama", + "furfrouDandy": "Caballero", + "furfrouLaReine": "Aristócrata", "furfrouKabuki": "Kabuki", - "furfrouPharaoh": "Faraón", + "furfrouPharaoh": "Faraónico", "pumpkabooSmall": "Pequeño", "pumpkabooLarge": "Grande", "pumpkabooSuper": "Enorme", @@ -127,11 +134,15 @@ "magearnaOriginal": "Vetusto", "marshadowZenith": "Cénit", "sinisteaPhony": "Falsificada", - "sinisteaAntique": "Auténtica", + "sinisteaAntique": "Genuina", "eiscueNoIce": "Cara Deshielo", "indeedeeMale": "Macho", "indeedeeFemale": "Hembra", + "morpekoFullBelly": "Saciada", + "zacianHeroOfManyBattles": "Guerrero avezado", + "zamazentaHeroOfManyBattles": "Guerrero avezado", "zarudeDada": "Papá", + "enamorusIncarnate": "Avatar", "squawkabillyGreenPlumage": "Plumaje Verde", "squawkabillyBluePlumage": "Plumaje Azul", "squawkabillyYellowPlumage": "Plumaje Amarillo", @@ -141,9 +152,19 @@ "tatsugiriStretchy": "Estirada", "gimmighoulChest": "Cofre", "gimmighoulRoaming": "Andante", - "poltchageistCounterfeit": "Imitación", - "poltchageistArtisan": "Original", + "koraidonApexBuild": "Forma Plena", + "koraidonLimitedBuild": "Forma Limitada", + "koraidonSprintingBuild": "Forma Carrera", + "koraidonSwimmingBuild": "Forma Nado", + "koraidonGlidingBuild": "Forma Planeo", + "miraidonUltimateMode": "Modo Pleno", + "miraidonLowPowerMode": "Modo Limitado", + "miraidonDriveMode": "Modo Conducción", + "miraidonAquaticMode": "Modo Flote", + "miraidonGlideMode": "Modo Planeo", + "poltchageistCounterfeit": "Fraudulenta", + "poltchageistArtisan": "Opulenta", "paldeaTaurosCombat": "Combatiente", "paldeaTaurosBlaze": "Ardiente", "paldeaTaurosAqua": "Acuático" -} \ No newline at end of file +} diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 9c16fbb0fd6..dc441d48eb8 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -100,7 +100,7 @@ "moveTouchControls": "Controles táctiles", "shopOverlayOpacity": "Opacidad de la fase de compra", "shopCursorTarget": "Cursor de la tienda", - "items": "Objetos", + "rewards": "Objetos", "reroll": "Actualizar", "shop": "Tienda", "checkTeam": "Ver equipo" diff --git a/src/locales/es/splash-messages.json b/src/locales/es/splash-messages.json index 90ce3593b89..b1d4820b06e 100644 --- a/src/locales/es/splash-messages.json +++ b/src/locales/es/splash-messages.json @@ -3,12 +3,12 @@ "joinTheDiscord": "¡Únete al Discord!", "infiniteLevels": "¡Niveles infinitos!", "everythingStacks": "¡Todo se acumula!", - "optionalSaveScumming": "¡Trampas guardando (¡opcionales!)!", + "optionalSaveScumming": "¡Trampas de guardado opcionales!", "biomes": "¡35 biomas!", "openSource": "¡Código abierto!", "playWithSpeed": "¡Juega a velocidad 5x!", - "liveBugTesting": "¡Arreglo de bugs sobre la marcha!", - "heavyInfluence": "¡Influencia Alta en RoR2!", + "liveBugTesting": "¡Testeo de bugs en directo!", + "heavyInfluence": "¡Mucha Influencia de RoR2!", "pokemonRiskAndPokemonRain": "¡Pokémon Risk y Pokémon Rain!", "nowWithMoreSalt": "¡Con un 33% más de polémica!", "infiniteFusionAtHome": "¡Infinite Fusion en casa!", @@ -17,16 +17,16 @@ "mubstitute": "¡Mubstituto!", "thatsCrazy": "¡De locos!", "oranceJuice": "¡Zumo de narancia!", - "questionableBalancing": "¡Balance cuestionable!", + "questionableBalancing": "¡Cambios en balance cuestionables!", "coolShaders": "¡Shaders impresionantes!", "aiFree": "¡Libre de IA!", "suddenDifficultySpikes": "¡Saltos de dificultad repentinos!", "basedOnAnUnfinishedFlashGame": "¡Basado en un juego Flash inacabado!", - "moreAddictiveThanIntended": "¡Más adictivo de la cuenta!", + "moreAddictiveThanIntended": "¡Más adictivo de lo previsto!", "mostlyConsistentSeeds": "¡Semillas CASI consistentes!", "achievementPointsDontDoAnything": "¡Los Puntos de Logro no hacen nada!", "youDoNotStartAtLevel": "¡No empiezas al nivel 2000!", - "dontTalkAboutTheManaphyEggIncident": "¡No hablen del incidente del Huevo Manaphy!", + "dontTalkAboutTheManaphyEggIncident": "¡No se habla del Incidente Manaphy!", "alsoTryPokengine": "¡Prueba también Pokéngine!", "alsoTryEmeraldRogue": "¡Prueba también Emerald Rogue!", "alsoTryRadicalRed": "¡Prueba también Radical Red!", diff --git a/src/locales/es/terrain.json b/src/locales/es/terrain.json index 9e26dfeeb6e..912f5186180 100644 --- a/src/locales/es/terrain.json +++ b/src/locales/es/terrain.json @@ -1 +1,16 @@ -{} \ No newline at end of file +{ + "misty": "Niebla", + "mistyStartMessage": "¡La niebla ha envuelto el terreno de combate!", + "mistyClearMessage": "La niebla se ha disipado.", + "mistyBlockMessage": "¡El campo de niebla ha protegido a {{pokemonNameWithAffix}} ", + "electric": "Eléctrico", + "electricStartMessage": "¡Se ha formado un campo de corriente eléctrica en el terreno\nde combate!", + "electricClearMessage": "El campo de corriente eléctrica ha desaparecido.\t", + "grassy": "Hierba", + "grassyStartMessage": "¡El terreno de combate se ha cubierto de hierba!", + "grassyClearMessage": "La hierba ha desaparecido.", + "psychic": "Psíquico", + "psychicStartMessage": "¡El terreno de combate se ha vuelto muy extraño!", + "psychicClearMessage": "Ha desaparecido la extraña sensación que se percibía en el terreno\nde combate.", + "defaultBlockMessage": "¡El campo {{terrainName}} ha protegido a {{pokemonNameWithAffix}} " +} diff --git a/src/locales/es/trainer-names.json b/src/locales/es/trainer-names.json index c6758366db7..ce09a0c9037 100644 --- a/src/locales/es/trainer-names.json +++ b/src/locales/es/trainer-names.json @@ -1,7 +1,7 @@ { "brock": "Brock", "misty": "Misty", - "lt_surge": "Tt. Surge", + "lt_surge": "Teniente Surge", "erika": "Erika", "janine": "Sachiko", "sabrina": "Sabrina", @@ -23,7 +23,7 @@ "winona": "Alana", "tate": "Vito", "liza": "Leti", - "juan": "Galán", + "juan": "Galano", "roark": "Roco", "gardenia": "Gardenia", "maylene": "Brega", @@ -34,7 +34,7 @@ "volkner": "Lectro", "cilan": "Millo", "chili": "Zeo", - "cress": "Maiz", + "cress": "Maíz", "cheren": "Cheren", "lenora": "Aloe", "roxie": "Hiedra", @@ -57,7 +57,7 @@ "nessa": "Cathy", "kabu": "Naboru", "bea": "Judith", - "allister": "Allistair", + "allister": "Alistair", "opal": "Sally", "bede": "Berto", "gordie": "Morris", @@ -123,30 +123,28 @@ "leon": "Lionel", "rival": "Finn", "rival_female": "Ivy", - "archer": "Archer", - "ariana": "Ariana", - "proton": "Proton", + "archer": "Atlas", + "ariana": "Atenea", + "proton": "Protón", "petrel": "Petrel", - "tabitha": "Tabitha", - "courtney": "Courtney", - "shelly": "Shelly", - "matt": "Matt", - "mars": "Mars", - "jupiter": "Jupiter", - "saturn": "Saturn", - "zinzolin": "Zinzolin", - "rood": "Rood", - "xerosic": "Xerosic", - "bryony": "Bryony", + "tabitha": "Tatiano", + "courtney": "Carola", + "shelly": "Silvina", + "matt": "Matías", + "mars": "Venus", + "jupiter": "Ceres", + "saturn": "Saturno", + "zinzolin": "Menek", + "rood": "Ruga", + "xerosic": "Xero", + "bryony": "Begonia", + "maxie": "Magno", + "archie": "Aquiles", + "cyrus": "Helio", + "ghetsis": "Ghechis", + "lysandre": "Lysson", "faba": "Fabio", - - "maxie": "Maxie", - "archie": "Archie", - "cyrus": "Cyrus", - "ghetsis": "Ghetsis", - "lysandre": "Lysandre", "lusamine": "Samina", - "blue_red_double": "Azul y Rojo", "red_blue_double": "Rojo y Azul", "tate_liza_double": "Vito y Leti", diff --git a/src/locales/fr/ability-trigger.json b/src/locales/fr/ability-trigger.json index d10fc18a146..761242b3d94 100644 --- a/src/locales/fr/ability-trigger.json +++ b/src/locales/fr/ability-trigger.json @@ -3,14 +3,15 @@ "badDreams": "{{pokemonName}} a le sommeil agité !", "costar": "{{pokemonName}} copie les changements de stats\nde {{allyName}} !", "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} évite les dégâts\navec {{abilityName}} !", - "perishBody": "{{abilityName}} de {{pokemonName}}\nmettra les deux Pokémon K.O. dans trois tours !", - "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaure un peu ses PV !", + "perishBody": "{{abilityName}} de {{pokemonName}}\nmettra les deux Pokémon K.O. dans trois tours !", + "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaure un peu ses PV !", "trace": "{{pokemonName}} copie le talent {{abilityName}}\nde {{targetName}} !", - "windPowerCharged": "{{pokemonName}} a été touché par la capacité {{moveName}} et se charge en électricité !", + "windPowerCharged": "{{pokemonName}} a été touché par la capacité {{moveName}} et se charge en électricité !", "quickDraw": "Tir Vif permet à {{pokemonName}}\nd’agir plus vite que d’habitude !", "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nempêche son objet d’être volé !", - "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", + "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} évite\nles dégâts avec {{abilityName}} !", + "fullHpResistType": "{{pokemonNameWithAffix}} fait briller sa carapace\net fausse les affinités de type !", "disguiseAvoidedDamage": "Le déguisement de {{pokemonNameWithAffix}}\ntombe !", "moveImmunity": "Ça n’affecte pas {{pokemonNameWithAffix}}…", "reverseDrain": "{{pokemonNameWithAffix}} aspire\nle suintement !", @@ -33,12 +34,12 @@ "battlerTagImmunity": "{{abilityName}} de {{pokemonNameWithAffix}}\nempêche {{battlerTagName}} !", "forewarn": "La capacité {{moveName}}\nde {{pokemonNameWithAffix}} a été détectée !", "frisk": "{{pokemonNameWithAffix}} fouille {{opponentName}}\net trouve son talent {{opponentAbilityName}} !", - "postWeatherLapseHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", + "postWeatherLapseHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", "postWeatherLapseDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} a récolté\nune {{berryName}} !", - "postTurnHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", + "postTurnHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", "fetchBall": "{{pokemonNameWithAffix}} trouve\nune {{pokeballName}} !", - "healFromBerryUse": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", + "healFromBerryUse": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", "arenaTrap": "{{pokemonNameWithAffix}} empêche\nles changements grâce à son talent {{abilityName}} !", "postBattleLoot": "{{pokemonNameWithAffix}} ramasse\nl’objet {{itemName}} !", "postFaintContactDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", @@ -49,8 +50,9 @@ "postSummonAnticipation": "{{pokemonNameWithAffix}}\nest tout tremblant !", "postSummonTurboblaze": "{{pokemonNameWithAffix}} dégage\nune aura de flammes incandescentes !", "postSummonTeravolt": "{{pokemonNameWithAffix}} dégage\nune aura électrique instable !", - "postSummonDarkAura": "{{pokemonNameWithAffix}} dégage\nune aura ténébreuse !", + "postSummonDarkAura": "{{pokemonNameWithAffix}} dégage\nune aura ténébreuse !", "postSummonFairyAura": "{{pokemonNameWithAffix}} dégage\nune aura enchanteresse !", + "postSummonAuraBreak": "{{pokemonNameWithAffix}} inverse\ntoutes les auras !", "postSummonNeutralizingGas": "Le gaz inhibiteur {{pokemonNameWithAffix}}\nenvahit les lieux !", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}}\na deux talents !", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}}\na deux talents !", diff --git a/src/locales/fr/achv.json b/src/locales/fr/achv.json index 60655ae22cf..a557a423db7 100644 --- a/src/locales/fr/achv.json +++ b/src/locales/fr/achv.json @@ -92,7 +92,7 @@ "name": "Master Maitre de la Ligue", "name_female": "Master Maitresse de la Ligue" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Travail d’équipe", "description": "Utiliser Relais avec au moins une statistique montée à fond." }, @@ -227,7 +227,7 @@ "name": "Angry Birds" }, "MONO_POISON": { - "name": "Touche moi je t’empoisonne !" + "name": "Touche moi je t’empoisonne !" }, "MONO_GROUND": { "name": "Prévisions : Séisme" @@ -242,7 +242,7 @@ "name": "SOS Fantômes" }, "MONO_STEEL": { - "name": "De type Acier !" + "name": "De type Acier !" }, "MONO_FIRE": { "name": "Allumer le feu" diff --git a/src/locales/fr/arena-flyout.json b/src/locales/fr/arena-flyout.json index ce78643862e..e90de13b20a 100644 --- a/src/locales/fr/arena-flyout.json +++ b/src/locales/fr/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "Tatamigaeshi", "craftyShield": "Vigilance", "tailwind": "Vent Arrière", - "happyHour": "Étrennes" -} \ No newline at end of file + "happyHour": "Étrennes", + "safeguard": "Rune Protect" +} diff --git a/src/locales/fr/arena-tag.json b/src/locales/fr/arena-tag.json index 16355816ae4..9cb2f342068 100644 --- a/src/locales/fr/arena-tag.json +++ b/src/locales/fr/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "Le vent arrière soufflant\nsur votre équipe s’arrête !", "tailwindOnRemoveEnemy": "Le vent arrière soufflant\nsur l’équipe ennemie s’arrête !", "happyHourOnAdd": "L’ambiance est euphorique !", - "happyHourOnRemove": "L’ambiance se calme !" -} \ No newline at end of file + "happyHourOnRemove": "L’ambiance se calme !", + "safeguardOnAdd": "Un voile mystérieux recouvre\ntout le terrain !", + "safeguardOnAddPlayer": "Un voile mystérieux recouvre\nvotre équipe !", + "safeguardOnAddEnemy": "Un voile mystérieux recouvre\nl’équipe ennemie !", + "safeguardOnRemove": "Le terrain n’est plus protégé\npar le voile mystérieux !", + "safeguardOnRemovePlayer": "Votre équipe n’est plus protégée\npar le voile mystérieux !", + "safeguardOnRemoveEnemy": "L’équipe ennemie n’est plus protégée\npar le voile mystérieux !" +} diff --git a/src/locales/fr/battle.json b/src/locales/fr/battle.json index 1c108c89ded..7b78c963187 100644 --- a/src/locales/fr/battle.json +++ b/src/locales/fr/battle.json @@ -1,70 +1,71 @@ { "bossAppeared": "Un {{bossName}} apparait.", - "trainerAppeared": "Un combat est lancé\npar {{trainerName}} !", - "trainerAppearedDouble": "Un combat est lancé\npar {{trainerName}} !", + "trainerAppeared": "Un combat est lancé\npar {{trainerName}} !", + "trainerAppearedDouble": "Un combat est lancé\npar {{trainerName}} !", "trainerSendOut": "{{pokemonName}} est envoyé par\n{{trainerName}} !", - "singleWildAppeared": "Un {{pokemonName}} sauvage apparait !", - "multiWildAppeared": "Un {{pokemonName1}} et un {{pokemonName2}}\nsauvages apparaissent !", - "playerComeBack": "{{pokemonName}} !\nReviens !", - "trainerComeBack": "{{trainerName}} retire {{pokemonName}} !", - "playerGo": "{{pokemonName}} ! Go !", - "trainerGo": "{{pokemonName}} est envoyé par\n{{trainerName}} !", - "switchQuestion": "Voulez-vous changer\nvotre {{pokemonName}} ?", - "trainerDefeated": "Vous avez battu\n{{trainerName}} !", - "moneyWon": "Vous remportez\n{{moneyAmount}} ₽ !", + "singleWildAppeared": "Un {{pokemonName}} sauvage apparait !", + "multiWildAppeared": "Un {{pokemonName1}} et un {{pokemonName2}}\nsauvages apparaissent !", + "playerComeBack": "{{pokemonName}} !\nReviens !", + "trainerComeBack": "{{trainerName}} retire\n{{pokemonName}} !", + "playerGo": "{{pokemonName}} ! Go !", + "trainerGo": "{{pokemonName}} est envoyé par\n{{trainerName}} !", + "switchQuestion": "Voulez-vous changer\n{{pokemonName}} ?", + "trainerDefeated": "Vous avez battu\n{{trainerName}} !", + "moneyWon": "Vous remportez\n{{moneyAmount}} ₽ !", "moneyPickedUp": "Vous obtenez {{moneyAmount}} ₽ !", - "pokemonCaught": "Vous avez attrapé {{pokemonName}} !", + "pokemonCaught": "Vous avez attrapé\n{{pokemonName}} !", "addedAsAStarter": "{{pokemonName}} est ajouté\ncomme starter !", "partyFull": "Votre équipe est pleine.\nRelâcher un Pokémon pour {{pokemonName}} ?", - "pokemon": "Pokémon", - "sendOutPokemon": "{{pokemonName}} ! Go !", + "pokemon": "de Pokémon", + "sendOutPokemon": "{{pokemonName}} ! Go !", "hitResultCriticalHit": "Coup critique !", "hitResultSuperEffective": "C’est super efficace !", "hitResultNotVeryEffective": "Ce n’est pas très efficace…", "hitResultNoEffect": "Ça n’affecte pas {{pokemonName}}…", "hitResultImmune": "{{pokemonName}} n’est pas affecté !", "hitResultOneHitKO": "K.O. en un coup !", - "attackFailed": "Mais cela échoue !", + "attackFailed": "Mais cela échoue !", "attackMissed": "{{pokemonNameWithAffix}}\névite l’attaque !", - "attackHitsCount": "Touché {{count}} fois !", + "attackHitsCount": "Touché {{count}} fois !", "rewardGain": "Vous recevez\n{{modifierName}} !", - "expGain": "{{pokemonName}} gagne\n{{exp}} Points d’Exp !", - "levelUp": "{{pokemonName}} monte au\nN. {{level}} !", - "learnMove": "{{pokemonName}} apprend\n{{moveName}} !", + "expGain": "{{pokemonName}} gagne\n{{exp}} Points d’Exp !", + "levelUp": "{{pokemonName}} monte au\nN. {{level}} !", + "learnMove": "{{pokemonName}} apprend\n{{moveName}} !", "learnMovePrompt": "{{pokemonName}} veut apprendre\n{{moveName}}.", "learnMoveLimitReached": "Cependant, {{pokemonName}} connait\ndéjà quatre capacités.", - "learnMoveReplaceQuestion": "Voulez-vous oublier une capacité\net la remplacer par {{moveName}} ?", - "learnMoveStopTeaching": "Arrêter d’apprendre\n{{moveName}} ?", + "learnMoveReplaceQuestion": "Voulez-vous oublier une capacité\net la remplacer par {{moveName}} ?", + "learnMoveStopTeaching": "Arrêter d’apprendre\n{{moveName}} ?", "learnMoveNotLearned": "{{pokemonName}} n’a pas appris\n{{moveName}}.", - "learnMoveForgetQuestion": "Quelle capacité doit être oubliée ?", + "learnMoveForgetQuestion": "Quelle capacité doit être oubliée ?", "learnMoveForgetSuccess": "{{pokemonName}} oublie comment\nutiliser {{moveName}}.", "countdownPoof": "@d{32}1, @d{15}2, @d{15}et@d{15}… @d{15}… @d{15}… @d{15}@s{se/pb_bounce_1}Tadaaa !", "learnMoveAnd": "Et…", - "levelCapUp": "La limite de niveau\na été augmentée à {{levelCap}} !", + "levelCapUp": "La limite de niveau\na été augmentée à {{levelCap}} !", "moveNotImplemented": "{{moveName}} n’est pas encore implémenté et ne peut pas être sélectionné.", "moveNoPP": "Il n’y a plus de PP pour\ncette capacité !", - "moveDisabled": "{{moveName}} est sous entrave !", + "moveDisabled": "{{moveName}} est sous entrave !", + "disableInterruptedMove": "Il y a une entrave sur la capacité {{moveName}}\nde{{pokemonNameWithAffix}} !", "noPokeballForce": "Une force mystérieuse\nempêche l’utilisation des Poké Balls.", - "noPokeballTrainer": "Le Dresseur détourne la Ball\nVoler, c’est mal !", - "noPokeballMulti": "Impossible ! On ne peut pas viser\nquand il y a deux Pokémon !", - "noPokeballStrong": "Le Pokémon est trop fort pour être capturé !\nVous devez d’abord l’affaiblir !", + "noPokeballTrainer": "Le Dresseur détourne la Ball\nVoler, c’est mal !", + "noPokeballMulti": "Impossible ! On ne peut pas viser\nquand il y a deux Pokémon !", + "noPokeballStrong": "Le Pokémon est trop fort pour être capturé !\nVous devez d’abord l’affaiblir !", "noEscapeForce": "Une force mystérieuse\nempêche la fuite.", - "noEscapeTrainer": "On ne s’enfuit pas d’un\ncombat de Dresseurs !", - "noEscapePokemon": "{{moveName}} de {{pokemonName}}\nempêche {{escapeVerb}} !", - "runAwaySuccess": "Vous prenez la fuite !", - "runAwayCannotEscape": "Fuite impossible !", + "noEscapeTrainer": "On ne s’enfuit pas d’un\ncombat de Dresseurs !", + "noEscapePokemon": "{{moveName}} de {{pokemonName}}\nempêche {{escapeVerb}} !", + "runAwaySuccess": "Vous prenez la fuite !", + "runAwayCannotEscape": "Fuite impossible !", "escapeVerbSwitch": "le changement", "escapeVerbFlee": "la fuite", - "notDisabled": "La capacité {{moveName}}\nde {{pokemonName}} n’est plus sous entrave !", + "notDisabled": "La capacité {{moveName}}\nde {{pokemonName}} n’est plus sous entrave !", "turnEndHpRestore": "{{pokemonName}} récupère des PV !", "hpIsFull": "Les PV de {{pokemonName}}\nsont au maximum !", - "skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre d’objet ?", + "skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre d’objet ?", "itemStackFull": "Quantité maximale de {{fullItemName}} atteinte.\nVous recevez {{itemName}} à la place.", - "eggHatching": "Hein ?", - "ivScannerUseQuestion": "Utiliser le Scanner d’IV\nsur {{pokemonName}} ?", + "eggHatching": "Hein ?", + "ivScannerUseQuestion": "Utiliser le Scanner d’IV\nsur {{pokemonName}} ?", "wildPokemonWithAffix": "{{pokemonName}} sauvage", "foePokemonWithAffix": "{{pokemonName}} ennemi", - "useMove": "{{pokemonNameWithAffix}} utilise\n{{moveName}} !", + "useMove": "{{pokemonNameWithAffix}} utilise\n{{moveName}} !", "stealEatBerry": "{{pokemonName}} vole et mange\nla {{berryName}} de {{targetName}} !", "ppHealBerry": "La {{berryName}} de {{pokemonNameWithAffix}}\nrestaure les PP de sa capacité {{moveName}} !", "hpHealBerry": "La {{berryName}} de {{pokemonNameWithAffix}}\nrestaure son énergie !", @@ -73,26 +74,27 @@ "fainted": "{{pokemonNameWithAffix}}\nest K.O. !", "statsAnd": "et", "stats": "Les stats", - "statRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente !", - "statRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent !", - "statSharplyRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente beaucoup !", - "statSharplyRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent beaucoup !", - "statRoseDrastically_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente énormément !", - "statRoseDrastically_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent énormément !", - "statWontGoAnyHigher_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus augmenter !", - "statWontGoAnyHigher_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus augmenter !", - "statFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse !", - "statFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent !", - "statHarshlyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse beaucoup !", - "statHarshlyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent beaucoup !", - "statSeverelyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse énormément !", - "statSeverelyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent énormément !", - "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus baisser !", - "statWontGoAnyLower_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus baisser !", - "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", - "ppReduced": "Les PP de la capacité {{moveName}}\nde {{targetName}} baissent de {{reduction}} !", - "retryBattle": "Voulez-vous réessayer depuis le début du combat ?", + "statRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente !", + "statRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent !", + "statSharplyRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente beaucoup !", + "statSharplyRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent beaucoup !", + "statRoseDrastically_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente énormément !", + "statRoseDrastically_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent énormément !", + "statWontGoAnyHigher_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus augmenter !", + "statWontGoAnyHigher_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus augmenter !", + "statFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse !", + "statFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent !", + "statHarshlyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse beaucoup !", + "statHarshlyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent beaucoup !", + "statSeverelyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse énormément !", + "statSeverelyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent énormément !", + "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus baisser !", + "statWontGoAnyLower_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus baisser !", + "transformedIntoType": "{{pokemonName}} prend\nle type {{type}} !", + "ppReduced": "Les PP de la capacité {{moveName}}\nde {{targetName}} baissent de {{reduction}} !", + "retryBattle": "Voulez-vous réessayer depuis le début du combat ?", "unlockedSomething": "{{unlockedThing}}\na été débloqué.", - "congratulations": "Félicitations !", - "beatModeFirstTime": "{{speciesName}} a battu le mode {{gameMode}} pour la première fois !\nVous avez reçu {{newModifier}} !" -} \ No newline at end of file + "congratulations": "Félicitations !", + "beatModeFirstTime": "{{speciesName}} a battu le mode {{gameMode}} pour la première fois !\nVous avez reçu {{newModifier}} !", + "eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?" +} diff --git a/src/locales/fr/battler-tags.json b/src/locales/fr/battler-tags.json index 46b086938b3..c4a88bb91aa 100644 --- a/src/locales/fr/battler-tags.json +++ b/src/locales/fr/battler-tags.json @@ -29,8 +29,8 @@ "nightmareOnAdd": "{{pokemonNameWithAffix}} commence à cauchemarder !", "nightmareOnOverlap": "{{pokemonNameWithAffix}} est\ndéjà prisonnier d’un cauchemar !", "nightmareLapse": "{{pokemonNameWithAffix}}est\nprisonnier d’un cauchemar !", - "encoreOnAdd": "{{pokemonNameWithAffix}} !\nEncore une fois !", - "encoreOnRemove": "{{pokemonNameWithAffix}} n’est\nplus obligé d’utiliser la même capacité !", + "encoreOnAdd": "{{pokemonNameWithAffix}} !\nEncore une fois !", + "encoreOnRemove": "{{pokemonNameWithAffix}} n’est\nplus obligé d’utiliser la même capacité !", "helpingHandOnAdd": "{{pokemonNameWithAffix}} est prêt\nà aider {{pokemonName}} !", "ingrainLapse": "{{pokemonNameWithAffix}} absorbe\ndes nutriments avec ses racines !", "ingrainOnTrap": "{{pokemonNameWithAffix}}\nplante ses racines !", @@ -50,22 +50,25 @@ "protectedOnAdd": "{{pokemonNameWithAffix}}\nest prêt à se protéger !", "protectedLapse": "{{pokemonNameWithAffix}}\nse protège !", "enduringOnAdd": "{{pokemonNameWithAffix}} se prépare\nà encaisser les coups !", - "enduringLapse": "{{pokemonNameWithAffix}}\nencaisse les coups !", - "sturdyLapse": "{{pokemonNameWithAffix}}\nencaisse les coups !", + "enduringLapse": "{{pokemonNameWithAffix}}\nencaisse les coups !", + "sturdyLapse": "{{pokemonNameWithAffix}}\nencaisse les coups !", "perishSongLapse": "Le compte à rebours de Requiem\nde {{pokemonNameWithAffix}} descend à {{turnCount}} !", - "centerOfAttentionOnAdd": "{{pokemonNameWithAffix}} devient\nle centre de l’attention !", - "truantLapse": "{{pokemonNameWithAffix}} paresse !", - "slowStartOnAdd": "{{pokemonNameWithAffix}}\nn’arrive pas à se motiver !", - "slowStartOnRemove": "{{pokemonNameWithAffix}}\narrive enfin à s’y mettre sérieusement !", - "highestStatBoostOnAdd": "{{statName}} de {{pokemonNameWithAffix}}\nest renforcée !", - "highestStatBoostOnRemove": "L’effet du talent {{abilityName}}\nde {{pokemonNameWithAffix}} se dissipe !", - "magnetRisenOnAdd": "{{pokemonNameWithAffix}} lévite\nsur un champ magnétique !", - "magnetRisenOnRemove": "Le magnétisme de{{pokemonNameWithAffix}}\nse dissipe !", + "centerOfAttentionOnAdd": "{{pokemonNameWithAffix}} devient\nle centre de l’attention !", + "truantLapse": "{{pokemonNameWithAffix}} paresse !", + "slowStartOnAdd": "{{pokemonNameWithAffix}}\nn’arrive pas à se motiver !", + "slowStartOnRemove": "{{pokemonNameWithAffix}}\narrive enfin à s’y mettre sérieusement !", + "highestStatBoostOnAdd": "{{statName}} de {{pokemonNameWithAffix}}\nest renforcée !", + "highestStatBoostOnRemove": "L’effet du talent {{abilityName}}\nde {{pokemonNameWithAffix}} se dissipe !", + "magnetRisenOnAdd": "{{pokemonNameWithAffix}} lévite\nsur un champ magnétique !", + "magnetRisenOnRemove": "Le magnétisme de{{pokemonNameWithAffix}}\nse dissipe !", "critBoostOnAdd": "{{pokemonNameWithAffix}}\nest prêt à tout donner !", "critBoostOnRemove": "{{pokemonNameWithAffix}} se détend.", - "saltCuredOnAdd": "{{pokemonNameWithAffix}}\nest couvert de sel !", + "saltCuredOnAdd": "{{pokemonNameWithAffix}}\nest couvert de sel !", "saltCuredLapse": "{{pokemonNameWithAffix}} est blessé\npar la capacité {{moveName}} !", "cursedOnAdd": "{{pokemonNameWithAffix}} sacrifie des PV\net lance une malédiction sur {{pokemonName}} !", "cursedLapse": "{{pokemonNameWithAffix}} est touché par la malédiction !", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} utilise\nla capacité Stockage {{stockpiledCount}} fois !" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}} utilise\nla capacité Stockage {{stockpiledCount}} fois !", + "disabledOnAdd": "La capacité {{moveName}}\nde {{pokemonNameWithAffix}} est mise sous entrave !", + "disabledLapse": "La capacité {{moveName}}\nde {{pokemonNameWithAffix}} n’est plus sous entrave !", + "tarShotOnAdd": "{{pokemonNameWithAffix}} est maintenant\nvulnérable au feu !" +} diff --git a/src/locales/fr/berry.json b/src/locales/fr/berry.json index 833e1d1d8fd..17f28fb8dd0 100644 --- a/src/locales/fr/berry.json +++ b/src/locales/fr/berry.json @@ -1,46 +1,46 @@ { "SITRUS": { "name": "Baie Sitrus", - "effect": "Restaure 25% des PV s’ils sont inférieurs à 50%." + "effect": "Restaure 25% des PV du porteur s’ils sont inférieurs à 50%." }, "LUM": { "name": "Baie Prine", - "effect": "Soigne tout problème de statut permanant et la confusion." + "effect": "Soigne tout problème de statut et la confusion du porteur." }, "ENIGMA": { "name": "Baie Enigma", - "effect": "Restaure 25% des PV si touché par une capacité super efficace." + "effect": "Restaure 25% des PV du porteur s’il est touché par une capacité super efficace." }, "LIECHI": { "name": "Baie Lichii", - "effect": "Augmente l’Attaque si les PV sont inférieurs à 25%." + "effect": "Augmente l’Attaque du porteur si ses PV sont inférieurs à 25%." }, "GANLON": { "name": "Baie Lingan", - "effect": "Augmente la Défense si les PV sont inférieurs à 25%." + "effect": "Augmente la Défense du porteur si ses PV sont inférieurs à 25%." }, "PETAYA": { "name": "Baie Pitaye", - "effect": "Augmente l’Atq. Spé. si les PV sont inférieurs à 25%." + "effect": "Augmente l’Atq. Spé. du porteur si ses PV sont inférieurs à 25%." }, "APICOT": { "name": "Baie Abriko", - "effect": "Augmente la Déf. Spé. si les PV sont inférieurs à 25%." + "effect": "Augmente la Déf. Spé. du porteur si ses PV sont inférieurs à 25%." }, "SALAC": { "name": "Baie Sailak", - "effect": "Augmente la Vitesse si les PV sont inférieurs à 25%." + "effect": "Augmente la Vitesse du porteur si ses PV sont inférieurs à 25%." }, "LANSAT": { "name": "Baie Lansat", - "effect": "Augmente le taux de coups critiques si les PV sont inférieurs à 25%." + "effect": "Augmente le taux de coups critiques du porteur si ses PV sont inférieurs à 25%." }, "STARF": { "name": "Baie Frista", - "effect": "Augmente énormément une statistique au hasard si les PV sont inférieurs à 25%." + "effect": "Augmente énormément une statistique au hasard du porteur si ses PV sont inférieurs à 25%." }, "LEPPA": { "name": "Baie Mepo", - "effect": "Restaure 10 PP à une capacité dès que ses PP tombent à 0." + "effect": "Restaure 10 PP à une capacité du porteur dès que ses PP tombent à 0." } -} \ No newline at end of file +} diff --git a/src/locales/fr/command-ui-handler.json b/src/locales/fr/command-ui-handler.json index a991c618820..82733837375 100644 --- a/src/locales/fr/command-ui-handler.json +++ b/src/locales/fr/command-ui-handler.json @@ -3,5 +3,5 @@ "ball": "Ball", "pokemon": "Pokémon", "run": "Fuite", - "actionMessage": "Que doit faire\n{{pokemonName}} ?" -} \ No newline at end of file + "actionMessage": "Que doit faire\n{{pokemonName}} ?" +} diff --git a/src/locales/fr/dialogue-double-battle.json b/src/locales/fr/dialogue-double-battle.json index d35e9d87ced..fbc5d56ed26 100644 --- a/src/locales/fr/dialogue-double-battle.json +++ b/src/locales/fr/dialogue-double-battle.json @@ -1,83 +1,83 @@ { "blue_red_double": { "encounter": { - "1": "Blue : Hé Red, montrons-lui de quel bois on se chauffe !\n$Red : …\n$Blue : Voilà la puissance du Bourg Palette !" + "1": "Blue : Hé Red, montrons-lui de quel bois on se chauffe !\n$Red : …\n$Blue : Voilà la puissance du Bourg Palette !" }, "victory": { - "1": "Blue : C’était un magnifique combat !\n$Red : …" + "1": "Blue : C’était un magnifique combat !\n$Red : …" } }, "red_blue_double": { "encounter": { - "1": "Red : … !\n$Blue : Il est pas très loquace.\n$Blue : Mais ne te laisse pas avoir, ça reste un Maitre Pokémon !" + "1": "Red : … !\n$Blue : Il est pas très loquace.\n$Blue : Mais ne te laisse pas avoir, ça reste un Maitre Pokémon !" }, "victory": { - "1": "Red : … !\n$Blue : La prochaine fois, on va te battre !" + "1": "Red : … !\n$Blue : La prochaine fois, on va te battre !" } }, "tate_liza_double": { "encounter": { - "1": "Lévy : Héhéhé… Tu en fais une drôle de tête.\n$Tatia : Tu ne t’attendais pas à rencontrer deux Champions, n’est-ce pas ?\n$Lévy : Nous sommes des jumeaux !\n$Tatia : Nous n’avons pas besoin de parler entre nous !\n$Lévy : Tu crois pouvoir briser…\n$Tatia : … Notre duo parfait ?" + "1": "Lévy : Héhéhé… Tu en fais une drôle de tête.\n$Tatia : Tu ne t’attendais pas à rencontrer deux Champions, n’est-ce pas ?\n$Lévy : Nous sommes des jumeaux !\n$Tatia : Nous n’avons pas besoin de parler entre nous !\n$Lévy : Tu crois pouvoir briser…\n$Tatia : … Notre duo parfait ?" }, "victory": { - "1": "Lévy : Quoi ? Notre combinaison était parfaite !\n$Tatia : Nous avons encore besoin d’entrainement…" + "1": "Lévy : Quoi ? Notre combinaison était parfaite !\n$Tatia : Nous avons encore besoin d’entrainement…" } }, "liza_tate_double": { "encounter": { - "1": "Tatia : Hihih… Si tu voyais ta tête !\n$Lévy : Oui, nous sommes deux Champions en un !\n$Tatia : Voici mon frère, Lévy…\n$Lévy : … Et ma sœur, Tatia !\n$Tatia : Tu ne penses pas que notre combinaison est parfaite ?" + "1": "Tatia : Hihih… Si tu voyais ta tête !\n$Lévy : Oui, nous sommes deux Champions en un !\n$Tatia : Voici mon frère, Lévy…\n$Lévy : … Et ma sœur, Tatia !\n$Tatia : Tu ne penses pas que notre combinaison est parfaite ?" }, "victory": { - "1": "Tatia : Quoi ? Notre combinaison…\n$Lévy : … a échoué !" + "1": "Tatia : Quoi ? Notre combinaison…\n$Lévy : … a échoué !" } }, "wallace_steven_double": { "encounter": { - "1": "Pierre R. : Marc, montrons-lui la puissance des Maitres !\n$Marc : Tu vas gouter au pouvoir de Hoenn !\n$Pierre R. : C’est parti !" + "1": "Pierre R. : Marc, montrons-lui la puissance des Maitres !\n$Marc : Tu vas gouter au pouvoir de Hoenn !\n$Pierre R. : C’est parti !" }, "victory": { - "1": "Pierre R. : C’était un beau combat !\n$Marc : Ce sera notre tour la prochaine fois !" + "1": "Pierre R. : C’était un beau combat !\n$Marc : Ce sera notre tour la prochaine fois !" } }, "steven_wallace_double": { "encounter": { - "1": "Pierre R. : Excuse-moi, aurais-tu des Pokémon rares ?\n$Marc : Pierre… Nous sommes là pour nous battre, pas pour frimer avec nos Pokémon.\n$Pierre R. : Oh… Je vois… Commençons alors !" + "1": "Pierre R. : Excuse-moi, aurais-tu des Pokémon rares ?\n$Marc : Pierre… Nous sommes là pour nous battre, pas pour frimer avec nos Pokémon.\n$Pierre R. : Oh… Je vois… Commençons alors !" }, "victory": { - "1": "Pierre R. : Bien, maintenant que ce combat est clos, montrons-nous nos Pokémon !\n$Marc : Pierre…" + "1": "Pierre R. : Bien, maintenant que ce combat est clos, montrons-nous nos Pokémon !\n$Marc : Pierre…" } }, "alder_iris_double": { "encounter": { - "1": "Goyah : Nous sommes l’élite des Dresseurs d’Unys !\n$Iris : Rien de mieux que des combats contre des prodiges !" + "1": "Goyah : Nous sommes l’élite des Dresseurs d’Unys !\n$Iris : Rien de mieux que des combats contre des prodiges !" }, "victory": { - "1": "Goyah : INCROYABLE ! T’es trop doué !\n$Iris : On gagnera la prochaine fois !" + "1": "Goyah : INCROYABLE ! T’es trop doué !\n$Iris : On gagnera la prochaine fois !" } }, "iris_alder_double": { "encounter": { - "1": "Iris : Bienvenue, Dresseur ! Je suis LA Maitresse d’Unys !\n$Goyah : Iris, concentre-toi s’il te plait…" + "1": "Iris : Bienvenue, Dresseur ! Je suis LA Maitresse d’Unys !\n$Goyah : Iris, concentre-toi s’il te plait…" }, "victory": { - "1": "Iris : On a tout donné et pourtant…\n$Goyah : Cette défaite ne pourra que nous être bénéfique !" + "1": "Iris : On a tout donné et pourtant…\n$Goyah : Cette défaite ne pourra que nous être bénéfique !" } }, "piers_marnie_double": { "encounter": { - "1": "Rosemary : Frérot, montrons-lui la puissance de Smashings !\n$Peterson : Nous sommes les ténèbres !" + "1": "Rosemary : Frérot, montrons-lui la puissance de Smashings !\n$Peterson : Nous sommes les ténèbres !" }, "victory": { - "1": "Rosemary : T’as amené la lumière dans les ténèbres !\n$Peterson : P’têtre un peu trop…" + "1": "Rosemary : T’as amené la lumière dans les ténèbres !\n$Peterson : P’têtre un peu trop…" } }, "marnie_piers_double": { "encounter": { - "1": "Peterson : Chauds pour un concert ?\n$Rosemary : Frérot… Il est pas là pour chanter, mais se battre…", - "1_female": "Peterson : Chauds pour un concert ?\n$Rosemary : Frérot… Elle est pas là pour chanter, mais se battre…" + "1": "Peterson : Chauds pour un concert ?\n$Rosemary : Frérot… Il est pas là pour chanter, mais se battre…", + "1_female": "Peterson : Chauds pour un concert ?\n$Rosemary : Frérot… Elle est pas là pour chanter, mais se battre…" }, "victory": { - "1": "Peterson : Ça c’est du rock !\n$Rosemary : Frérot…" + "1": "Peterson : Ça c’est du rock !\n$Rosemary : Frérot…" } } } diff --git a/src/locales/fr/dialogue-final-boss.json b/src/locales/fr/dialogue-final-boss.json index c5a5e3b7d89..b7226ebb480 100644 --- a/src/locales/fr/dialogue-final-boss.json +++ b/src/locales/fr/dialogue-final-boss.json @@ -1,6 +1,6 @@ { - "encounter": "Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ?\n$Tu as été appelé ici parce que t’y es déjà venu.\nUn nombre inimaginable de fois.\n$Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle.\n$Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi.\n$Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n\n$Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre.\n$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?\n$Commençons.", - "encounter_female": "Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ?\n$Tu as été appelée ici parce que t’y es déjà venue.\nUn nombre inimaginable de fois.\n$Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle.\n$Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi.\n$Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n\n$Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre.\n$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?\n$Commençons.", + "encounter": "Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ?\n$Tu as été appelé ici parce que t’y es déjà venu.\nUn nombre inimaginable de fois.\n$Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle.\n$Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi.\n$Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n\n$Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre.\n$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?\n$Commençons.", + "encounter_female": "Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ?\n$Tu as été appelée ici parce que t’y es déjà venue.\nUn nombre inimaginable de fois.\n$Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle.\n$Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi.\n$Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n\n$Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre.\n$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?\n$Commençons.", "firstStageWin": "Je vois. Cette présence était bien réelle.\nJe n’ai donc plus besoin de retenir mes coups.\n$Ne me déçoit pas.", "secondStageWin": "… Magnifique." -} \ No newline at end of file +} diff --git a/src/locales/fr/dialogue-misc.json b/src/locales/fr/dialogue-misc.json index d932d283d37..864bd53eab5 100644 --- a/src/locales/fr/dialogue-misc.json +++ b/src/locales/fr/dialogue-misc.json @@ -1,4 +1,6 @@ { - "ending": "@c{smile}Oh ? T’as gagné ?@d{96} @c{smile_eclosed}J’aurais dû le savoir.\nMais de voilà de retour.\n$@c{smile}C’est terminé.@d{64} T’as brisé ce cycle infernal.\n$@c{serious_smile_fists}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$@c{neutral}Je suis le seul à me souvenir de ce que t’as fait.@d{96}\nJe pense que ça ira, non ?\n$@c{serious_smile_fists}Ta légende vivra à jamais dans nos cœurs.\n$@c{smile_eclosed}Bref, j’en ai un peu marre de ce endroit, pas toi ? Rentrons à la maison.\n$@c{serious_smile_fists}On se fera un p’tit combat une fois rentrés ?\nSi t’es d’accord.", - "ending_female": "@c{shock}T’es revenu ?@d{32} Ça veut dire…@d{96} que t’as gagné ?!\n@c{smile_ehalf}J’aurais dû le savoir.\n$@c{smile_eclosed}Bien sûr… J’ai toujours eu ce sentiment.\n@c{smile}C’est fini maitenant hein ? T’as brisé ce cycle.\n$@c{smile_ehalf}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$Je serai la seule à me souvenir de ce que t’as fait.\n@c{angry_mopen}Je tâcherai de ne pas oublier !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile}Jamais j’oublierai.@d{32}\nTa légende vivra à jamais dans nos cœurs.\n$@c{smile_wave}Bon,@d{64} il se fait tard…@d{96} je crois ?\nDifficile à dire ici.\n$Rentrons, @c{smile_wave_wink}et demain on se fera un p’tit combat, comme au bon vieux temps ?" -} \ No newline at end of file + "ending": "@c{shock}T’es revenu ?@d{32} Ça veut dire…@d{96} que t’as gagné ?!\n@c{smile_ehalf}J’aurais dû m’en douter.\n$@c{smile_eclosed}Bien sûr… J’ai toujours eu ce sentiment.\n@c{smile}C’est fini maintenant hein ? T’as brisé ce cycle.\n$@c{smile_ehalf}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$Je serai la seule à me souvenir de ce que t’as fait.\n@c{angry_mopen}Je tâcherai de ne pas oublier !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile}Jamais j’oublierai.@d{32}\nTa légende vivra à jamais dans nos cœurs.\n$@c{smile_wave}Bon,@d{64} il se fait tard…@d{96} je crois ?\nDifficile à dire ici.\n$Rentrons, @c{smile_wave_wink}et demain on se fera un p’tit combat, comme au bon vieux temps ?", + "ending_female": "@c{smile}Oh ? T’as gagné ?@d{96} @c{smile_eclosed}J’aurais dû m’en douter.\nMais te voilà enfin de retour.\n$@c{smile}C’est terminé.@d{64} T’as brisé ce cycle infernal.\n$@c{serious_smile_fists}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$@c{neutral}Je suis le seul à me souvenir de ce que t’as fait.@d{96}\nJe pense que ça ira, non ?\n$@c{serious_smile_fists}Ta légende vivra à jamais dans nos cœurs.\n$@c{smile_eclosed}Bref, j’en ai un peu marre de ce endroit, pas toi ? Rentrons à la maison.\n$@c{serious_smile_fists}On se fera un p’tit combat une fois rentrés ?\nSi t’es d’accord.", + "ending_endless": "Félicitations ! Vous avez atteint la fin actuelle.\nPlus de contenu à venir bientôt !", + "ending_name": "Les devs" +} diff --git a/src/locales/fr/dialogue.json b/src/locales/fr/dialogue.json index c9bb3c417c7..adc58de0563 100644 --- a/src/locales/fr/dialogue.json +++ b/src/locales/fr/dialogue.json @@ -1,50 +1,50 @@ { "youngster": { "encounter": { - "1": "Hé ! Combat ?", - "2": "Toi aussi tu débutes ?", - "3": "Hé, j’me souviens pas de ta tête. Combat !", - "4": "J’ai perdu, alors j’essaye de capturer d’autres Pokémon.\nHé, t’as l’air faible toi ! Allez, combat !", - "5": "On s’connait ? J’ai comme un doute. Dans tous les cas, sympa de te rencontrer !", - "6": "Allez, c’est parti !", - "7": "Attention, me voilà !\nTu vas voir comment j’suis fort !", - "8": "Coucou… Tu veux voir mes bô Pokémon ?", - "9": "Trève de mondanités. Ramène-toi quand tu le sens !", + "1": "Hé ! Combat ?", + "2": "Toi aussi tu débutes ?", + "3": "Hé, j’me souviens pas de ta tête. Combat !", + "4": "J’ai perdu, alors j’essaye de capturer d’autres Pokémon.\nHé, t’as l’air faible toi ! Allez, combat !", + "5": "On s’connait ? J’ai comme un doute. Dans tous les cas, sympa de te rencontrer !", + "6": "Allez, c’est parti !", + "7": "Attention, me voilà !\nTu vas voir comment j’suis fort !", + "8": "Coucou… Tu veux voir mes bô Pokémon ?", + "9": "Trêve de mondanités. Ramène-toi quand tu le sens !", "10": "Baisse pas ta garde si tu veux pas pleurer d’avoir perdu face à un gamin.", - "11": "J’ai tout donné pour élever mes Pokémon. Attention à toi si tu leur fait du mal !", - "12": "Incroyable que t’y sois parvenu ! Mais la suite va pas être une partie de plaisir.", - "12_female": "Incroyable que t’y sois parvenue ! Mais la suite va pas être une partie de plaisir.", - "13": "Les combats sont éternels ! Bienvenue dans un monde sans fin !" + "11": "J’ai tout donné pour élever mes Pokémon. Attention à toi si tu leur fait du mal !", + "12": "Incroyable que t’y sois parvenu ! Mais la suite va pas être une partie de plaisir.", + "12_female": "Incroyable que t’y sois parvenue ! Mais la suite va pas être une partie de plaisir.", + "13": "Les combats sont éternels ! Bienvenue dans un monde sans fin !" }, "victory": { - "1": "Hé, mais t’es trop fort !", - "1_female": "Hé, mais t’es trop forte !", - "2": "En vrai j’avais aucune chance hein ?", - "3": "J’te retrouverai un jour, et là j’te battrai !", + "1": "Hé, mais t’es trop fort !", + "1_female": "Hé, mais t’es trop forte !", + "2": "En vrai j’avais aucune chance hein ?", + "3": "J’te retrouverai un jour, et là j’te battrai !", "4": "Arg… J’ai plus aucun Pokémon.", - "5": "Non… IMPOSSIBLE ! Pourquoi j’ai encore perdu…", - "6": "Non ! J’ai perdu !", - "7": "Waah ! T’es trop incroyable ! J’suis bouche bée !", + "5": "Non… IMPOSSIBLE ! Pourquoi j’ai encore perdu…", + "6": "Non ! J’ai perdu !", + "7": "Waah ! T’es trop incroyable ! J’suis bouche bée !", "8": "Pourquoi… Comment… Pourtant on est les plus forts, mes Pokémon et moi…", - "9": "J’perdrai pas la prochaine fois ! Remettons ça un jour !", - "10": "Weeeesh ! Tu vois que j’suis qu’un gamin ? C’est pas juste de me bully comme ça !", - "11": "Tes Pokémon sont trop incroyables !\n… P’tit échange ?", - "12": "Je me suis fait un peu aider plus tôt, mais de quel taf je parlais ?", - "13": "Ahaha ! Et voilà, ça y est !\nT’es déjà comme chez toi dans ce monde !" + "9": "J’perdrai pas la prochaine fois ! Remettons ça un jour !", + "10": "Weeeesh ! Tu vois que j’suis qu’un gamin ? C’est pas juste de me bully comme ça !", + "11": "Tes Pokémon sont trop incroyables !\n… P’tit échange ?", + "12": "Je me suis fait un peu aider plus tôt, mais de quel taf je parlais ?", + "13": "Ahaha ! Et voilà, ça y est !\nT’es déjà comme chez toi dans ce monde !" } }, "lass": { "encounter": { - "1": "Affrontons-nous, d’accord ?", - "2": "T’as l’air d’un nouveau Dresseur. Battons nous !", - "2_female": "T’as l’air d’une nouvelle Dresseuse. Battons nous !", - "3": "Je te connais pas. Ça te dis de te battre ?", - "4": "Prenons du bon temps avec ce combat Pokémon !", - "5": "Je vais t’apprendre à te battre avec tes Pokémon !", + "1": "Affrontons-nous, d’accord ?", + "2": "T’as l’air d’un nouveau Dresseur. Battons nous !", + "2_female": "T’as l’air d’une nouvelle Dresseuse. Battons nous !", + "3": "Je te connais pas. Ça te dis de te battre ?", + "4": "Prenons du bon temps avec ce combat Pokémon !", + "5": "Je vais t’apprendre à te battre avec tes Pokémon !", "6": "Un combat doit toujours être pris au sérieux.\nT’es prêt à te battre ?", - "6_female": "Un combat doit toujours être pris au sérieux.\nT’es prête à te battre ?", + "6_female": "Un combat doit toujours être pris au sérieux.\nT’es prête à te battre ?", "7": "Tu seras pas jeune éternellement. T’as qu’une chance pendant un combat. Bientôt, tu seras plus qu’un souvenir.", - "8": "Tu ferais mieux d’y aller doucement avec moi. Mais je vais me battre sérieusement !", + "8": "Tu ferais mieux d’y aller doucement avec moi. Mais je vais me battre sérieusement !", "9": "Je m’ennuie à l’école. Y’a rien à y faire. *Baille*\nJe me bats juste pour passer le temps." }, "victory": { @@ -52,12 +52,12 @@ "2": "Je ne pensais pas que je perdrais comme ça…", "2_female": "Je pensais pas que je perdrais comme ça…", "3": "J’espère que j’aurai ma revanche un jour.", - "4": "C’était super amusant ! Mais ce combat m’a épuisée…", - "5": "Tu m’as appris une belle leçon ! T’es vraiment incroyable !", - "6": "Vraiment ? J’ai perdu… ? C’est des choses qui arrivent, ça me déprime mais tu es vraiment très cool.", - "6_female": "Vraiment ? J’ai perdu… ? C’est des choses qui arrivent, ça me déprime mais t’es vraiment très cool.", + "4": "C’était super amusant ! Mais ce combat m’a épuisée…", + "5": "Tu m’as appris une belle leçon ! T’es vraiment incroyable !", + "6": "Vraiment ? J’ai perdu… ? Bon, ça arrive, mais c’était cool quand même.", + "6_female": "Vraiment ? J’ai perdu… ? Bon, ça arrive, mais c’était cool quand même.", "7": "J’ai pas besoin de ce genre de souvenirs.\n*Suppression de mémoire en cours…*", - "8": "Hé ! Je t’avais dit d’y aller doucement avec moi ! Mais t’es vraiment si cool quand tu te bats sérieusement…", + "8": "Hé ! Je t’avais dit d’y aller doucement avec moi ! Mais t’es vraiment si cool quand tu te bats sérieusement…", "9": "J’en ai marre des combats Pokémon…\nJe vais chercher d’autres trucs à faire…" } }, @@ -68,7 +68,7 @@ "3": "Hum, t’es pas trop laxiste avec tes Pokémon ?\nTrop les chouchouter n’est pas bon." }, "victory": { - "1": "Il est primordial de nourir et développer toutes les caractéristiques de chaque Pokémon.", + "1": "Il est primordial de nourrir et développer toutes les caractéristiques de chaque Pokémon.", "2": "Contrairement à moi, ces Pokémon ont un bon fond.", "3": "Trop d’éloges peut ruiner les Pokémon et les gens." }, @@ -123,7 +123,7 @@ "encounter": { "1": "C’est l’heure de plonger dans le vif !", "2": "C’est le moment de surfer sur les vagues de la victoire !", - "3": "Je vais t’éclabousser de mon talent !" + "3": "Je vais t’éclabousser de mon talent !" }, "victory": { "1": "Tu m’as complètement séché", @@ -169,10 +169,10 @@ }, "parasol_lady": { "encounter": { - "1": "Honorons ce terrain de combat avec élégance et équilibre !" + "1": "Honorons ce terrain de combat avec élégance et équilibre !" }, "victory": { - "1": "Mon élégance demeure inébranlable !" + "1": "Mon élégance demeure inébranlable !" } }, "rocket_grunt": { @@ -229,7 +229,7 @@ "encounter": { "1": "Ne te mets pas en travers de la Team Galaxie !", "2": "Sois témoin de la puissance de notre technologie et du futur qui se profile !", - "3": "Au nom de la Team Galaxie, j’éliminerai quiconque se mettera sur notre route !", + "3": "Au nom de la Team Galaxie, j’éliminerai quiconque se mettra sur notre route !", "4": "Prépare ta défaite !", "5": "J’espère que t’es prêt à te prendre une raclée de l’espace !", "5_female": "J’espère que t’es prête à te prendre une raclée de l’espace !" @@ -244,7 +244,7 @@ }, "plasma_grunt": { "encounter": { - "1": "Pas de quatiers à ceux qui ne suivent pas notre idéal !", + "1": "Pas de quartiers à quiconque ne suit pas notre idéal !", "2": "Si je gagne, tu relâches tous tes Pokémon !", "3": "Si tu te mets en travers de la Team Plasma, je m’occuperai de toi personnellement !", "4": "La Team Plasma va libérer les Pokémon de tous les humains égoïstes dans ton genre !", @@ -275,6 +275,96 @@ "5": "J’appelle pas ça perdre, j’appelle ça échouer avec panache !" } }, + "aether_grunt": { + "encounter": { + "1": "Je vais te mettre ta raclée !", + "2": "J’en ai rien à faire que tu sois une gosse. Tu vas tutoyer les étoiles si tu nous menaces !", + "2_female": "J’en ai rien à faire que tu sois une gosse. Tu vas tutoyer les étoiles si tu nous menaces !", + "3": "J’ai pour ordre de ne laisser passer aucun Dresseur, peu importe qui c’est !", + "4": "Je vais te montrer le pouvoir du Paradis Æther !", + "5": "Maintenant que t’es au courant de ce qu’il se passe au cœur du Paradis Æther, fais-moi une faveur et disparait !" + }, + "victory": { + "1": "C’est plutôt toi qui devrait m’apprendre à en mettre…", + "2": "Pardon ? J’ai pas compris…", + "3": "Peu importe les ordres, jamais j’aurais pu te retenir en fait…", + "4": "Mhh… Il semblerait que j’ai perdu.", + "5": "C’est plutôt moi qui va disparaitre je crois." + } + }, + "faba": { + "encounter": { + "1": "Moi, Directeur Saubohne, je vais te montrer de quel bois je me chauffe !", + "2": "Donc là, l’homme supposé être la dernière ligne défense du Paradis Æther doit affronter un mioche ?", + "2_female": "Donc là, l’homme supposé être la dernière ligne défense du Paradis Æther doit affronter un mioche ?", + "3": "S’il n’y a qu’un seul nom à retenir au sein de la Fondation Æther, c’est le mien : Saubohne !" + }, + "victory": { + "1": "Gloups !", + "2": "Malheur ! J’ai perdu face à un simple enfant ?!", + "2_female": "Malheur ! J’ai perdu face à une simple enfant ?!", + "3": "J’ai HORREUR des enfants !" + } + }, + "skull_grunt": { + "encounter": { + "1": "Oush oush ! On est pas méchants, sauf si tu viens nous allumer la mèche-han !", + "2": "Ce manque de respect, j’hallucine ! T’es allé trop loin, le mioche !", + "2_female": "Ce manque de respect, j’hallucine ! T’es allée trop loin, la mioche !", + "3": "On est juste des gars et des meufs normaux, on voit un Pokémon on le prend !", + "4": "Pourquoi tu te la joue comme ça ? C'est avec tes dents que t’vas jouer frérot.", + "4_female": "Pourquoi tu te la joue comme ça ? C'est avec tes dents que t’vas jouer ma reus.", + "5": "Cousin, écoute-nous bien ! ♪\nSe taper dessus, ça sert à rien ! ♪\n$Tu t’incrustes chez nous, ça s’fait pas ! ♪\nMais on est sympa, on a un plan pour toi ! ♪", + "5_female": "Cousine, écoute-nous bien ! ♪\nSe taper dessus, ça sert à rien ! ♪\n$Tu t’incrustes chez nous, ça s’fait pas ! ♪\nMais on est sympa, on a un plan pour toi ! ♪" + }, + "victory": { + "1": "Hein ? C’est déjà terminé ?", + "2": "… Ça craint grave ! On s’tire !", + "3": "Ouais de toute on en avait pas b’soin de ton Pokémon… Ah ah…", + "4": "Ouh là, c’est bon, j’en demandais pas tant…", + "5": "On pèse plus que des Pokémon, t’entends ?\nAlors tu vas nous respecter, oush !" + } + }, + "plumeria": { + "encounter": { + "1": "Tsk. T’es un gamin tout ce qu’il y a de plus banal, en fait.", + "1_female": "Tsk. T’es une gamine tout ce qu’il y a de plus banal, en fait.", + "2": "Abrutis de sbires. Trop incompétents pour arriver à se débarasser de gamins…", + "3": "Si tu touches encore à un cheveu de mes lascars, tu vas pas comprendre c’qui t’arrive !" + }, + "victory": { + "1": "Tsk. T’es pas mauvais. J’te l’accorde.", + "1_female": "Tsk. T’es pas mauvaise. J’te l’accorde.", + "2": "Tsk. J’dois reconnaitre que t’en as dans le ventre.\n$Maintenant, j’comprends pourquoi mes gars n’arrêtent pas de se faire battre par toi.", + "3": "Tsk. J’crois que j'ai plus qu’à assumer ma défaite." + } + }, + "macro_grunt": { + "encounter": { + "1": "Hop hop hop ! Terminus !", + "2": "T’es un Dresseur n’est-ce pas ?\n$J’ai bien peur ce que ne soit pas une excuse suffisante pour nous interrompre dans notre travail.", + "2_female": "T’es une Dresseuse n’est-ce pas ?\n$J’ai bien peur ce que ne soit pas une excuse suffisante pour nous interrompre dans notre travail.", + "3": "Je travaille à Macro Cosmos Assurances !\nBesoin d’une assurance-vie ?" + }, + "victory": { + "1": "Je n’ai d’autre choix que respectueusement me retirer.", + "2": "Mon argent de poche…\nPlus qu’à manger des pâtes pour la fin du mois…", + "3": "Chez Macro Cosmos, rien n’est comparable à notre dévotion au travail !" + } + }, + "oleana": { + "encounter": { + "1": "Je ne laisserai personne interférer avec les projets du président Shehroz.", + "2": "Je vois que vous avez su vous défaire de mes subalternes.\n$Mais assez joué. Il est temps de rentrer chez vous, maintenant.", + "3": "Je gagnerai en votre nom, monsieur le président." + }, + "victory": { + "1": "*soupir* Comment ai-je fait pour perdre ainsi… ?\nJe ne suis vraiment pas à la hauteur…", + "2": "Ah ! Quelle erreur… Je n’aurais pas dû sous-estimer un Dresseur de ton calibre…", + "2_female": "Ah ! Quelle erreur… Je n’aurais pas dû sous-estimer une Dresseuse de ton calibre…", + "3": "*soupir* Je suis fatiguée parton…" + } + }, "rocket_boss_giovanni_1": { "encounter": { "1": "Bien. Je dois admettre que je suis impressionné de te voir ici !" @@ -438,14 +528,14 @@ "3": "Ouah ! T’es super balèze !" }, "defeat": { - "1": "Qu’en dis-tu? C’est ça, la puissance des Pokémon Eau !", + "1": "Qu’en dis-tu ? C’est ça, la puissance des Pokémon Eau !", "2": "J’espère que t’as pris note des élégantes techniques de nage de mes Pokémon !", "3": "Tes Pokémon ne jouent visiblement pas dans le même bassin…" } }, "lt_surge": { "encounter": { - "1": "T’as pas froid aux yeux, soldat ! Les combats Pokémon, c’est la guerre !", + "1": "T’as pas froid aux yeux, soldat ! Les combats Pokémon, c’est la guerre !", "2": "Tu as du guts pour venir me fight ici ! Je vais te shock !", "3": "Compte tes dents, tu vas morfler !\nMes Pokémon Électrik vont t’atomiser !" }, @@ -468,7 +558,7 @@ "4": "Voir un tel jardin rempli de fleurs est si apaisant…" }, "victory": { - "1": "Bien joué, c’est mértié.", + "1": "Bien joué, c’est mérité.", "2": "Dommage, on s’amusait si bien…", "3": "Oh non, le combat est terminé…", "4": "Aaah, ça fait du bien !\nMerci, j’en avais besoin." @@ -483,56 +573,56 @@ }, "alder": { "encounter": { - "1": "Prépare-toi pour un combat contre le meilleur Dresseur d’Unys !" + "1": "Prépare-toi pour un combat contre le meilleur Dresseur d’Unys !" }, "victory": { - "1": "Bien joué ! Tu as sans aucun doute un talent inégalé." + "1": "Bien joué ! Tu as sans aucun doute un talent inégalé." }, "defeat": { - "1": "Une brise fraiche traverse mon cœur…\n$Quel effort extraordinaire !" + "1": "Une brise fraiche traverse mon cœur…\n$Quel effort extraordinaire !" } }, "kieran": { "encounter": { - "1": "Grâce à un travail acharné, je deviens de plus en plus fort !\n$Je ne perdrai pas." + "1": "Grâce à un travail acharné, je deviens de plus en plus fort !\n$Je ne perdrai pas." }, "victory": { - "1": "Je n’y crois pas…\n$Quel combat amusant et palpitant !" + "1": "Je n’y crois pas…\n$Quel combat amusant et palpitant !" }, "defeat": { - "1": "Eh beh, quel combat !\n$Il est temps pour toi de t’entrainer encore plus dur." + "1": "Eh beh, quel combat !\n$Il est temps pour toi de t’entrainer encore plus dur." } }, "rival": { "encounter": { - "1": "@c{smile}Ah, je te cherchais ! Je savais que t’étais pressé de partir, mais je m’attendais quand même à un au revoir…\n$@c{smile_eclosed}T’as finalement décidé de réaliser ton rêve ?\nJ’ai peine à y croire.\n$@c{serious_smile_fists}Vu que t’es là, ça te dis un petit combat ?\nJe voudrais quand même m’assurer que t’es prêt.\n$@c{serious_mopen_fists}Surtout ne te retiens pas et donne-moi tout ce que t’as !" + "1": "@c{smile}Ah, je te cherchais ! Je savais que t’étais pressée de partir, mais je m’attendais quand même à un au revoir…\n$@c{smile_eclosed}T’as finalement décidé de réaliser ton rêve ?\nJ’ai peine à y croire.\n$@c{serious_smile_fists}Vu que t’es là, ça te dis un petit combat ?\nJe voudrais quand même m’assurer que t’es prête.\n$@c{serious_mopen_fists}Surtout ne te retiens pas et donne-moi tout ce que t’as !" }, "victory": { - "1": "@c{shock}Wah… Tu m’as vraiment lavé.\nT’es vraiment un débutant ?\n$@c{smile}T’as peut-être eu de la chance, mais…\nPeut-être que t’arriveras jusqu’au bout du chemin.\n$D’ailleurs, le prof m’a demandé de te filer ces objets.\nIls ont l’air sympas.\n$@c{serious_smile_fists}Bonne chance à toi !" + "1": "@c{shock}Wah… Tu m’as vraiment lavé.\nT’es vraiment une débutante ?\n$@c{smile}T’as peut-être eu de la chance, mais…\nPeut-être que t’arriveras jusqu’au bout du chemin.\n$D’ailleurs, le prof m’a demandé de te filer ces objets.\nIls ont l’air sympas.\n$@c{serious_smile_fists}Bonne chance à toi !" } }, "rival_female": { "encounter": { - "1": "@c{smile_wave}Ah, te voilà ! Je t’ai cherché partout !\n@c{angry_mopen}On oublie de dire au revoir à sa meilleure amie ?\n$@c{smile_ehalf}T’as décidé de réaliser ton rêve, hein ?\nCe jour est donc vraiment arrivé…\n$@c{smile}Je veux bien te pardonner de m’avoir oubliée,\nà une conditon. @c{smile_wave_wink}Que tu m’affronte !\n$@c{angry_mopen}Donne tout ! Ce serait dommage que ton aventure finisse avant d’avoir commencé, hein ?" + "1": "@c{smile_wave}Ah, te voilà ! Je t’ai cherché partout !\n@c{angry_mopen}On oublie de dire au revoir à sa meilleure amie ?\n$@c{smile_ehalf}T’as décidé de réaliser ton rêve, hein ?\nCe jour est donc vraiment arrivé…\n$@c{smile}Je veux bien te pardonner de m’avoir oubliée,\nà une condition. @c{smile_wave_wink}Que tu m’affronte !\n$@c{angry_mopen}Donne tout ! Ce serait dommage que ton aventure finisse avant d’avoir commencé, hein ?" }, "victory": { - "1": "@c{shock}Tu viens de commencer et t’es déjà si fort ?!@d{96}\n@c{angry}T’as triché non ? Avoue !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile_eclosed}J’ai perdu dans les règles…\nJ’ai le sentiment que tu vas très bien t’en sortir.\n$@c{smile}D’ailleurs, le prof veut que je te donne ces quelques objets. Ils te seront utiles, pour sûr !\n$@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi !" + "1": "@c{shock}Tu viens de commencer et t’es déjà si fort ?!@d{96}\n@c{angry}T’as triché non ? Avoue !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile_eclosed}J’ai perdu dans les règles…\nJ’ai le sentiment que tu vas très bien t’en sortir.\n$@c{smile}D’ailleurs, le prof veut que je te donne ces quelques objets. Ils te seront utiles, pour sûr !\n$@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi !" } }, "rival_2": { "encounter": { - "1": "@c{smile}Hé, toi aussi t’es là ?\n@c{smile_eclosed}Toujours invaincu, hein… ?\n$@c{serious_mopen_fists}Je sais que j’ai l’air de t’avoir suivi ici, mais c’est pas complètement vrai.\n$@c{serious_smile_fists}Pour être honnête, ça me démangeait d’avoir une revanche depuis que tu m’as battu.\n$Je me suis beaucoup entrainé, alors sois sure que je vais pas retenir mes coups cette fois.\n$@c{serious_mopen_fists}Et comme la dernière fois, ne te retiens pas !\nC’est parti !" + "1": "@c{smile}Hé, toi aussi t’es là ?\n@c{smile_eclosed}Toujours invaincue, hein… ?\n$@c{serious_mopen_fists}Je sais que j’ai l’air de t’avoir suivie ici, mais c’est pas complètement vrai.\n$@c{serious_smile_fists}Pour être honnête, ça me démangeait d’avoir une revanche depuis que tu m’as battu.\n$Je me suis beaucoup entrainé, alors sois sure que je vais pas retenir mes coups cette fois.\n$@c{serious_mopen_fists}Et comme la dernière fois, ne te retiens pas !\nC’est parti !" }, "victory": { - "1": "@c{neutral_eclosed}Oh. Je crois que j’ai trop pris la confiance.\n$@c{smile}Pas grave, c’est OK. Je me doutais que ça arriverait.\n@c{serious_mopen_fists}Je vais juste devoir encore plus m’entrainer !\n\n$@c{smile}Ah, et pas que t’aies réellement besoin d’aide, mais j’ai ça en trop sur moi qui pourrait t’intéresser.\n\n$@c{serious_smile_fists}Mais n’espère plus en avoir d’autres !\nJe peux pas passer mon temps à aider mon adversaire.\n$@c{smile}Bref, prends soin de toi et profite bien de l’évènement !" + "1": "@c{neutral_eclosed}Oh. Je crois que j’ai trop pris la confiance.\n$@c{smile}Pas grave, c’est OK. Je me doutais que ça arriverait.\n@c{serious_mopen_fists}Je vais juste devoir encore plus m’entrainer !\n\n$@c{smile}Ah, et pas que t’aies réellement besoin d’aide, mais j’ai ça en trop sur moi qui pourrait t’intéresser.\n\n$@c{serious_smile_fists}Mais n’espère plus en avoir d’autres !\nJe peux pas passer mon temps à aider mon adversaire.\n$@c{smile}Bref, prends soin de toi !" } }, "rival_2_female": { "encounter": { - "1": "@c{smile_wave}Hé, sympa de te croiser ici. T’as toujours l’air invaincu. @c{angry_mopen}Eh… Pas mal !\n$@c{angry_mopen}Je sais à quoi tu penses et non, je t’espionne pas.\n@c{smile_eclosed}C’est juste que j’étais aussi dans le coin.\n$@c{smile_ehalf}Heureuse pour toi, mais je veux juste te rappeler que c’est pas grave de perdre parfois.\n$@c{smile}On apprend de nos erreurs, souvent plus que si on ne connaissait que le succès.\n$@c{angry_mopen}Dans tous les cas je me suis bien entrainée pour cette revanche, t’as intérêt à tout donner !" + "1": "@c{smile_wave}Hé, sympa de te croiser ici. T’as toujours l’air invaincu. @c{angry_mopen}Eh… Pas mal !\n$@c{angry_mopen}Je sais à quoi tu penses et non, je t’espionne pas.\n@c{smile_eclosed}C’est juste que j’étais aussi dans le coin.\n$@c{smile_ehalf}Heureuse pour toi, mais je veux juste te rappeler que c’est pas grave de perdre parfois.\n$@c{smile}On apprend de nos erreurs, souvent plus que si on ne connaissait que le succès.\n$@c{angry_mopen}Dans tous les cas je me suis bien entrainée pour cette revanche, t’as intérêt à tout donner !" }, "victory": { - "1": "@c{neutral}Je… J’étais pas encore supposée perdre…\n$@c{smile}Bon. Ça veut juste dire que je vais devoir encore plus m’entrainer !\n$@c{smile_wave}J’ai aussi ça en rab pour toi !\n@c{smile_wave_wink}Inutile de me remercier ~.\n$@c{angry_mopen}C’étaient les derniers, terminé les cadeaux après ceux-là !\n$@c{smile_wave}Allez, tiens le coup et profite bien de l’évènement !" + "1": "@c{neutral}Je… J’étais pas encore supposée perdre…\n$@c{smile}Bon. Ça veut juste dire que je vais devoir encore plus m’entrainer !\n$@c{smile_wave}J’ai aussi ça en rab pour toi !\n@c{smile_wave_wink}Inutile de me remercier ~.\n$@c{angry_mopen}C’étaient les derniers, terminé les cadeaux après ceux-là !\n$@c{smile_wave}Allez, tiens le coup !" }, "defeat": { "1": "Je suppose que c’est parfois normal de perdre…" @@ -540,18 +630,18 @@ }, "rival_3": { "encounter": { - "1": "@c{smile}Hé, mais qui voilà ! Ça fait un bail.\n@c{neutral}T’es… toujours invaincu ? Incroyable.\n$@c{neutral_eclosed}Tout est devenu un peu… étrange.\nC’est plus pareil sans toi au village.\n$@c{serious}Je sais que c’est égoïste, mais j’ai besoin d’expier ça.\n@c{neutral_eclosed}Je crois que tout ça te dépasse.\n$@c{serious}Ne jamais perdre, c’est juste irréaliste.\nGrandir, c’est parfois aussi savoir perdre.\n$@c{neutral_eclosed}T’as un beau parcours, mais il y a encore tellement à venir et ça va pas s’arranger. @c{neutral}T’es prêt pour ça ?\n$@c{serious_mopen_fists}Si tu l’es, alors prouve-le." + "1": "@c{smile}Hé, mais qui voilà ! Ça fait un bail.\n@c{neutral}T’es… toujours invaincue ? Incroyable.\n$@c{neutral_eclosed}Tout est devenu un peu… étrange.\nC’est plus pareil sans toi au village.\n$@c{serious}Je sais que c’est égoïste, mais j’ai besoin d’expier ça.\n@c{neutral_eclosed}Je crois que tout ça te dépasse.\n$@c{serious}Ne jamais perdre, c’est juste irréaliste.\nGrandir, c’est parfois aussi savoir perdre.\n$@c{neutral_eclosed}T’as un beau parcours, mais il y a encore tellement à venir et ça va pas s’arranger. @c{neutral}T’es prête pour ça ?\n$@c{serious_mopen_fists}Si tu l’es, alors prouve-le." }, "victory": { - "1": "@c{angry_mhalf}C’est lunaire… J’ai presque fait que m’entrainer…\nAlors pourquoi il y a encore un tel écart entre nous ?" + "1": "@c{angry_mhalf}C’est lunaire… J’ai presque fait que m’entrainer…\nAlors pourquoi il y a encore un tel écart entre nous ?" } }, "rival_3_female": { "encounter": { - "1": "@c{smile_wave}Ça fait une éternité ! Toujours debout hein ?\n@c{angry}Tu commences à me pousser à bout là. @c{smile_wave_wink}T’inquiètes j’déconne !\n$@c{smile_ehalf}Mais en vrai, ta maison te manque pas ? Ou… Moi ?\nJ… Je veux dire… Tu me manques vraiment beaucoup.\n$@c{smile_eclosed}Je te soutiendrai toujours dans tes ambitions, mais la vérité est que tu finiras par perdre un jour ou l’autre.\n$@c{smile}Quand ça arrivera, je serai là pour toi, comme toujours.\n@c{angry_mopen}Maintenant, montre-moi à quel point t’es devenu fort !" + "1": "@c{smile_wave}Ça fait une éternité ! Toujours debout hein ?\n@c{angry}Tu commences à me pousser à bout là. @c{smile_wave_wink}T’inquiètes j’déconne !\n$@c{smile_ehalf}Mais en vrai, ta maison te manque pas ? Ou… Moi ?\nJ… Je veux dire… Tu me manques vraiment beaucoup.\n$@c{smile_eclosed}Je te soutiendrai toujours dans tes ambitions, mais la vérité est que tu finiras par perdre un jour ou l’autre.\n$@c{smile}Quand ça arrivera, je serai là pour toi, comme toujours.\n@c{angry_mopen}Maintenant, montre-moi à quel point t’es devenu fort !" }, "victory": { - "1": "@c{shock}Après tout ça… Ça te suffit toujours pas… ?\nTu reviendras jamais à ce rythme…" + "1": "@c{shock}Après tout ça… Ça te suffit toujours pas… ?\nTu reviendras jamais à ce rythme…" }, "defeat": { "1": "T’as fait de ton mieux.\nAllez, rentrons à la maison." @@ -559,18 +649,18 @@ }, "rival_4": { "encounter": { - "1": "@c{neutral}Hé.\n$Je vais pas y aller par quatre chemins avec toi.\n@c{neutral_eclosed}Je suis là pour gagner. Simple, basique.\n$@c{serious_mhalf_fists}J’ai appris à maximiser tout mon potentiel en m’entrainant d’arrachepied.\n$@c{smile}C’est fou tout le temps que tu peux te dégager si tu dors pas en sacrifiant ta vie sociale.\n$@c{serious_mopen_fists}Plus rien n’a d’importance désormais, pas tant que j’aurai pas gagné.\n$@c{neutral_eclosed}J’ai atteint un stade où je ne peux plus perdre.\n@c{smile_eclosed}Je présume que ta philosophie était pas si fausse finalement.\n$@c{angry_mhalf}La défaite, c’est pour les faibles, et je ne suis plus un faible.\n$@c{serious_mopen_fists}Tiens-toi prêt." + "1": "@c{neutral}Hé.\n$Je vais pas y aller par quatre chemins avec toi.\n@c{neutral_eclosed}Je suis là pour gagner. Simple, basique.\n$@c{serious_mhalf_fists}J’ai appris à maximiser tout mon potentiel en m’entrainant d’arrachepied.\n$@c{smile}C’est fou tout le temps que tu peux te dégager si tu dors pas en sacrifiant ta vie sociale.\n$@c{serious_mopen_fists}Plus rien n’a d’importance désormais, pas tant que j’aurai pas gagné.\n$@c{neutral_eclosed}J’ai atteint un stade où je ne peux plus perdre.\n@c{smile_eclosed}Je présume que ta philosophie était pas si fausse finalement.\n$@c{angry_mhalf}La défaite, c’est pour les faibles, et je ne suis plus un faible.\n$@c{serious_mopen_fists}Tiens-toi prête." }, "victory": { - "1": "@c{neutral}Que…@d{64} Qui es-tu ?" + "1": "@c{neutral}Que…@d{64} Qui es-tu ?" } }, "rival_4_female": { "encounter": { - "1": "@c{neutral}C’est moi ! Tu m’as pas encore oubliée… n’est-ce pas ?\n$@c{smile}Tu devrais être fier d’être arrivé aussi loin. GG !\nMais c’est certainement pas la fin de ton aventure.\n$@c{smile_eclosed}T’as éveillé en moi quelque chose que j’ignorais.\nTout mon temps passe dans l’entrainement.\n$@c{smile_ehalf}Je dors et je mange à peine, je m’entraine juste tous les jours, et deviens de plus en plus forte.\n$@c{neutral}En vrai, Je… J’ai de la peine à me reconnaitre.\n$Mais maintenant, je suis au top de mes capacités.\nJe doute que tu sois de nouveau capable de me battre.\n$Et tu sais quoi ? Tout ça, c’est de ta faute.\n@c{smile_ehalf}Et j’ignore si je dois te remercier ou te haïr.\n$@c{angry_mopen}Tiens-toi prêt." + "1": "@c{neutral}C’est moi ! Tu m’as pas encore oubliée… n’est-ce pas ?\n$@c{smile}Tu devrais être fier d’être arrivé aussi loin. GG !\nMais c’est certainement pas la fin de ton aventure.\n$@c{smile_eclosed}T’as éveillé en moi quelque chose que j’ignorais.\nTout mon temps passe dans l’entrainement.\n$@c{smile_ehalf}Je dors et je mange à peine, je m’entraine juste tous les jours, et deviens de plus en plus forte.\n$@c{neutral}En vrai, Je… J’ai de la peine à me reconnaitre.\n$Mais maintenant, je suis au top de mes capacités.\nJe doute que tu sois de nouveau capable de me battre.\n$Et tu sais quoi ? Tout ça, c’est de ta faute.\n@c{smile_ehalf}Et j’ignore si je dois te remercier ou te haïr.\n$@c{angry_mopen}Tiens-toi prêt." }, "victory": { - "1": "@c{neutral}Que…@d{64} Qui es-tu ?" + "1": "@c{neutral}Que…@d{64} Qui es-tu ?" }, "defeat": { "1": "$@c{smile}Tu devrais être fier d’être arrivé jusque là." @@ -597,7 +687,7 @@ }, "rival_6": { "encounter": { - "1": "@c{smile_eclosed}Nous y revoilà.\n$@c{neutral}J’ai eu du temps pour réfléchir à tout ça.\nIl y a une raison à pourquoi tout semble étrange.\n$@c{neutral_eclosed}Ton rêve, ma volonté de te battre…\nFont partie de quelque chose de plus grand.\n$@c{serious}C’est même pas à propos de moi, ni de toi… Mais du monde, @c{serious_mhalf_fists}et te repousser dans tes limites est ma mission.\n$@c{neutral_eclosed}J’ignore si je serai capable de l’accomplir, mais je ferai tout ce qui est en mon pouvoir.\n$@c{neutral}Cet endroit est terrifiant… Et pourtant il m’a l’air familier, comme si j’y avais déjà mis les pieds.\n$@c{serious_mhalf_fists}Tu ressens la même chose, pas vrai ?\n$@c{serious}… et c’est comme si quelque chose ici me parlait.\n$Comme si c’était tout ce que ce monde avait toujours connu.\n$Ces précieux moments ensemble semblent si proches ne sont rien de plus qu’un lointain souvenir.\n$@c{neutral_eclosed}D’ailleurs, qui peut dire aujourd’hui qu’ils ont pu être réels ?\n$@c{serious_mopen_fists}Il faut que tu persévères. Si tu t’arrêtes, ça n’aura jamais de fin et t’es le seul à en être capable.\n$@c{serious_smile_fists}Difficile de comprendre le sens de tout ça, je sais juste que c’est la réalité.\n$@c{serious_mopen_fists}Si tu ne parviens pas à me battre ici et maintenant, tu n’as aucune chance." + "1": "@c{smile_eclosed}Nous y revoilà.\n$@c{neutral}J’ai eu du temps pour réfléchir à tout ça.\nIl y a une raison à pourquoi tout semble étrange.\n$@c{neutral_eclosed}Ton rêve, ma volonté de te battre…\nFont partie de quelque chose de plus grand.\n$@c{serious}C’est même pas à propos de moi, ni de toi… Mais du monde, @c{serious_mhalf_fists}et te repousser dans tes limites est ma mission.\n$@c{neutral_eclosed}J’ignore si je serai capable de l’accomplir, mais je ferai tout ce qui est en mon pouvoir.\n$@c{neutral}Cet endroit est terrifiant… Et pourtant il m’a l’air familier, comme si j’y avais déjà mis les pieds.\n$@c{serious_mhalf_fists}Tu ressens la même chose, pas vrai ?\n$@c{serious}… et c’est comme si quelque chose ici me parlait.\n$Comme si c’était tout ce que ce monde avait toujours connu.\n$Ces précieux moments ensemble qui semblent si proches ne sont rien de plus qu’un lointain souvenir.\n$@c{neutral_eclosed}D’ailleurs, qui peut dire aujourd’hui qu’ils ont pu être réels ?\n$@c{serious_mopen_fists}Il faut que tu persévères. Si tu t’arrêtes, ça n’aura jamais de fin et t’es la seule à en être capable.\n$@c{serious_smile_fists}Difficile de comprendre le sens de tout ça, je sais juste que c’est la réalité.\n$@c{serious_mopen_fists}Si tu ne parviens pas à me battre ici et maintenant, tu n’as aucune chance." }, "victory": { "1": "@c{smile_eclosed}J’ai fait ce que j’avais à faire.\n$Promets-moi juste une chose.\n@c{smile}Après avoir réparé ce monde… Rentre à la maison." @@ -605,7 +695,7 @@ }, "rival_6_female": { "encounter": { - "1": "@c{smile_ehalf}C’est donc encore entre toi et moi.\n$@c{smile_eclosed}Tu sais, j’ai beau retouner ça dans tous les sens…\n$@c{smile_ehalf}Quelque chose peut expliquer tout ça, pourquoi tout semble si étrange…\n$@c{smile}T’as tes rêves, j’ai mes ambitions…\n$J’ai juste le sentiment qu’il y a un grand dessein derrière tout ça, derrière ce qu’on fait toi et moi.\n$@c{smile_eclosed}Je crois que mon but est de… repousser tes limites.\n$@c{smile_ehalf}Je suis pas certaine de bien être douée à cet exercice, mais je fais de mon mieux.\n$Cet endroit épouvantable cache quelque chose d’étrange… Tout semble si limpide…\n$Comme… si c’était tout ce que ce monde avait toujours connu.\n$@c{smile_eclosed}J’ai le sentiment que nos précieux moments ensemble sont devenus si flous.\n$@c{smile_ehalf}Ont-ils au moins été réels ? Tout semble si loin maintenant…\n$@c{angry_mopen}Il faut que tu persévères. Si tu t’arrêtes, ça n’aura jamais de fin et t’es le seul à en être capable.\n$@c{smile_ehalf}Je… j’ignore le sens de tout ça… Mais je sais que c’est la réalité.\n$@c{neutral}Si tu ne parviens pas à me battre ici et maintenant, tu n’as aucune chance." + "1": "@c{smile_ehalf}C’est donc encore entre toi et moi.\n$@c{smile_eclosed}Tu sais, j’ai beau retouner ça dans tous les sens…\n$@c{smile_ehalf}Quelque chose peut expliquer tout ça, pourquoi tout semble si étrange…\n$@c{smile}T’as tes rêves, j’ai mes ambitions…\n$J’ai juste le sentiment qu’il y a un grand dessein derrière tout ça, derrière ce qu’on fait toi et moi.\n$@c{smile_eclosed}Je crois que mon but est de… repousser tes limites.\n$@c{smile_ehalf}Je suis pas certaine de bien être douée à cet exercice, mais je fais de mon mieux.\n$Cet endroit épouvantable cache quelque chose d’étrange… Tout semble si limpide…\n$Comme… si c’était tout ce que ce monde avait toujours connu.\n$@c{smile_eclosed}J’ai le sentiment que nos précieux moments ensemble sont devenus si flous.\n$@c{smile_ehalf}Ont-ils au moins été réels ? Tout semble si loin maintenant…\n$@c{angry_mopen}Il faut que tu persévères. Si tu t’arrêtes, ça n’aura jamais de fin et t’es le seul à en être capable.\n$@c{smile_ehalf}Je… j’ignore le sens de tout ça… Mais je sais que c’est la réalité.\n$@c{neutral}Si tu ne parviens pas à me battre ici et maintenant, tu n’as aucune chance." }, "victory": { "1": "@c{smile_ehalf}Je… Je crois que j’ai rempli ma mission…\n$@c{smile_eclosed}Promets-moi… Après avoir réparé ce monde… Reviens à la maison sain et sauf.\n$@c{smile_ehalf}… Merci." diff --git a/src/locales/fr/egg.json b/src/locales/fr/egg.json index 64f22aa330d..cbc912e9d50 100644 --- a/src/locales/fr/egg.json +++ b/src/locales/fr/egg.json @@ -4,7 +4,7 @@ "ultraTier": "Épique", "masterTier": "Légendaire", "defaultTier": "Commun", - "hatchWavesMessageSoon": "Il fait du bruit. Il va éclore !", + "hatchWavesMessageSoon": "Il fait du bruit.\nIl va éclore !", "hatchWavesMessageClose": "Il bouge de temps en temps. Il devrait bientôt éclore.", "hatchWavesMessageNotClose": "Qu’est-ce qui va en sortir ? Ça va mettre du temps.", "hatchWavesMessageLongTime": "Cet Œuf va surement mettre du temps à éclore.", @@ -16,7 +16,7 @@ "tooManyEggs": "Vous avez trop d’Œufs !", "pull": "Tirage", "pulls": "Tirages", - "sameSpeciesEgg": "{{species}} sortira de cet Œuf !", + "sameSpeciesEgg": "Un {{species}} sortira de cet Œuf !", "hatchFromTheEgg": "{{pokemonName}} sort de l’Œuf !", "eggMoveUnlock": "Capacité Œuf débloquée :\n{{moveName}}", "rareEggMoveUnlock": "Capacité Œuf Rare débloquée :\n{{moveName}}", diff --git a/src/locales/fr/fight-ui-handler.json b/src/locales/fr/fight-ui-handler.json index e8b41661995..bd4e6b52566 100644 --- a/src/locales/fr/fight-ui-handler.json +++ b/src/locales/fr/fight-ui-handler.json @@ -3,5 +3,6 @@ "power": "Puissance", "accuracy": "Précision", "abilityFlyInText": " {{passive}}{{abilityName}}\nde {{pokemonName}}", - "passive": "Passif " -} \ No newline at end of file + "passive": "Passif ", + "teraHover": "Téracristal {{type}}" +} diff --git a/src/locales/fr/menu-ui-handler.json b/src/locales/fr/menu-ui-handler.json index 807b34f1315..b8627bf91b5 100644 --- a/src/locales/fr/menu-ui-handler.json +++ b/src/locales/fr/menu-ui-handler.json @@ -25,5 +25,6 @@ "unlinkGoogle": "Délier Google", "cancel": "Retour", "losingProgressionWarning": "Vous allez perdre votre progression depuis le début du combat. Continuer ?", - "noEggs": "Vous ne faites actuellement\néclore aucun Œuf !" -} \ No newline at end of file + "noEggs": "Vous ne faites actuellement\néclore aucun Œuf !", + "donate": "Faire un don" +} diff --git a/src/locales/fr/menu.json b/src/locales/fr/menu.json index 83626a1f33f..277b0f5fd04 100644 --- a/src/locales/fr/menu.json +++ b/src/locales/fr/menu.json @@ -6,7 +6,7 @@ "newGame": "Nouvelle partie", "settings": "Paramètres", "selectGameMode": "Sélectionnez un mode de jeu.", - "logInOrCreateAccount": "Connectez-vous ou créez un compte pour commencer. Aucun e-mail requis !", + "logInOrCreateAccount": "Connectez-vous ou créez un compte pour commencer.\nAucun e-mail requis !", "username": "Nom d’utilisateur", "password": "Mot de passe", "login": "Connexion", @@ -19,29 +19,29 @@ "invalidRegisterPassword": "Le mot de passe doit contenir 6 caractères ou plus", "usernameAlreadyUsed": "Le nom d’utilisateur est déjà utilisé", "accountNonExistent": "Le nom d’utilisateur n’existe pas", - "unmatchingPassword": "Le mot de passe n’est pas correct", + "unmatchingPassword": "Le mot de passe est incorrect", "passwordNotMatchingConfirmPassword": "Les mots de passe ne correspondent pas", "confirmPassword": "Confirmer le MDP", - "registrationAgeWarning": "Vous confirmez en vous inscrivant que vous avez 13 ans ou plus.", + "registrationAgeWarning": "En vous inscrivant, vous certifiez que vous avez 13 ans ou plus.", "backToLogin": "Retour", "failedToLoadSaveData": "Échec du chargement des données. Veuillez recharger\nla page. Si cela persiste, contactez l’administrateur.", "sessionSuccess": "Session chargée avec succès.", "failedToLoadSession": "Vos données de session n’ont pas pu être chargées.\nElles pourraient être corrompues.", - "boyOrGirl": "Es-tu un garçon ou une fille ?", + "boyOrGirl": "Es-tu un garçon ou une fille ?", "evolving": "Quoi ?\n{{pokemonName}} évolue !", - "stoppedEvolving": "Hein ?\n{{pokemonName}} n’évolue plus !", - "pauseEvolutionsQuestion": "Mettre en pause les évolutions pour {{pokemonName}} ?\nElles peuvent être réactivées depuis l’écran d’équipe.", - "evolutionsPaused": "Les évolutions ont été mises en pause pour {{pokemonName}}.", + "stoppedEvolving": "Hein ?\n{{pokemonName}} n’évolue plus !", + "pauseEvolutionsQuestion": "Interrompre les évolutions pour {{pokemonName}} ?\nElles peuvent être réactivées depuis l’écran d’équipe.", + "evolutionsPaused": "Les évolutions de {{pokemonName}}\nsont interrompues.", "evolutionDone": "Félicitations !\n{{pokemonName}} a évolué en {{evolvedPokemonName}} !", - "dailyRankings": "Classement du Jour", - "weeklyRankings": "Classement de la Semaine", - "noRankings": "Pas de Classement", + "dailyRankings": "Classement du jour", + "weeklyRankings": "Classement de la semaine", + "noRankings": "Pas de classement", "positionIcon": "#", "usernameScoreboard": "Utilisateur", "score": "Score", "wave": "Vague", "loading": "Chargement…", - "loadingAsset": "Chargement de la ressource : {{assetName}}", + "loadingAsset": "Chargement des ressources : {{assetName}}", "playersOnline": "Joueurs connectés", "yes": "Oui", "no": "Non", @@ -51,5 +51,7 @@ "renamePokemon": "Renommer le Pokémon", "rename": "Renommer", "nickname": "Surnom", - "errorServerDown": "Oupsi ! Un problème de connexion au serveur est survenu.\n\nVous pouvez garder cette fenêtre ouverte,\nle jeu se reconnectera automatiquement." + "errorServerDown": "Oupsi ! Un problème de connexion au serveur est survenu.\n\nVous pouvez garder cette fenêtre ouverte,\nle jeu se reconnectera automatiquement.", + "noSaves": "Vous n’avez aucune sauvegarde enregistrée !", + "tooManySaves": "Vous avez trop de sauvegardes enregistrées !" } diff --git a/src/locales/fr/modifier-type.json b/src/locales/fr/modifier-type.json index 6d5cfb098ef..4f8033b50d7 100644 --- a/src/locales/fr/modifier-type.json +++ b/src/locales/fr/modifier-type.json @@ -2,7 +2,7 @@ "ModifierType": { "AddPokeballModifierType": { "name": "{{pokeballName}} x{{modifierCount}}", - "description": "Recevez {{modifierCount}} {{pokeballName}}·s. (Inventaire : {{pokeballAmount}})\nTaux de capture : {{catchRate}}" + "description": "Recevez {{modifierCount}} {{pokeballName}}·s. (Inventaire : {{pokeballAmount}})\nTaux de capture : {{catchRate}}" }, "AddVoucherModifierType": { "name": "{{voucherTypeName}} x{{modifierCount}}", @@ -10,8 +10,8 @@ }, "PokemonHeldItemModifierType": { "extra": { - "inoperable": "{{pokemonName}} ne peut pas\nporter cet objet !", - "tooMany": "{{pokemonName}} porte trop\nd’exemplaires de cet objet !" + "inoperable": "{{pokemonName}} ne peut pas\nporter cet objet !", + "tooMany": "{{pokemonName}} porte trop\nd’exemplaires de cet objet !" } }, "PokemonHpRestoreModifierType": { @@ -47,10 +47,14 @@ "description": "Donne la nature {{natureName}} à un Pokémon et la débloque pour le starter lui étant lié." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Double les chances de tomber sur un combat double pendant {{battleCount}} combats." + "description": "Quadruple les chances de tomber sur un combat double pendant {{battleCount}} combats." }, - "TempBattleStatBoosterModifierType": { - "description": "Augmente d’un cran {{tempBattleStatName}} pour toute l’équipe pendant 5 combats." + "TempStatStageBoosterModifierType": { + "description": "Augmente {{amount}} {{stat}} de toute l’équipe pendant 5 combats.", + "extra": { + "stage": "d’un cran", + "percentage": "de 30%" + } }, "AttackTypeBoosterModifierType": { "description": "Augmente de 20% la puissance des capacités de type {{moveType}} d’un Pokémon." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "Fait monter toute l’équipe de {{levels}} niveau·x." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter." + "BaseStatBoosterModifierType": { + "description": "Augmente de 10% {{stat}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter." }, "AllPokemonFullHpRestoreModifierType": { "description": "Restaure tous les PV de toute l’équipe." @@ -85,7 +89,7 @@ "description": "Augmente de {{boostPercent}}% le gain de Points d’Exp du porteur." }, "PokemonFriendshipBoosterModifierType": { - "description": "Augmente le gain d’amitié de 50% par victoire." + "description": "Augmente le gain de bonheur de 50% par victoire." }, "PokemonMoveAccuracyBoosterModifierType": { "description": "Augmente de {{accuracyAmount}} la précision des capacités (maximum 100)." @@ -102,17 +106,17 @@ "description": "Apprend la capacité {{moveName}} à un Pokémon.\n(Maintenez C ou Maj pour plus d’infos)" }, "EvolutionItemModifierType": { - "description": "Permet à certains Pokémon d’évoluer." + "description": "Permet à certains Pokémon d’évoluer à son contact." }, "FormChangeItemModifierType": { - "description": "Permet à certains Pokémon de changer de forme." + "description": "Permet à certains Pokémon de changer de forme à son contact." }, "FusePokemonModifierType": { "description": "Fusionne deux Pokémon (transfère le talent, sépare les stats de base et les types, partage les capacités)." }, "TerastallizeModifierType": { "name": "Téra-Éclat {{teraType}}", - "description": "{{teraType}} Téracristallise son porteur pendant 10 combats." + "description": "Téracristallise son porteur en type {{teraType}} pendant 10 combats." }, "ContactHeldItemTransferChanceModifierType": { "description": "{{chancePercent}}% de chances de voler un objet de l’adversaire en l’attaquant." @@ -183,6 +187,7 @@ "SOOTHE_BELL": { "name": "Grelot Zen" }, "SCOPE_LENS": { "name": "Lentilscope", "description": "Une lentille qui augmente d’un cran le taux de critiques du porteur." }, + "DIRE_HIT": { "name": "Muscle +", "extra": { "raises": "Taux de critique" } }, "LEEK": { "name": "Poireau", "description": "À faire tenir à Canarticho ou Palarticho. Un poireau très long et solide qui augmente de 2 crans le taux de critiques." }, "EVIOLITE": { "name": "Évoluroc", "description": "Augmente de 50% la Défense et Déf. Spé. si le porteur peut évoluer, 25% aux fusions dont une moitié le peut encore." }, @@ -197,7 +202,7 @@ "GOLDEN_PUNCH": { "name": "Poing Doré", "description": "La moitié des dégâts infligés sont convertis en argent." }, "COIN_CASE": { "name": "Boite Jetons", "description": "Tous les 10 combats, recevez 10% de votre argent en intérêts." }, - "LOCK_CAPSULE": { "name": "Poké Écrin", "description": "Permet de conserver la rareté des objets si vous relancez les objets proposés." }, + "LOCK_CAPSULE": { "name": "Poké Écrin", "description": "Permet de choisir de bloquer le niveau de rareté lors d’une relance des objets gratuits proposés." }, "GRIP_CLAW": { "name": "Accro Griffe" }, "WIDE_LENS": { "name": "Loupe" }, @@ -246,32 +251,18 @@ }, "SpeciesBoosterItem": { "LIGHT_BALL": { "name": "Balle Lumière", "description": "À faire tenir à Pikachu. Un orbe énigmatique qui double son Attaque et son Atq. Spé. ." }, - "THICK_CLUB": { "name": "Masse Os", "description": "À faire tenir à Osselait ou Ossatueur. Un os dur qui double leur Attaque." }, + "THICK_CLUB": { "name": "Masse Os", "description": "À faire tenir à Osselait ou à Ossatueur, formes d’Alola incluses. Un os dur qui double leur Attaque." }, "METAL_POWDER": { "name": "Poudre Métal", "description": "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Défense." }, "QUICK_POWDER": { "name": "Poudre Vite", "description": "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Vitesse." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "Attaque +", "x_defense": "Défense +", "x_sp_atk": "Atq. Spé. +", "x_sp_def": "Déf. Spé. +", "x_speed": "Vitesse +", - "x_accuracy": "Précision +", - "dire_hit": "Muscle +" + "x_accuracy": "Précision +" }, - - "TempBattleStatBoosterStatName": { - "ATK": "l’Attaque", - "DEF": "la Défense", - "SPATK": "l’Atq. Spé.", - "SPDEF": "la Déf. Spé.", - "SPD": "la Vitesse", - "ACC": "la précision", - "CRIT": "le taux de critique", - "EVA": "l’esquive", - "DEFAULT": "???" - }, - "AttackTypeBoosterItem": { "silk_scarf": "Mouchoir Soie", "black_belt": "Ceinture Noire", @@ -450,6 +441,6 @@ "DRAGON_MEMORY": "ROM Dragon", "DARK_MEMORY": "ROM Ténèbres", "FAIRY_MEMORY": "ROM Fée", - "BLANK_MEMORY": "ROM Vierge" + "NORMAL_MEMORY": "ROM Normal" } } diff --git a/src/locales/fr/modifier.json b/src/locales/fr/modifier.json index 8a15c9e5ddf..101b1d56164 100644 --- a/src/locales/fr/modifier.json +++ b/src/locales/fr/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "Les PV de {{pokemonNameWithAffix}}\nsont un peu restaurés par les {{typeName}} !", "hitHealApply": "Les PV de {{pokemonNameWithAffix}}\nsont un peu restaurés par le {{typeName}} !", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} a repris connaissance\navec sa {{typeName}} et est prêt à se battre de nouveau !", - "pokemonResetNegativeStatStageApply": "Les stats baissées de {{pokemonNameWithAffix}}\nsont restaurées par l’{{typeName}} !", + "resetNegativeStatStageApply": "Les stats baissées de {{pokemonNameWithAffix}}\nsont restaurées par l’{{typeName}} !", "moneyInterestApply": "La {{typeName}} vous rapporte\n{{moneyAmount}} ₽ d’intérêts !", "turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} est absorbé·e\npar le {{typeName}} de {{pokemonName}} !", "contactHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} est volé·e\npar l’{{typeName}} de {{pokemonName}} !", diff --git a/src/locales/fr/move-trigger.json b/src/locales/fr/move-trigger.json index 43cf09d5bf6..3704bc90718 100644 --- a/src/locales/fr/move-trigger.json +++ b/src/locales/fr/move-trigger.json @@ -1,8 +1,12 @@ { "hitWithRecoil": "{{pokemonName}} est blessé par le contrecoup !", - "cutHpPowerUpMove": "{{pokemonName}} sacrifie des PV\net augmente la puissance ses capacités !", + "cutHpPowerUpMove": "{{pokemonName}} sacrifie des PV\net augmente la puissance de ses capacités !", "absorbedElectricity": "{{pokemonName}} absorbe de l’électricité !", "switchedStatChanges": "{{pokemonName}} permute\nles changements de stats avec ceux de sa cible !", + "switchedTwoStatChanges": "{{pokemonName}} permute les changements de {{firstStat}} et de {{secondStat}} avec ceux de sa cible !", + "switchedStat": "{{pokemonName}} et sa cible échangent leur {{stat}} !", + "sharedGuard": "{{pokemonName}} additionne sa garde à celle de sa cible et redistribue le tout équitablement !", + "sharedPower": "{{pokemonName}} additionne sa force à celle de sa cible et redistribue le tout équitablement !", "goingAllOutForAttack": "{{pokemonName}} a pris\ncette capacité au sérieux !", "regainedHealth": "{{pokemonName}}\nrécupère des PV !", "keptGoingAndCrashed": "{{pokemonName}}\ns’écrase au sol !", @@ -18,7 +22,7 @@ "loweredItsHead": "{{pokemonName}}\nbaisse la tête !", "isGlowing": "{{pokemonName}} est entouré\nd’une lumière intense !", "bellChimed": "Un grelot sonne !", - "foresawAnAttack": "{{pokemonName}}\nprévoit une attaque !", + "foresawAnAttack": "{{pokemonName}}\nprévoit une attaque !", "isTighteningFocus": "{{pokemonName}} se concentre\nau maximum !", "hidUnderwater": "{{pokemonName}}\nse cache sous l’eau !", "soothingAromaWaftedThroughArea": "Une odeur apaisante flotte dans l’air !", @@ -30,7 +34,7 @@ "becameCloakedInFreezingAir": "{{pokemonName}} est entouré\nd’un air glacial !", "isChargingPower": "{{pokemonName}}\nconcentre son énergie !", "burnedItselfOut": "Le feu intérieur de {{pokemonName}}\ns’est entièrement consumé !", - "startedHeatingUpBeak": "{{pokemonName}}\nfait chauffer son bec !", + "startedHeatingUpBeak": "{{pokemonName}}\nfait chauffer son bec !", "setUpShellTrap": "{{pokemonName}} déclenche\nle Carapiège !", "isOverflowingWithSpacePower": "La puissance du cosmos afflue dans le corps\nde {{pokemonName}} !", "usedUpAllElectricity": "{{pokemonName}}a utilisé\ntoute son électricité !", @@ -61,5 +65,7 @@ "suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !", "revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !", "swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !", - "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !" -} \ No newline at end of file + "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !", + "safeguard": "{{targetName}} est protégé\npar la capacité Rune Protect !", + "afterYou": "{{pokemonName}} accepte\navec joie !" +} diff --git a/src/locales/fr/pokemon-info-container.json b/src/locales/fr/pokemon-info-container.json index 46b5b80bab0..084310f7c94 100644 --- a/src/locales/fr/pokemon-info-container.json +++ b/src/locales/fr/pokemon-info-container.json @@ -1,7 +1,7 @@ { "moveset": "Capacités", - "gender": "Sexe :", - "ability": "Talent :", - "nature": "Nature :", + "gender": "Sexe :", + "ability": "Talent :", + "nature": "Nature :", "form": "Forme :" -} \ No newline at end of file +} diff --git a/src/locales/fr/pokemon-info.json b/src/locales/fr/pokemon-info.json index 1e55f332432..4e53a31035d 100644 --- a/src/locales/fr/pokemon-info.json +++ b/src/locales/fr/pokemon-info.json @@ -6,17 +6,18 @@ "ATKshortened": "Atq", "DEF": "Défense", "DEFshortened": "Déf", - "SPATK": "Atq. Spé.", + "SPATK": "Atq. Spé.", "SPATKshortened": "AtqSp", - "SPDEF": "Déf. Spé.", + "SPDEF": "Déf. Spé.", "SPDEFshortened": "DéfSp", "SPD": "Vitesse", "SPDshortened": "Vit", "ACC": "Précison", - "EVA": "Esquive" + "EVA": "Esquive", + "HPStat": "PV" }, "Type": { - "UNKNOWN": "Inconnu", + "UNKNOWN": "???", "NORMAL": "Normal", "FIGHTING": "Combat", "FLYING": "Vol", @@ -37,4 +38,4 @@ "FAIRY": "Fée", "STELLAR": "Stellaire" } -} \ No newline at end of file +} diff --git a/src/locales/fr/pokemon-summary.json b/src/locales/fr/pokemon-summary.json index f0b4f5a474f..01e712c8468 100644 --- a/src/locales/fr/pokemon-summary.json +++ b/src/locales/fr/pokemon-summary.json @@ -13,5 +13,32 @@ "metFragment": { "normal": "rencontré au N.{{level}},\n{{biome}}.", "apparently": "apparemment rencontré au N.{{level}},\n{{biome}}." + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" } -} \ No newline at end of file +} diff --git a/src/locales/fr/settings.json b/src/locales/fr/settings.json index 181a593cc99..c752b336b6e 100644 --- a/src/locales/fr/settings.json +++ b/src/locales/fr/settings.json @@ -100,7 +100,7 @@ "moveTouchControls": "Déplacer les contrôles tactiles", "shopOverlayOpacity": "Opacité boutique", "shopCursorTarget": "Choix après relance", - "items": "Obj. gratuits", + "rewards": "Obj. gratuits", "reroll": "Relance", "shop": "Boutique", "checkTeam": "Équipe" diff --git a/src/locales/fr/splash-messages.json b/src/locales/fr/splash-messages.json index 499a32d2cbd..9dd3e86fb32 100644 --- a/src/locales/fr/splash-messages.json +++ b/src/locales/fr/splash-messages.json @@ -1,36 +1,36 @@ { - "battlesWon": "combats gagnés !", - "joinTheDiscord": "Rejoins le Discord !", - "infiniteLevels": "Niveaux infinis !", - "everythingStacks": "Tout se cumule !", - "optionalSaveScumming": "Optional Save Scumming!", - "biomes": "35 biomes !", - "openSource": "Open Source !", - "playWithSpeed": "Joue en vitesse x5 !", - "liveBugTesting": "Tests de bugs en direct !", - "heavyInfluence": "Grosse influence de RoR2 !", - "pokemonRiskAndPokemonRain": "Pokémon Risk et Pokémon Rain !", - "nowWithMoreSalt": "Désormais avec 33% de sel en plus !", - "infiniteFusionAtHome": "Infinite Fusion, chez vous !", - "brokenEggMoves": "Des Capacités Œuf craquées !", - "magnificent": "Magnifique !", - "mubstitute": "Mubstitute !", - "thatsCrazy": "C’est une dinguerie !", - "oranceJuice": "Jus d’orange !", - "questionableBalancing": "Équilibrage douteux !", - "coolShaders": "Cool shaders !", - "aiFree": "Garanti sans IA !", - "suddenDifficultySpikes": "De soudains pics de difficultés !", - "basedOnAnUnfinishedFlashGame": "Basé sur un jeu Flash abandonné !", - "moreAddictiveThanIntended": "Plus addictif que prévu !", - "mostlyConsistentSeeds": "Des seeds à peu près stables !", - "achievementPointsDontDoAnything": "Les Points de Succès servent à rien !", - "youDoNotStartAtLevel": "Ne commence pas au Niveau 2000 !", - "dontTalkAboutTheManaphyEggIncident": "Ne parle pas de l’incident de l’Œuf de Manaphy !", - "alsoTryPokengine": "Essaye aussi Pokéngine !", + "battlesWon": "combats gagnés !", + "joinTheDiscord": "Rejoins le Discord !", + "infiniteLevels": "Niveaux infinis !", + "everythingStacks": "Tout se cumule !", + "optionalSaveScumming": "Optional Save Scumming !", + "biomes": "35 biomes !", + "openSource": "Open Source !", + "playWithSpeed": "Joue en vitesse x5 !", + "liveBugTesting": "Tests de bugs en direct !", + "heavyInfluence": "Grosse influence de RoR2 !", + "pokemonRiskAndPokemonRain": "Pokémon Risk et Pokémon Rain !", + "nowWithMoreSalt": "Désormais avec 33% de sel en plus !", + "infiniteFusionAtHome": "Infinite Fusion, chez vous !", + "brokenEggMoves": "Des Capacités Œuf craquées !", + "magnificent": "Magnifique !", + "mubstitute": "Mubstitute !", + "thatsCrazy": "C’est une dinguerie !", + "oranceJuice": "Jus d’orange !", + "questionableBalancing": "Équilibrage douteux !", + "coolShaders": "Cool shaders !", + "aiFree": "Garanti sans IA !", + "suddenDifficultySpikes": "De soudains pics de difficultés !", + "basedOnAnUnfinishedFlashGame": "Basé sur un jeu Flash abandonné !", + "moreAddictiveThanIntended": "Plus addictif que prévu !", + "mostlyConsistentSeeds": "Des seeds à peu près stables !", + "achievementPointsDontDoAnything": "Les Points de Succès servent à rien !", + "youDoNotStartAtLevel": "Ne commence pas au Niveau 2000 !", + "dontTalkAboutTheManaphyEggIncident": "Ne parle pas de l’incident de l’Œuf de Manaphy !", + "alsoTryPokengine": "Essaye aussi Pokéngine !", "alsoTryEmeraldRogue": "Essaye aussi Emerald Rogue!", - "alsoTryRadicalRed": "Essaye aussi Radical Red !", - "eeveeExpo": "Eevee Expo !", - "ynoproject": "YNOproject !", + "alsoTryRadicalRed": "Essaye aussi Radical Red !", + "eeveeExpo": "Eevee Expo !", + "ynoproject": "YNOproject !", "breedersInSpace": "Des Éleveurs dans l’espace !" -} \ No newline at end of file +} diff --git a/src/locales/fr/status-effect.json b/src/locales/fr/status-effect.json index 4a58f804906..bfb7121f522 100644 --- a/src/locales/fr/status-effect.json +++ b/src/locales/fr/status-effect.json @@ -33,7 +33,7 @@ "obtainSource": "{{pokemonNameWithAffix}} est paralysé\npar {{sourceText}} ! Il aura du mal à attaquer !", "activation": "{{pokemonNameWithAffix}} est paralysé !\nIl n’a pas pu attaquer !", "overlap": "{{pokemonNameWithAffix}} est\ndéjà paralysé.", - "heal": "{{pokemonNameWithAffix}} n’est\nplus paralysé !" + "heal": "{{pokemonNameWithAffix}} n’est\nplus paralysé !" }, "sleep": { "name": "Sommeil", @@ -62,4 +62,4 @@ "overlap": "{{pokemonNameWithAffix}} est\ndéjà brulé.", "heal": "{{pokemonNameWithAffix}} n’est\nplus brulé !" } -} \ No newline at end of file +} diff --git a/src/locales/fr/trainer-classes.json b/src/locales/fr/trainer-classes.json index cebdadf09e9..b7027cf544f 100644 --- a/src/locales/fr/trainer-classes.json +++ b/src/locales/fr/trainer-classes.json @@ -101,8 +101,8 @@ "workers": "Ouvriers", "youngster": "Gamin", "rocket_grunt": "Sbire de la Team Rocket", - "rocket_grunt_female": "Sbire de la Team Rocket", "rocket_grunts": "Sbires de la Team Rocket", + "rocket_grunt_female": "Sbire de la Team Rocket", "magma_grunt": "Sbire de la Team Magma", "magma_grunt_female": "Sbire de la Team Magma", "magma_grunts": "Sbires de la Team Magma", @@ -123,6 +123,7 @@ "aether_grunts": "Employés de la Fondation Æther", "skull_grunt": "Sbire de la Team Skull", "skull_grunt_female": "Sbire de la Team Skull", + "skull_grunts": "Sbires de la Team Skull", "macro_grunt": "Employé de Macro Cosmos", "macro_grunt_female": "Employée de Macro Cosmos", "macro_grunts": "Employés de Macro Cosmos" diff --git a/src/locales/fr/tutorial.json b/src/locales/fr/tutorial.json index 53ec05c7d81..f15a7c7c6d4 100644 --- a/src/locales/fr/tutorial.json +++ b/src/locales/fr/tutorial.json @@ -1,10 +1,10 @@ { - "intro": "Bienvenue dans PokéRogue, un fangame axé sur les combats Pokémon avec des éléments roguelite !\n$Ce jeu n’est pas monétisé et nous ne prétendons pas à la propriété de Pokémon, ni des éléments sous copyright\n$utilisés.\n$Ce jeu est toujours en développement, mais entièrement jouable.\n$Tout signalement de bugs passe par le serveur Discord.\n$Si le jeu est lent, vérifiez que l’Accélération Matérielle est activée dans les paramètres du navigateur.", - "accessMenu": "Accédez au menu avec M ou Échap lors de l’attente d’une\naction.\n$Il contient les paramètres et diverses fonctionnalités", - "menu": "Vous pouvez accéder aux paramètres depuis ce menu.\n$Vous pouvez entre autres y changer la vitesse du jeu ou le style de fenêtre.\n$Il y a également toute une variété d’autres fonctionnalités,\n$jetez-y un œil !", - "starterSelect": "Choisissez vos starters depuis cet écran avec Z ou Espace.\nIls formeront votre équipe de départ.\n$Chacun possède une valeur. Votre équipe peut avoir jusqu’à\n6 membres, tant que vous ne dépassez pas un cout de 10.\n$Vous pouvez aussi choisir le sexe, le talent et la forme en\nfonction des variants déjà capturés ou éclos.\n$Les IVs d’un starter sont les meilleurs de tous ceux de son\nespèce déjà obtenus. Essayez donc d’en obtenir plusieurs !", - "pokerus": "Chaque jour, 3 starters tirés aléatoirement ont un contour\n$violet. Si un starter que vous possédez l’a, essayez de\n$l’ajouter à votre équipe. Vérifiez bien son résumé !", - "statChange": "Les changements de stats restent à travers les combats tant que le Pokémon n’est pas rappelé.\n$Vos Pokémon sont rappelés avant un combat de Dresseur et avant d’entrer dans un nouveau biome.\n$Vous pouvez voir en combat les changements de stats d’un Pokémon en maintenant C ou Maj.\n$Vous pouvez également voir les capacités de l’adversaire en maintenant V.\n$Seules les capacités que le Pokémon a utilisées dans ce combat sont consultables.", - "selectItem": "Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre qu’un.\n$Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents.\n$La plupart des effets des objets non-consommables se cumuleront de diverses manières.\n$Certains objets apparaitront s’ils peuvent être utilisés, comme les objets d’évolution.\n$Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant l’option de transfert.\n$L’option de transfert apparait en bas à droite dès que vous avez obtenu un objet à faire tenir.\n$Vous pouvez acheter des consommables avec de l’argent.\nPlus vous progressez, plus le choix sera varié.\n$Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.", - "eggGacha": "Depuis cet écran, vous pouvez échanger vos coupons\ncontre des Œufs de Pokémon.\n$Les Œufs éclosent après avoir remporté un certain nombre\nde combats. Les plus rares mettent plus de temps.\n$Les Pokémon éclos ne rejoindront pas votre équipe,\nmais seront ajoutés à vos starters.\n$Les Pokémon issus d’Œufs ont généralement de\nmeilleurs IVs que les Pokémon sauvages.\n$Certains Pokémon ne peuvent être obtenus\nque dans des Œufs.\n$Il y a 3 différentes machines à actionner avec différents\nbonus, prenez celle qui vous convient le mieux !" -} \ No newline at end of file + "intro": "Bienvenue dans PokéRogue, un fangame axé sur les combats Pokémon avec des éléments roguelite !\n$Ce jeu n’est pas monétisé et nous ne prétendons à la propriété d’aucun élément sous copyright utilisé.\n$Bien qu’en développement permanent, PokéRogue reste entièrement jouable.\n$Tout signalement de bugs et d’erreurs quelconques passe par le serveur Discord.\n$Si le jeu est lent, vérifiez que l’Accélération Matérielle est activée dans les paramètres du navigateur.", + "accessMenu": "Accédez au menu avec M ou Échap lors de l’attente d’une\naction.\n$Il contient les paramètres et diverses fonctionnalités.", + "menu": "Vous pouvez accéder aux paramètres depuis ce menu.\n$Vous pouvez entre autres y changer la vitesse du jeu ou le style de fenêtre…\n$Mais également des tonnes d’autres fonctionnalités, jetez-y un œil !", + "starterSelect": "Choisissez vos starters depuis cet écran avec Z ou Espace.\nIls formeront votre équipe de départ.\n$Chacun possède une valeur. Votre équipe peut avoir jusqu’à 6 membres, sans dépasser un cout de 10.\n$Vous pouvez aussi choisir le sexe, le talent et la forme en\nfonction des variants déjà capturés ou éclos.\n$Les IV d’un starter sont les meilleurs de tous ceux de son espèce déjà possédés. Obtenez-en plusieurs !", + "pokerus": "Chaque jour, 3 starters tirés aléatoirement ont un contour violet.\n$Si un starter que vous possédez l’a, essayez de l’ajouter à votre équipe. Vérifiez bien son résumé !", + "statChange": "Les changements de stats persistent à travers les combats tant que le Pokémon n’est pas rappelé.\n$Vos Pokémon sont rappelés avant un combat de Dresseur et avant d’entrer dans un nouveau biome.\n$Vous pouvez voir en combat les changements de stats d’un Pokémon en maintenant C ou Maj.\n$Vous pouvez également voir les capacités de l’adversaire en maintenant V.\n$Seules les capacités que le Pokémon a utilisées dans ce combat sont consultables.", + "selectItem": "Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre qu’un.\n$Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents.\n$La plupart des effets des objets non-consommables se cumuleront de diverses manières.\n$Certains objets n’apparaitront que s’ils ont une utilité immédiate, comme les objets d’évolution.\n$Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant l’option de transfert.\n$L’option de transfert apparait en bas à droite dès qu’un Pokémon de l’équipe porte un objet.\n$Vous pouvez acheter des consommables avec de l’argent.\nPlus vous progressez, plus le choix sera large.\n$Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.", + "eggGacha": "Depuis cet écran, vous pouvez utiliser vos coupons\npour recevoir Œufs de Pokémon au hasard.\n$Les Œufs éclosent après avoir remporté un certain nombre de combats. Plus ils sont rares, plus ils mettent de temps.\n$Les Pokémon éclos ne rejoindront pas votre équipe, mais seront ajoutés à vos starters.\n$Les Pokémon issus d’Œufs ont généralement de meilleurs IV que les Pokémon sauvages.\n$Certains Pokémon ne peuvent être obtenus que dans des Œufs.\n$Il y a 3 différentes machines à actionner avec différents\nbonus, prenez celle qui vous convient le mieux !" +} diff --git a/src/locales/fr/voucher.json b/src/locales/fr/voucher.json index 5e26adfcc49..ef6126b4435 100644 --- a/src/locales/fr/voucher.json +++ b/src/locales/fr/voucher.json @@ -1,9 +1,9 @@ { "vouchers": "Coupons", - "eggVoucher": "Coupon Œuf", - "eggVoucherPlus": "Coupon Œuf +", - "eggVoucherPremium": "Coupon Œuf Premium", - "eggVoucherGold": "Coupon Œuf Or", + "eggVoucher": "Coupon Œuf", + "eggVoucherPlus": "Coupon Œuf +", + "eggVoucherPremium": "Coupon Œuf Premium", + "eggVoucherGold": "Coupon Œuf Or", "locked": "Verrouillé", "defeatTrainer": "Vaincre {{trainerName}}" -} \ No newline at end of file +} diff --git a/src/locales/fr/weather.json b/src/locales/fr/weather.json index 7cd8a83fd09..7afe538b064 100644 --- a/src/locales/fr/weather.json +++ b/src/locales/fr/weather.json @@ -1,32 +1,32 @@ { - "sunnyStartMessage": "Les rayons du soleil brillent !", - "sunnyLapseMessage": "Les rayons du soleil brillent fort !", - "sunnyClearMessage": "Les rayons du soleil s’affaiblissent !", - "rainStartMessage": "Il commence à pleuvoir !", - "rainLapseMessage": "La pluie continue de tomber !", - "rainClearMessage": "La pluie s’est arrêtée !", - "sandstormStartMessage": "Une tempête de sable se prépare !", - "sandstormLapseMessage": "La tempête de sable fait rage !", - "sandstormClearMessage": "La tempête de sable se calme !", - "sandstormDamageMessage": "La tempête de sable inflige des dégâts\nà {{pokemonNameWithAffix}} !", - "hailStartMessage": "Il commence à grêler !", - "hailLapseMessage": "La grêle continue de tomber !", - "hailClearMessage": "La grêle s’est arrêtée !", - "hailDamageMessage": "La grêle inflige des dégâts\nà {{pokemonNameWithAffix}} !", - "snowStartMessage": "Il commence à neiger !", - "snowLapseMessage": "Il y a une tempête de neige !", - "snowClearMessage": "La neige s’est arrêtée !", + "sunnyStartMessage": "Les rayons du soleil brillent !", + "sunnyLapseMessage": "Les rayons du soleil brillent fort !", + "sunnyClearMessage": "Les rayons du soleil s’affaiblissent !", + "rainStartMessage": "Il commence à pleuvoir !", + "rainLapseMessage": "La pluie continue de tomber !", + "rainClearMessage": "La pluie s’est arrêtée !", + "sandstormStartMessage": "Une tempête de sable se prépare !", + "sandstormLapseMessage": "La tempête de sable fait rage !", + "sandstormClearMessage": "La tempête de sable se calme !", + "sandstormDamageMessage": "La tempête de sable inflige des dégâts\nà {{pokemonNameWithAffix}} !", + "hailStartMessage": "Il commence à grêler !", + "hailLapseMessage": "La grêle continue de tomber !", + "hailClearMessage": "La grêle s’est arrêtée !", + "hailDamageMessage": "La grêle inflige des dégâts\nà {{pokemonNameWithAffix}} !", + "snowStartMessage": "Il commence à neiger !", + "snowLapseMessage": "Il y a une tempête de neige !", + "snowClearMessage": "La neige s’est arrêtée !", "fogStartMessage": "Le brouillard devient épais…", - "fogLapseMessage": "Le brouillard continue !", - "fogClearMessage": "Le brouillard s’est dissipé !", - "heavyRainStartMessage": "Une pluie battante s’abat soudainement !", + "fogLapseMessage": "Le brouillard continue !", + "fogClearMessage": "Le brouillard s’est dissipé !", + "heavyRainStartMessage": "Une pluie battante s’abat soudainement !", "heavyRainLapseMessage": "La pluie battante continue.", "heavyRainClearMessage": "La pluie battante s’est arrêtée…", - "harshSunStartMessage": "Les rayons du soleil s’intensifient !", - "harshSunLapseMessage": "Les rayons du soleil sont brulants !", - "harshSunClearMessage": "Les rayons du soleil s’affaiblissent !", - "strongWindsStartMessage": "Un vent mystérieux se lève !", - "strongWindsLapseMessage": "Le vent mystérieux souffle violemment !", - "strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit l’attaque !", + "harshSunStartMessage": "Les rayons du soleil s’intensifient !", + "harshSunLapseMessage": "Les rayons du soleil sont brulants !", + "harshSunClearMessage": "Les rayons du soleil s’affaiblissent !", + "strongWindsStartMessage": "Un vent mystérieux se lève !", + "strongWindsLapseMessage": "Le vent mystérieux souffle violemment !", + "strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit l’attaque !", "strongWindsClearMessage": "Le vent mystérieux s’est dissipé…" -} \ No newline at end of file +} diff --git a/src/locales/it/ability-trigger.json b/src/locales/it/ability-trigger.json index 7f55b289c0b..333cb5719d8 100644 --- a/src/locales/it/ability-trigger.json +++ b/src/locales/it/ability-trigger.json @@ -11,6 +11,7 @@ "blockItemTheft": "{{abilityName}} di {{pokemonNameWithAffix}}\nlo rende immune ai furti!", "typeImmunityHeal": "{{pokemonName}} recupera alcuni PS\ncon {{abilityName}}!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evita il colpo\ncon {{abilityName}}!", + "fullHpResistType": "{{pokemonNameWithAffix}} fa risplendere la sua corazza\ne altera i rapporti tra i tipi!", "disguiseAvoidedDamage": "{{pokemonNameWithAffix}} è stato smascherato!", "moveImmunity": "Non ha effetto su {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} ha assorbito la melma!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}} emana un’aura repulsiva!", "postSummonDarkAura": "L’abilità Auratetra di {{pokemonNameWithAffix}} è attiva.", "postSummonFairyAura": "L’abilità Aurafolletto di {{pokemonNameWithAffix}} è attiva.", + "postSummonAuraBreak": "{{pokemonNameWithAffix}} inverte gli effetti di tutte le aure!", "postSummonNeutralizingGas": "Il Gas Reagente di {{pokemonNameWithAffix}}\nsi diffonde tutt’intorno!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} ha due abilità!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} ha due abilità!", @@ -59,4 +61,4 @@ "postSummonTabletsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Amuleto Nefasto di {{pokemonNameWithAffix}}!", "postSummonBeadsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Monile Nefasto di {{pokemonNameWithAffix}}!", "preventBerryUse": "{{pokemonNameWithAffix}} non riesce a\nmangiare le bacche per l'agitazione!" -} \ No newline at end of file +} diff --git a/src/locales/it/achv.json b/src/locales/it/achv.json index 98e41005c46..d1607f6c548 100644 --- a/src/locales/it/achv.json +++ b/src/locales/it/achv.json @@ -80,7 +80,7 @@ "100_RIBBONS": { "name": "Campione Lega Assoluta" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Lavoro di Squadra", "description": "Trasferisci almeno sei bonus statistiche tramite staffetta" }, @@ -261,4 +261,4 @@ "name": "Buona la prima!", "description": "Completa la modalità sfida 'Un nuovo inizio'." } -} \ No newline at end of file +} diff --git a/src/locales/it/arena-flyout.json b/src/locales/it/arena-flyout.json index ac6dd4225cc..31c2a4c0015 100644 --- a/src/locales/it/arena-flyout.json +++ b/src/locales/it/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "Ribaltappeto", "craftyShield": "Truccodifesa", "tailwind": "Ventoincoda", - "happyHour": "Cuccagna" -} \ No newline at end of file + "happyHour": "Cuccagna", + "safeguard": "Salvaguardia" +} diff --git a/src/locales/it/arena-tag.json b/src/locales/it/arena-tag.json index 9e26dfeeb6e..a1c5ee5b3c9 100644 --- a/src/locales/it/arena-tag.json +++ b/src/locales/it/arena-tag.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "safeguardOnAdd": "Un velo mistico ricopre il campo!", + "safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!", + "safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!", + "safeguardOnRemove": "Il campo non è più protetto da Salvaguardia!", + "safeguardOnRemovePlayer": "La tua squadra non è più protetta da Salvaguardia!", + "safeguardOnRemoveEnemy": "La squadra avversaria non è più protetta da Salvaguardia!" +} \ No newline at end of file diff --git a/src/locales/it/battle.json b/src/locales/it/battle.json index f0339f761c7..9b187756025 100644 --- a/src/locales/it/battle.json +++ b/src/locales/it/battle.json @@ -44,6 +44,7 @@ "moveNotImplemented": "{{moveName}} non è ancora implementata e non può essere selezionata.", "moveNoPP": "Non ci sono PP rimanenti\nper questa mossa!", "moveDisabled": "{{moveName}} è disabilitata!", + "disableInterruptedMove": "La mossa {{moveName}} di\n{{pokemonNameWithAffix}} è bloccata!", "noPokeballForce": "Una forza misteriosa\nimpedisce l'uso delle Poké Ball.", "noPokeballTrainer": "Non puoi catturare\nPokémon di altri allenatori!", "noPokeballMulti": "Puoi lanciare una Poké Ball\nsolo quando rimane un singolo Pokémon!", @@ -95,4 +96,4 @@ "congratulations": "Congratulazioni!", "beatModeFirstTime": "{{speciesName}} ha completato la modalità {{gameMode}} per la prima volta!\nHai ricevuto {{newModifier}}!", "ppReduced": "I PP della mossa {{moveName}} di\n{{targetName}} sono stati ridotti di {{reduction}}!" -} \ No newline at end of file +} diff --git a/src/locales/it/battler-tags.json b/src/locales/it/battler-tags.json index a0f852141f9..bd24f380f9e 100644 --- a/src/locales/it/battler-tags.json +++ b/src/locales/it/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}} viene colpito da {{moveName}}!", "cursedOnAdd": "{{pokemonNameWithAffix}} ha sacrificato metà dei suoi PS per\nlanciare una maledizione su {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} subisce la maledizione!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} ha usato Accumulo per la\n{{stockpiledCount}}ª volta!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}} ha usato Accumulo per la\n{{stockpiledCount}}ª volta!", + "disabledOnAdd": "La mossa {{moveName}} di\n{{pokemonNameWithAffix}} è stata bloccata!", + "disabledLapse": "La mossa {{moveName}} di\n{{pokemonNameWithAffix}} non è più bloccata!", + "tarShotOnAdd": "{{pokemonNameWithAffix}} è diventato vulnerabile\nal tipo Fuoco!" +} diff --git a/src/locales/it/fight-ui-handler.json b/src/locales/it/fight-ui-handler.json index 969d8dad370..1c24112a1a7 100644 --- a/src/locales/it/fight-ui-handler.json +++ b/src/locales/it/fight-ui-handler.json @@ -2,6 +2,7 @@ "pp": "PP", "power": "Potenza", "accuracy": "Precisione", - "abilityFlyInText": "{{passive}} {{pokemonName}} {{abilityName}}", - "passive": "Passiva di " -} \ No newline at end of file + "abilityFlyInText": "{{passive}}{{pokemonName}}\n{{abilityName}}", + "passive": "Passiva di ", + "teraHover": "Teracristallizzato {{type}}" +} diff --git a/src/locales/it/menu.json b/src/locales/it/menu.json index 2d37f9db912..5a40ae05087 100644 --- a/src/locales/it/menu.json +++ b/src/locales/it/menu.json @@ -51,5 +51,7 @@ "renamePokemon": "Rinomina un Pokémon", "rename": "Rinomina", "nickname": "Nickname", - "errorServerDown": "Poffarbacco! C'è stato un errore nella comunicazione col server.\n\nPuoi lasciare questa finestra aperta,\nil gioco si riconnetterà automaticamente." -} \ No newline at end of file + "errorServerDown": "Poffarbacco! C'è stato un errore nella comunicazione col server.\n\nPuoi lasciare questa finestra aperta,\nil gioco si riconnetterà automaticamente.", + "noSaves": "Non ci sono file di salvataggio registrati!", + "tooManySaves": "Ci sono troppi file di salvataggio registrati!" +} diff --git a/src/locales/it/modifier-type.json b/src/locales/it/modifier-type.json index f5cee70bbc9..f06755bdfa0 100644 --- a/src/locales/it/modifier-type.json +++ b/src/locales/it/modifier-type.json @@ -47,10 +47,14 @@ "description": "Cambia la natura del Pokémon in {{natureName}} e sblocca la natura nel menu degli starter." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Raddoppia la possibilità di imbattersi in doppie battaglie per {{battleCount}} battaglie." + "description": "Quadruplica la possibilità di imbattersi in doppie battaglie per {{battleCount}} battaglie." }, - "TempBattleStatBoosterModifierType": { - "description": "Aumenta {{tempBattleStatName}} di un livello a tutti i Pokémon nel gruppo per 5 battaglie." + "TempStatStageBoosterModifierType": { + "description": "Aumenta la statistica {{stat}} di {{amount}}\na tutti i Pokémon nel gruppo per 5 battaglie", + "extra": { + "stage": "un livello", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "Aumenta la potenza delle mosse di tipo {{moveType}} del 20% per un Pokémon." @@ -59,10 +63,10 @@ "description": "Aumenta il livello di un Pokémon di {{levels}}." }, "AllPokemonLevelIncrementModifierType": { - "description": "Aumenta i livell di tutti i Pokémon della squadra di {{levels}}." + "description": "Aumenta il livello di tutti i Pokémon della squadra di {{levels}}." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Aumenta {{statName}} di base del possessore del 10%." + "BaseStatBoosterModifierType": { + "description": "Aumenta l'/la {{stat}} di base del possessore del 10%." }, "AllPokemonFullHpRestoreModifierType": { "description": "Restituisce il 100% dei PS a tutti i Pokémon." @@ -248,6 +252,12 @@ "name": "Mirino", "description": "Lente che aumenta la probabilità di sferrare brutti colpi." }, + "DIRE_HIT": { + "name": "Supercolpo", + "extra": { + "raises": "Tasso di brutti colpi" + } + }, "LEEK": { "name": "Porro", "description": "Strumento da dare a Farfetch'd. Lungo gambo di porro che aumenta la probabilità di sferrare brutti colpi." @@ -411,25 +421,13 @@ "description": "Strumento da dare a Ditto. Questa strana polvere, fine e al contempo dura, aumenta la Velocità." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "Attacco X", "x_defense": "Difesa X", "x_sp_atk": "Att. Speciale X", "x_sp_def": "Dif. Speciale X", "x_speed": "Velocità X", - "x_accuracy": "Precisione X", - "dire_hit": "Supercolpo" - }, - "TempBattleStatBoosterStatName": { - "ATK": "Attacco", - "DEF": "Difesa", - "SPATK": "Att. Speciale", - "SPDEF": "Dif. Speciale", - "SPD": "Velocità", - "ACC": "Precisione", - "CRIT": "Tasso di brutti colpi", - "EVA": "Elusione", - "DEFAULT": "???" + "x_accuracy": "Precisione X" }, "AttackTypeBoosterItem": { "silk_scarf": "Sciarpa seta", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "ROM Drago", "DARK_MEMORY": "ROM Buio", "FAIRY_MEMORY": "ROM Folletto", - "BLANK_MEMORY": "ROM Vuota" + "NORMAL_MEMORY": "ROM Normale" } -} \ No newline at end of file +} diff --git a/src/locales/it/modifier.json b/src/locales/it/modifier.json index 397a1f21f9a..c42bf04bc8a 100644 --- a/src/locales/it/modifier.json +++ b/src/locales/it/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", "hitHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} torna in forze\ngrazie al/alla suo/a {{typeName}}!", - "pokemonResetNegativeStatStageApply": "La riduzione alle statistiche di {{pokemonNameWithAffix}}\nviene annullata grazie al/alla suo/a {{typeName}}!", + "resetNegativeStatStageApply": "La riduzione alle statistiche di {{pokemonNameWithAffix}}\nviene annullata grazie al/alla suo/a {{typeName}}!", "moneyInterestApply": "Ricevi un interesse pari a {{moneyAmount}}₽\ngrazie al/allo/a {{typeName}}!", "turnHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato assorbito\ndal {{typeName}} di {{pokemonName}}!", "contactHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato rubato\nda {{pokemonName}} con {{typeName}}!", diff --git a/src/locales/it/move-trigger.json b/src/locales/it/move-trigger.json index e852c2fb52a..c8fb390e53f 100644 --- a/src/locales/it/move-trigger.json +++ b/src/locales/it/move-trigger.json @@ -3,6 +3,10 @@ "cutHpPowerUpMove": "{{pokemonName}} riduce i suoi PS per potenziare la sua mossa!", "absorbedElectricity": "{{pokemonName}} assorbe elettricità!", "switchedStatChanges": "{{pokemonName}} scambia con il bersaglio le modifiche alle statistiche!", + "switchedTwoStatChanges": "{{pokemonName}} scambia con il bersaglio le modifiche a {{firstStat}} e {{secondStat}}!", + "switchedStat": "{{pokemonName}} scambia la sua {{stat}} con quella del bersaglio!", + "sharedGuard": "{{pokemonName}} somma le sue capacità difensive con quelle del bersaglio e le ripartisce equamente!", + "sharedPower": "{{pokemonName}} somma le sue capacità offensive con quelle del bersaglio e le ripartisce equamente!", "goingAllOutForAttack": "{{pokemonName}} fa sul serio!", "regainedHealth": "{{pokemonName}} s'è\nripreso!", "keptGoingAndCrashed": "{{pokemonName}} si sbilancia e\nsi schianta!", @@ -61,5 +65,7 @@ "suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!", "revivalBlessing": "{{pokemonName}} torna in forze!", "swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!", - "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!", + "safeguard": "Salvaguardia protegge {{targetName}}!", + "afterYou": "{{pokemonName}} approfitta della cortesia!" +} diff --git a/src/locales/it/party-ui-handler.json b/src/locales/it/party-ui-handler.json index f5582e5b630..95466779727 100644 --- a/src/locales/it/party-ui-handler.json +++ b/src/locales/it/party-ui-handler.json @@ -1,42 +1,47 @@ { - "SEND_OUT": "Manda in campo", - "SUMMARY": "Sommario", - "CANCEL": "Annulla", - "RELEASE": "Rilascia", - "APPLY": "Applica", - "TEACH": "Insegna", - "SPLICE": "Unisci", - "UNSPLICE": "Dividi", - "ACTIVATE": "Attiva", - "DEACTIVATE": "Disattiva", - "TRANSFER": "Trasferisci", - "ALL": "Tutto", - "PASS_BATON": "Staffetta", - "UNPAUSE_EVOLUTION": "Consenti evoluzione", - "REVIVE": "Revitalizza", - "RENAME": "Rinomina", - "choosePokemon": "Scegli un Pokémon.", - "doWhatWithThisPokemon": "Hai selezionato questo Pokémon.", - "noEnergy": "{{pokemonName}} non ha più energie\nper lottare!", - "hasEnergy": "{{pokemonName}} ha ancora energie\nper lottare!", - "cantBeUsed": "{{pokemonName}} non può essere usato\nin questa sfida!", - "tooManyItems": "{{pokemonName}} possiede già\nquest'oggetto in abbondanza!", - "anyEffect": "Non avrebbe alcun effetto.", - "unpausedEvolutions": "{{pokemonName}} può di nuovo evolversi.", - "unspliceConfirmation": "Vuoi davvero dividere {{fusionName}}\nda {{pokemonName}}? {{fusionName}} andrà perduto.", - "wasReverted": "{{fusionName}} è tornato ad essere {{pokemonName}}.", - "releaseConfirmation": "Vuoi davvero liberare {{pokemonName}}?", - "releaseInBattle": "Non puoi liberare un Pokémon che sta combattendo!", - "selectAMove": "Scegli una mossa.", - "changeQuantity": "Scegli un oggetto da trasferire.\nUsa < e > per cambiarne la quantità.", - "selectAnotherPokemonToSplice": "Scegli un altro Pokémon da unire.", - "cancel": "Annulla", - "goodbye": "Addio, {{pokemonName}}!", - "byebye": "Ciao ciao, {{pokemonName}}!", - "farewell": "Arrivederci, {{pokemonName}}!", - "soLong": "È stato bello, {{pokemonName}}!", - "thisIsWhereWePart": "Le nostre strade si dividono, {{pokemonName}}!", - "illMissYou": "Mi mancherai, {{pokemonName}}!", - "illNeverForgetYou": "Non ti dimenticherò, {{pokemonName}}!", - "untilWeMeetAgain": "Alla prossima, {{pokemonName}}!" - } \ No newline at end of file + "SEND_OUT": "Manda in campo", + "SUMMARY": "Sommario", + "CANCEL": "Annulla", + "RELEASE": "Rilascia", + "APPLY": "Applica", + "TEACH": "Insegna", + "SPLICE": "Unisci", + "UNSPLICE": "Dividi", + "ACTIVATE": "Attiva", + "DEACTIVATE": "Disattiva", + "TRANSFER": "Trasferisci", + "ALL": "Tutto", + "PASS_BATON": "Staffetta", + "UNPAUSE_EVOLUTION": "Consenti evoluzione", + "REVIVE": "Revitalizza", + "RENAME": "Rinomina", + "choosePokemon": "Scegli un Pokémon.", + "doWhatWithThisPokemon": "Hai selezionato questo Pokémon.", + "noEnergy": "{{pokemonName}} non ha più energie\nper lottare!", + "hasEnergy": "{{pokemonName}} ha ancora energie\nper lottare!", + "cantBeUsed": "{{pokemonName}} non può essere usato\nin questa sfida!", + "tooManyItems": "{{pokemonName}} possiede già\nquest'oggetto in abbondanza!", + "anyEffect": "Non avrebbe alcun effetto.", + "unpausedEvolutions": "{{pokemonName}} può di nuovo evolversi.", + "unspliceConfirmation": "Vuoi davvero dividere {{fusionName}}\nda {{pokemonName}}? {{fusionName}} andrà perduto.", + "wasReverted": "{{fusionName}} è tornato ad essere {{pokemonName}}.", + "releaseConfirmation": "Vuoi davvero liberare {{pokemonName}}?", + "releaseInBattle": "Non puoi liberare un Pokémon che sta combattendo!", + "selectAMove": "Scegli una mossa.", + "changeQuantity": "Scegli un oggetto da trasferire.\nUsa < e > per cambiarne la quantità.", + "selectAnotherPokemonToSplice": "Scegli un altro Pokémon da unire.", + "cancel": "Annulla", + "able": "Sì!", + "notAble": "No!", + "learned": "La conosce!", + "goodbye": "Addio, {{pokemonName}}!", + "byebye": "Ciao ciao, {{pokemonName}}!", + "farewell": "Arrivederci, {{pokemonName}}!", + "soLong": "È stato bello, {{pokemonName}}!", + "thisIsWhereWePart": "Le nostre strade si dividono, {{pokemonName}}!", + "illMissYou": "Mi mancherai, {{pokemonName}}!", + "illNeverForgetYou": "Non ti dimenticherò, {{pokemonName}}!", + "untilWeMeetAgain": "Alla prossima, {{pokemonName}}!", + "sayonara": "Sayonara, {{pokemonName}}!", + "smellYaLater": "Ci becchiamo, {{pokemonName}}!" +} diff --git a/src/locales/it/pokemon-info-container.json b/src/locales/it/pokemon-info-container.json index c3cc8d49ce1..f3de9081ebc 100644 --- a/src/locales/it/pokemon-info-container.json +++ b/src/locales/it/pokemon-info-container.json @@ -2,5 +2,6 @@ "moveset": "Set di mosse", "gender": "Genere:", "ability": "Abilità:", - "nature": "Natura:" -} \ No newline at end of file + "nature": "Natura:", + "form": "Forma:" +} diff --git a/src/locales/it/pokemon.json b/src/locales/it/pokemon.json index 9e26dfeeb6e..dcc4cc7f310 100644 --- a/src/locales/it/pokemon.json +++ b/src/locales/it/pokemon.json @@ -1 +1,23 @@ -{} \ No newline at end of file +{ + "type_null": "Tipo Zero", + "great_tusk": "Grandizanne", + "scream_tail": "Codaurlante", + "brute_bonnet": "Fungofurioso", + "flutter_mane": "Crinealato", + "slither_wing": "Alirasenti", + "sandy_shocks": "Peldisabbia", + "iron_treads": "Solcoferreo", + "iron_bundle": "Saccoferreo", + "iron_hands": "Manoferrea", + "iron_jugulis": "Colloferreo", + "iron_moth": "Falenaferrea", + "iron_thorns": "Spineferree", + "roaring_moon": "Lunaruggente", + "iron_valiant": "Eroeferreo", + "walking_wake": "Acquecrespe", + "iron_leaves": "Fogliaferrea", + "gouging_fire": "Vampeaguzze", + "raging_bolt": "Furiatonante", + "iron_boulder": "Massoferreo", + "iron_crown": "Capoferreo" +} diff --git a/src/locales/it/settings.json b/src/locales/it/settings.json index 381503f21bd..c09f5e22d4d 100644 --- a/src/locales/it/settings.json +++ b/src/locales/it/settings.json @@ -8,7 +8,7 @@ "moveTouchControls": "Move Touch Controls", "shopOverlayOpacity": "Opacità Finestra Negozio", "shopCursorTarget": "Target Cursore Negozio", - "items": "Oggetti", + "rewards": "Oggetti", "reroll": "Rerolla", "shop": "Negozio", "checkTeam": "Squadra" diff --git a/src/locales/ja/ability-trigger.json b/src/locales/ja/ability-trigger.json index f9d1cc60e4c..26d27701aef 100644 --- a/src/locales/ja/ability-trigger.json +++ b/src/locales/ja/ability-trigger.json @@ -2,17 +2,17 @@ "blockRecoilDamage": "{{pokemonName}}は {{abilityName}}で 反動ダメージを 受けない!", "badDreams": "{{pokemonName}}は ナイトメアに うなされている!", "costar": "{{pokemonName}}は {{allyName}}の\n能力変化を コピーした!", - "iceFaceAvoidedDamage": "{{pokemonName}}は\n{{abilityName}}で ダメージを 受けない!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}は\n{{abilityName}}で ダメージを 受けない!", "perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターン後に ほろびいてしまう!", "poisonHeal": "{{pokemonName}}は {{abilityName}}で 回復した!", "trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!", - "windPowerCharged": "{{pokemonName}}は\n{{moveName}}を 受けて じゅうでんした!", + "windPowerCharged": "{{pokemonNameWithAffix}}は\n{{moveName}}を 受けて じゅうでんした!", "quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった!", - "disguiseAvoidedDamage": "{{pokemonNameWithAffix}}'s disguise was busted!", + "disguiseAvoidedDamage": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!", "blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!", "typeImmunityHeal": "{{pokemonNameWithAffix}}は {{abilityName}}で\n体力を 回復した!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}は {{abilityName}}で\nダメージを 受けない。", - "postDefendDisguise": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!", + "fullHpResistType": "{{pokemonNameWithAffix}}は\n甲羅を かがやかせ タイプ相性を 歪める!", "moveImmunity": "{{pokemonNameWithAffix}}には\n効果が ないようだ…", "reverseDrain": "{{pokemonNameWithAffix}}は\nヘドロえきを 吸い取った!", "postDefendTypeChange": "{{pokemonNameWithAffix}}は {{abilityName}}で\n{{typeName}}タイプに なった!", @@ -52,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}}は\n弾(はじ)ける オーラを 放っている!", "postSummonDarkAura": "{{pokemonNameWithAffix}}は\nダークオーラを 放っている!", "postSummonFairyAura": "{{pokemonNameWithAffix}}は\nフェアリーオーラを 放っている!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}}は\nすべての オーラを 制圧する!", "postSummonNeutralizingGas": "あたりに かがくへんかガスが 充満した!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}}は\nふたつの 特性を あわせ持つ!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}}は\nふたつの 特性を あわせ持つ!", @@ -60,4 +61,4 @@ "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}の わざわいのおふだ\nまわりの {{statName}}が 弱まった!", "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}の わざわいのたまで\nまわりの {{statName}}が 弱まった!", "preventBerryUse": "{{pokemonNameWithAffix}}は 緊張して\nきのみが 食べられなくなった!" -} \ No newline at end of file +} diff --git a/src/locales/ja/ability.json b/src/locales/ja/ability.json index bfbf5d3a3c8..c44eeb06234 100644 --- a/src/locales/ja/ability.json +++ b/src/locales/ja/ability.json @@ -1,1071 +1,1071 @@ { "stench": { "name": "あくしゅう", - "description": "臭い においを 放つことによって 攻撃した ときに 相手を ひるませることが ある。" + "description": "臭い においを 放つことによって 攻撃した ときに 相手を ひるませることが ある。" }, "drizzle": { "name": "あめふらし", - "description": "登場 したときに 天気を 雨に する。" + "description": "登場 したときに 天気を 雨に する。" }, "speedBoost": { "name": "かそく", - "description": "毎ターン 素早さが 上がる。" + "description": "毎ターン 素早さが 上がる。" }, "battleArmor": { "name": "カブトアーマー", - "description": "硬い 甲羅に 守られて 相手の 攻撃が 急所に 当たらない。" + "description": "硬い 甲羅に 守られて 相手の 攻撃が 急所に 当たらない。" }, "sturdy": { "name": "がんじょう", - "description": "相手の 技を 受けても 一撃で 倒されることが ない。 一撃必殺技も 効かない。" + "description": "相手の 技を 受けても 一撃で 倒されることが ない。 一撃必殺技も 効かない。" }, "damp": { "name": "しめりけ", - "description": "あたりを 湿らせることに よって じばく などの 爆発する 技を だれも 使えなくなる。" + "description": "あたりを 湿らせることに よって じばく などの 爆発する 技を だれも 使えなくなる。" }, "limber": { "name": "じゅうなん", - "description": "柔軟な 体によって まひ状態に ならない。" + "description": "柔軟な 体によって まひ状態に ならない。" }, "sandVeil": { "name": "すながくれ", - "description": "砂あらしの とき 回避率が 上がる。" + "description": "砂あらしの とき 回避率が 上がる。" }, "static": { "name": "せいでんき", - "description": "静電気を 体に まとい 触った 相手を まひさせる ことがある。" + "description": "静電気を 体に まとい 触った 相手を まひさせる ことがある。" }, "voltAbsorb": { "name": "ちくでん", - "description": "でんきタイプの 技を 受けると ダメージを 受けずに 回復する。" + "description": "でんきタイプの 技を 受けると ダメージを 受けずに 回復する。" }, "waterAbsorb": { "name": "ちょすい", - "description": "みずタイプの 技を 受けると ダメージを 受けずに 回復する。" + "description": "みずタイプの 技を 受けると ダメージを 受けずに 回復する。" }, "oblivious": { "name": "どんかん", - "description": "鈍感なので メロメロや ちょうはつ状態に ならない。" + "description": "鈍感なので メロメロや ちょうはつ状態に ならない。" }, "cloudNine": { "name": "ノーてんき", - "description": "あらゆる 天気の 影響が なくなって しまう。" + "description": "あらゆる 天気の 影響が なくなって しまう。" }, "compoundEyes": { "name": "ふくがん", - "description": "複眼を 持っているため 技の 命中率が 上がる。" + "description": "複眼を 持っているため 技の 命中率が 上がる。" }, "insomnia": { "name": "ふみん", - "description": "眠れない 体質 なので ねむり状態に ならない。" + "description": "眠れない 体質 なので ねむり状態に ならない。" }, "colorChange": { "name": "へんしょく", - "description": "相手から 受けた 技の タイプに 自分の タイプが 変化 する。" + "description": "相手から 受けた 技の タイプに 自分の タイプが 変化 する。" }, "immunity": { "name": "めんえき", - "description": "体内に 免疫を 持っているため どく状態に ならない。" + "description": "体内に 免疫を 持っているため どく状態に ならない。" }, "flashFire": { "name": "もらいび", - "description": "ほのおタイプの 技を 受けると 炎を もらい 自分が 出す ほのおタイプの 技が 強くなる。" + "description": "ほのおタイプの 技を 受けると 炎を もらい 自分が 出す ほのおタイプの 技が 強くなる。" }, "shieldDust": { "name": "りんぷん", - "description": "りんぷんに 守られて 技の 追加効果を 受けなくなる。" + "description": "りんぷんに 守られて 技の 追加効果を 受けなくなる。" }, "ownTempo": { "name": "マイペース", - "description": "マイペースなので こんらん状態に ならない。" + "description": "マイペースなので こんらん状態に ならない。" }, "suctionCups": { "name": "きゅうばん", - "description": "吸盤で 地面に 張り付き ポケモンを 入れ替えさせる 技や 道具が 効かなくなる。" + "description": "吸盤で 地面に 張り付き ポケモンを 入れ替えさせる 技や 道具が 効かなくなる。" }, "intimidate": { "name": "いかく", - "description": "登場 したとき 威嚇して 相手を 萎縮させ 相手の 攻撃を 下げて しまう。" + "description": "登場 したとき 威嚇して 相手を 萎縮させ 相手の 攻撃を 下げて しまう。" }, "shadowTag": { "name": "かげふみ", - "description": "相手の 影を 踏み 逃げたり 交代 できなくする。" + "description": "相手の 影を 踏み 逃げたり 交代 できなくする。" }, "roughSkin": { "name": "さめはだ", - "description": "攻撃を 受けたとき 自分に 触れた 相手を ざらざらの 肌で キズつける。" + "description": "攻撃を 受けたとき 自分に 触れた 相手を ざらざらの 肌で キズつける。" }, "wonderGuard": { "name": "ふしぎなまもり", - "description": "効果バツグンの 技しか 当たらない 不思議な 力。" + "description": "効果バツグンの 技しか 当たらない 不思議な 力。" }, "levitate": { "name": "ふゆう", - "description": "地面から 浮くことによって じめんタイプの 技を 受けない。" + "description": "地面から 浮くことによって じめんタイプの 技を 受けない。" }, "effectSpore": { "name": "ほうし", - "description": "攻撃で 自分に 触れた 相手を どくや まひや ねむり状態に する ことがある。" + "description": "攻撃で 自分に 触れた 相手を どくや まひや ねむり状態に する ことがある。" }, "synchronize": { "name": "シンクロ", - "description": "自分が なってしまった どくや まひや やけどを 相手に うつす。" + "description": "自分が なってしまった どくや まひや やけどを 相手に うつす。" }, "clearBody": { "name": "クリアボディ", - "description": "相手の 技や 特性で 能力を 下げられない。" + "description": "相手の 技や 特性で 能力を 下げられない。" }, "naturalCure": { "name": "しぜんかいふく", - "description": "手持ちに ひっこむと 状態異常が 治る。" + "description": "手持ちに ひっこむと 状態異常が 治る。" }, "lightningRod": { "name": "ひらいしん", - "description": "でんきタイプの 技を 自分に 寄せつけ ダメージを 受けずに 特攻が 上がる。" + "description": "でんきタイプの 技を 自分に 寄せつけ ダメージを 受けずに 特攻が 上がる。" }, "sereneGrace": { "name": "てんのめぐみ", - "description": "天の恵みの おかげで 技の 追加効果が でやすい。" + "description": "天の恵みの おかげで 技の 追加効果が でやすい。" }, "swiftSwim": { "name": "すいすい", - "description": "天気が 雨のとき 素早さが 上がる。" + "description": "天気が 雨のとき 素早さが 上がる。" }, "chlorophyll": { "name": "ようりょくそ", - "description": "天気が 晴れのとき 素早さが 上がる。" + "description": "天気が 晴れのとき 素早さが 上がる。" }, "illuminate": { "name": "はっこう", - "description": "あたりを 明るくすることで 命中率を 下げられない。" + "description": "あたりを 明るくすることで 命中率を 下げられない。" }, "trace": { "name": "トレース", - "description": "登場 したとき 相手の 特性を トレースして 同じ 特性に なる。" + "description": "登場 したとき 相手の 特性を トレースして 同じ 特性に なる。" }, "hugePower": { "name": "ちからもち", - "description": "物理攻撃の 威力が 2倍になる。" + "description": "物理攻撃の 威力が 2倍になる。" }, "poisonPoint": { "name": "どくのトゲ", - "description": "自分に 触った 相手を どく状態に することがある。" + "description": "自分に 触った 相手を どく状態に することがある。" }, "innerFocus": { "name": "せいしんりょく", - "description": "鍛えられた 精神に よって 相手の 攻撃に ひるまない。" + "description": "鍛えられた 精神に よって 相手の 攻撃に ひるまない。" }, "magmaArmor": { "name": "マグマのよろい", - "description": "熱い マグマを 身にまとい こおり状態に ならない。" + "description": "熱い マグマを 身にまとい こおり状態に ならない。" }, "waterVeil": { "name": "みずのベール", - "description": "水のベールを 身にまとい やけど状態に ならない。" + "description": "水のベールを 身にまとい やけど状態に ならない。" }, "magnetPull": { "name": "じりょく", - "description": "はがねタイプの ポケモンを 磁力で 引きつけて 逃げられなくする。" + "description": "はがねタイプの ポケモンを 磁力で 引きつけて 逃げられなくする。" }, "soundproof": { "name": "ぼうおん", - "description": "音を 遮断 することに よって 音の 攻撃を 受けない。" + "description": "音を 遮断 することに よって 音の 攻撃を 受けない。" }, "rainDish": { "name": "あめうけざら", - "description": "天気が 雨のとき 少しずつ HPを 回復する。" + "description": "天気が 雨のとき 少しずつ HPを 回復する。" }, "sandStream": { "name": "すなおこし", - "description": "登場 したとき 天気を 砂あらしにする。" + "description": "登場 したとき 天気を 砂あらしにする。" }, "pressure": { "name": "プレッシャー", - "description": "プレッシャーを あたえて 相手の 使う 技の PPを 多く 減らす。" + "description": "プレッシャーを あたえて 相手の 使う 技の PPを 多く 減らす。" }, "thickFat": { "name": "あついしぼう", - "description": "厚い 脂肪で 守られているので ほのおタイプと こおりタイプの 技の ダメージを 半減させる。" + "description": "厚い 脂肪で 守られているので ほのおタイプと こおりタイプの 技の ダメージを 半減させる。" }, "earlyBird": { "name": "はやおき", - "description": "ねむり状態に なっても 2倍の 早さで 目覚める ことが できる。" + "description": "ねむり状態に なっても 2倍の 早さで 目覚める ことが できる。" }, "flameBody": { "name": "ほのおのからだ", - "description": "自分に 触った 相手を やけど状態に する ことがある。" + "description": "自分に 触った 相手を やけど状態に する ことがある。" }, "runAway": { "name": "にげあし", - "description": "野生の ポケモンから 必ず 逃げられる。" + "description": "野生の ポケモンから 必ず 逃げられる。" }, "keenEye": { "name": "するどいめ", - "description": "鋭い 目の おかげで 命中率を 下げられない。" + "description": "鋭い 目の おかげで 命中率を 下げられない。" }, "hyperCutter": { "name": "かいりきバサミ", - "description": "力自慢の ハサミを 持っているので 相手に 攻撃を 下げられない。" + "description": "力自慢の ハサミを 持っているので 相手に 攻撃を 下げられない。" }, "pickup": { "name": "ものひろい", - "description": "戦闘が 終わったとき 相手の 持った 道具を 一つ 拾ってくることが ある。" + "description": "戦闘が 終わったとき 相手の 持った 道具を 一つ 拾ってくることが ある。" }, "truant": { "name": "なまけ", - "description": "技を 出すと 次の ターンは 休んでしまう。" + "description": "技を 出すと 次の ターンは 休んでしまう。" }, "hustle": { "name": "はりきり", - "description": "自分の 攻撃が 高くなるが 命中率が 下がる。" + "description": "自分の 攻撃が 高くなるが 命中率が 下がる。" }, "cuteCharm": { "name": "メロメロボディ", - "description": "自分に 触った 相手を メロメロに することが ある。" + "description": "自分に 触った 相手を メロメロに することが ある。" }, "plus": { "name": "プラス", - "description": "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。" + "description": "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。" }, "minus": { "name": "マイナス", - "description": "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。" + "description": "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。" }, "forecast": { "name": "てんきや", - "description": "天気の 影響を 受けて みずタイプ ほのおタイプ こおりタイプの どれかに 変化する。" + "description": "天気の 影響を 受けて みずタイプ ほのおタイプ こおりタイプの どれかに 変化する。" }, "stickyHold": { "name": "ねんちゃく", - "description": "粘着質の 体に 道具が くっついているため 相手に 道具を 奪われない。" + "description": "粘着質の 体に 道具が くっついているため 相手に 道具を 奪われない。" }, "shedSkin": { "name": "だっぴ", - "description": "体の 皮を 脱ぎ捨てることで 状態異常を 治すことが ある。" + "description": "体の 皮を 脱ぎ捨てることで 状態異常を 治すことが ある。" }, "guts": { "name": "こんじょう", - "description": "状態異常に なると 根性を だして 攻撃が 上がる。" + "description": "状態異常に なると 根性を だして 攻撃が 上がる。" }, "marvelScale": { "name": "ふしぎなうろこ", - "description": "状態異常に なると 不思議なウロコが 反応して 防御が 上がる。" + "description": "状態異常に なると 不思議なウロコが 反応して 防御が 上がる。" }, "liquidOoze": { "name": "ヘドロえき", - "description": "ヘドロ液を 吸い取った 相手は 強烈な 悪臭で ダメージを 受けて HPを 減らす。" + "description": "ヘドロ液を 吸い取った 相手は 強烈な 悪臭で ダメージを 受けて HPを 減らす。" }, "overgrow": { "name": "しんりょく", - "description": "HPが 減ったとき くさタイプの 技の 威力が 上がる。" + "description": "HPが 減ったとき くさタイプの 技の 威力が 上がる。" }, "blaze": { "name": "もうか", - "description": "HPが 減ったとき ほのおタイプの 技の 威力が 上がる。" + "description": "HPが 減ったとき ほのおタイプの 技の 威力が 上がる。" }, "torrent": { "name": "げきりゅう", - "description": "HPが 減ったとき みずタイプの 技の 威力が 上がる。" + "description": "HPが 減ったとき みずタイプの 技の 威力が 上がる。" }, "swarm": { "name": "むしのしらせ", - "description": "HPが 減ったとき むしタイプの 技の 威力が 上がる。" + "description": "HPが 減ったとき むしタイプの 技の 威力が 上がる。" }, "rockHead": { "name": "いしあたま", - "description": "反動を 受ける 技を 出しても HPが 減らない。" + "description": "反動を 受ける 技を 出しても HPが 減らない。" }, "drought": { "name": "ひでり", - "description": "登場 したときに 天気を 晴れに する。" + "description": "登場 したときに 天気を 晴れに する。" }, "arenaTrap": { "name": "ありじごく", - "description": "戦闘で 相手を 逃げられなくする。" + "description": "戦闘で 相手を 逃げられなくする。" }, "vitalSpirit": { "name": "やるき", - "description": "やる気を だすことに よって ねむり状態に ならない。" + "description": "やる気を だすことに よって ねむり状態に ならない。" }, "whiteSmoke": { "name": "しろいけむり", - "description": "白い煙に 守られて 相手に 能力を 下げられない。" + "description": "白い煙に 守られて 相手に 能力を 下げられない。" }, "purePower": { "name": "ヨガパワー", - "description": "ヨガの 力で 物理攻撃の 威力が 2倍に なる。" + "description": "ヨガの 力で 物理攻撃の 威力が 2倍に なる。" }, "shellArmor": { "name": "シェルアーマー", - "description": "硬い 殻に 守られ 相手の 攻撃が 急所に 当たらない。" + "description": "硬い 殻に 守られ 相手の 攻撃が 急所に 当たらない。" }, "airLock": { "name": "エアロック", - "description": "あらゆる 天気の 影響が なくなって しまう。" + "description": "あらゆる 天気の 影響が なくなって しまう。" }, "tangledFeet": { "name": "ちどりあし", - "description": "こんらん状態の ときは 回避率が アップする。" + "description": "こんらん状態の ときは 回避率が アップする。" }, "motorDrive": { "name": "でんきエンジン", - "description": "でんきタイプの 技を 受けると ダメージを 受けずに 素早さが 上がる。" + "description": "でんきタイプの 技を 受けると ダメージを 受けずに 素早さが 上がる。" }, "rivalry": { "name": "とうそうしん", - "description": "性別が 同じだと 闘争心を 燃やして 強くなる。 性別が 違うと 弱くなる。" + "description": "性別が 同じだと 闘争心を 燃やして 強くなる。 性別が 違うと 弱くなる。" }, "steadfast": { "name": "ふくつのこころ", - "description": "ひるむ たびに 不屈の心を 燃やして 素早さが 上がる。" + "description": "ひるむ たびに 不屈の心を 燃やして 素早さが 上がる。" }, "snowCloak": { "name": "ゆきがくれ", - "description": "天気が ゆきのとき 回避率が 上がる。" + "description": "天気が ゆきのとき 回避率が 上がる。" }, "gluttony": { "name": "くいしんぼう", - "description": "HPが 少なくなったら 食べる きのみを HP 半分の 時に 食べてしまう。" + "description": "HPが 少なくなったら 食べる きのみを HP 半分の 時に 食べてしまう。" }, "angerPoint": { "name": "いかりのつぼ", - "description": "急所に 攻撃が 当たると 怒りくるって 攻撃力が 最大に なる。" + "description": "急所に 攻撃が 当たると 怒りくるって 攻撃力が 最大に なる。" }, "unburden": { "name": "かるわざ", - "description": "持っていた 道具が なくなると 素早さが 上がる。" + "description": "持っていた 道具が なくなると 素早さが 上がる。" }, "heatproof": { "name": "たいねつ", - "description": "耐熱の 体に よって ほのおタイプの 技の 威力を 半減させる。" + "description": "耐熱の 体に よって ほのおタイプの 技の 威力を 半減させる。" }, "simple": { "name": "たんじゅん", - "description": "能力 変化が いつもの 2倍に なる。" + "description": "能力 変化が いつもの 2倍に なる。" }, "drySkin": { "name": "かんそうはだ", - "description": "天気が 雨の時や みずタイプの 技で HPが 回復し はれの時や ほのおタイプの 技で 減ってしまう。" + "description": "天気が 雨の時や みずタイプの 技で HPが 回復し はれの時や ほのおタイプの 技で 減ってしまう。" }, "download": { "name": "ダウンロード", - "description": "相手の 防御と 特防を くらべて 低い ほうの 能力に あわせて 自分の 攻撃か 特攻を 上げる。" + "description": "相手の 防御と 特防を くらべて 低い ほうの 能力に あわせて 自分の 攻撃か 特攻を 上げる。" }, "ironFist": { "name": "てつのこぶし", - "description": "パンチを 使う 技の 威力が 上がる。" + "description": "パンチを 使う 技の 威力が 上がる。" }, "poisonHeal": { "name": "ポイズンヒール", - "description": "どく状態に なると HPが 減らずに 増えていく。" + "description": "どく状態に なると HPが 減らずに 増えていく。" }, "adaptability": { "name": "てきおうりょく", - "description": "自分と おなじ タイプの 技の 威力が 上がる。" + "description": "自分と おなじ タイプの 技の 威力が 上がる。" }, "skillLink": { "name": "スキルリンク", - "description": "連続技を 使うと いつも 最高回数 出すことが できる。" + "description": "連続技を 使うと いつも 最高回数 出すことが できる。" }, "hydration": { "name": "うるおいボディ", - "description": "天気が 雨のとき 状態異常が 治る。" + "description": "天気が 雨のとき 状態異常が 治る。" }, "solarPower": { "name": "サンパワー", - "description": "天気が 晴れると 特攻が 上がるが 毎ターン HPが 減る。" + "description": "天気が 晴れると 特攻が 上がるが 毎ターン HPが 減る。" }, "quickFeet": { "name": "はやあし", - "description": "状態異常に なると 素早さが 上がる。" + "description": "状態異常に なると 素早さが 上がる。" }, "normalize": { "name": "ノーマルスキン", - "description": "どんな タイプの 技でも すべて ノーマルタイプに なる。 威力が 少し 上がる。" + "description": "どんな タイプの 技でも すべて ノーマルタイプに なる。 威力が 少し 上がる。" }, "sniper": { "name": "スナイパー", - "description": "攻撃を 急所に 当てると 威力が さらに 上がる。" + "description": "攻撃を 急所に 当てると 威力が さらに 上がる。" }, "magicGuard": { "name": "マジックガード", - "description": "攻撃 以外では ダメージを 受けない。" + "description": "攻撃 以外では ダメージを 受けない。" }, "noGuard": { "name": "ノーガード", - "description": "ノーガード戦法に よって お互いの 出す 技が かならず 当たる ようになる。" + "description": "ノーガード戦法に よって お互いの 出す 技が かならず 当たる ようになる。" }, "stall": { "name": "あとだし", - "description": "技を 出す 順番が かならず 最後に なる。" + "description": "技を 出す 順番が かならず 最後に なる。" }, "technician": { "name": "テクニシャン", - "description": "威力が 低い 技の 威力を 高くして 攻撃できる。" + "description": "威力が 低い 技の 威力を 高くして 攻撃できる。" }, "leafGuard": { "name": "リーフガード", - "description": "天気が 晴れのときは 状態異常に ならない。" + "description": "天気が 晴れのときは 状態異常に ならない。" }, "klutz": { "name": "ぶきよう", - "description": "持っている 道具を 使うことが できない。" + "description": "持っている 道具を 使うことが できない。" }, "moldBreaker": { "name": "かたやぶり", - "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" + "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" }, "superLuck": { "name": "きょううん", - "description": "強運を 持っているため 相手の 急所に 攻撃が 当たりやすい。" + "description": "強運を 持っているため 相手の 急所に 攻撃が 当たりやすい。" }, "aftermath": { "name": "ゆうばく", - "description": "ひんしに なったとき 触った 相手に ダメージを あたえる。" + "description": "ひんしに なったとき 触った 相手に ダメージを あたえる。" }, "anticipation": { "name": "きけんよち", - "description": "相手の 持つ 危険な 技を 察知する ことができる。" + "description": "相手の 持つ 危険な 技を 察知する ことができる。" }, "forewarn": { "name": "よちむ", - "description": "登場 したとき 相手の 持つ 技を ひとつだけ 読み取る。" + "description": "登場 したとき 相手の 持つ 技を ひとつだけ 読み取る。" }, "unaware": { "name": "てんねん", - "description": "相手の 能力の 変化を 無視して 攻撃が できる。" + "description": "相手の 能力の 変化を 無視して 攻撃が できる。" }, "tintedLens": { "name": "いろめがね", - "description": "効果が いまひとつの 技を 通常の 威力で 出すことが できる。" + "description": "効果が いまひとつの 技を 通常の 威力で 出すことが できる。" }, "filter": { "name": "フィルター", - "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" + "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" }, "slowStart": { "name": "スロースタート", - "description": "5ターンの あいだ 攻撃と 素早さが 半分に なる。" + "description": "5ターンの あいだ 攻撃と 素早さが 半分に なる。" }, "scrappy": { "name": "きもったま", - "description": "ゴーストタイプの ポケモンに ノーマルタイプと かくとうタイプの 技を 当てることが できる。" + "description": "ゴーストタイプの ポケモンに ノーマルタイプと かくとうタイプの 技を 当てることが できる。" }, "stormDrain": { "name": "よびみず", - "description": "みずタイプの 技を 自分に よせつけ ダメージは 受けずに 特攻が 上がる。" + "description": "みずタイプの 技を 自分に よせつけ ダメージは 受けずに 特攻が 上がる。" }, "iceBody": { "name": "アイスボディ", - "description": "天気が ゆきのとき HPを 少しずつ 回復 する。" + "description": "天気が ゆきのとき HPを 少しずつ 回復 する。" }, "solidRock": { "name": "ハードロック", - "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" + "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" }, "snowWarning": { "name": "ゆきふらし", - "description": "登場 したときに 天気を ゆきに する。" + "description": "登場 したときに 天気を ゆきに する。" }, "honeyGather": { "name": "みつあつめ", - "description": "戦闘が 終わったとき あまいミツを 拾う。そのあまいミツが 売られて お金を もらう。" + "description": "戦闘が 終わったとき あまいミツを 拾う。そのあまいミツが 売られて お金を もらう。" }, "frisk": { "name": "おみとおし", - "description": "登場 したとき 相手の 持ち物を 見通すことが できる。" + "description": "登場 したとき 相手の 持ち物を 見通すことが できる。" }, "reckless": { "name": "すてみ", - "description": "反動で ダメージを 受ける 技の 威力が 上がる。" + "description": "反動で ダメージを 受ける 技の 威力が 上がる。" }, "multitype": { "name": "マルチタイプ", - "description": "持っている プレートや Zクリスタルの タイプによって 自分の タイプが 変わる。" + "description": "持っている プレートや Zクリスタルの タイプによって 自分の タイプが 変わる。" }, "flowerGift": { "name": "フラワーギフト", - "description": "天気が 晴れのとき 自分と 味方の 攻撃と 特防の 能力が 上がる。" + "description": "天気が 晴れのとき 自分と 味方の 攻撃と 特防の 能力が 上がる。" }, "badDreams": { "name": "ナイトメア", - "description": "ねむり状態の 相手に ダメージを あたえる。" + "description": "ねむり状態の 相手に ダメージを あたえる。" }, "pickpocket": { "name": "わるいてぐせ", - "description": "触られた 相手の 道具を 盗んで しまう。" + "description": "触られた 相手の 道具を 盗んで しまう。" }, "sheerForce": { "name": "ちからずく", - "description": "技の 追加効果は なくなるが そのぶん 高い 威力で 技を 出すことが できる。" + "description": "技の 追加効果は なくなるが そのぶん 高い 威力で 技を 出すことが できる。" }, "contrary": { "name": "あまのじゃく", - "description": "能力の 変化が 逆転して 上がるときに 下がり 下がるときに 上がる。" + "description": "能力の 変化が 逆転して 上がるときに 下がり 下がるときに 上がる。" }, "unnerve": { "name": "きんちょうかん", - "description": "相手を 緊張させて きのみを 食べられなく させる。" + "description": "相手を 緊張させて きのみを 食べられなく させる。" }, "defiant": { "name": "まけんき", - "description": "能力を 下げられると 攻撃が ぐーんと 上がる。" + "description": "能力を 下げられると 攻撃が ぐーんと 上がる。" }, "defeatist": { "name": "よわき", - "description": "HPが 半分に なると 弱気に なって 攻撃と 特攻が 半減する。" + "description": "HPが 半分に なると 弱気に なって 攻撃と 特攻が 半減する。" }, "cursedBody": { "name": "のろわれボディ", - "description": "攻撃を 受けると 相手の 技を かなしばり状態に することが ある。" + "description": "攻撃を 受けると 相手の 技を かなしばり状態に することが ある。" }, "healer": { "name": "いやしのこころ", - "description": "状態異常の 味方を たまに 治してあげる。" + "description": "状態異常の 味方を たまに 治してあげる。" }, "friendGuard": { "name": "フレンドガード", - "description": "味方の ダメージを 減らすことが できる。" + "description": "味方の ダメージを 減らすことが できる。" }, "weakArmor": { "name": "くだけるよろい", - "description": "物理技で ダメージを 受けると 防御が 下がり 素早さが ぐーんと 上がる。" + "description": "物理技で ダメージを 受けると 防御が 下がり 素早さが ぐーんと 上がる。" }, "heavyMetal": { "name": "ヘヴィメタル", - "description": "自分の 重さが 2倍に なる。" + "description": "自分の 重さが 2倍に なる。" }, "lightMetal": { "name": "ライトメタル", - "description": "自分の 重さが 半分に なる。" + "description": "自分の 重さが 半分に なる。" }, "multiscale": { "name": "マルチスケイル", - "description": "HPが 満タンの ときに 受ける ダメージが 少なくなる。" + "description": "HPが 満タンの ときに 受ける ダメージが 少なくなる。" }, "toxicBoost": { "name": "どくぼうそう", - "description": "どく状態に なったとき 物理技の 威力が 上がる。" + "description": "どく状態に なったとき 物理技の 威力が 上がる。" }, "flareBoost": { "name": "ねつぼうそう", - "description": "やけど状態に なったとき 特殊技の 威力が 上がる。" + "description": "やけど状態に なったとき 特殊技の 威力が 上がる。" }, "harvest": { "name": "しゅうかく", - "description": "使った きのみを 何回も 作りだす。" + "description": "使った きのみを 何回も 作りだす。" }, "telepathy": { "name": "テレパシー", - "description": "味方の 攻撃を 読み取って 技を 回避する。" + "description": "味方の 攻撃を 読み取って 技を 回避する。" }, "moody": { "name": "ムラっけ", - "description": "毎ターン 能力の どれかが ぐーんと 上がって どれかが 下がる。" + "description": "毎ターン 能力の どれかが ぐーんと 上がって どれかが 下がる。" }, "overcoat": { "name": "ぼうじん", - "description": "すなあらしや あられなどの ダメージを 受けない。 粉の 技を 受けない。" + "description": "すなあらしや あられなどの ダメージを 受けない。 粉の 技を 受けない。" }, "poisonTouch": { "name": "どくしゅ", - "description": "触る だけで 相手を どく 状態に することがある。" + "description": "触る だけで 相手を どく 状態に することがある。" }, "regenerator": { "name": "さいせいりょく", - "description": "手持ちに 引っ込むと HPが 少し 回復する。" + "description": "手持ちに 引っ込むと HPが 少し 回復する。" }, "bigPecks": { "name": "はとむね", - "description": "防御を 下げる 効果を 受けない。" + "description": "防御を 下げる 効果を 受けない。" }, "sandRush": { "name": "すなかき", - "description": "天気が すなあらし のとき 素早さが 上がる。" + "description": "天気が すなあらし のとき 素早さが 上がる。" }, "wonderSkin": { "name": "ミラクルスキン", - "description": "変化技を 受けにくい 体に なっている。" + "description": "変化技を 受けにくい 体に なっている。" }, "analytic": { "name": "アナライズ", - "description": "いちばん 最後に 技を 出すと 技の 威力が 上がる。" + "description": "いちばん 最後に 技を 出すと 技の 威力が 上がる。" }, "illusion": { "name": "イリュージョン", - "description": "手持ちの いちばん うしろに いる ポケモンに なりきって 登場して 相手を 化かす。" + "description": "手持ちの いちばん うしろに いる ポケモンに なりきって 登場して 相手を 化かす。" }, "imposter": { "name": "かわりもの", - "description": "目の前の ポケモンに 変身 してしまう。" + "description": "目の前の ポケモンに 変身 してしまう。" }, "infiltrator": { "name": "すりぬけ", - "description": "相手の 壁や 身代わりを すりぬけて 攻撃 できる" + "description": "相手の 壁や 身代わりを すりぬけて 攻撃 できる" }, "mummy": { "name": "ミイラ", - "description": "相手に 触られると 相手を ミイラに してしまう。" + "description": "相手に 触られると 相手を ミイラに してしまう。" }, "moxie": { "name": "じしんかじょう", - "description": "相手を 倒すと 自信が ついて 攻撃が 上がる。" + "description": "相手を 倒すと 自信が ついて 攻撃が 上がる。" }, "justified": { "name": "せいぎのこころ", - "description": "あくタイプの 攻撃を 受けると 正義感で 攻撃が 上がる。" + "description": "あくタイプの 攻撃を 受けると 正義感で 攻撃が 上がる。" }, "rattled": { "name": "びびり", - "description": "あく ゴースト むしタイプの 攻撃を 受けたり いかくを されると びびって 素早さが 上がる。" + "description": "あく ゴースト むしタイプの 攻撃を 受けたり いかくを されると びびって 素早さが 上がる。" }, "magicBounce": { "name": "マジックミラー", - "description": "相手に だされた 変化技を 受けずに そのまま 返す ことが できる。" + "description": "相手に だされた 変化技を 受けずに そのまま 返す ことが できる。" }, "sapSipper": { "name": "そうしょく", - "description": "くさタイプの 技を 受けると ダメージを 受けずに 攻撃が 上がる。" + "description": "くさタイプの 技を 受けると ダメージを 受けずに 攻撃が 上がる。" }, "prankster": { "name": "いたずらごころ", - "description": "変化技を 先制で 出すことが できる。" + "description": "変化技を 先制で 出すことが できる。" }, "sandForce": { "name": "すなのちから", - "description": "天気が すなあらしの とき いわタイプと じめんタイプと はがねタイプの 威力が 上がる。" + "description": "天気が すなあらしの とき いわタイプと じめんタイプと はがねタイプの 威力が 上がる。" }, "ironBarbs": { "name": "てつのトゲ", - "description": "自分に 触った 相手に 鉄のトゲで ダメージを あたえる。" + "description": "自分に 触った 相手に 鉄のトゲで ダメージを あたえる。" }, "zenMode": { "name": "ダルマモード", - "description": "HPが 半分 以下に なると 姿が 変化する。" + "description": "HPが 半分 以下に なると 姿が 変化する。" }, "victoryStar": { "name": "しょうりのほし", - "description": "自分や 味方の 命中率が 上がる。" + "description": "自分や 味方の 命中率が 上がる。" }, "turboblaze": { "name": "ターボブレイズ", - "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" + "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" }, "teravolt": { "name": "テラボルテージ", - "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" + "description": "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。" }, "aromaVeil": { "name": "アロマベール", - "description": "自分と 味方への メンタル 攻撃を 防ぐことが できる。" + "description": "自分と 味方への メンタル 攻撃を 防ぐことが できる。" }, "flowerVeil": { "name": "フラワーベール", - "description": "味方の 草ポケモンは 能力が 下がらず 状態異常にも ならない。" + "description": "味方の 草ポケモンは 能力が 下がらず 状態異常にも ならない。" }, "cheekPouch": { "name": "ほおぶくろ", - "description": "どんな きのみでも 食べると HPも 回復する。" + "description": "どんな きのみでも 食べると HPも 回復する。" }, "protean": { "name": "へんげんじざい", - "description": "自分が 出す 技と 同じ タイプに 変化する。" + "description": "自分が 出す 技と 同じ タイプに 変化する。" }, "furCoat": { "name": "ファーコート", - "description": "相手から 受ける 物理技の ダメージが 半分に なる。" + "description": "相手から 受ける 物理技の ダメージが 半分に なる。" }, "magician": { "name": "マジシャン", - "description": "技を 当てた 相手の 道具を 奪ってしまう。" + "description": "技を 当てた 相手の 道具を 奪ってしまう。" }, "bulletproof": { "name": "ぼうだん", - "description": "相手の 弾や 爆弾などの 技を 防ぐことが できる。" + "description": "相手の 弾や 爆弾などの 技を 防ぐことが できる。" }, "competitive": { "name": "かちき", - "description": "能力を 下げられると 特攻が ぐーんと 上がる。" + "description": "能力を 下げられると 特攻が ぐーんと 上がる。" }, "strongJaw": { "name": "がんじょうあご", - "description": "あごが 頑丈で 噛む 技の 威力が 高くなる。" + "description": "あごが 頑丈で 噛む 技の 威力が 高くなる。" }, "refrigerate": { "name": "フリーズスキン", - "description": "ノーマルタイプの 技が こおりタイプに なる。 威力が 少し 上がる。" + "description": "ノーマルタイプの 技が こおりタイプに なる。 威力が 少し 上がる。" }, "sweetVeil": { "name": "スイートベール", - "description": "味方の ポケモンは 眠らなくなる。" + "description": "味方の ポケモンは 眠らなくなる。" }, "stanceChange": { "name": "バトルスイッチ", - "description": "攻撃技を 出すと ブレードフォルムに 技 キングシールドを 出すと シールドフォルムに 変化する。" + "description": "攻撃技を 出すと ブレードフォルムに 技 キングシールドを 出すと シールドフォルムに 変化する。" }, "galeWings": { "name": "はやてのつばさ", - "description": "HPが 満タン だと ひこうタイプの 技を 先制で 出すことが できる。" + "description": "HPが 満タン だと ひこうタイプの 技を 先制で 出すことが できる。" }, "megaLauncher": { "name": "メガランチャー", - "description": "波動の 技の 威力が 高くなる。" + "description": "波動の 技の 威力が 高くなる。" }, "grassPelt": { "name": "くさのけがわ", - "description": "グラスフィールドのとき 防御が 上がる。" + "description": "グラスフィールドのとき 防御が 上がる。" }, "symbiosis": { "name": "きょうせい", - "description": "味方が 道具を 使うと 自分の 持っている 道具を 味方に 渡す。" + "description": "味方が 道具を 使うと 自分の 持っている 道具を 味方に 渡す。" }, "toughClaws": { "name": "かたいツメ", - "description": "相手に 接触する 技の 威力が 高くなる。" + "description": "相手に 接触する 技の 威力が 高くなる。" }, "pixilate": { "name": "フェアリースキン", - "description": "ノーマルタイプの 技が フェアリータイプになる。 威力が 少し 上がる。" + "description": "ノーマルタイプの 技が フェアリータイプになる。 威力が 少し 上がる。" }, "gooey": { "name": "ぬめぬめ", - "description": "攻撃で 自分に 触れた 相手の 素早さを 下げる。" + "description": "攻撃で 自分に 触れた 相手の 素早さを 下げる。" }, "aerilate": { "name": "スカイスキン", - "description": "ノーマルタイプの 技が ひこうタイプになる。 威力が 少し 上がる。" + "description": "ノーマルタイプの 技が ひこうタイプになる。 威力が 少し 上がる。" }, "parentalBond": { "name": "おやこあい", - "description": "親子 2匹で 2回 攻撃することが できる。" + "description": "親子 2匹で 2回 攻撃することが できる。" }, "darkAura": { "name": "ダークオーラ", - "description": "全員の あくタイプの 技が 強くなる。" + "description": "全員の あくタイプの 技が 強くなる。" }, "fairyAura": { "name": "フェアリーオーラ", - "description": "全員の フェアリータイプの 技が 強くなる。" + "description": "全員の フェアリータイプの 技が 強くなる。" }, "auraBreak": { "name": "オーラブレイク", - "description": "オーラの 効果を 逆転させて 威力を 下げる。" + "description": "オーラの 効果を 逆転させて 威力を 下げる。" }, "primordialSea": { "name": "はじまりのうみ", - "description": "ほのおタイプの 攻撃を 受けない 天気にする。" + "description": "ほのおタイプの 攻撃を 受けない 天気にする。" }, "desolateLand": { "name": "おわりのだいち", - "description": "みずタイプの 攻撃を 受けない 天気にする。" + "description": "みずタイプの 攻撃を 受けない 天気にする。" }, "deltaStream": { "name": "デルタストリーム", - "description": "ひこうタイプの 弱点が なくなる 天気にする。" + "description": "ひこうタイプの 弱点が なくなる 天気にする。" }, "stamina": { "name": "じきゅうりょく", - "description": "攻撃を 受けると 防御が 上がる。" + "description": "攻撃を 受けると 防御が 上がる。" }, "wimpOut": { "name": "にげごし", - "description": "HPが 半分に なると あわてて 逃げ出して 手持ちに 引っ込んで しまう。" + "description": "HPが 半分に なると あわてて 逃げ出して 手持ちに 引っ込んで しまう。" }, "emergencyExit": { "name": "ききかいひ", - "description": "HPが 半分に なると 危険を 回避するため 手持ちに 引っ込んで しまう。" + "description": "HPが 半分に なると 危険を 回避するため 手持ちに 引っ込んで しまう。" }, "waterCompaction": { "name": "みずがため", - "description": "みずタイプの 技を 受けると 防御が ぐーんと 上がる。" + "description": "みずタイプの 技を 受けると 防御が ぐーんと 上がる。" }, "merciless": { "name": "ひとでなし", - "description": "どく状態の 相手を 攻撃すると かならず 急所に 当たる。" + "description": "どく状態の 相手を 攻撃すると かならず 急所に 当たる。" }, "shieldsDown": { "name": "リミットシールド", - "description": "HPが 半分に なると 殻が 壊れて 攻撃的に なる。" + "description": "HPが 半分に なると 殻が 壊れて 攻撃的に なる。" }, "stakeout": { "name": "はりこみ", - "description": "交代で 出てきた 相手に 2倍の ダメージで 攻撃 できる。" + "description": "交代で 出てきた 相手に 2倍の ダメージで 攻撃 できる。" }, "waterBubble": { "name": "すいほう", - "description": "自分に 対する ほのおタイプの 技の 威力を 下げる。 やけど しない。" + "description": "自分に 対する ほのおタイプの 技の 威力を 下げる。 やけど しない。" }, "steelworker": { "name": "はがねつかい", - "description": "はがねタイプの 技の 威力が 上がる。" + "description": "はがねタイプの 技の 威力が 上がる。" }, "berserk": { "name": "ぎゃくじょう", - "description": "相手の 攻撃で HPが 半分に なると 特攻が 上がる。" + "description": "相手の 攻撃で HPが 半分に なると 特攻が 上がる。" }, "slushRush": { "name": "ゆきかき", - "description": "天気が ゆき のとき 素早さが 上がる。" + "description": "天気が ゆき のとき 素早さが 上がる。" }, "longReach": { "name": "えんかく", - "description": "すべての 技を 相手に 接触 しないで 出すことが できる。" + "description": "すべての 技を 相手に 接触 しないで 出すことが できる。" }, "liquidVoice": { "name": "うるおいボイス", - "description": "すべての 音技が みずタイプに なる。" + "description": "すべての 音技が みずタイプに なる。" }, "triage": { "name": "ヒーリングシフト", - "description": "回復技を 先制で 出すことが できる。" + "description": "回復技を 先制で 出すことが できる。" }, "galvanize": { "name": "エレキスキン", - "description": "ノーマルタイプの 技が でんきタイプになる。 威力が 少し 上がる。" + "description": "ノーマルタイプの 技が でんきタイプになる。 威力が 少し 上がる。" }, "surgeSurfer": { "name": "サーフテール", - "description": "エレキフィールド のとき 素早さが 2倍に なる。" + "description": "エレキフィールド のとき 素早さが 2倍に なる。" }, "schooling": { "name": "ぎょぐん", - "description": "HPが 多いときは 群れて 強くなる。 HPの 残りが 少なくなると 群れは 散り散りに なってしまう。" + "description": "HPが 多いときは 群れて 強くなる。 HPの 残りが 少なくなると 群れは 散り散りに なってしまう。" }, "disguise": { "name": "ばけのかわ", - "description": "体を 被う 化けの皮で 1回 攻撃を 防ぐことが できる。" + "description": "体を 被う 化けの皮で 1回 攻撃を 防ぐことが できる。" }, "battleBond": { "name": "きずなへんげ", - "description": "相手を 倒すと トレーナーとの キズナが 深まり サトシゲッコウガに 変化する。みずしゅりけんが 強くなる。" + "description": "相手を 倒すと トレーナーとの キズナが 深まり サトシゲッコウガに 変化する。みずしゅりけんが 強くなる。" }, "powerConstruct": { "name": "スワームチェンジ", - "description": "HPが 半分に なると セルたちが 応援に 駆けつけ パーフェクトフォルムに 姿を 変える。" + "description": "HPが 半分に なると セルたちが 応援に 駆けつけ パーフェクトフォルムに 姿を 変える。" }, "corrosion": { "name": "ふしょく", - "description": "はがねタイプや どくタイプも どく状態に することが できる。" + "description": "はがねタイプや どくタイプも どく状態に することが できる。" }, "comatose": { "name": "ぜったいねむり", - "description": "つねに 夢うつつの 状態で 絶対に 目覚めない。 眠ったまま 攻撃が できる。" + "description": "つねに 夢うつつの 状態で 絶対に 目覚めない。 眠ったまま 攻撃が できる。" }, "queenlyMajesty": { "name": "じょおうのいげん", - "description": "相手に 威圧感を あたえ こちらに むかって 先制技を 出せない ようにする。" + "description": "相手に 威圧感を あたえ こちらに むかって 先制技を 出せない ようにする。" }, "innardsOut": { "name": "とびだすなかみ", - "description": "相手に 倒されたとき HPの 残りの ぶんだけ 相手に ダメージを あたえる。" + "description": "相手に 倒されたとき HPの 残りの ぶんだけ 相手に ダメージを あたえる。" }, "dancer": { "name": "おどりこ", - "description": "だれかが 踊り技を 使うと 自分も それに 続いて 踊り技を 出すことが できる。" + "description": "だれかが 踊り技を 使うと 自分も それに 続いて 踊り技を 出すことが できる。" }, "battery": { "name": "バッテリー", - "description": "味方の 特殊技の 威力を 上げる。" + "description": "味方の 特殊技の 威力を 上げる。" }, "fluffy": { "name": "もふもふ", - "description": "相手から 受けた 接触する 技の ダメージを 半減するが ほのおタイプの 技の ダメージは 2倍になる。" + "description": "相手から 受けた 接触する 技の ダメージを 半減するが ほのおタイプの 技の ダメージは 2倍になる。" }, "dazzling": { "name": "ビビッドボディ", - "description": "相手を びっくり させて こちらに むかって 先制技を 出せない ようにする。" + "description": "相手を びっくり させて こちらに むかって 先制技を 出せない ようにする。" }, "soulHeart": { "name": "ソウルハート", - "description": "ポケモンが ひんしに なるたびに 特攻が 上がる。" + "description": "ポケモンが ひんしに なるたびに 特攻が 上がる。" }, "tanglingHair": { "name": "カーリーヘアー", - "description": "攻撃で 自分に 触れた 相手の 素早さを 下げる。" + "description": "攻撃で 自分に 触れた 相手の 素早さを 下げる。" }, "receiver": { "name": "レシーバー", - "description": "倒された 味方の 特性を 受け継いで 同じ 特性に なる。" + "description": "倒された 味方の 特性を 受け継いで 同じ 特性に なる。" }, "powerOfAlchemy": { "name": "かがくのちから", - "description": "倒された 味方の 特性を 受け継いで 同じ 特性に なる。" + "description": "倒された 味方の 特性を 受け継いで 同じ 特性に なる。" }, "beastBoost": { "name": "ビーストブースト", - "description": "相手を 倒したとき 自分の いちばん 高い 能力が 上がる。" + "description": "相手を 倒したとき 自分の いちばん 高い 能力が 上がる。" }, "rksSystem": { "name": "ARシステム", - "description": "持っている メモリで 自分の タイプが 変わる。" + "description": "持っている メモリで 自分の タイプが 変わる。" }, "electricSurge": { "name": "エレキメイカー", - "description": "登場 したときに エレキフィールドを はりめぐらせる。" + "description": "登場 したときに エレキフィールドを はりめぐらせる。" }, "psychicSurge": { "name": "サイコメイカー", - "description": "登場 したときに サイコフィールドを はりめぐらせる。" + "description": "登場 したときに サイコフィールドを はりめぐらせる。" }, "mistySurge": { "name": "ミストメイカー", - "description": "登場 したときに ミストフィールドを はりめぐらせる。" + "description": "登場 したときに ミストフィールドを はりめぐらせる。" }, "grassySurge": { "name": "グラスメイカー", - "description": "登場 したときに グラスフィールドを はりめぐらせる。" + "description": "登場 したときに グラスフィールドを はりめぐらせる。" }, "fullMetalBody": { "name": "メタルプロテクト", - "description": "相手の 技や 特性で 能力を 下げられない。" + "description": "相手の 技や 特性で 能力を 下げられない。" }, "shadowShield": { "name": "ファントムガード", - "description": "HPが 満タンの ときに 受ける ダメージが 少なくなる。" + "description": "HPが 満タンの ときに 受ける ダメージが 少なくなる。" }, "prismArmor": { "name": "プリズムアーマー", - "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" + "description": "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。" }, "neuroforce": { "name": "ブレインフォース", - "description": "効果バツグンの 攻撃で 威力が さらに 上がる。" + "description": "効果バツグンの 攻撃で 威力が さらに 上がる。" }, "intrepidSword": { "name": "ふとうのけん", - "description": "登場 したときに 攻撃が 上がる。" + "description": "登場 したときに 攻撃が 上がる。" }, "dauntlessShield": { "name": "ふくつのたて", - "description": "登場 したときに 防御が 上がる。" + "description": "登場 したときに 防御が 上がる。" }, "libero": { "name": "リベロ", - "description": "自分が 出す 技と 同じ タイプに 変化する。" + "description": "自分が 出す 技と 同じ タイプに 変化する。" }, "ballFetch": { "name": "たまひろい", - "description": "1回目に 投げて 失敗 した モンスターボールを 拾ってくる。" + "description": "1回目に 投げて 失敗 した モンスターボールを 拾ってくる。" }, "cottonDown": { "name": "わたげ", - "description": "攻撃を 受けると わたげを ばらまいて 自分以外の ポケモン すべての 素早さを 下げる。" + "description": "攻撃を 受けると わたげを ばらまいて 自分以外の ポケモン すべての 素早さを 下げる。" }, "propellerTail": { "name": "スクリューおびれ", - "description": "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。" + "description": "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。" }, "mirrorArmor": { "name": "ミラーアーマー", - "description": "自分が 受けた 能力 ダウンの 効果 だけを 跳ね返す。" + "description": "自分が 受けた 能力 ダウンの 効果 だけを 跳ね返す。" }, "gulpMissile": { "name": "うのミサイル", - "description": "なみのりか ダイビングを すると 獲物を くわえてくる。 ダメージを 受けると 獲物を 吐きだして 攻撃。" + "description": "なみのりか ダイビングを すると 獲物を くわえてくる。 ダメージを 受けると 獲物を 吐きだして 攻撃。" }, "stalwart": { "name": "すじがねいり", - "description": "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。" + "description": "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。" }, "steamEngine": { "name": "じょうききかん", - "description": "みずタイプ ほのおタイプの 技を 受けると 素早さが ぐぐーんと 上がる。" + "description": "みずタイプ ほのおタイプの 技を 受けると 素早さが ぐぐーんと 上がる。" }, "punkRock": { "name": "パンクロック", - "description": "音技の 威力が 上がる。 受けた 音技の ダメージは 半分に なる。" + "description": "音技の 威力が 上がる。 受けた 音技の ダメージは 半分に なる。" }, "sandSpit": { "name": "すなはき", - "description": "攻撃を 受けると 砂あらしを 起こす。" + "description": "攻撃を 受けると 砂あらしを 起こす。" }, "iceScales": { "name": "こおりのりんぷん", - "description": "こおりのりんぷんに 守られて 特殊攻撃で 受ける ダメージが 半減 する。" + "description": "こおりのりんぷんに 守られて 特殊攻撃で 受ける ダメージが 半減 する。" }, "ripen": { "name": "じゅくせい", - "description": "熟成 させることで きのみの 効果が 倍に なる。" + "description": "熟成 させることで きのみの 効果が 倍に なる。" }, "iceFace": { "name": "アイスフェイス", - "description": "物理攻撃は 頭の 氷が みがわりに なるが 姿も 変わる。 氷は あられが 降ると 元に戻る。" + "description": "物理攻撃は 頭の 氷が みがわりに なるが 姿も 変わる。 氷は あられが 降ると 元に戻る。" }, "powerSpot": { "name": "パワースポット", - "description": "隣に いるだけで 技の 威力が 上がる。" + "description": "隣に いるだけで 技の 威力が 上がる。" }, "mimicry": { "name": "ぎたい", - "description": "フィールドの 状態に あわせて ポケモンの タイプが 変わる。" + "description": "フィールドの 状態に あわせて ポケモンの タイプが 変わる。" }, "screenCleaner": { "name": "バリアフリー", - "description": "登場 したときに 敵と 味方の ひかりのかべ リフレクター オーロラベールの 効果が 消える。" + "description": "登場 したときに 敵と 味方の ひかりのかべ リフレクター オーロラベールの 効果が 消える。" }, "steelySpirit": { "name": "はがねのせいしん", - "description": "味方の はがねタイプの 攻撃の 威力が 上がる。" + "description": "味方の はがねタイプの 攻撃の 威力が 上がる。" }, "perishBody": { "name": "ほろびのボディ", - "description": "接触する 技を 受けると お互い 3ターン たつと ひんしになる。 交代すると 効果は なくなる。" + "description": "接触する 技を 受けると お互い 3ターン たつと ひんしになる。 交代すると 効果は なくなる。" }, "wanderingSpirit": { "name": "さまようたましい", - "description": "接触する 技で 攻撃 してきた ポケモンと 特性を 入れ替える。" + "description": "接触する 技で 攻撃 してきた ポケモンと 特性を 入れ替える。" }, "gorillaTactics": { "name": "ごりむちゅう", - "description": "攻撃は 上がるが 最初に 選んだ 技しか 出せなくなる。" + "description": "攻撃は 上がるが 最初に 選んだ 技しか 出せなくなる。" }, "neutralizingGas": { "name": "かがくへんかガス", - "description": "かがくへんかガスの ポケモンが 場にいると すべての ポケモンの 特性の 効果が 消えたり 発動 しなくなる。" + "description": "かがくへんかガスの ポケモンが 場にいると すべての ポケモンの 特性の 効果が 消えたり 発動 しなくなる。" }, "pastelVeil": { "name": "パステルベール", - "description": "自分も 味方も どくの 状態異常を 受けなくなる。" + "description": "自分も 味方も どくの 状態異常を 受けなくなる。" }, "hungerSwitch": { "name": "はらぺこスイッチ", - "description": "ターンの 終わりに まんぷくもよう はらぺこもよう まんぷくもよう……と 交互に 姿を 変える。" + "description": "ターンの 終わりに まんぷくもよう はらぺこもよう まんぷくもよう……と 交互に 姿を 変える。" }, "quickDraw": { "name": "クイックドロウ", - "description": "相手より 先に 行動できることが ある。" + "description": "相手より 先に 行動できることが ある。" }, "unseenFist": { "name": "ふかしのこぶし", - "description": "相手に 接触する 技なら 守りの 効果を 無視して 攻撃することが できる。" + "description": "相手に 接触する 技なら 守りの 効果を 無視して 攻撃することが できる。" }, "curiousMedicine": { "name": "きみょうなくすり", - "description": "登場 したときに 貝がらから 薬を 振りまいて 味方の 能力変化を 元に戻す。" + "description": "登場 したときに 貝がらから 薬を 振りまいて 味方の 能力変化を 元に戻す。" }, "transistor": { "name": "トランジスタ", - "description": "でんきタイプの 技の 威力が 上がる。" + "description": "でんきタイプの 技の 威力が 上がる。" }, "dragonsMaw": { "name": "りゅうのあぎと", - "description": "ドラゴンタイプの 技の 威力が 上がる。" + "description": "ドラゴンタイプの 技の 威力が 上がる。" }, "chillingNeigh": { "name": "しろのいななき", - "description": "相手を 倒すと 冷たい 声で いなないて 攻撃が 上がる。" + "description": "相手を 倒すと 冷たい 声で いなないて 攻撃が 上がる。" }, "grimNeigh": { "name": "くろのいななき", - "description": "相手を 倒すと 恐ろしい 声で いなないて 特攻が 上がる。" + "description": "相手を 倒すと 恐ろしい 声で いなないて 特攻が 上がる。" }, "asOneGlastrier": { "name": "じんばいったい", - "description": "バドレックスの きんちょうかんと ブリザポスの しろのいななきの 二つの 特性を あわせ持つ。" + "description": "バドレックスの きんちょうかんと ブリザポスの しろのいななきの 二つの 特性を あわせ持つ。" }, "asOneSpectrier": { "name": "じんばいったい", - "description": "バドレックスの きんちょうかんと レイスポスの くろのいななきの 二つの 特性を あわせ持つ。" + "description": "バドレックスの きんちょうかんと レイスポスの くろのいななきの 二つの 特性を あわせ持つ。" }, "lingeringAroma": { "name": "とれないにおい", @@ -1073,170 +1073,170 @@ }, "seedSower": { "name": "こぼれダネ", - "description": "攻撃を 受けると グラスフィールドに する。" + "description": "攻撃を 受けると グラスフィールドに する。" }, "thermalExchange": { "name": "ねつこうかん", - "description": "ほのおタイプの 技を 受けると 攻撃が 上がる。 やけど状態に ならない。" + "description": "ほのおタイプの 技を 受けると 攻撃が 上がる。 やけど状態に ならない。" }, "angerShell": { "name": "いかりのこうら", - "description": "相手の攻撃で HPが 半分に なると 怒りで 防御と 特防が 下がるが 攻撃 特攻 素早さが 上がる。" + "description": "相手の攻撃で HPが 半分に なると 怒りで 防御と 特防が 下がるが 攻撃 特攻 素早さが 上がる。" }, "purifyingSalt": { "name": "きよめのしお", - "description": "清らかな塩で 状態異常に ならない。 ゴーストタイプの 技の ダメージを 半減させる。" + "description": "清らかな塩で 状態異常に ならない。 ゴーストタイプの 技の ダメージを 半減させる。" }, "wellBakedBody": { "name": "こんがりボディ", - "description": "ほのおタイプの 技を 受けると ダメージを 受けずに 防御が ぐーんと 上がる。" + "description": "ほのおタイプの 技を 受けると ダメージを 受けずに 防御が ぐーんと 上がる。" }, "windRider": { "name": "かぜのり", - "description": "おいかぜが 吹いたり 風技を 受けると ダメージを 受けずに 攻撃が 上がる。" + "description": "おいかぜが 吹いたり 風技を 受けると ダメージを 受けずに 攻撃が 上がる。" }, "guardDog": { "name": "ばんけん", - "description": "いかく されると 攻撃が 上がる。 ポケモンを 入れ替えさせる 技や 道具が 効かない。" + "description": "いかく されると 攻撃が 上がる。 ポケモンを 入れ替えさせる 技や 道具が 効かない。" }, "rockyPayload": { "name": "いわはこび", - "description": "いわタイプの 技の 威力が 上がる。" + "description": "いわタイプの 技の 威力が 上がる。" }, "windPower": { "name": "ふうりょくでんき", - "description": "風技を 受けると じゅうでん 状態に なる。" + "description": "風技を 受けると じゅうでん 状態に なる。" }, "zeroToHero": { "name": "マイティチェンジ", - "description": "手持ちに ひっこむと マイティフォルムに 変化する。" + "description": "手持ちに ひっこむと マイティフォルムに 変化する。" }, "commander": { "name": "しれいとう", - "description": "登場したとき 味方に ヘイラッシャが いると 口の中に 入って そこから 指令を だす。" + "description": "登場したとき 味方に ヘイラッシャが いると 口の中に 入って そこから 指令を だす。" }, "electromorphosis": { "name": "でんきにかえる", - "description": "ダメージを 受けると じゅうでん 状態に なる。" + "description": "ダメージを 受けると じゅうでん 状態に なる。" }, "protosynthesis": { "name": "こだいかっせい", - "description": "ブーストエナジーを 持たせるか 天気が 晴れのとき いちばん 高い能力が 上がる。" + "description": "ブーストエナジーを 持たせるか 天気が 晴れのとき いちばん 高い能力が 上がる。" }, "quarkDrive": { "name": "クォークチャージ", - "description": "ブーストエナジーを 持たせるか エレキフィールドのとき いちばん 高い能力が 上がる。" + "description": "ブーストエナジーを 持たせるか エレキフィールドのとき いちばん 高い能力が 上がる。" }, "goodAsGold": { "name": "おうごんのからだ", - "description": "酸化せず 丈夫な 黄金の体は 相手からの 変化技を 受けない。" + "description": "酸化せず 丈夫な 黄金の体は 相手からの 変化技を 受けない。" }, "vesselOfRuin": { "name": "わざわいのうつわ", - "description": "災厄を 呼ぶ 器の力で 自分以外の 特攻が 弱くなる。" + "description": "災厄を 呼ぶ 器の力で 自分以外の 特攻が 弱くなる。" }, "swordOfRuin": { "name": "わざわいのつるぎ", - "description": "災厄を 呼ぶ 剣の力で 自分以外の 防御が 弱くなる。" + "description": "災厄を 呼ぶ 剣の力で 自分以外の 防御が 弱くなる。" }, "tabletsOfRuin": { "name": "わざわいのおふだ", - "description": "災厄を 呼ぶ 木札の力で 自分以外の 攻撃が 弱くなる。" + "description": "災厄を 呼ぶ 木札の力で 自分以外の 攻撃が 弱くなる。" }, "beadsOfRuin": { "name": "わざわいのたま", - "description": "災厄を 呼ぶ 勾玉の力で 自分以外の 特防が 弱くなる。" + "description": "災厄を 呼ぶ 勾玉の力で 自分以外の 特防が 弱くなる。" }, "orichalcumPulse": { "name": "ひひいろのこどう", - "description": "登場したとき 天気を 晴れにする。 日差しが 強いと 古代の 鼓動により 攻撃が 高まる。" + "description": "登場したとき 天気を 晴れにする。 日差しが 強いと 古代の 鼓動により 攻撃が 高まる。" }, "hadronEngine": { "name": "ハドロンエンジン", - "description": "登場したとき エレキフィールドを はる。 エレキフィールドだと 未来の 機関により 特攻が 高まる。" + "description": "登場したとき エレキフィールドを はる。 エレキフィールドだと 未来の 機関により 特攻が 高まる。" }, "opportunist": { "name": "びんじょう", - "description": "相手の 能力が 上がったとき 自分も 便乗して 同じように 能力を 上げる。" + "description": "相手の 能力が 上がったとき 自分も 便乗して 同じように 能力を 上げる。" }, "cudChew": { "name": "はんすう", - "description": "きのみを 食べると 次のターンの 終わりに 胃から 出して もう1回だけ 食べる。" + "description": "きのみを 食べると 次のターンの 終わりに 胃から 出して もう1回だけ 食べる。" }, "sharpness": { "name": "きれあじ", - "description": "相手を 切る技の 威力が 上がる。" + "description": "相手を 切る技の 威力が 上がる。" }, "supremeOverlord": { "name": "そうだいしょう", - "description": "登場したとき 今まで 倒された 味方の 数が 多いほど 少しずつ 攻撃と 特攻が 上がる。" + "description": "登場したとき 今まで 倒された 味方の 数が 多いほど 少しずつ 攻撃と 特攻が 上がる。" }, "costar": { "name": "きょうえん", - "description": "登場 したときに 味方の 能力変化を コピーする。" + "description": "登場 したときに 味方の 能力変化を コピーする。" }, "toxicDebris": { "name": "どくげしょう", - "description": "物理技で ダメージを 受けると 相手の 足下に どくびしが ちらばる。" + "description": "物理技で ダメージを 受けると 相手の 足下に どくびしが ちらばる。" }, "armorTail": { "name": "テイルアーマー", - "description": "頭を包む 謎のしっぽが こちらに むかって 先制技を 出せない ようにする。" + "description": "頭を包む 謎のしっぽが こちらに むかって 先制技を 出せない ようにする。" }, "earthEater": { "name": "どしょく", - "description": "じめんタイプの 技を 受けると ダメージを 受けずに 回復する。" + "description": "じめんタイプの 技を 受けると ダメージを 受けずに 回復する。" }, "myceliumMight": { "name": "きんしのちから", - "description": "変化技を 出すとき 必ず 行動が 遅くなるが 相手の 特性に ジャマされない。" + "description": "変化技を 出すとき 必ず 行動が 遅くなるが 相手の 特性に ジャマされない。" }, "mindsEye": { "name": "しんがん", - "description": "ノーマル かくとうタイプの技を ゴーストタイプに 当てることが できる。 相手の 回避率の 変化を 無視し 命中率も 下げられない。" + "description": "ノーマル かくとうタイプの技を ゴーストタイプに 当てることが できる。 相手の 回避率の 変化を 無視し 命中率も 下げられない。" }, "supersweetSyrup": { "name": "かんろなミツ", - "description": "最初に 登場 したとき 甘ったるい 蜜の香りを ふりまいて 相手の 回避率を 下げる。" + "description": "最初に 登場 したとき 甘ったるい 蜜の香りを ふりまいて 相手の 回避率を 下げる。" }, "hospitality": { "name": "おもてなし", - "description": "登場したとき 味方を もてなして HPを 少しだけ 回復してあげる。" + "description": "登場したとき 味方を もてなして HPを 少しだけ 回復してあげる。" }, "toxicChain": { "name": "どくのくさり", - "description": "毒素を ふくんだ 鎖の力で 技を 当てた 相手を 猛毒の状態に することが ある。" + "description": "毒素を ふくんだ 鎖の力で 技を 当てた 相手を 猛毒の状態に することが ある。" }, "embodyAspectTeal": { "name": "おもかげやどし", - "description": "思い出を 心に 宿すことで みどりのめんを かがやかせ 自分の 素早さを 上げる。" + "description": "思い出を 心に 宿すことで みどりのめんを かがやかせ 自分の 素早さを 上げる。" }, "embodyAspectWellspring": { "name": "おもかげやどし", - "description": "思い出を 心に 宿すことで いどのめんを かがやかせ 自分の 特防を 上げる。" + "description": "思い出を 心に 宿すことで いどのめんを かがやかせ 自分の 特防を 上げる。" }, "embodyAspectHearthflame": { "name": "おもかげやどし", - "description": "思い出を 心に 宿すことで かまどのめんを かがやかせ 自分の 攻撃を 上げる。" + "description": "思い出を 心に 宿すことで かまどのめんを かがやかせ 自分の 攻撃を 上げる。" }, "embodyAspectCornerstone": { "name": "おもかげやどし", - "description": "思い出を 心に 宿すことで いしずえのめんを かがやかせ 自分の 防御を 上げる。" + "description": "思い出を 心に 宿すことで いしずえのめんを かがやかせ 自分の 防御を 上げる。" }, "teraShift": { "name": "テラスチェンジ", - "description": "登場したとき 周囲の エネルギーを 吸収し テラスタルフォルムに 変化する。" + "description": "登場したとき 周囲の エネルギーを 吸収し テラスタルフォルムに 変化する。" }, "teraShell": { "name": "テラスシェル", - "description": "全タイプの力を 秘めた甲羅は HPが 満タンの ときに 受ける ダメージを すべて 今ひとつに する。" + "description": "全タイプの力を 秘めた甲羅は HPが 満タンの ときに 受ける ダメージを すべて 今ひとつに する。" }, "teraformZero": { "name": "ゼロフォーミング", - "description": "テラパゴスが ステラフォルムに なったとき 秘められた力で 天気と フィールドの 影響を すべて ゼロにする。" + "description": "テラパゴスが ステラフォルムに なったとき 秘められた力で 天気と フィールドの 影響を すべて ゼロにする。" }, "poisonPuppeteer": { "name": "どくくぐつ", - "description": "モモワロウの 技によって どく状態に なった 相手は こんらん状態にも なってしまう。" + "description": "モモワロウの 技によって どく状態に なった 相手は こんらん状態にも なってしまう。" } -} \ No newline at end of file +} diff --git a/src/locales/ja/achv.json b/src/locales/ja/achv.json index 0dc5dc8185a..fd5e4b9e6c4 100644 --- a/src/locales/ja/achv.json +++ b/src/locales/ja/achv.json @@ -6,7 +6,7 @@ "name": "なし" }, "MoneyAchv": { - "description": "一回の ランで ₽{{moneyAmount}}を 稼ぐ" + "description": "一回の ランで {{moneyAmount}}円を 稼ぐ" }, "10K_MONEY": { "name": "お金を持つ人" @@ -21,7 +21,7 @@ "name": "超富裕層" }, "DamageAchv": { - "description": "一撃で {{damageAmount}}ダメージを 与える" + "description": "一撃で HP{{damageAmount}}の ダメージを 与える" }, "250_DMG": { "name": "力持ち" @@ -33,10 +33,11 @@ "name": "カカロット" }, "10000_DMG": { - "name": "ワンパンマン" + "name": "ワンパンマン", + "name_female": "ワンパンウーマン" }, "HealAchv": { - "description": "一つの 技や 特性や 持っているアイテムで {{healAmount}}{{HP}}を 一気に 回復する" + "description": "一つの 技や 特性や 持たせたアイテムで\n{{HP}}{{healAmount}}を 一気に 回復する" }, "250_HEAL": { "name": "回復発見者" @@ -51,7 +52,7 @@ "name": "ジョーイさん" }, "LevelAchv": { - "description": "一つの ポケモンを Lv{{level}}まで レベルアップする" + "description": "一つの ポケモンを Lv.{{level}}まで 上げる" }, "LV_100": { "name": "まだまだだよ" @@ -80,9 +81,9 @@ "100_RIBBONS": { "name": "マスターリーグチャンピオン" }, - "TRANSFER_MAX_BATTLE_STAT": { - "name": "同力", - "description": "少なくとも 一つの 能力を 最大まで あげて 他の 手持ちポケモンに バトンタッチする" + "TRANSFER_MAX_STAT_STAGE": { + "name": "連係プレー", + "description": "少なくとも 一つの 能力を 最大まで あげて\n他の 手持ちポケモンに バトンタッチする" }, "MAX_FRIENDSHIP": { "name": "マブ達", @@ -93,7 +94,7 @@ "description": "一つの 手持ちポケモンを メガシンカさせる" }, "GIGANTAMAX": { - "name": "太―くて 堪らない", + "name": "太ーくて堪らない", "description": "一つの 手持ちポケモンを キョダイマックスさせる" }, "TERASTALLIZE": { @@ -105,8 +106,8 @@ "description": "一つの 手持ちポケモンを ステラ・テラスタルさせる" }, "SPLICE": { - "name": "インフィニット・フュジョン", - "description": "いでんしのくさびで 二つの ポケモンを 吸収合体させる" + "name": "インフィニット・フュージョン", + "description": "遺伝子のくさびで 二つの ポケモンを 吸収合体させる" }, "MINI_BLACK_HOLE": { "name": "アイテムホーリック", @@ -161,8 +162,8 @@ "description": "クラシックモードを クリアする" }, "UNEVOLVED_CLASSIC_VICTORY": { - "name": "Bring Your Child To Work Day", - "description": "Beat the game in Classic Mode with at least one unevolved party member." + "name": "はじめてのおつかい", + "description": "少なくとも 一つの 進化していない 手持ちポケモンで\nクラシックモードを クリアする" }, "MONO_GEN_ONE": { "name": "原始", @@ -204,7 +205,7 @@ "description": "{{type}}タイプの 単一タイプチャレンジを クリアする" }, "MONO_NORMAL": { - "name": "凡人" + "name": "超凡人" }, "MONO_FIGHTING": { "name": "八千以上だ!!" @@ -222,7 +223,7 @@ "name": "タケシの挑戦状" }, "MONO_BUG": { - "name": "チョウチョウせん者" + "name": "チョウチョウ戦者" }, "MONO_GHOST": { "name": "貞子ちゃん" @@ -260,5 +261,9 @@ "FRESH_START": { "name": "一発で!", "description": "出直しチャレンジを クリアする" + }, + "INVERSE_BATTLE": { + "name": "カガミよミガカ", + "description": "反転バトルチャレンジを クリアする\nるすアリク をジンレャチルトバ転反" } -} \ No newline at end of file +} diff --git a/src/locales/ja/arena-flyout.json b/src/locales/ja/arena-flyout.json index 9e26dfeeb6e..8585b8e8fb4 100644 --- a/src/locales/ja/arena-flyout.json +++ b/src/locales/ja/arena-flyout.json @@ -1 +1,44 @@ -{} \ No newline at end of file +{ + "activeBattleEffects": "場の効果", + "player": "味方", + "neutral": "場の全員", + "enemy": "相手", + + "sunny": "晴れ", + "rain": "雨", + "sandstorm": "砂あらし", + "hail": "あられ", + "snow": "雪", + "fog": "きり", + "heavyRain": "強い雨", + "harshSun": "大日照り", + "strongWinds": "乱気流", + + "misty": "ミストフィールド", + "electric": "エレキフィールド", + "grassy": "グラスフィールド", + "psychic": "サイコフィールド", + + "mudSport": "どろあそび", + "waterSport": "みずあそび", + "spikes": "まきびし", + "toxicSpikes": "どくびし", + "mist": "しろいきり", + "futureSight": "みらいよち", + "doomDesire": "はめつのねがい", + "wish": "ねがいごと", + "stealthRock": "ステルスロック", + "stickyWeb": "ねばねばネット", + "trickRoom": "トリックルーム", + "gravity": "じゅうりょく", + "reflect": "リフレクター", + "lightScreen": "ひかりのかべ", + "auroraVeil": "オーロラベール", + "quickGuard": "ファストガード", + "wideGuard": "ワイドガード", + "matBlock": "たたみがえし", + "craftyShield": "トリックガード", + "tailwind": "おいかぜ", + "happyHour": "ハッピータイム", + "safeguard": "しんぴなまもり" +} diff --git a/src/locales/ja/arena-tag.json b/src/locales/ja/arena-tag.json index 56be30d8d55..a81942338fd 100644 --- a/src/locales/ja/arena-tag.json +++ b/src/locales/ja/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "味方の 追い風が 止んだ!", "tailwindOnRemoveEnemy": "相手の 追い風が 止んだ!", "happyHourOnAdd": "みんなが ハッピーな気分に\n包まれた!", - "happyHourOnRemove": "みんなの 気分が 元に戻った" -} \ No newline at end of file + "happyHourOnRemove": "みんなの 気分が 元に戻った", + "safeguardOnAdd": "場の全体は 神秘のベールに 包まれた!", + "safeguardOnAddPlayer": "味方は 神秘のベールに 包まれた!", + "safeguardOnAddEnemy": "相手は 神秘のベールに 包まれた!", + "safeguardOnRemove": "場の全体を 包んでいた\n神秘のベールが なくなった!", + "safeguardOnRemovePlayer": "味方を 包んでいた\n神秘のベールが なくなった!", + "safeguardOnRemoveEnemy": "相手を 包んでいた\n神秘のベールが なくなった!" +} diff --git a/src/locales/ja/battle.json b/src/locales/ja/battle.json index f3f1d134d16..1fe24068cdf 100644 --- a/src/locales/ja/battle.json +++ b/src/locales/ja/battle.json @@ -4,7 +4,7 @@ "trainerAppearedDouble": "{{trainerName}}が\n勝負を しかけてきた!", "trainerSendOut": "{{trainerName}}は\n{{pokemonName}}を 繰り出した!", "singleWildAppeared": "あっ! 野生の {{pokemonName}}が 飛び出してきた!", - "multiWildAppeared": "あっ! 野生の {{pokemonName1}}と\n{{pokemonName2}}が 飛び出してきた!", + "multiWildAppeared": "あっ! 野生の {{pokemonName1}}と\n{{pokemonName2}}が 飛び出してきた!", "playerComeBack": "{{pokemonName}}! 戻れ!", "trainerComeBack": "{{trainerName}}は\n{{pokemonName}}を 引っ込めた!", "playerGo": "ゆけっ! {{pokemonName}}!", @@ -44,13 +44,14 @@ "moveNotImplemented": "{{moveName}}は まだ 実装されておらず 選択できません。", "moveNoPP": "しかし 技の\n残りポイントが なかった!", "moveDisabled": "かなしばりで\n{{moveName}}が 出せない!", + "disableInterruptedMove": "{{pokemonNameWithAffix}}は かなしばりで\n{{moveName}}が 出せない!", "noPokeballForce": "見えない 力の せいで\nボールが 投げられない!", "noPokeballTrainer": "人の ものを 取ったら 泥棒!", "noPokeballMulti": "相手の ポケモンが 一つしか\nいない 前に ボールが 使えない!", "noPokeballStrong": "相手の ポケモンが 強すぎて 捕まえられない!\nまずは 弱めよう!", "noEscapeForce": "見えない 力の せいで\n逃げることが できない!", "noEscapeTrainer": "ダメだ! 勝負の最中に\n相手に 背中を 見せられない!", - "noEscapePokemon": "{{pokemonName}}の {{moveName}}で {{escapeVerb}}!", + "noEscapePokemon": "{{pokemonName}}の {{moveName}}で\n{{escapeVerb}}!", "runAwaySuccess": " うまく 逃げ切れた!", "runAwayCannotEscape": "逃げることが できない!", "escapeVerbSwitch": "入れ替えることが できない", @@ -61,6 +62,7 @@ "skipItemQuestion": "本当に アイテムを 取らずに 進みますか?", "itemStackFull": "{{fullItemName}}の スタックが いっぱいです。\n代わりに {{itemName}}を 取得します。", "eggHatching": "おや?", + "eggSkipPrompt": "タマゴは ふかします!\nタマゴまとめに 飛ばしますか?", "ivScannerUseQuestion": "{{pokemonName}}を\n個体値スキャナーで 操作しますか?", "wildPokemonWithAffix": "野生の {{pokemonName}}", "foePokemonWithAffix": "相手の {{pokemonName}}", @@ -95,4 +97,4 @@ "congratulations": "おめでとうございます!!", "beatModeFirstTime": "初めて {{speciesName}}が {{gameMode}}モードを クリアした!\n{{newModifier}}を 手に入れた!", "ppReduced": "{{targetName}}の {{moveName}}を {{reduction}}削った!" -} \ No newline at end of file +} diff --git a/src/locales/ja/battler-tags.json b/src/locales/ja/battler-tags.json index beef485ffd2..f8c6d44c0b4 100644 --- a/src/locales/ja/battler-tags.json +++ b/src/locales/ja/battler-tags.json @@ -1,4 +1,12 @@ { + "trappedDesc": "捕らわれること", + "flinchedDesc": "ひるむこと", + "confusedDesc": "混乱", + "infatuatedDesc": "メロメロ", + "seedDesc": "種を植えつくこと", + "nightmareDesc": "あくむ", + "ingrainDesc": "根", + "drowsyDesc": "ねむけ", "rechargingLapse": "{{pokemonNameWithAffix}}は 攻撃の 反動で 動けない!", "trappedOnAdd": "{{pokemonNameWithAffix}}は もう 逃げられない!", "trappedOnRemove": "{{pokemonNameWithAffix}}は\n{{moveName}}の 効果が 解けた!", @@ -13,9 +21,9 @@ "infatuatedOnAdd": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロに なった!", "infatuatedOnOverlap": "{{pokemonNameWithAffix}}は すでに メロメロだ!", "infatuatedLapse": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロだ!", - "infatuatedLapseImmobilize": "{{pokemonNameWithAffix}}は\nメロメロで わざが 出せなかった!", + "infatuatedLapseImmobilize": "{{pokemonNameWithAffix}}は メロメロで 技が出せなかった!", "infatuatedOnRemove": "{{pokemonNameWithAffix}}は メロメロ状態が 治った!", - "seededOnAdd": "{{pokemonNameWithAffix}}に 種を 植(う)えつけた!", + "seededOnAdd": "{{pokemonNameWithAffix}}に 種を 植えつけた!", "seededLapse": "やどりぎが {{pokemonNameWithAffix}}の 体力を うばう!", "seededLapseShed": "{{pokemonNameWithAffix}}は ヘドロえきを 吸い取った!", "nightmareOnAdd": "{{pokemonNameWithAffix}}は あくむを 見始めた!", @@ -59,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}}は {{moveName}}の\n ダメージを 受けている", "cursedOnAdd": "{{pokemonNameWithAffix}}は 自分の 体力を 削って\n{{pokemonName}}に のろいを かけた!", "cursedLapse": "{{pokemonNameWithAffix}}は のろわれている!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}}は {{stockpiledCount}}つ たくわえた!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}}は {{stockpiledCount}}つ たくわえた!", + "disabledOnAdd": "{{pokemonNameWithAffix}}の\n{{moveName}}\nを 封じこめた!", + "disabledLapse": "{{pokemonNameWithAffix}}の\nかなしばりが 解けた!", + "tarShotOnAdd": "{{pokemonNameWithAffix}}は ほのおに 弱くなった!" +} diff --git a/src/locales/ja/berry.json b/src/locales/ja/berry.json index 641901583b2..1d1e01296d1 100644 --- a/src/locales/ja/berry.json +++ b/src/locales/ja/berry.json @@ -1,46 +1,46 @@ { "SITRUS": { "name": "オボンのみ", - "effect": "HP 50%いかのとき HPを 25パーセント かいふくする" + "effect": "持たせると HPが 50%以下に なるとき HPを 25% 回復する" }, "LUM": { "name": "ラムのみ", - "effect": "すべての じょうたい いじょうと こんらんを かいふくする" + "effect": "持たせると 状態異常や 混乱に なるとき 回復する" }, "ENIGMA": { "name": "ナゾのみ", - "effect": "こうかばつぐんの わざを うけたとき HPを 25パーセント かいふくする" + "effect": "持たせると 効果バツグンの 技を 受けたとき HPを 25%回復する" }, "LIECHI": { "name": "チイラのみ", - "effect": "HP 25%いかのとき こうげきが あがる" + "effect": "持たせると HPが 25%以下に なるとき 攻撃が あがる" }, "GANLON": { "name": "リュガのみ", - "effect": "HP 25%いかのとき ぼうぎょが あがる" + "effect": "持たせると HPが 25%以下に なるとき 防御が あがる" }, "PETAYA": { "name": "ヤタピのみ", - "effect": "HP 25%いかのとき とくこうが あがる" + "effect": "持たせると HPが 25%以下に なるとき 特攻が あがる" }, "APICOT": { "name": "ズアのみ", - "effect": "HP 25%いかのとき とくぼうが あがる" + "effect": "持たせると HPが 25%以下に なるとき 特防が あがる" }, "SALAC": { "name": "カムラのみ", - "effect": "HP 25%いかのとき すばやさが あがる" + "effect": "持たせると HPが 25%以下に なるとき 素早さが あがる" }, "LANSAT": { "name": "サンのみ", - "effect": "HP 25%いかのとき こうげきが きゅうしょに あたりやすくなる" + "effect": "持たせると HPが 25%以下に なるとき 攻撃が 急所に 当たりやすくなる" }, "STARF": { "name": "スターのみ", - "effect": "HP 25%いかのとき のうりょくの どれか 1つが ぐーんと あがる" + "effect": "持たせると HPが 25%以下に なるとき どれか 1つの 能力が ぐーんと あがる" }, "LEPPA": { "name": "ヒメリのみ", - "effect": "PPが 0に なった わざの PPを 10だけ かいふくする" + "effect": "持たせると PPが0になる 技の PPを 10回復する" } -} \ No newline at end of file +} diff --git a/src/locales/ja/bgm-name.json b/src/locales/ja/bgm-name.json index 9e26dfeeb6e..fc3d4c0fdd2 100644 --- a/src/locales/ja/bgm-name.json +++ b/src/locales/ja/bgm-name.json @@ -1 +1,150 @@ -{} \ No newline at end of file +{ + "music": "BGM: ", + "missing_entries": "{{name}}", + "battle_kanto_champion": "B2W2 戦闘!チャンピオン(カントー)", + "battle_johto_champion": "B2W2 戦闘!チャンピオン(ジョウト)", + "battle_hoenn_champion_g5": "B2W2 戦闘!チャンピオン(ホウエン)", + "battle_hoenn_champion_g6": "ORAS 決戦!ダイゴ", + "battle_sinnoh_champion": "B2W2 戦闘!チャンピオン(シンオウ)", + "battle_champion_alder": "BW チャンピオン アデク", + "battle_champion_iris": "B2W2 戦闘!チャンピオンアイリス", + "battle_kalos_champion": "XY 戦闘!チャンピオン", + "battle_alola_champion": "USUM 頂上決戦!ハウ", + "battle_galar_champion": "SWSH 決戦!チャンピオンダンデ", + "battle_champion_geeta": "SV 戦闘!トップチャンピオン", + "battle_champion_nemona": "SV 戦闘!チャンピオンネモ", + "battle_champion_kieran": "SV 戦闘!チャンピオンスグリ", + "battle_hoenn_elite": "ORAS 戦闘!四天王", + "battle_unova_elite": "BW 戦闘!四天王", + "battle_kalos_elite": "XY 戦闘!四天王", + "battle_alola_elite": "SM 戦闘!四天王", + "battle_galar_elite": "SWSH 戦闘!ファイナルトーナメント!", + "battle_paldea_elite": "SV 戦闘!四天王", + "battle_bb_elite": "SV 戦闘!ブルベリーグ四天王", + "battle_final_encounter": "ポケダンDX レックウザ登場", + "battle_final": "BW 戦闘!ゲーチス", + "battle_kanto_gym": "B2W2 戦闘!ジムリーダー(カントー)", + "battle_johto_gym": "B2W2 戦闘!ジムリーダー(ジョウト)", + "battle_hoenn_gym": "B2W2 戦闘!ジムリーダー(ホウエン)", + "battle_sinnoh_gym": "B2W2 戦闘!ジムリーダー(シンオウ)", + "battle_unova_gym": "BW 戦闘!ジムリーダー", + "battle_kalos_gym": "XY 戦闘!ジムリーダー", + "battle_galar_gym": "SWSH 戦闘!ジムリーダー", + "battle_paldea_gym": "SV 戦闘!ジムリーダー", + "battle_legendary_kanto": "XY 戦闘!ミュウツー", + "battle_legendary_raikou": "HGSS 戦闘!ライコウ", + "battle_legendary_entei": "HGSS 戦闘!エンテイ", + "battle_legendary_suicune": "HGSS 戦闘!スイクン", + "battle_legendary_lugia": "HGSS 戦闘!ルギア", + "battle_legendary_ho_oh": "HGSS 戦闘!ホウオウ", + "battle_legendary_regis_g5": "B2W2 戦闘!レジロック・レジアイス・レジスチル", + "battle_legendary_regis_g6": "ORAS 戦闘!レジロック・レジアイス・レジスチル", + "battle_legendary_gro_kyo": "ORAS 戦闘!ゲンシカイキ", + "battle_legendary_rayquaza": "ORAS 戦闘!超古代ポケモン", + "battle_legendary_deoxys": "ORAS 戦闘!デオキシス", + "battle_legendary_lake_trio": "ORAS 戦闘!ユクシー・エムリット・アグノム", + "battle_legendary_sinnoh": "ORAS 戦闘!伝説のポケモン(シンオウ)", + "battle_legendary_dia_pal": "ORAS 戦闘!ディアルガ・パルキア", + "battle_legendary_origin_forme": "LA 戦い:ディアルガ・パルキア(オリジンフォルム)", + "battle_legendary_giratina": "ORAS 戦闘!ギラティナ", + "battle_legendary_arceus": "HGSS アルセウス", + "battle_legendary_unova": "BW 戦闘!伝説のポケモン", + "battle_legendary_kyurem": "BW 戦闘!キュレム", + "battle_legendary_res_zek": "BW 戦闘!ゼクロム・レシラム", + "battle_legendary_xern_yvel": "XY 戦闘!ゼルネアス・イベルタル・ジガルデ", + "battle_legendary_tapu": "SM 戦闘!カプ", + "battle_legendary_sol_lun": "SM 戦闘!ソルガレオ・ルナアーラ", + "battle_legendary_ub": "SM 戦闘!ウルトラビースト", + "battle_legendary_dusk_dawn": "USUM 戦闘!日食・月食ネクロズマ", + "battle_legendary_ultra_nec": "USUM 戦闘!ウルトラネクロズマ", + "battle_legendary_zac_zam": "SWSH 戦闘!ザシアン・ザマゼンタ", + "battle_legendary_glas_spec": "SWSH 戦闘!ブリザポス・レイスポス", + "battle_legendary_calyrex": "SWSH 戦闘!バドレックス", + "battle_legendary_riders": "SWSH 戦闘!豊穣の王", + "battle_legendary_birds_galar": "SWSH 戦闘!伝説のとりポケモン", + "battle_legendary_ruinous": "SV 戦闘!災厄ポケモン", + "battle_legendary_kor_mir": "SV 戦闘!エリアゼロのポケモン", + "battle_legendary_loyal_three": "SV 戦闘!ともっこ", + "battle_legendary_ogerpon": "SV 戦闘!オーガポン", + "battle_legendary_terapagos": "SV 戦闘!テラパゴス", + "battle_legendary_pecharunt": "SV 戦闘!モモワロウ", + "battle_rival": "BW 戦闘!チェレン・ベル", + "battle_rival_2": "BW 戦闘!N", + "battle_rival_3": "BW 決戦!N", + "battle_trainer": "BW 戦闘!トレーナー", + "battle_wild": "BW 戦闘!野生ポケモン", + "battle_wild_strong": "BW 戦闘!強い野生ポケモン", + "end_summit": "ポケダンDX 天空の塔 最上階", + "battle_rocket_grunt": "HGSS 戦闘!ロケット団", + "battle_aqua_magma_grunt": "ORAS 戦闘!アクア団・マグマ団", + "battle_galactic_grunt": "BDSP 戦闘!ギンガ団", + "battle_plasma_grunt": "BW 戦闘!プラズマ団", + "battle_flare_grunt": "XY 戦闘!フレア団", + "battle_aether_grunt": "SM 戦闘!エーテル財団トレーナー", + "battle_skull_grunt": "SM 戦闘!スカル団", + "battle_macro_grunt": "SWSH 戦闘!トレーナー", + "battle_galactic_admin": "BDSP 戦闘!ギンガ団幹部", + "battle_skull_admin": "SM 戦闘!スカル団幹部", + "battle_oleana": "SWSH 戦闘!オリーヴ", + "battle_rocket_boss": "USUM 戦闘!レインボーロケット団ボス", + "battle_aqua_magma_boss": "ORAS 戦闘!アクア団・マグマ団のリーダー", + "battle_galactic_boss": "BDSP 戦闘!ギンガ団ボス", + "battle_plasma_boss": "B2W2 戦闘!ゲーチス", + "battle_flare_boss": "XY 戦闘!フラダリ", + "battle_aether_boss": "SM 戦闘!ルザミーネ", + "battle_skull_boss": "SM 戦闘!スカル団ボス", + "battle_macro_boss": "SWSH 戦闘!ローズ", + + "abyss": "ポケダン空 やみのかこう", + "badlands": "ポケダン空 こかつのたに", + "beach": "ポケダン空 しめったいわば", + "cave": "ポケダン空 そらのいただき(どうくつ)", + "construction_site": "ポケダン空 きょだいがんせきぐん", + "desert": "ポケダン空 きたのさばく", + "dojo": "ポケダン空 ガラガラどうじょう", + "end": "ポケダンDX 天空の塔", + "factory": "ポケダン空 かくされたいせき", + "fairy_cave": "ポケダン空 ほしのどうくつ", + "forest": "ポケダン空 くろのもり", + "grass": "ポケダン空 リンゴのもり", + "graveyard": "ポケダン空 しんぴのもり", + "ice_cave": "ポケダン空 だいひょうざん", + "island": "ポケダン空 えんがんのいわば", + "jungle": "Lmz - Jungle(ジャングル)", + "laboratory": "Firel - Laboratory(ラボラトリー)", + "lake": "ポケダン空 すいしょうのどうくつ", + "meadow": "ポケダン空 そらのいただき(もり)", + "metropolis": "Firel - Metropolis(大都市)", + "mountain": "ポケダン空 ツノやま", + "plains": "ポケダン空 そらのいただき(そうげん)", + "power_plant": "ポケダン空 エレキへいげん", + "ruins": "ポケダン空 ふういんのいわば", + "sea": "Andr06 - Marine Mystique(海の神秘性)", + "seabed": "Firel - Seabed(海底)", + "slum": "Andr06 - Sneaky Snom(ずるいユキハミ)", + "snowy_forest": "ポケダン空 そらのいただき(ゆきやま)", + "space": "Firel - Aether(エーテル)", + "swamp": "ポケダン空 とざされたうみ", + "tall_grass": "ポケダン空 のうむのもり", + "temple": "ポケダン空 ばんにんのどうくつ", + "town": "ポケダン空 ランダムダンジョン3", + "volcano": "ポケダン空 ねっすいのどうくつ", + "wasteland": "ポケダン空 まぼろしのだいち", + "encounter_ace_trainer": "BW 視線!エリートトレーナー", + "encounter_backpacker": "BW 視線!バックパッカー", + "encounter_clerk": "BW 視線!ビジネスマン", + "encounter_cyclist": "BW 視線!サイクリング", + "encounter_lass": "BW 視線!ミニスカート", + "encounter_parasol_lady": "BW 視線!パラソルおねえさん", + "encounter_pokefan": "BW 視線!だいすきクラブ", + "encounter_psychic": "BW 視線!サイキッカー", + "encounter_rich": "BW 視線!ジェントルマン", + "encounter_rival": "BW チェレンのテーマ", + "encounter_roughneck": "BW 視線!スキンヘッズ", + "encounter_scientist": "BW 視線!けんきゅういん", + "encounter_twins": "BW 視線!ふたごちゃん", + "encounter_youngster": "BW 視線!たんぱんこぞう", + "heal": "BW 回復", + "menu": "ポケダン空 ようこそ! ポケモンたちのせかいへ!", + "title": "ポケダン空 トップメニュー" +} diff --git a/src/locales/ja/challenges.json b/src/locales/ja/challenges.json index 54225ebf766..d137df14086 100644 --- a/src/locales/ja/challenges.json +++ b/src/locales/ja/challenges.json @@ -1,10 +1,10 @@ { - "title": "チャレンジを 設定", + "title": "チャレンジの設定", "illegalEvolution": "{{pokemon}}は このチャレンジで\n対象外の ポケモンに なってしまった!", "singleGeneration": { "name": "単一世代", - "desc": "{{gen}}世代からの ポケモンしか 使えません", - "desc_default": "選んだ 世代からの ポケモンしか 使えません", + "desc": "{{gen}}世代からの ポケモンしか 使えません", + "desc_default": "選んだ 世代からの ポケモンしか 使えません", "gen_1": "1", "gen_2": "2", "gen_3": "3", @@ -17,14 +17,20 @@ }, "singleType": { "name": "単一タイプ", - "desc": "{{type}}タイプの ポケモンしか 使えません", - "desc_default": "選んだ タイプの ポケモンしか 使えません" + "desc": "{{type}}タイプの ポケモンしか 使えません", + "desc_default": "選んだ タイプの ポケモンしか 使えません" }, "freshStart": { "name": "出直し", - "shortName": "出直し", - "desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません", + "desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の スターターしか 使えません", + "value.0": "オフ", + "value.1": "オン" + }, + "inverseBattle": { + "name": "反転バトル", + "shortName": "反バ", + "desc": "タイプ相性が 反転で、なんの タイプも 「効果はなし」が ありません\n他の チャレンジの 実績が 無効に されます", "value.0": "オフ", "value.1": "オン" } -} \ No newline at end of file +} diff --git a/src/locales/ja/command-ui-handler.json b/src/locales/ja/command-ui-handler.json index 0b2020a9517..6248a19785f 100644 --- a/src/locales/ja/command-ui-handler.json +++ b/src/locales/ja/command-ui-handler.json @@ -3,5 +3,5 @@ "ball": "ボール", "pokemon": "ポケモン", "run": "にげる", - "actionMessage": "{{pokemonName}}は どうする?" -} \ No newline at end of file + "actionMessage": "{{pokemonName}}は どうする?" +} diff --git a/src/locales/ja/common.json b/src/locales/ja/common.json index 9e26dfeeb6e..d10e3e42c70 100644 --- a/src/locales/ja/common.json +++ b/src/locales/ja/common.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "start": "スタート", + "luckIndicator": "運:", + "shinyOnHover": "色違い", + "commonShiny": "ふつう", + "rareShiny": "レア", + "epicShiny": "超レア" +} diff --git a/src/locales/ja/dialogue-double-battle.json b/src/locales/ja/dialogue-double-battle.json index 9e26dfeeb6e..b2632f3b990 100644 --- a/src/locales/ja/dialogue-double-battle.json +++ b/src/locales/ja/dialogue-double-battle.json @@ -1 +1,84 @@ -{} \ No newline at end of file +{ + "blue_red_double": { + "encounter": { + "1": "Blue: Hey Red, let's show them what we're made of!\n$Red: ...\n$Blue: This is Pallet Town Power!" + }, + "victory": { + "1": "Blue: That was a great battle!\n$Red: ..." + } + }, + "red_blue_double": { + "encounter": { + "1": "Red: ...!\n$Blue: He never talks much.\n$Blue: But dont let that fool you! He is a champ after all!" + }, + "victory": { + "1": "Red: ...!\n$Blue: Next time we will beat you!" + } + }, + "tate_liza_double": { + "encounter": { + "1": "Tate: Are you surprised?\n$Liza: We are two gym leaders at once!\n$Tate: We are twins!\n$Liza: We dont need to talk to understand each other!\n$Tate: Twice the power...\n$Liza: Can you handle it?" + }, + "victory": { + "1": "Tate: What? Our combination was perfect!\n$Liza: Looks like we need to train more..." + } + }, + "liza_tate_double": { + "encounter": { + "1": "Liza: Hihihi... Are you surprised?\n$Tate: Yes, we are really two gym leaders at once!\n$Liza: This is my twin brother Tate!\n$Tate: And this is my twin sister Liza!\n$Liza: Don't you think we are a perfect combination?" + }, + "victory": { + "1": "Liza: Are we...\n$Tate: ...not as strong as we thought?" + } + }, + "wallace_steven_double": { + "encounter": { + "1": "Steven: Wallace, let's show them the power of the champions!\n$Wallace: We will show you the power of Hoenn!\n$Steven: Let's go!" + }, + "victory": { + "1": "Steven: That was a great battle!\n$Wallace: We will win next time!" + } + }, + "steven_wallace_double": { + "encounter": { + "1": "Steven: Do you have any rare Pokémon?\n$Wallace: Steven... We are here for a battle, not to show off our Pokémon.\n$Steven: Oh... I see... Let's go then!" + }, + "victory": { + "1": "Steven: Now that we are done with the battle, let's show off our Pokémon!\n$Wallace: Steven..." + } + }, + "alder_iris_double": { + "encounter": { + "1": "Alder: We are the strongest trainers in Unova!\n$Iris: Fights against strong trainers are the best!" + }, + "victory": { + "1": "Alder: Wow! You are super strong!\n$Iris: We will win next time!" + } + }, + "iris_alder_double": { + "encounter": { + "1": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?", + "1_female": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?" + }, + "victory": { + "1": "Iris: A loss like this is not easy to take...\n$Alder: But we will only get stronger with every loss!" + } + }, + "piers_marnie_double": { + "encounter": { + "1": "Marnie: Brother, let's show them the power of Spikemuth!\n$Piers: We bring darkness!" + }, + "victory": { + "1": "Marnie: You brought light to our darkness!\n$Piers: Its too bright..." + } + }, + "marnie_piers_double": { + "encounter": { + "1": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing...", + "1_female": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing..." + }, + "victory": { + "1": "Piers: Now that was a great concert!\n$Marnie: Brother..." + } + } +} diff --git a/src/locales/ja/dialogue-final-boss.json b/src/locales/ja/dialogue-final-boss.json index 9e26dfeeb6e..f20d0f013d1 100644 --- a/src/locales/ja/dialogue-final-boss.json +++ b/src/locales/ja/dialogue-final-boss.json @@ -1 +1,10 @@ -{} \ No newline at end of file +{ + "encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", + "encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", + "firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.", + "secondStageWin": "…Magnificent.", + "key_ordinal_one": "st", + "key_ordinal_two": "nd", + "key_ordinal_few": "rd", + "key_ordinal_other": "th" +} diff --git a/src/locales/ja/dialogue-misc.json b/src/locales/ja/dialogue-misc.json index 9e26dfeeb6e..2f333b5f383 100644 --- a/src/locales/ja/dialogue-misc.json +++ b/src/locales/ja/dialogue-misc.json @@ -1 +1,6 @@ -{} \ No newline at end of file +{ + "ending": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?", + "ending_female": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.", + "ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.", + "ending_name": "Devs" +} diff --git a/src/locales/ja/dialogue.json b/src/locales/ja/dialogue.json index 9e26dfeeb6e..6130ade1cb4 100644 --- a/src/locales/ja/dialogue.json +++ b/src/locales/ja/dialogue.json @@ -1 +1,2827 @@ -{} \ No newline at end of file +{ + "youngster": { + "encounter": { + "1": "よっ 勝負する?", + "2": "あっ 君も 駆け出し トレーナーだね?", + "2_female": "あっ きみも 駆け出し トレーナーだね?", + "3": "あっ 初めてみる 人! 勝負だ!", + "4": "さっき 負けて 悔しいから ポケモン 探してるんだよ。\nでも 君 弱そうだな…… よしっ! 勝負 しようぜ!", + "4_female": "さっき 負けて 悔しいから ポケモン 探してるんだよ。\nでも 君 弱そうだな…… よしっ! 勝負 しようぜ!", + "5": "お久しぶりかな? 初めましてかな?\n覚えてないけど ヨロシクね!", + "6": "よーし 行くぞー!", + "7": "よっしゃ 行っくぜー!\n僕の パワー 見せてやる!", + "8": "へへへ…… 見せて あげるね!\n僕の ポケモンの すごさを!", + "9": "挨拶 なんて よかよか! いつでも かかってきんしゃい!", + "9_female": "挨拶 なんて よかよか! いつでも かかってきんしゃい!", + "10": "小僧  だからって 油断してると\n君 泣いちゃう かもしれないよー.", + "11": "大事に 育てた ポケモンだぞー!\n絶対 倒しちゃ ダメなんだぞ!", + "12": "よく来たな!\nこっから 先は 一筋縄じゃ いかないぞ!", + "12_female": "よく来たな!\nこっから 先は 一筋縄じゃ いかないぞ!", + "13": "勝負は 続くよ! いつまでも!\n終わりなき 世界へ ようこそ!", + "13_female": "勝負は 続くよ!\nいつまでも! 終わりなき 世界へ ようこそ!" + }, + "victory": { + "1": "スゴッ! 君 強いんだ!", + "1_femal1e": "スゴッ! 君 強いんだ!", + "2": "敵わなかったね?", + "3": "僕 大きくなったら 絶対 君を 倒すよ!", + "4": "ゲッ 戦えるポケモン もう いない…", + "5": "トホ…… トホホーイ……\nまた 負けちゃったよ……", + "5_female": "トホ…… トホホーイ……\nまた 負けちゃったよ……", + "6": "うわー! 負けたー!!", + "7": "すごいな 君の パワーは!\nビックリで ドッキリだよ!", + "8": "そんな…… どうして……\n僕の ポケモンは サイキョー なのに……", + "9": "また バトルして くれよな!\n次は 負けないからな!", + "10": "も~! 僕は 小僧だけ だよ!\nそんな全力で 戦っちゃ ひどいぞ!", + "11": "君の ポケモンの 方が すごい!\n僕のと 交換しておくれよ!", + "12": "さっきは 勢いで 言ったけど\n一筋縄って どんな縄?", + "13": "あはは さすが! さすがだね!\nもう 君も この世界の 住人だ!" + } + }, + "lass": { + "encounter": { + "1": "Let's have a battle, shall we?", + "2": "You look like a new trainer. Let's have a battle!", + "2_female": "You look like a new trainer. Let's have a battle!", + "3": "I don't recognize you. How about a battle?", + "4": "Let's have a fun Pokémon battle!", + "5": "I'll show you the ropes of how to really use Pokémon!", + "6": "A serious battle starts from a serious beginning! Are you sure you're ready?", + "6_female": "A serious battle starts from a serious beginning! Are you sure you're ready?", + "7": "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", + "8": "You'd better go easy on me, OK? Though I'll be seriously fighting!", + "9": "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." + }, + "victory": { + "1": "That was impressive! I've got a lot to learn.", + "2": "I didn't think you'd beat me that bad…", + "2_female": "I didn't think you'd beat me that bad…", + "3": "I hope we get to have a rematch some day.", + "4": "That was pretty amazingly fun! You've totally exhausted me…", + "5": "You actually taught me a lesson! You're pretty amazing!", + "6": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", + "6_female": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", + "7": "I don't need memories like this. Deleting memory…", + "8": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", + "8_female": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", + "9": "I'm actually getting tired of battling… There's gotta be something new to do…" + } + }, + "breeder": { + "encounter": { + "1": "Obedient Pokémon, selfish Pokémon… Pokémon have unique characteristics.", + "2": "Even though my upbringing and behavior are poor, I've raised my Pokémon well.", + "3": "Hmm, do you discipline your Pokémon? Pampering them too much is no good." + }, + "victory": { + "1": "It is important to nurture and train each Pokémon's characteristics.", + "2": "Unlike my diabolical self, these are some good Pokémon.", + "3": "Too much praise can spoil both Pokémon and people." + }, + "defeat": { + "1": "You should not get angry at your Pokémon, even if you lose a battle.", + "2": "Right? Pretty good Pokémon, huh? I'm suited to raising things.", + "3": "No matter how much you love your Pokémon, you still have to discipline them when they misbehave." + } + }, + "breeder_female": { + "encounter": { + "1": "Pokémon never betray you. They return all the love you give them.", + "2": "Shall I give you a tip for training good Pokémon?", + "3": "I have raised these very special Pokémon using a special method." + }, + "victory": { + "1": "Ugh… It wasn't supposed to be like this. Did I administer the wrong blend?", + "2": "How could that happen to my Pokémon… What are you feeding your Pokémon?", + "3": "If I lose, that tells you I was just killing time. It doesn't damage my ego at all." + }, + "defeat": { + "1": "This proves my Pokémon have accepted my love.", + "2": "The real trick behind training good Pokémon is catching good Pokémon.", + "3": "Pokémon will be strong or weak depending on how you raise them." + } + }, + "fisherman": { + "encounter": { + "1": "Aack! You made me lose a bite!\nWhat are you going to do about it?", + "2": "Go away! You're scaring the Pokémon!", + "3": "Let's see if you can reel in a victory!" + }, + "victory": { + "1": "Just forget about it.", + "2": "Next time, I'll be reelin' in the triumph!", + "3": "Guess I underestimated the currents this time." + } + }, + "fisherman_female": { + "encounter": { + "1": "Woah! I've hooked a big one!", + "2": "Line's in, ready to reel in success!", + "3": "Ready to make waves!" + }, + "victory": { + "1": "I'll be back with a stronger hook.", + "2": "I'll reel in victory next time.", + "3": "I'm just sharpening my hooks for the comeback!" + } + }, + "swimmer": { + "encounter": { + "1": "Time to dive in!", + "2": "Let's ride the waves of victory!", + "3": "Ready to make a splash!" + }, + "victory": { + "1": "Drenched in defeat!", + "2": "A wave of defeat!", + "3": "Back to shore, I guess." + } + }, + "backpacker": { + "encounter": { + "1": "Pack up, game on!", + "2": "Let's see if you can keep pace!", + "3": "Gear up, challenger!", + "4": "I've spent 20 years trying to find myself… But where am I?" + }, + "victory": { + "1": "Tripped up this time!", + "2": "Oh, I think I'm lost.", + "3": "Dead end!", + "4": "Wait up a second! Hey! Don't you know who I am?" + } + }, + "ace_trainer": { + "encounter": { + "1": "You seem quite confident.", + "1_female": "You seem quite confident.", + "2": "Your Pokémon… Show them to me…", + "3": "Because I'm an Ace Trainer, people think I'm strong.", + "4": "Are you aware of what it takes to be an Ace Trainer?" + }, + "victory": { + "1": "Yes… You have good Pokémon…", + "2": "What?! But I'm a battling genius!", + "3": "Of course, you are the main character!", + "3_female": "Of course, you are the main character!", + "4": "OK! OK! You could be an Ace Trainer!", + "4_female": "OK! OK! You could be an Ace Trainer!" + }, + "defeat": { + "1": "I am devoting my body and soul to Pokémon battles!", + "2": "All within my expectations… Nothing to be surprised about…", + "3": "I thought I'd grow up to be a frail person who looked like they would break if you squeezed them too hard.", + "4": "Of course I'm strong and don't lose. It's important that I win gracefully." + } + }, + "parasol_lady": { + "encounter": { + "1": "Time to grace the battlefield with elegance and poise!" + }, + "victory": { + "1": "My elegance remains unbroken!" + } + }, + "twins": { + "encounter": { + "1": "Get ready, because when we team up, it's double the trouble!", + "2": "Two hearts, one strategy – let's see if you can keep up with our twin power!", + "3": "Hope you're ready for double trouble, because we're about to bring the heat!", + "3_female": "Hope you're ready for double trouble, because we're about to bring the heat!" + }, + "victory": { + "1": "We may have lost this round, but our bond remains unbreakable!", + "2": "Our twin spirit won't be dimmed for long.", + "3": "We'll come back stronger as a dynamic duo!" + }, + "defeat": { + "1": "Twin power reigns supreme!", + "2": "Two hearts, one triumph!", + "3": "Double the smiles, double the victory dance!" + } + }, + "cyclist": { + "encounter": { + "1": "Get ready to eat my dust!", + "2": "Gear up, challenger! I'm about to leave you in the dust!", + "3": "Pedal to the metal, let's see if you can keep pace!" + }, + "victory": { + "1": "Spokes may be still, but determination pedals on.", + "2": "Outpaced!", + "3": "The road to victory has many twists and turns yet to explore." + } + }, + "black_belt": { + "encounter": { + "1": "I praise your courage in challenging me! For I am the one with the strongest kick!", + "2": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?", + "2_female": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?" + }, + "victory": { + "1": "Oh. The Pokémon did the fighting. My strong kick didn't help a bit.", + "2": "Hmmm… If I was going to lose anyway, I was hoping to get totally messed up in the process." + } + }, + "battle_girl": { + "encounter": { + "1": "You don't have to try to impress me. You can lose against me." + }, + "victory": { + "1": "It's hard to say good-bye, but we are running out of time…" + } + }, + "hiker": { + "encounter": { + "1": "My middle-age spread has given me as much gravitas as the mountains I hike!", + "2": "I inherited this big-boned body from my parents… I'm like a living mountain range…" + }, + "victory": { + "1": "At least I cannot lose when it comes to BMI!", + "2": "It's not enough… It's never enough. My bad cholesterol isn't high enough…" + } + }, + "ranger": { + "encounter": { + "1": "When I am surrounded by nature, most other things cease to matter.", + "2": "When I'm living without nature in my life, sometimes I'll suddenly feel an anxiety attack coming on." + }, + "victory": { + "1": "It doesn't matter to the vastness of nature whether I win or lose…", + "2": "Something like this is pretty trivial compared to the stifling feelings of city life." + }, + "defeat": { + "1": "I won the battle. But victory is nothing compared to the vastness of nature…", + "2": "I'm sure how you feel is not so bad if you compare it to my anxiety attacks…" + } + }, + "scientist": { + "encounter": { + "1": "My research will lead this world to peace and joy." + }, + "victory": { + "1": "I am a genius… I am not supposed to lose against someone like you…" + } + }, + "school_kid": { + "encounter": { + "1": "…Heehee. I'm confident in my calculations and analysis.", + "2": "I'm gaining as much experience as I can because I want to be a Gym Leader someday." + }, + "victory": { + "1": "Ohhhh… Calculation and analysis are perhaps no match for chance…", + "2": "Even difficult, trying experiences have their purpose, I suppose." + } + }, + "artist": { + "encounter": { + "1": "I used to be popular, but now I am all washed up." + }, + "victory": { + "1": "As times change, values also change. I realized that too late." + } + }, + "guitarist": { + "encounter": { + "1": "Get ready to feel the rhythm of defeat as I strum my way to victory!" + }, + "victory": { + "1": "Silenced for now, but my melody of resilience will play on." + } + }, + "worker": { + "encounter": { + "1": "It bothers me that people always misunderstand me. I'm a lot more pure than everyone thinks." + }, + "victory": { + "1": "I really don't want my skin to burn, so I want to stay in the shade while I work." + } + }, + "worker_female": { + "encounter": { + "1": "It bothers me that people always misunderstand me.\n$I'm a lot more pure than everyone thinks." + }, + "victory": { + "1": "I really don't want my skin to burn, so I want to stay in the shade while I work." + }, + "defeat": { + "1": "My body and mind aren't necessarily always in sync." + } + }, + "worker_double": { + "encounter": { + "1": "I'll show you we can break you. We've been training in the field!" + }, + "victory": { + "1": "How strange… How could this be… I shouldn't have been outmuscled." + } + }, + "hex_maniac": { + "encounter": { + "1": "I normally only ever listen to classical music, but if I lose, I think I shall try a bit of new age!", + "2": "I grow stronger with each tear I cry." + }, + "victory": { + "1": "Is this the dawning of the age of Aquarius?", + "2": "Now I can get even stronger. I grow with every grudge." + }, + "defeat": { + "1": "New age simply refers to twentieth century classical composers, right?", + "2": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself.", + "2_female": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself." + } + }, + "psychic": { + "encounter": { + "1": "Hi! Focus!" + }, + "victory": { + "1": "Eeeeek!" + } + }, + "officer": { + "encounter": { + "1": "Brace yourself, because justice is about to be served!", + "2": "Ready to uphold the law and serve justice on the battlefield!" + }, + "victory": { + "1": "The weight of justice feels heavier than ever…", + "2": "The shadows of defeat linger in the precinct." + } + }, + "beauty": { + "encounter": { + "1": "My last ever battle… That's the way I'd like us to view this match…" + }, + "victory": { + "1": "It's been fun… Let's have another last battle again someday…" + } + }, + "baker": { + "encounter": { + "1": "Hope you're ready to taste defeat!", + "1_female": "Hope you're ready to taste defeat!" + }, + "victory": { + "1": "I'll bake a comeback." + } + }, + "biker": { + "encounter": { + "1": "Time to rev up and leave you in the dust!" + }, + "victory": { + "1": "I'll tune up for the next race." + } + }, + "firebreather": { + "encounter": { + "1": "My flames shall devour you!", + "2": "My soul is on fire. I'll show you how hot it burns!", + "3": "Step right up and take a look!" + }, + "victory": { + "1": "I burned down to ashes...", + "2": "Yow! That's hot!", + "3": "Ow! I scorched the tip of my nose!" + } + }, + "sailor": { + "encounter": { + "1": "Matey, you're walking the plank if you lose!", + "2": "Come on then! My sailor's pride is at stake!", + "3": "Ahoy there! Are you seasick?", + "3_female": "Ahoy there! Are you seasick?" + }, + "victory": { + "1": "Argh! Beaten by a kid!", + "2": "Your spirit sank me!", + "3": "I think it's me that's seasick..." + } + }, + "archer": { + "encounter": { + "1": "Before you go any further, let's see how you fare against us, Team Rocket!", + "2": "I have received reports that your skills are not insignificant. Let's see if they are true.", + "3": "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." + }, + "victory": { + "1": "What a blunder!", + "2": "With my current skills, I was not up to the task after all.", + "3": "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + } + }, + "ariana": { + "encounter": { + "1": "Hold it right there! We can't someone on the loose.\n$It's harmful to Team Rocket's pride, you see.", + "2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told", + "3": "Your trip ends here. I'm going to take you down!" + }, + "victory": { + "1": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", + "1_female": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", + "2": "I... I'm shattered...", + "3": "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + } + }, + "proton": { + "encounter": { + "1": "What do you want? If you interrupt our work, don't expect any mercy!", + "2": "What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket…\n$I strongly urge you not to interfere with our business!", + "3": "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + "1": "The fortress came down!", + "2": "You may have won this time… But all you did was make Team Rocket's wrath grow…", + "3": "I am defeated… But I will not forget this!" + } + }, + "petrel": { + "encounter": { + "1": "Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni.\n$The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni?\n$I don't even look like Giovanni? How come? I've worked so hard to mimic him!", + "2": "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + "3": "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + "1": "OK, OK. I'll tell you where he is.", + "2": "I… I couldn't do a thing… Giovanni, please forgive me…", + "3": "No, I can't let this affect me. I have to inform the others…" + } + }, + "tabitha": { + "encounter": { + "1": "Hehehe! So you've come all the way here! But you're too late!", + "2": "Hehehe... Got here already, did you? We underestimated you! But this is it! \n$I'm a cut above the Grunts you've seen so far. I'm not stalling for time.\n$I'm going to pulverize you!", + "3": "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + "1": "Hehehe! You might have beaten me, but you don't stand a chance against the boss!\n$If you get lost now, you won't have to face a sound whipping!", + "2": "Hehehe... So, I lost, too...", + "3": "Ahya! How could this be? For an Admin like me to lose to some random trainer...", + "3_female": "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + } + }, + "courtney": { + "encounter": { + "1": "Don't. Get. In. My. Way.", + "2": "You... ...I want to...analyze. Ahahaha", + "3": "... Well then...Deleting..." + }, + "victory": { + "1": "Hah hah... Uhn...hah hah...", + "2": "As anticipated. Unanticipated. You. Target lock...completed.\n$Commencing...experiment. You. Forever. Aha... ♪", + "3": "That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + } + }, + "shelly": { + "encounter": { + "1": "Ahahahaha! You're going to meddle in Team Aqua's affairs?\n$You're either absolutely fearless, simply ignorant, or both!\n$You're so cute, you're disgusting! I'll put you down", + "2": "What's this? Who's this spoiled brat?", + "3": "Cool your jets. Be patient. I'll crush you shortly.", + "3_female": "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + "1": "Ahahahaha! We got meddled with unexpectedly! We're out of options.\n$We'll have to pull out. But this isn't the last you'll see of Team Aqua!\n$We have other plans! Don't you forget it!", + "2": "Ahhh?! Did I go too easy on you?!", + "3": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie.", + "3_female": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie." + } + }, + "matt": { + "encounter": { + "1": "All right then, until the boss has time for you, I'll be your opponent!", + "2": "Hooah! Full on! I'm burning up! Well! Welll! Wellllll! Let's battle it out until we've got nothing left!", + "3": "Hoo hah! I'm gonna smash you up!" + }, + "victory": { + "1": "Muwuhahaha! That battle was fun even though I lost!", + "2": "I can feel it! I can feel it, all right! The strength coming offa you!\n$More! I still want more! But looks like we're outta time...", + "3": "Oho! That's a loss I can be proud of!", + "3_female": "Oho! That's a loss I can be proud of!" + } + }, + "mars": { + "encounter": { + "1": "I'm Mars, one of Team Galactic's top Commanders.", + "2": "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + "3": "Feeling nervous? You should be!", + "3_female": "Feeling nervous? You should be!" + }, + "victory": { + "1": "This can't be happening! How did I lose?!", + "2": "You have some skill, I'll give you that.", + "3": "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + "1": "Jupiter, Commander of Team Galactic, at your service.", + "2": "Resistance is futile. Team Galactic will prevail!", + "3": "You're trembling... scared already?" + }, + "victory": { + "1": "No way... I lost?!", + "2": "Impressive, you've got guts!", + "3": "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + "1": "I am Saturn, Commander of Team Galactic.", + "2": "Our mission is absolute. Any hindrance will be obliterated!", + "3": "Is that fear I see in your eyes?" + }, + "victory": { + "1": "Impossible... Defeated by you?!", + "2": "You have proven yourself a worthy adversary.", + "3": "Bestowed in defeat... This is unacceptable." + } + }, + "zinzolin": { + "encounter": { + "1": "You could become a threat to Team Plasma, so we will eliminate you here and now!", + "1_female": "You could become a threat to Team Plasma, so we will eliminate you here and now!", + "2": "You don't have the sense to know when to quit, it seems. It's an act of mercy on my part to bring an end to this now!", + "3": "You're an impressive Trainer to have made it this far. But it ends here.", + "3_female": "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + "1": "Ghetsis... I have failed you...", + "2": "It's bitter cold. I'm shivering. I'm suffering. Yet, we will stand victorious.", + "3": "Hmph. You're a smarter Trainer than I expected, but not smart enough.", + "3_female": "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + "1": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + "1_female": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + "2": "It seems you don't know when to give up. I'll make sure no one interferes with our plans!", + "3": "You are a remarkable Trainer to have made it this far. But this is where it ends.", + "3_female": "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + "1": "Ghetsis... I have failed my mission...", + "2": "The cold is piercing. I'm shivering. I'm suffering. Yet, we will stand triumphant.", + "3": "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + "1": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + "1_female": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + "2": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + "2_female": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + "3": "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + "1": "Ah, you're quite strong. Oh yes—very strong, indeed.", + "2": "Ding-ding-ding! You did it! To the victor go the spoils!", + "2_female": "Ding-ding-ding! You did it! To the victor go the spoils!", + "3": "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + "1": "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + "2": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + "2_female": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + "3": "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + "1": "You're quite strong. Oh yes—very strong, indeed.", + "2": "Ding-ding-ding! You've done well. Victory is yours.", + "3": "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + "1": "Prepare for trouble!", + "2": "We're pulling a big job here! Get lost, kid!", + "2_female": "We're pulling a big job here! Get lost, kid!", + "3": "Hand over your Pokémon, or face the wrath of Team Rocket!", + "4": "You're about to experience the true terror of Team Rocket!", + "5": "Hey, kid! Me am a Team Rocket member kind of guy!", + "5_female": "Hey, kid! Me am a Team Rocket member kind of guy!" + }, + "victory": { + "1": "Team Rocket blasting off again!", + "2": "Oh no! I dropped the Lift Key!", + "3": "I blew it!", + "4": "My associates won't stand for this!", + "5": "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" + } + }, + "magma_grunt": { + "encounter": { + "1": "If you get in the way of Team Magma, don’t expect any mercy!", + "2": "You'd better not interfere with our plans! We're making the world a better place!", + "3": "You're in the way! Team Magma has no time for kids like you!", + "4": "I hope you brought marshmallows because things are about to heat up!", + "5": "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + "1": "Huh? I lost?!", + "2": "I can't believe I lost! I even skipped lunch for this", + "3": "No way! You're just a kid!", + "3_female": "No way! You're just a kid!", + "4": "Urrrgh... I should've ducked into our hideout right away...", + "5": "You beat me... Do you think the boss will dock my pay for this?" + } + }, + "aqua_grunt": { + "encounter": { + "1": "No one who crosses Team Aqua gets any mercy, not even kids!", + "2": "Grrr... You've got some nerve meddling with Team Aqua!", + "3": "You're about to get soaked! And not just from my water Pokémon!", + "4": "We, Team Aqua, exist for the good of all!", + "5": "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + "1": "You're kidding me!", + "2": "Arrgh, I didn't count on being meddled with by some meddling kid!", + "3": "I lost?! Guess I'll have to swim back to the hideout now...", + "4": "Oh, man, what a disaster... The boss is going to be furious...", + "5": "You beat me... Do you think the boss will make me walk the plank for this?" + } + }, + "galactic_grunt": { + "encounter": { + "1": "Don't mess with Team Galactic!", + "2": "Witness the power of our technology and the future we envision!", + "3": "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + "4": "Get ready to lose!", + "5": "Hope you're ready for a cosmic beatdown!", + "5_female": "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + "1": "Shut down...", + "2": "This setback means nothing in the grand scheme.", + "3": "Our plans are bigger than this defeat.", + "4": "How?!", + "5": "Note to self: practice Pokémon battling, ASAP." + } + }, + "plasma_grunt": { + "encounter": { + "1": "We won't tolerate people who have different ideas!", + "2": "If I win against you, release your Pokémon!", + "3": "If you get in the way of Team Plasma, I'll take care of you!", + "4": "Team Plasma will liberate Pokémon from selfish humans like you!", + "5": "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + "1": "Plasmaaaaaaaaa!", + "2": "How could I lose...", + "3": "...What a weak Pokémon, I'll just have to go steal some better ones!", + "4": "Great plans are always interrupted.", + "5": "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + } + }, + "flare_grunt": { + "encounter": { + "1": "Your Pokémon are no match for the elegance of Team Flare.", + "2": "Hope you brought your sunglasses, because things are about to get bright!", + "2_female": "Hope you brought your sunglasses, because things are about to get bright!", + "3": "Team Flare will cleanse the world of imperfection!", + "4": "Prepare to face the brilliance of Team Flare!", + "5": "Fashion is most important to us!" + }, + "victory": { + "1": "The future doesn't look bright for me.", + "2": "Perhaps there's more to battling than I thought. Back to the drawing board.", + "3": "Gahh?! I lost?!", + "4": "Even in defeat, Team Flare's elegance shines through.", + "5": "You may have beaten me, but when I lose, I go out in style!" + } + }, + "aether_grunt": { + "encounter": { + "1": "I'll fight you with all I have to wipe you out!", + "2": "I don't care if you're a kid or what. I'll send you flying if you threaten us!", + "2_female": "I don't care if you're a kid or what. I'll send you flying if you threaten us!", + "3": "I was told to turn away Trainers, whomever they might be!", + "4": "I'll show you the power of Aether Paradise!", + "5": "Now that you've learned of the darkness at the heart of Aether Paradise, we'll need you to conveniently disappear!" + }, + "victory": { + "1": "Hmph! You seem to have a lot of skill.", + "2": "What does this mean? What does this mean!", + "3": "Hey! You're so strong that there's no way I can turn you away!", + "4": "Hmm... It seems as though I may have lost.", + "5": "Here's an impression for you: Aiyee!" + } + }, + "faba": { + "encounter": { + "1": "I, Branch Chief Faba, shall show you the harshness of the real world!", + "2": "The man who is called Aether Paradise's last line of defense is to battle a mere child?", + "2_female": "The man who is called Aether Paradise's last line of defense is to battle a mere child?", + "3": "I, Faba, am the Aether Branch Chief. The only one in the world, I'm irreplaceable." + }, + "victory": { + "1": "Aiyee!", + "2": "H-h-how can this be?! How could this child...", + "2_female": "H-h-how can this be?! How could this child...", + "3": "This is why... This is why I can't bring myself to like children." + } + }, + "skull_grunt": { + "encounter": { + "1": "We're not bad-we're just hard!", + "2": "You want some? That's how we say hello! Nice knowing you, punks!", + "2_female": "You want some? That's how we say hello! Nice knowing you, punks!", + "3": "We're just a bunch of guys and gals with a great interest in other people's Pokémon!", + "4": "Why you trying to act hard when we're already hard as bones out here, homie?", + "4_female": "Why you trying to act hard when we're already hard as bones out here, homie?", + "5": "Team Skull represent! We can't pay the rent! Had a lot of fun, but our youth was misspent!", + "5_female": "Team Skull represent! We can't pay the rent! Had a lot of fun, but our youth was misspent!" + }, + "victory": { + "1": "Huh? Is it over already?", + "2": "Time for us to break out, yo! Gotta tell y'all peace out, yo!", + "3": "We don't need your wack Pokémon anyway!", + "4": "Wha-?! This kid's way too strong-no bones about it!", + "5": "So, what? I'm lower than a Pokémon?! I already got self-esteem issues, man." + } + }, + "plumeria": { + "encounter": { + "1": " ...Hmph. You don't look like anything special to me.", + "1_female": " ...Hmph. You don't look like anything special to me.", + "2": "It takes these dumb Grunts way too long to deal with you kids...", + "3": "Mess with anyone in Team Skull, and I'll show you how serious I can get." + }, + "victory": { + "1": "Hmmph! You're pretty strong. I'll give you that.", + "1_female": "Hmmph! You're pretty strong. I'll give you that.", + "2": "Hmmph. Guess you are pretty tough. Now I understand why my Grunts waste so much time battling kids.", + "3": "Hmmph! I guess I just have to hold that loss." + } + }, + "macro_grunt": { + "encounter": { + "1": "It looks like this is the end of the line for you!", + "2": "You are a trainer aren't you? I'm afraid that doesn't give you the right to interfere in our work.", + "2_female": "You are a trainer aren't you? I'm afraid that doesn't give you the right to interfere in our work.", + "3": "I'm from Macro Cosmos Insurance! Do you have a life insurance policy?" + }, + "victory": { + "1": "I have little choice but to respectfully retreat.", + "2": "Having to give up my pocket money... Losing means I'm back in the red...", + "3": "Nobody can beat Macro Cosmos when it comes to our dedication to our work!" + } + }, + "oleana": { + "encounter": { + "1": "I won't let anyone interfere with Mr. Rose's plan!", + "2": "So, you got through all of the special staff that I had ordered to stop you. I would expect nothing less.", + "3": "For the chairman! I won't lose!" + }, + "victory": { + "1": "*sigh* I wasn't able to win... Oleana...you really are a hopeless woman.", + "2": "Arghhh! This is inexcusable... What was I thinking... Any trainer who's made it this far would be no pushover..", + "2_female": "Arghhh! This is inexcusable... What was I thinking... Any trainer who's made it this far would be no pushover..", + "3": "*sigh* I am one tired Oleana..." + } + }, + "rocket_boss_giovanni_1": { + "encounter": { + "1": "So! I must say, I am impressed you got here!" + }, + "victory": { + "1": "WHAT! This cannot be!" + }, + "defeat": { + "1": "Mark my words. Not being able to measure your own strength shows that you are still a child.", + "1_female": "Mark my words. Not being able to measure your own strength shows that you are still a child." + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + "1": "My old associates need me... Are you going to get in my way?" + }, + "victory": { + "1": "How is this possible...? The precious dream of Team Rocket has become little more than an illusion..." + }, + "defeat": { + "1": "Team Rocket will be reborn again, and I will rule the world!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + "1": "I will bury you by my own hand. I hope you appreciate this honor!" + }, + "victory": { + "1": "Ugh! You are... quite capable...\nI fell behind, but only by an inch..." + }, + "defeat": { + "1": "Team Magma will prevail!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + "1": "You are the final obstacle remaining between me and my goals.\n$Brace yourself for my ultimate attack! Fuhahaha!" + }, + "victory": { + "1": "This... This is not.. Ngh..." + }, + "defeat": { + "1": "And now... I will transform this planet to a land ideal for humanity." + } + }, + "aqua_boss_archie_1": { + "encounter": { + "1": "I'm the leader of Team Aqua, so I'm afraid it's the rope's end for you." + }, + "victory": { + "1": "Let's meet again somewhere. I'll be sure to remember that face." + }, + "defeat": { + "1": "Brilliant! My team won't hold back now!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + "1": "I've been waiting so long for this day to come.\nThis is the true power of my team!" + }, + "victory": { + "1": "Like I figured..." + }, + "defeat": { + "1": "I'll return everything in this world to its original, pure state!!" + } + }, + "galactic_boss_cyrus_1": { + "encounter": { + "1": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!", + "1_female": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!" + }, + "victory": { + "1": "Interesting. And quite curious." + }, + "defeat": { + "1": "I will create my new world..." + } + }, + "galactic_boss_cyrus_2": { + "encounter": { + "1": "So we meet again. It seems our fates have become intertwined.\n$But here and now, I will finally break that bond!" + }, + "victory": { + "1": "How? How? HOW?!" + }, + "defeat": { + "1": "Farewell." + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + "1": "I won't allow anyone to stop me! No matter who does what!" + }, + "victory": { + "1": "How can this be? I'm the creator of Team Plasma! I'm perfect!" + }, + "defeat": { + "1": "I am the perfect ruler of a perfect new world! Mwa ha ha!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + "1": "Come now! I want to see your face at the moment you lose all hope!" + }, + "victory": { + "1": "My calculations... No! My careful schemes! The world should be mine!" + }, + "defeat": { + "1": "Kyurem! Use Absofusion!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + "1": "Do you want to stop me? Show me in battle." + }, + "victory": { + "1": "You are here to stop me. But I ask you to wait. " + }, + "defeat": { + "1": "Pokemon...Shall no longer exist." + } + }, + "flare_boss_lysandre_2": { + "encounter": { + "1": "The future you want, or the future I want... Let us see which one is more deserving, shall we?" + }, + "victory": { + "1": "Whaugh!" + }, + "defeat": { + "1": "Fools with no vision will continue to befoul this beautiful world." + } + }, + "aether_boss_lusamine_1": { + "encounter": { + "1": "You're going to startle my sweet beast! It looks like I'll need to silence you first." + }, + "victory": { + "1": "How... how can you be so awful!" + }, + "defeat": { + "1": "Hmph..." + } + }, + "aether_boss_lusamine_2": { + "encounter": { + "1": "Why must you continue to pester me?! I am sick of you. Sick through and through!\n$Enough with this useless talk.. with Nihilego's power I will show you how wrong you were to come here!" + }, + "victory": { + "1": "Aaauuuggghhhhhhhhh!!!" + }, + "defeat": { + "1": "All that I want is my precious beast! I don't care about any of the rest of you!" + } + }, + "skull_boss_guzma_1": { + "encounter": { + "1": "The hated boss who beats you down and beats you down and never lets up...\n$Yeah. Big bad Guzma is here!" + }, + "victory": { + "1": "Tch. I'm gonna beat you down one of these days!" + }, + "defeat": { + "1": "And you came all the way out here just for that, huh?" + } + }, + "skull_boss_guzma_2": { + "encounter": { + "1": "Doesn't matter who I'm up against, I'm gonna beat them down!\n$That's what big bad Guzma is all about!" + }, + "victory": { + "1": "Guzma!!! What is wrong with you?!" + }, + "defeat": { + "1": "Y'all are stupid!" + } + }, + "macro_boss_rose_1": { + "encounter": { + "1": "I must provide limitless energy to ensure everlasting prosperity for everyone...\n$It is my purpose, my duty, my destiny!" + }, + "victory": { + "1": "You still don't understand, trainer...\n$We... No, I am going to change the course of history!" + }, + "defeat": { + "1": "You still don't understand a thing!" + } + }, + "macro_boss_rose_2": { + "encounter": { + "1": "I'm committed to solving the energy problem in the Galar region—and, of course, around the world.\n$My experience and accomplishments that made Macro Cosmos a success are proof my methods work.\n$I don't intend to change my mind, even if I lose." + }, + "victory": { + "1": "I'd forgotten how great Pokémon battles are! It's been so long since I battled...\n$That sure was satisfying, I accept defeat for this battle." + }, + "defeat": { + "1": "I suppose it must seem that I am doing something terrible. I don't expect you to understand.\n$But I must provide the Galar region with limitless energy to ensure everlasting prosperity." + } + }, + "brock": { + "encounter": { + "1": "My expertise on Rock-type Pokémon will take you down! Come on!", + "2": "My rock-hard willpower will overwhelm you!", + "3": "Allow me to show you the true strength of my Pokémon!" + }, + "victory": { + "1": "Your Pokémon's strength have overcome my rock-hard defenses!", + "2": "The world is huge! I'm glad to have had a chance to battle you.", + "3": "Perhaps I should go back to pursuing my dream as a Pokémon Breeder…" + }, + "defeat": { + "1": "The best offense is a good defense!\nThat's my way of doing things!", + "2": "Come study rocks with me next time to better learn how to fight them!", + "3": "Hah, all my traveling around the regions is paying off!" + } + }, + "misty": { + "encounter": { + "1": "My policy is an all out offensive with Water-type Pokémon!", + "1_female": "My policy is an all out offensive with Water-type Pokémon!", + "2": "Hiya, I'll show you the strength of my aquatic Pokémon!", + "3": "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" + }, + "victory": { + "1": "You really are strong… I'll admit that you are skilled…", + "2": "Grrr… You know you just got lucky, right?!", + "3": "Wow, you're too much! I can't believe you beat me!" + }, + "defeat": { + "1": "Was the mighty Misty too much for you?", + "2": "I hope you saw my Pokémon's elegant swimming techniques!", + "3": "Your Pokémon were no match for my pride and joys!" + } + }, + "lt_surge": { + "encounter": { + "1": "My Electric Pokémon saved me during the war! I'll show you how!", + "1_female": "My Electric Pokémon saved me during the war! I'll show you how!", + "2": "Ten-hut! I'll shock you into surrender!", + "3": "I'll zap you just like I do to all my enemies in battle!" + }, + "victory": { + "1": "Whoa! Your team's the real deal, kid!", + "2": "Aaargh, you're strong! Even my electric tricks lost against you.", + "2_female": "Aaargh, you're strong! Even my electric tricks lost against you.", + "3": "That was an absolutely shocking loss!" + }, + "defeat": { + "1": "Oh yeah! When it comes to Electric-type Pokémon, I'm number one in the world!", + "2": "Hahaha! That was an electrifying battle, kid!", + "3": "A Pokémon battle is war, and I have showed you first-hand combat!" + } + }, + "erika": { + "encounter": { + "1": "Ah, the weather is lovely here…\nOh, a battle? Very well then.", + "2": "My Pokémon battling skills rival that of my flower arranging skills.", + "3": "Oh, I hope the pleasant aroma of my Pokémon doesn't put me to sleep again…", + "4": "Seeing flowers in a garden is so soothing." + }, + "victory": { + "1": "Oh! I concede defeat.", + "2": "That match was most delightful.", + "3": "Ah, it appears it is my loss…", + "4": "Oh, my goodness." + }, + "defeat": { + "1": "I was afraid I would doze off…", + "2": "Oh my, it seems my Grass Pokémon overwhelmed you.", + "2_female": "Oh my, it seems my Grass Pokémon overwhelmed you.", + "3": "That battle was such a soothing experience.", + "4": "Oh… Is that all?" + } + }, + "janine": { + "encounter": { + "1": "I am mastering the art of poisonous attacks.\nI shall spar with you today!", + "2": "Father trusts that I can hold my own.\nI will prove him right!", + "3": "My ninja techniques are only second to my Father's!\nCan you keep up?" + }, + "victory": { + "1": "Even now, I still need training… I understand.", + "2": "Your battle technique has outmatched mine.", + "3": "I'm going to really apply myself and improve my skills." + }, + "defeat": { + "1": "Fufufu… the poison has sapped all your strength to battle.", + "2": "Ha! You didn't stand a chance against my superior ninja skills!", + "3": "Father's faith in me has proven to not be misplaced." + } + }, + "sabrina": { + "encounter": { + "1": "Through my psychic ability, I had a vision of your arrival!", + "2": "I dislike fighting, but if you wish, I will show you my powers!", + "3": "I can sense great ambition in you. I shall see if it not unfounded." + }, + "victory": { + "1": "Your power… It far exceeds what I foresaw…", + "2": "I failed to accurately predict your power.", + "3": "Even with my immense psychic powers, I cannot sense another as strong as you." + }, + "defeat": { + "1": "This victory… It is exactly as I foresaw in my visions!", + "2": "Perhaps it was another I sensed a great desire in…", + "3": "Hone your abilities before recklessly charging into battle.\nYou never know what the future may hold if you do…" + } + }, + "blaine": { + "encounter": { + "1": "Hah! Hope you brought a Burn Heal!", + "2": "My fiery Pokémon will incinerate all challengers!", + "3": "Get ready to play with fire!" + }, + "victory": { + "1": "I have burned down to nothing! Not even ashes remain!", + "2": "Didn't I stoke the flames high enough?", + "3": "I'm all burned out… But this makes my motivation to improve burn even hotter!" + }, + "defeat": { + "1": "My raging inferno cannot be quelled!", + "2": "My Pokémon have been powered up with the heat from this victory!", + "3": "Hah! My passion burns brighter than yours!" + } + }, + "giovanni": { + "encounter": { + "1": "I, the leader of Team Rocket, will make you feel a world of pain!", + "2": "My training here will be vital before I am to face my old associates again.", + "3": "I do not think you are prepared for the level of failure you are about to experience!", + "3_female": "I do not think you are prepared for the level of failure you are about to experience!" + }, + "victory": { + "1": "WHAT! Me, lose?! There is nothing I wish to say to you!", + "2": "Hmph… You could never understand what I hope to achieve.", + "3": "This defeat is merely delaying the inevitable.\nI will rise Team Rocket from the ashes in due time." + }, + "defeat": { + "1": "Not being able to measure your own strength shows that you are still but a child.", + "2": "Do not try to interfere with me again.", + "3": "I hope you understand how foolish challenging me was." + } + }, + "roxanne": { + "encounter": { + "1": "Would you kindly demonstrate how you battle?", + "2": "You can learn many things by battling many trainers.", + "3": "Oh, you caught me strategizing.\nWould you like to battle?" + }, + "victory": { + "1": "Oh, I appear to have lost.\nI understand.", + "2": "It seems that I still have so much more to learn when it comes to battle.", + "3": "I'll take what I learned here today to heart." + }, + "defeat": { + "1": "I have learned many things from our battle.\nI hope you have too.", + "2": "I look forward to battling you again.\nI hope you'll use what you've learned here.", + "3": "I won due to everything I have learned." + } + }, + "brawly": { + "encounter": { + "1": "Oh man, a challenger!\nLet's see what you can do!", + "1_female": "Oh man, a challenger!\nLet's see what you can do!", + "2": "You seem like a big splash.\nLet's battle!", + "3": "Time to create a storm!\nLet's go!" + }, + "victory": { + "1": "Oh woah, you've washed me out!", + "2": "You surfed my wave and crashed me down!", + "3": "I feel like I'm lost in Granite Cave!" + }, + "defeat": { + "1": "Haha, I surfed the big wave!\nChallenge me again sometime.", + "2": "Surf with me again some time!", + "3": "Just like the tides come in and out, I hope you return to challenge me again." + } + }, + "wattson": { + "encounter": { + "1": "Time to get shocked!\nWahahahaha!", + "2": "I'll make sparks fly!\nWahahahaha!", + "3": "I hope you brought Paralyz Heal!\nWahahahaha!" + }, + "victory": { + "1": "Seems like I'm out of charge!\nWahahahaha!", + "2": "You've completely grounded me!\nWahahahaha!", + "3": "Thanks for the thrill!\nWahahahaha!" + }, + "defeat": { + "1": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", + "1_female": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", + "2": "I hope you found our battle electrifying!\nWahahahaha!", + "3": "Aren't you shocked I won?\nWahahahaha!" + } + }, + "flannery": { + "encounter": { + "1": "Nice to meet you! Wait, no…\nI will crush you!", + "2": "I've only been a leader for a little while, but I'll smoke you!", + "3": "It's time to demonstrate the moves my grandfather has taught me! Let's battle!" + }, + "victory": { + "1": "You remind me of my grandfather…\nNo wonder I lost.", + "2": "Am I trying too hard?\nI should relax, can't get too heated.", + "3": "Losing isn't going to smother me out.\nTime to reignite training!" + }, + "defeat": { + "1": "I hope I've made my grandfather proud…\nLet's battle again some time.", + "2": "I…I can't believe I won!\nDoing things my way worked!", + "3": "Let's exchange burning hot moves again soon!" + } + }, + "norman": { + "encounter": { + "1": "I'm surprised you managed to get here.\nLet's battle.", + "2": "I'll do everything in my power as a Gym Leader to win.\nLet's go!", + "3": "You better give this your all.\nIt's time to battle!" + }, + "victory": { + "1": "I lost to you…?\nRules are rules, though.", + "2": "Was moving from Olivine a mistake…?", + "3": "I can't believe it.\nThat was a great match." + }, + "defeat": { + "1": "We both tried our best.\nI hope we can battle again soon.", + "2": "You should try challenging my kid instead.\nYou might learn something!", + "3": "Thank you for the excellent battle.\nBetter luck next time." + } + }, + "winona": { + "encounter": { + "1": "I've been soaring the skies looking for prey…\nAnd you're my target!", + "2": "No matter how our battle is, my Flying Pokémon and I will triumph with grace. Let's battle!", + "3": "I hope you aren't scared of heights.\nLet's ascend!" + }, + "victory": { + "1": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", + "1_female": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", + "2": "Oh, my Flying Pokémon have plummeted!\nVery well.", + "3": "Though I may have fallen, my Pokémon will continue to fly!" + }, + "defeat": { + "1": "My Flying Pokémon and I will forever dance elegantly!", + "2": "I hope you enjoyed our show.\nOur graceful dance is finished.", + "3": "Won't you come see our elegant choreography again?" + } + }, + "tate": { + "encounter": { + "1": "Hehehe…\nWere you surprised to see me without my sister?", + "1_female": "Hehehe…\nWere you surprised to see me without my sister?", + "2": "I can see what you're thinking…\nYou want to battle!", + "3": "How can you defeat someone…\nWho knows your every move?" + }, + "victory": { + "1": "It can't be helped…\nI miss Liza…", + "2": "Your bond with your Pokémon was stronger than mine.", + "3": "If I were with Liza, we would have won.\nWe can finish each other's thoughts!" + }, + "defeat": { + "1": "My Pokémon and I are superior!", + "2": "If you can't even defeat me, you'll never be able to defeat Liza either.", + "3": "It's all thanks to my strict training with Liza.\nI can make myself one with Pokémon." + } + }, + "liza": { + "encounter": { + "1": "Fufufu…\nWere you surprised to see me without my brother?", + "1_female": "Fufufu…\nWere you surprised to see me without my brother?", + "2": "I can determine what you desire…\nYou want to battle, don't you?", + "3": "How can you defeat someone…\nWho's one with their Pokémon?" + }, + "victory": { + "1": "It can't be helped…\nI miss Tate…", + "2": "Your bond with your Pokémon…\nIt's stronger than mine.", + "3": "If I were with Tate, we would have won.\nWe can finish each other's sentences!" + }, + "defeat": { + "1": "My Pokémon and I are victorious.", + "2": "If you can't even defeat me, you'll never be able to defeat Tate either.", + "3": "It's all thanks to my strict training with Tate.\nI can synchronize myself with my Pokémon." + } + }, + "juan": { + "encounter": { + "1": "Now's not the time to act coy.\nLet's battle!", + "2": "Ahahaha, You'll be witness to my artistry with Water Pokémon!", + "3": "A typhoon approaches!\nWill you be able to test me?", + "4": "Please, you shall bear witness to our artistry.\nA grand illusion of water sculpted by my Pokémon and myself!" + }, + "victory": { + "1": "You may be a genius who can take on Wallace!", + "2": "I focused on elegance while you trained.\nIt's only natural that you defeated me.", + "3": "Ahahaha!\nVery well, You have won this time.", + "4": "From you, I sense the brilliant shine of skill that will overcome all." + }, + "defeat": { + "1": "My Pokémon and I have sculpted an illusion of Water and come out victorious.", + "2": "Ahahaha, I have won, and you have lost.", + "3": "Shall I loan you my outfit? It may help you battle!\nAhahaha, I jest!", + "4": "I'm the winner! Which is to say, you lost." + } + }, + "crasher_wake": { + "encounter": { + "1": "Crash! Crash! Watch out!\nCrasher Wake…is…heeere!", + "2": "Crash! Crash! Crasher Wake!", + "3": "I'm the tidal wave of power to wash you away!" + }, + "victory": { + "1": "That puts a grin on my face!\nGuhahaha! That was a blast!", + "2": "Hunwah! It's gone and ended!\nHow will I say this…\nI want more! I wanted to battle a lot more!", + "3": "WHAAAAT!?" + }, + "defeat": { + "1": "Yeeeeah! That's right!", + "2": "I won, but I want more! I wanted to battle a lot more!", + "3": "So long!" + } + }, + "falkner": { + "encounter": { + "1": "I'll show you the real power of the magnificent bird Pokémon!", + "2": "Winds, stay with me!", + "3": "Dad! I hope you're watching me battle from above!" + }, + "victory": { + "1": "I understand… I'll bow out gracefully.", + "2": "A defeat is a defeat. You are strong indeed.", + "3": "…Shoot! Yeah, I lost." + }, + "defeat": { + "1": "Dad! I won with your cherished bird Pokémon…", + "2": "Bird Pokémon are the best after all!", + "3": "Feels like I'm catching up to my dad!" + } + }, + "nessa": { + "encounter": { + "1": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", + "1_female": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", + "2": "I'm not here to chat. I'm here to win!", + "3": "This is a little gift from my Pokémon… I hope you can take it!", + "3_female": "This is a little gift from my Pokémon… I hope you can take it!" + }, + "victory": { + "1": "You and your Pokémon are just too much…", + "2": "How…? How can this be?!", + "3": "I was totally washed away!" + }, + "defeat": { + "1": "The raging wave crashes again!", + "2": "Time to ride the wave of victory!", + "3": "Ehehe!" + } + }, + "melony": { + "encounter": { + "1": "I'm not going to hold back!", + "2": "All righty, I suppose we should get started.", + "3": "I'll freeze you solid!" + }, + "victory": { + "1": "You… You're pretty good, huh?", + "1_female": "You… You're pretty good, huh?", + "2": "If you find Gordie around, be sure to give him a right trashing, would you?", + "3": "I think you took breaking the ice a little too literally…" + }, + "defeat": { + "1": "Now do you see how severe battles can be?", + "2": "Hee! Looks like I went and won again!", + "3": "Are you holding back?" + } + }, + "marlon": { + "encounter": { + "1": "You look strong! Shoots! Let's start!", + "2": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", + "2_female": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", + "3": "Oh ho, so I'm facing you! That's off the wall." + }, + "victory": { + "1": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", + "1_female": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", + "2": "You don't just look strong, you're strong fo' reals! Eh, I was swept away, too!", + "3": "You're strong as a gnarly wave!" + }, + "defeat": { + "1": "You're tough, but it's not enough to sway the sea, 'K!", + "2": "Hee! Looks like I went and won again!", + "3": "Sweet, sweet victory!" + } + }, + "shauntal": { + "encounter": { + "1": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", + "1_female": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", + "2": "I absolutely love writing about Trainers who come here and the Pokémon they train.\nCould I use you and your Pokémon as a subject?", + "3": "Every person who works with Pokémon has a story to tell.\nWhat story is about to be told?" + }, + "victory": { + "1": "Wow. I'm dumbstruck!", + "2": "S-sorry! First, I must apologize to my Pokémon…\n\nI'm really sorry you had a bad experience because of me!", + "3": "Even in light of that, I'm still one of the Elite Four!" + }, + "defeat": { + "1": "Eheh.", + "2": "That gave me excellent material for my next novel!", + "3": "And so, another tale ends…" + } + }, + "marshal": { + "encounter": { + "1": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", + "1_female": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", + "2": "Victory, decisive victory, is my intention! Challenger, here I come!", + "3": "In myself, I seek to develop the strength of a fighter and shatter any weakness in myself!\nPrevailing with the force of my convictions!" + }, + "victory": { + "1": "Whew! Well done!", + "2": "As your battles continue, aim for even greater heights!", + "3": "The strength shown by you and your Pokémon has deeply impressed me…" + }, + "defeat": { + "1": "Hmm.", + "2": "That was good battle.", + "3": "Haaah! Haaah! Haiyaaaah!" + } + }, + "cheren": { + "encounter": { + "1": "You remind me of an old friend. That makes me excited about this Pokémon battle!", + "2": "Pokémon battles have no meaning if you don't think why you battle.\n$Or better said, it makes battling together with Pokémon meaningless.", + "3": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you.", + "3_female": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you." + }, + "victory": { + "1": "Thank you! I saw what was missing in me.", + "2": "Thank you! I feel like I saw a little of the way toward my ideals.", + "3": "Hmm… This is problematic." + }, + "defeat": { + "1": "As a Gym Leader, I aim to be a wall for you to overcome.", + "2": "All right!", + "3": "I made it where I am because Pokémon were by my side.\nPerhaps we need to think about why Pokémon help us not in terms of Pokémon and Trainers but as a relationship between living beings." + } + }, + "chili": { + "encounter": { + "1": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", + "1_female": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", + "2": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", + "2_female": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", + "3": "I'm going to show you what me and my blazing Fire types can do!", + "3_female": "I'm going to show you what me and my blazing Fire types can do!" + }, + "victory": { + "1": "You got me. I am… burned… out…", + "1_female": "You got me. I am… burned… out…", + "2": "Whoa ho! You're on fire!", + "2_female": "Whoa ho! You're on fire!", + "3": "Augh! You got me!" + }, + "defeat": { + "1": "I'm on fire! Play with me, and you'll get burned!", + "1_female": "I'm on fire! Play with me, and you'll get burned!", + "2": "When you play with fire, you get burned!", + "3": "I mean, c'mon, your opponent was me! You didn't have a chance!", + "3_female": "I mean, c'mon, your opponent was me! You didn't have a chance!" + } + }, + "cilan": { + "encounter": { + "1": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", + "1_female": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", + "2": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", + "2_female": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", + "3": "OK… So, um, I'm Cilan, I like Grass-type Pokémon.", + "3_female": "OK… So, um, I'm Cilan, I like Grass-type Pokémon." + }, + "victory": { + "1": "Er… Is it over now?", + "1_female": "Er… Is it over now?", + "2": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", + "2_female": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", + "3": "…Huh. Looks like my timing was, um, off?" + }, + "defeat": { + "1": "Huh? Did I win?", + "1_female": "Huh? Did I win?", + "2": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", + "2_female": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", + "3": "It…it was quite a thrilling experience…", + "3_female": "It…it was quite a thrilling experience…" + } + }, + "roark": { + "encounter": { + "1": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", + "1_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", + "2": "Here goes! These are my rocking Pokémon, my pride and joy!", + "3": "Rock-type Pokémon are simply the best!", + "4": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?", + "4_female": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?" + }, + "victory": { + "1": "W-what? That can't be! My buffed-up Pokémon!", + "2": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", + "2_female": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", + "3": "With skill like yours, it's natural for you to win.", + "4": "Wh-what?! It can't be! Even that wasn't enough?" + }, + "defeat": { + "1": "See? I'm proud of my rocking battle style!", + "2": "Thanks! The battle gave me confidence that I may be able to beat my dad!", + "3": "See? These are my rocking Pokémon, my pride and joy!", + "4": "I knew I would win!" + } + }, + "morty": { + "encounter": { + "1": "With a little more, I could see a future in which I meet the legendary Pokémon.\n$You're going to help me reach that level!", + "2": "It's said that a rainbow-hued Pokémon will come down to appear before a truly powerful Trainer. \n$I believed that tale, so I have secretly trained here all my life. As a result, I can now see what others cannot. \n$I see a shadow of the person who will make the Pokémon appear. \n$I believe that person is me! You're going to help me reach that level!", + "3": "Whether you choose to believe or not, mystic power does exist.", + "4": "You can bear witness to the fruits of my training.", + "5": "You must make your soul one with that of Pokémon. Can you do this?", + "6": "Say, do you want to be part of my training?" + }, + "victory": { + "1": "I'm not good enough yet…", + "2": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", + "2_female": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", + "3": "How is this possible…", + "4": "I don't think our potentials are so different.\n$But you seem to have something more than that… So be it.", + "5": "Guess I need more training.", + "6": "That's a shame." + }, + "defeat": { + "1": "I moved… one step ahead again.", + "2": "Fufufu…", + "3": "Wh-what?! It can't be! Even that wasn't enough?", + "4": "I feel like I just smashed through a really stubborn boulder!", + "5": "Ahahahah!", + "6": "I knew I would win!" + } + }, + "crispin": { + "encounter": { + "1": "I wanna win, so that's exactly what I'll do!", + "2": "I battle because I wanna battle! And you know what? That's how it should be!" + }, + "victory": { + "1": "I wanted to win…but I lost!", + "2": "I lost…'cause I couldn't win!" + }, + "defeat": { + "1": "Hey, wait a sec. Did I just win? I think I just won! Talk about satisfying!", + "2": "Wooo! That was amazing!" + } + }, + "amarys": { + "encounter": { + "1": "I want to be the one to help a certain person. That being the case, I cannot afford to lose.\n$… Our battle starts now." + }, + "victory": { + "1": "I am… not enough, I see." + }, + "defeat": { + "1": "Victory belongs to me. Well fought." + } + }, + "lacey": { + "encounter": { + "1": "I'll be facing you with my usual party as a member of the Elite Four." + }, + "victory": { + "1": "That was a great battle!" + }, + "defeat": { + "1": "Let's give your Pokémon a nice round of applause for their efforts!" + } + }, + "drayton": { + "encounter": { + "1": "Man, I love chairs. Don't you love chairs? What lifesavers. \n$I don't get why everyone doesn't just sit all the time. Standing up's tiring work!" + }, + "victory": { + "1": "Guess I should've expected that!" + }, + "defeat": { + "1": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?", + "1_female": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?" + } + }, + "ramos": { + "encounter": { + "1": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?", + "1_female": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?" + }, + "victory": { + "1": "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." + }, + "defeat": { + "1": "Hohoho… Indeed. Frail little blades o' grass'll break through even concrete." + } + }, + "viola": { + "encounter": { + "1": "Whether it's the tears of frustration that follow a loss or the blossoming of joy that comes with victory…\n$They're both great subjects for my camera! Fantastic! This'll be just fantastic! \n$Now come at me!", + "2": "My lens is always focused on victory--I won't let anything ruin this shot!" + }, + "victory": { + "1": "You and your Pokémon have shown me a whole new depth of field! Fantastic! Just fantastic!", + "2": "The world you see through a lens, and the world you see with a Pokémon by your side…\n$The same world can look entirely different depending on your view." + }, + "defeat": { + "1": "The photo from the moment of my victory will be a real winner, all right!", + "2": "Yes! I took some great photos!" + } + }, + "candice": { + "encounter": { + "1": "You want to challenge Candice? Sure thing! I was waiting for someone tough! \n$But I should tell you, I'm tough because I know how to focus.", + "2": "Pokémon, fashion, romance… It's all about focus! \n$I'll show you just what I mean. Get ready to lose!" + }, + "victory": { + "1": "I must say, I'm warmed up to you! I might even admire you a little.", + "2": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. ", + "2_female": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. " + }, + "defeat": { + "1": "I sensed your will to win, but I don't lose!", + "2": "See? Candice's focus! My Pokémon's focus is great, too!" + } + }, + "gardenia": { + "encounter": { + "1": "You have a winning aura about you. So, anyway, this will be fun. Let's have our battle!" + }, + "victory": { + "1": "Amazing! You're very good, aren't you?", + "1_female": "Amazing! You're very good, aren't you?" + }, + "defeat": { + "1": "Yes! My Pokémon and I are perfectly good!" + } + }, + "aaron": { + "encounter": { + "1": "Ok! Let me take you on!" + }, + "victory": { + "1": "Battling is a deep and complex affair…" + }, + "defeat": { + "1": "Victory over an Elite Four member doesn't come easily." + } + }, + "cress": { + "encounter": { + "1": "That is correct! It shall be I and my esteemed Water types that you must face in battle!" + }, + "victory": { + "1": "Lose? Me? I don't believe this." + }, + "defeat": { + "1": "This is the appropriate result when I'm your opponent." + } + }, + "allister": { + "encounter": { + "1": "'M Allister.\nH-here… I go…" + }, + "victory": { + "1": "I nearly lost my mask from the shock… That was…\n$Wow. I can see your skill for what it is." + }, + "defeat": { + "1": "Th-that was ace!" + } + }, + "clay": { + "encounter": { + "1": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!", + "1_female": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!" + }, + "victory": { + "1": "Man oh man… It feels good to go all out and still be defeated!" + }, + "defeat": { + "1": "What's important is how ya react to losin'. \n$That's why folks who use losin' as fuel to get better are tough." + } + }, + "kofu": { + "encounter": { + "1": "I'mma serve you a full course o' Water-type Pokémon! Don't try to eat 'em, though!" + }, + "victory": { + "1": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!", + "1_female": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" + }, + "defeat": { + "1": "You come back to see me again now, ya hear?" + } + }, + "tulip": { + "encounter": { + "1": "Allow me to put my skills to use to make your cute little Pokémon even more beautiful!" + }, + "victory": { + "1": "Your strength has a magic to it that cannot be washed away." + }, + "defeat": { + "1": "You know, in my line of work, people who lack talent in one area or the other often fade away quickly—never to be heard of again." + } + }, + "sidney": { + "encounter": { + "1": "I like that look you're giving me. I guess you'll give me a good match.\n$That's good! Looking real good! All right!\n$You and me, let's enjoy a battle that can only be staged here!" + }, + "victory": { + "1": "Well, how do you like that? I lost! Eh, it was fun, so it doesn't matter." + }, + "defeat": { + "1": "No hard feelings, alright?" + } + }, + "phoebe": { + "encounter": { + "1": "While I trained, I gained the ability to commune with Ghost-type Pokémon. \n$Yes, the bond I developed with Pokémon is extremely tight. \n$So, come on, just try and see if you can even inflict damage on my Pokémon!" + }, + "victory": { + "1": "Oh, darn. I've gone and lost." + }, + "defeat": { + "1": "I look forward to battling you again sometime!" + } + }, + "glacia": { + "encounter": { + "1": "All I have seen are challenges by weak Trainers and their Pokémon. \n$What about you? It would please me to no end if I could go all out against you!" + }, + "victory": { + "1": "You and your Pokémon… How hot your spirits burn!\n$The all-consuming heat overwhelms. \n$It's no surprise that my icy skills failed to harm you." + }, + "defeat": { + "1": "A fiercely passionate battle, indeed." + } + }, + "drake": { + "encounter": { + "1": "For us to battle with Pokémon as partners, do you know what it takes? Do you know what is needed? \n$If you don't, then you will never prevail over me!" + }, + "victory": { + "1": "Superb, it should be said." + }, + "defeat": { + "1": "I gave my all for that battle!" + } + }, + "wallace": { + "encounter": { + "1": "There's something about you… A difference in your demeanor. \n$I think I sense that in you. Now, show me. Show me the power you wield with your Pokémon. \n$And I, in turn, shall present you with a performance of illusions in water by me and my Pokémon!" + }, + "victory": { + "1": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy.", + "1_female": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy." + }, + "defeat": { + "1": "A grand illusion!" + } + }, + "lorelei": { + "encounter": { + "1": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?", + "1_female": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?" + }, + "victory": { + "1": "How dare you!" + }, + "defeat": { + "1": "There's nothing you can do once you're frozen.", + "1_female": "There's nothing you can do once you're frozen." + } + }, + "will": { + "encounter": { + "1": "I have trained all around the world, making my psychic Pokémon powerful.\n$I can only keep getting better! Losing is not an option!" + }, + "victory": { + "1": "I… I can't… believe it…" + }, + "defeat": { + "1": "That was close. I wonder what it is that you lack." + } + }, + "malva": { + "encounter": { + "1": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!", + "1_female": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!" + }, + "victory": { + "1": "What news… So a new challenger has defeated Malva!", + "1_female": "What news… So a new challenger has defeated Malva!" + }, + "defeat": { + "1": "I am delighted! Yes, delighted that I could squash you beneath my heel." + } + }, + "hala": { + "encounter": { + "1": "Old Hala is here to make you holler!" + }, + "victory": { + "1": "I could feel the power you gained on your journey." + }, + "defeat": { + "1": "Haha! What a delightful battle!" + } + }, + "molayne": { + "encounter": { + "1": "I gave the captain position to my cousin Sophocles, but I'm confident in my ability. \n$My strength is like that of a supernova!" + }, + "victory": { + "1": "I certainly found an interesting Trainer to face!", + "1_female": "I certainly found an interesting Trainer to face!" + }, + "defeat": { + "1": "Ahaha. What an interesting battle." + } + }, + "rika": { + "encounter": { + "1": "I'd say I'll go easy on you, but… I'd be lying! Think fast!" + }, + "victory": { + "1": "Not bad, kiddo.", + "1_female": "Not bad, kiddo." + }, + "defeat": { + "1": "Nahahaha! You really are something else, kiddo!", + "1_female": "Nahahaha! You really are something else, kiddo!" + } + }, + "bruno": { + "encounter": { + "1": "We will grind you down with our superior power! Hoo hah!" + }, + "victory": { + "1": "Why? How could I lose?" + }, + "defeat": { + "1": "You can challenge me all you like, but the results will never change!" + } + }, + "bugsy": { + "encounter": { + "1": "I'm Bugsy! I never lose when it comes to bug Pokémon!" + }, + "victory": { + "1": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win.", + "1_female": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win." + }, + "defeat": { + "1": "Thanks! Thanks to our battle, I was also able to make progress in my research!" + } + }, + "koga": { + "encounter": { + "1": "Fwahahahaha! Pokémon are not merely about brute force--you shall see soon enough!" + }, + "victory": { + "1": "Ah! You've proven your worth!" + }, + "defeat": { + "1": "Have you learned to fear the techniques of the ninja?" + } + }, + "bertha": { + "encounter": { + "1": "Well, would you show this old lady how much you've learned?" + }, + "victory": { + "1": "Well! Dear child, I must say, that was most impressive. \n$Your Pokémon believed in you and did their best to earn you the win. \n$Even though I've lost, I find myself with this silly grin!" + }, + "defeat": { + "1": "Hahahahah! Looks like this old lady won!" + } + }, + "lenora": { + "encounter": { + "1": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!", + "1_female": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!" + }, + "victory": { + "1": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!", + "1_female": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!" + }, + "defeat": { + "1": "Ah ha ha! If you lose, make sure to analyze why, and use that knowledge in your next battle!" + } + }, + "siebold": { + "encounter": { + "1": "As long as I am alive, I shall strive onward to seek the ultimate cuisine... and the strongest opponents in battle!" + }, + "victory": { + "1": "I shall store my memory of you and your Pokémon forever away within my heart." + }, + "defeat": { + "1": "Our Pokémon battle was like food for my soul. It shall keep me going. \n$That is how I will pay my respects to you for giving your all in battle!" + } + }, + "roxie": { + "encounter": { + "1": "Get ready! I'm gonna knock some sense outta ya!" + }, + "victory": { + "1": "Wild! Your reason's already more toxic than mine!" + }, + "defeat": { + "1": "Hey, c'mon! Get serious! You gotta put more out there!", + "1_female": "Hey, c'mon! Get serious! You gotta put more out there!" + } + }, + "olivia": { + "encounter": { + "1": "No introduction needed here. Time to battle me, Olivia!" + }, + "victory": { + "1": "Really lovely… Both you and your Pokémon…" + }, + "defeat": { + "1": "Mmm-hmm." + } + }, + "poppy": { + "encounter": { + "1": "Oooh! Do you wanna have a Pokémon battle with me?" + }, + "victory": { + "1": "Uagh?! Mmmuuuggghhh…" + }, + "defeat": { + "1": "Yaaay! I did it! I de-feet-ed you! You can come for… For… An avenge match? \n$Come for an avenge match anytime you want!" + } + }, + "agatha": { + "encounter": { + "1": "Pokémon are for battling! I'll show you how a real Trainer battles!" + }, + "victory": { + "1": "Oh my! You're something special, child!" + }, + "defeat": { + "1": "Bahaha. That's how a proper battle's done!" + } + }, + "flint": { + "encounter": { + "1": "Hope you're warmed up, cause here comes the Big Bang!", + "1_female": "Hope you're warmed up, cause here comes the Big Bang!" + }, + "victory": { + "1": "Incredible! Your moves are so hot, they make mine look lukewarm!" + }, + "defeat": { + "1": "Huh? Is that it? I think you need a bit more passion." + } + }, + "grimsley": { + "encounter": { + "1": "The winner takes everything, and there's nothing left for the loser." + }, + "victory": { + "1": "When one loses, they lose everything… The next thing I'll look for will be victory, too!" + }, + "defeat": { + "1": "If somebody wins, the person who fought against that person will lose." + } + }, + "caitlin": { + "encounter": { + "1": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!", + "1_female": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!" + }, + "victory": { + "1": "My Pokémon and I learned so much! I offer you my thanks." + }, + "defeat": { + "1": "I aspire to claim victory with elegance and grace." + } + }, + "diantha": { + "encounter": { + "1": "Battling against you and your Pokémon, all of you brimming with hope for the future… \n$Honestly, it just fills me up with energy I need to keep facing each new day! It does!" + }, + "victory": { + "1": "Witnessing the noble spirits of you and your Pokémon in battle has really touched my heart…" + }, + "defeat": { + "1": "Oh, fantastic! What did you think? My team was pretty cool, right?" + } + }, + "wikstrom": { + "encounter": { + "1": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!", + "1_female": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!" + }, + "victory": { + "1": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!", + "1_female": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!" + }, + "defeat": { + "1": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!", + "1_female": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!" + } + }, + "acerola": { + "encounter": { + "1": "Battling is just plain fun! Come on, I can take you!" + }, + "victory": { + "1": "I'm… I'm speechless! How did you do it?!" + }, + "defeat": { + "1": "Ehaha! What an amazing victory!" + } + }, + "larry_elite": { + "encounter": { + "1": "Hello there… It's me, Larry.\n$I serve as a member of the Elite Four too, yes… Unfortunately for me." + }, + "victory": { + "1": "Well, that took the wind from under our wings…" + }, + "defeat": { + "1": "It's time for a meeting with the boss." + } + }, + "lance": { + "encounter": { + "1": "I've been waiting for you. Allow me to test your skill.", + "2": "I thought that you would be able to get this far. Let's get this started." + }, + "victory": { + "1": "You got me. You are magnificent!", + "1_female": "You got me. You are magnificent!", + "2": "I never expected another trainer to beat me… I'm surprised.", + "2_female": "I never expected another trainer to beat me… I'm surprised." + }, + "defeat": { + "1": "That was close. Want to try again?", + "2": "It's not that you are weak. Don't let it bother you.", + "2_female": "It's not that you are weak. Don't let it bother you." + } + }, + "karen": { + "encounter": { + "1": "I am Karen. Would you care for a showdown with my Dark-type Pokémon?", + "2": "I am unlike those you've already met.", + "3": "You've assembled a charming team. Our battle should be a good one." + }, + "victory": { + "1": "No! I can't win. How did you become so strong?", + "2": "I will not stray from my chosen path.", + "3": "The Champion is looking forward to meeting you." + }, + "defeat": { + "1": "That's about what I expected.", + "2": "Well, that was relatively entertaining.", + "3": "Come visit me anytime." + } + }, + "milo": { + "encounter": { + "1": "Sure seems like you understand Pokémon real well. \n$This is gonna be a doozy of a battle! \n$I'll have to Dynamax my Pokémon if I want to win!" + }, + "victory": { + "1": "The power of Grass has wilted… What an incredible Challenger!", + "1_female": "The power of Grass has wilted… What an incredible Challenger!" + }, + "defeat": { + "1": "This'll really leave you in shock and awe." + } + }, + "lucian": { + "encounter": { + "1": "Just a moment, please. The book I'm reading has nearly reached its thrilling climax… \n$The hero has obtained a mystic sword and is about to face their final trial… Ah, never mind. \n$Since you've made it this far, I'll put that aside and battle you. \n$Let me see if you'll achieve as much glory as the hero of my book!" + }, + "victory": { + "1": "I see… It appears you've put me in checkmate." + }, + "defeat": { + "1": "I have a reputation to uphold." + } + }, + "drasna": { + "encounter": { + "1": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!", + "1_female": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!" + }, + "victory": { + "1": "Oh, dear me. That sure was a quick battle… I do hope you'll come back again sometime!" + }, + "defeat": { + "1": "How can this be?" + } + }, + "kahili": { + "encounter": { + "1": "So, here you are… Why don't we see who the winds favor today, you… Or me?" + }, + "victory": { + "1": "It's frustrating to me as a member of the Elite Four, but it seems your strength is the real deal." + }, + "defeat": { + "1": "That was an ace!" + } + }, + "hassel": { + "encounter": { + "1": "Prepare to learn firsthand how the fiery breath of ferocious battle feels!" + }, + "victory": { + "1": "Fortune smiled on me this time, but… \n$Judging from how the match went, who knows if I will be so lucky next time." + }, + "defeat": { + "1": "That was an ace!" + } + }, + "blue": { + "encounter": { + "1": "You must be pretty good to get this far.", + "1_female": "You must be pretty good to get this far." + }, + "victory": { + "1": "I've only lost to him and now to you… Him? Hee, hee…" + }, + "defeat": { + "1": "See? My power is what got me here." + } + }, + "piers": { + "encounter": { + "1": "Get ready for a mosh pit with me and my party! Spikemuth, it's time to rock!" + }, + "victory": { + "1": "Me an' my team gave it our best. Let's meet up again for a battle some time…" + }, + "defeat": { + "1": "My throat's ragged from shoutin'… But 'at was an excitin' battle!" + } + }, + "red": { + "encounter": { + "1": "…!" + }, + "victory": { + "1": "…?" + }, + "defeat": { + "1": "…!" + } + }, + "jasmine": { + "encounter": { + "1": "Oh… Your Pokémon are impressive. I think I will enjoy this." + }, + "victory": { + "1": "You are truly strong. I'll have to try much harder, too." + }, + "defeat": { + "1": "I never expected to win." + } + }, + "lance_champion": { + "encounter": { + "1": "I am still the Champion. I won't hold anything back." + }, + "victory": { + "1": "This is the emergence of a new Champion.", + "1_female": "This is the emergence of a new Champion." + }, + "defeat": { + "1": "I successfully defended my Championship." + } + }, + "steven": { + "encounter": { + "1": "Tell me… What have you seen on your journey with your Pokémon? \n$What have you felt, meeting so many other Trainers out there? \n$Traveling this rich land… Has it awoken something inside you? \n$I want you to come at me with all that you've learned. \n$My Pokémon and I will respond in turn with all that we know!" + }, + "victory": { + "1": "So I, the Champion, fall in defeat…" + }, + "defeat": { + "1": "That was time well spent! Thank you!" + } + }, + "cynthia": { + "encounter": { + "1": "I, Cynthia, accept your challenge! There won't be any letup from me!" + }, + "victory": { + "1": "No matter how fun the battle is, it will always end sometime…" + }, + "defeat": { + "1": "Even if you lose, never lose your love of Pokémon." + } + }, + "iris": { + "encounter": { + "1": "Know what? I really look forward to having serious battles with strong Trainers! \n$I mean, come on! The Trainers who make it here are Trainers who desire victory with every fiber of their being! \n$And they are battling alongside Pokémon that have been through countless difficult battles! \n$If I battle with people like that, not only will I get stronger, my Pokémon will, too! \n$And we'll get to know each other even better! OK! Brace yourself! \n$I'm Iris, the Pokémon League Champion, and I'm going to defeat you!" + }, + "victory": { + "1": "Aghhhh… I did my best, but we lost…" + }, + "defeat": { + "1": "Yay! We won!" + } + }, + "hau": { + "encounter": { + "1": "I wonder if a Trainer battles differently depending on whether they're from a warm region or a cold region.\n$Let's test it out!" + }, + "victory": { + "1": "That was awesome! I think I kinda understand your vibe a little better now!" + }, + "defeat": { + "1": "Ma-an, that was some kinda battle!" + } + }, + "geeta": { + "encounter": { + "1": "I decided to throw my hat in the ring once more. \n$Come now… Show me the fruits of your training." + }, + "victory": { + "1": "I eagerly await news of all your achievements!" + }, + "defeat": { + "1": "What's the matter? This isn't all, is it?" + } + }, + "nemona": { + "encounter": { + "1": "Yesss! I'm so psyched! Time for us to let loose!" + }, + "victory": { + "1": "Well, that stinks, but I still had fun! I'll getcha next time!" + }, + "defeat": { + "1": "Well, that was a great battle! Fruitful for sure." + } + }, + "leon": { + "encounter": { + "1": "We're gonna have an absolutely champion time!" + }, + "victory": { + "1": "My time as Champion is over… \n$But what a champion time it's been! \n$Thank you for the greatest battle I've ever had!" + }, + "defeat": { + "1": "An absolute champion time, that was!" + } + }, + "whitney": { + "encounter": { + "1": "Hey! Don't you think Pokémon are, like, super cute?" + }, + "victory": { + "1": "Waaah! Waaah! You're so mean!", + "1_female": "Waaah! Waaah! You're so mean!" + }, + "defeat": { + "1": "And that's that!" + } + }, + "chuck": { + "encounter": { + "1": "Hah! You want to challenge me? Are you brave or just ignorant?", + "1_female": "Hah! You want to challenge me? Are you brave or just ignorant?" + }, + "victory": { + "1": "You're strong! Would you please make me your apprentice?" + }, + "defeat": { + "1": "There. Do you realize how much more powerful I am than you?" + } + }, + "katy": { + "encounter": { + "1": "Don't let your guard down unless you would like to find yourself knocked off your feet!", + "1_female": "Don't let your guard down unless you would like to find yourself knocked off your feet!" + }, + "victory": { + "1": "All of my sweet little Pokémon dropped like flies!" + }, + "defeat": { + "1": "Eat up, my cute little Vivillon!" + } + }, + "pryce": { + "encounter": { + "1": "Youth alone does not ensure victory! Experience is what counts." + }, + "victory": { + "1": "Outstanding! That was perfect. Try not to forget what you feel now." + }, + "defeat": { + "1": "Just as I envisioned." + } + }, + "clair": { + "encounter": { + "1": "Do you know who I am? And you still dare to challenge me?" + }, + "victory": { + "1": "I wonder how far you can get with your skill level. This should be fascinating." + }, + "defeat": { + "1": "That's that." + } + }, + "maylene": { + "encounter": { + "1": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!", + "1_female": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!" + }, + "victory": { + "1": "I admit defeat…" + }, + "defeat": { + "1": "That was awesome." + } + }, + "fantina": { + "encounter": { + "1": "You shall challenge me, yes? But I shall win. \n$That is what the Gym Leader of Hearthome does, non?" + }, + "victory": { + "1": "You are so fantastically strong. I know why I have lost." + }, + "defeat": { + "1": "I am so, so, very happy!" + } + }, + "byron": { + "encounter": { + "1": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!", + "1_female": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!" + }, + "victory": { + "1": "Hmm! My sturdy Pokémon--defeated!" + }, + "defeat": { + "1": "Gwahahaha! How were my sturdy Pokémon?!" + } + }, + "olympia": { + "encounter": { + "1": "An ancient custom deciding one's destiny. The battle begins!" + }, + "victory": { + "1": "Create your own path. Let nothing get in your way. Your fate, your future." + }, + "defeat": { + "1": "Our path is clear now." + } + }, + "volkner": { + "encounter": { + "1": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!", + "1_female": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!" + }, + "victory": { + "1": "You've got me beat…\n$Your desire and the noble way your Pokémon battled for you… \n$I even felt thrilled during our match. That was a very good battle." + }, + "defeat": { + "1": "It was not shocking at all… \n$That is not what I wanted!" + } + }, + "burgh": { + "encounter": { + "1": "M'hm… If I win this battle, I feel like I can draw a picture unlike any before it. \n$OK! I can hear my battle muse loud and clear. Let's get straight to it!", + "2": "Of course, I'm really proud of all of my Pokémon! \n$Well now… Let's get right to it!" + }, + "victory": { + "1": "Is it over? Has my muse abandoned me?", + "2": "Hmm… It's over! You're incredible!" + }, + "defeat": { + "1": "Wow… It's beautiful somehow, isn't it…", + "2": "Sometimes I hear people say something was an ugly win. \n$I think if you're trying your best, any win is beautiful." + } + }, + "elesa": { + "encounter": { + "1": "C'est fini! When I'm certain of that, I feel an electric jolt run through my body! \n$I want to feel the sensation, so now my beloved Pokémon are going to make your head spin!" + }, + "victory": { + "1": "I meant to make your head spin, but you shocked me instead." + }, + "defeat": { + "1": "That was unsatisfying somehow… Will you give it your all next time?" + } + }, + "skyla": { + "encounter": { + "1": "It's finally time for a showdown! That means the Pokémon battle that decides who's at the top, right? \n$I love being on the summit! 'Cause you can see forever and ever from high places! \n$So, how about you and I have some fun?" + }, + "victory": { + "1": "Being your opponent in battle is a new source of strength to me. Thank you!" + }, + "defeat": { + "1": "Win or lose, you always gain something from a battle, right?" + } + }, + "brycen": { + "encounter": { + "1": "There is also strength in being with other people and Pokémon. \n$Receiving their support makes you stronger. I'll show you this power!" + }, + "victory": { + "1": "The wonderful combination of you and your Pokémon! What a beautiful friendship!" + }, + "defeat": { + "1": "Extreme conditions really test you and train you!" + } + }, + "drayden": { + "encounter": { + "1": "What I want to find is a young Trainer who can show me a bright future. \n$Let's battle with everything we have: your skill, my experience, and the love we've raised our Pokémon with!" + }, + "victory": { + "1": "This intense feeling that floods me after a defeat… I don't know how to describe it." + }, + "defeat": { + "1": "Harrumph! I know your ability is greater than that!" + } + }, + "grant": { + "encounter": { + "1": "There is only one thing I wish for. \n$That by surpassing one another, we find a way to even greater heights." + }, + "victory": { + "1": "You are a wall that I am unable to surmount!" + }, + "defeat": { + "1": "Do not give up. \n$That is all there really is to it. \n$The most important lessons in life are simple." + } + }, + "korrina": { + "encounter": { + "1": "Time for Lady Korrina's big appearance!" + }, + "victory": { + "1": "It's your very being that allows your Pokémon to evolve!" + }, + "defeat": { + "1": "What an explosive battle!" + } + }, + "clemont": { + "encounter": { + "1": "Oh! I'm glad that we got to meet!" + }, + "victory": { + "1": "Your passion for battle inspires me!" + }, + "defeat": { + "1": "Looks like my Trainer-Grow-Stronger Machine, Mach 2 is really working!" + } + }, + "valerie": { + "encounter": { + "1": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong.", + "1_female": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong." + }, + "victory": { + "1": "I hope that you will find things worth smiling about tomorrow…" + }, + "defeat": { + "1": "Oh goodness, what a pity…" + } + }, + "wulfric": { + "encounter": { + "1": "You know what? We all talk big about what you learn from battling and bonds and all that…\n$But really, I just do it 'cause it's fun. \n$Who cares about the grandstanding? Let's get to battling!" + }, + "victory": { + "1": "Outstanding! I'm tough as an iceberg, but you smashed me through and through!" + }, + "defeat": { + "1": "Tussle with me and this is what happens!" + } + }, + "kabu": { + "encounter": { + "1": "Every Trainer and Pokémon trains hard in pursuit of victory. \n$But that means your opponent is also working hard to win. \n$In the end, the match is decided by which side is able to unleash their true potential." + }, + "victory": { + "1": "I'm glad I could battle you today!" + }, + "defeat": { + "1": "That's a great way for me to feel my own growth!" + } + }, + "bea": { + "encounter": { + "1": "Do you have an unshakable spirit that won't be moved, no matter how you are attacked? \n$I think I'll just test that out, shall I?" + }, + "victory": { + "1": "I felt the fighting spirit of your Pokémon as you led them in battle." + }, + "defeat": { + "1": "That was the best sort of match anyone could ever hope for." + } + }, + "opal": { + "encounter": { + "1": "Let me have a look at how you and your partner Pokémon behave!" + }, + "victory": { + "1": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon.", + "1_female": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon." + }, + "defeat": { + "1": "Too bad for you, I guess." + } + }, + "bede": { + "encounter": { + "1": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am.", + "1_female": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am." + }, + "victory": { + "1": "I see… Well, that's fine. I wasn't really trying all that hard anyway." + }, + "defeat": { + "1": "Not a bad job, I suppose." + } + }, + "gordie": { + "encounter": { + "1": "So, let's get this over with." + }, + "victory": { + "1": "I just want to climb into a hole… Well, I guess it'd be more like falling from here." + }, + "defeat": { + "1": "Battle like you always do, victory will follow!" + } + }, + "marnie": { + "encounter": { + "1": "The truth is, when all's said and done… I really just wanna become Champion for myself! \n$So don't take it personal when I kick your butt!" + }, + "victory": { + "1": "OK, so I lost… But I got to see a lot of the good points of you and your Pokémon!" + }, + "defeat": { + "1": "Hope you enjoyed our battle tactics." + } + }, + "raihan": { + "encounter": { + "1": "I'm going to defeat the Champion, win the whole tournament, and prove to the world just how strong the great Raihan really is!" + }, + "victory": { + "1": "I look this good even when I lose. \n$It's a real curse. \n$Guess it's time for another selfie!" + }, + "defeat": { + "1": "Let's take a selfie to remember this." + } + }, + "brassius": { + "encounter": { + "1": "I assume you are ready? Let our collaborative work of art begin!", + "1_female": "I assume you are ready? Let our collaborative work of art begin!" + }, + "victory": { + "1": "Ahhh…vant-garde!" + }, + "defeat": { + "1": "I will begin on a new piece at once!" + } + }, + "iono": { + "encounter": { + "1": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!", + "1_female": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!" + }, + "victory": { + "1": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!", + "1_female": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" + }, + "defeat": { + "1": "Your eyeballs are MINE!" + } + }, + "larry": { + "encounter": { + "1": "When all's said and done, simplicity is strongest." + }, + "victory": { + "1": "A serving of defeat, huh?" + }, + "defeat": { + "1": "I'll call it a day." + } + }, + "ryme": { + "encounter": { + "1": "Come on, baby! Rattle me down to the bone!" + }, + "victory": { + "1": "You're cool, my friend—you move my SOUL!", + "1_female": "You're cool, my friend—you move my SOUL!" + }, + "defeat": { + "1": "Later, baby!" + } + }, + "grusha": { + "encounter": { + "1": "All I need to do is make sure the power of my Pokémon chills you to the bone!" + }, + "victory": { + "1": "Your burning passion… I kinda like it, to be honest." + }, + "defeat": { + "1": "Things didn't heat up for you." + } + }, + "marnie_elite": { + "encounter": { + "1": "You've made it this far, huh? Let's see if you can handle my Pokémon!", + "2": "I'll give it my best shot, but don't think I'll go easy on you!" + }, + "victory": { + "1": "I can't believe I lost... But you deserved that win. Well done!", + "2": "Looks like I've still got a lot to learn. Great battle, though!" + }, + "defeat": { + "1": "You put up a good fight, but I've got the edge! Better luck next time!", + "2": "Seems like my training's paid off. Thanks for the battle!" + } + }, + "nessa_elite": { + "encounter": { + "1": "The tides are turning in my favor. Ready to get swept away?", + "1_female": "The tides are turning in my favor. Ready to get swept away?", + "2": "Let's make some waves with this battle! I hope you're prepared!", + "2_female": "Let's make some waves with this battle! I hope you're prepared!" + }, + "victory": { + "1": "You navigated those waters perfectly... Well done!", + "2": "Looks like my currents were no match for you. Great job!" + }, + "defeat": { + "1": "Water always finds a way. That was a refreshing battle!", + "2": "You fought well, but the ocean's power is unstoppable!" + } + }, + "bea_elite": { + "encounter": { + "1": "Prepare yourself! My fighting spirit burns bright!", + "2": "Let's see if you can keep up with my relentless pace!" + }, + "victory": { + "1": "Your strength... It's impressive. You truly deserve this win.", + "2": "I've never felt this intensity before. Amazing job!" + }, + "defeat": { + "1": "Another victory for my intense training regimen! Well done!", + "2": "You've got strength, but I trained harder. Great battle!" + } + }, + "allister_elite": { + "encounter": { + "1": "Shadows fall... Are you ready to face your fears?", + "1_female": "Shadows fall... Are you ready to face your fears?", + "2": "Let's see if you can handle the darkness that I command." + }, + "victory": { + "1": "You've dispelled the shadows... For now. Well done.", + "2": "Your light pierced through my darkness. Great job." + }, + "defeat": { + "1": "The shadows have spoken... Your strength isn't enough.", + "2": "Darkness triumphs... Maybe next time you'll see the light." + } + }, + "raihan_elite": { + "encounter": { + "1": "Storm's brewing! Let's see if you can weather this fight!", + "2": "Get ready to face the eye of the storm!" + }, + "victory": { + "1": "You've bested the storm... Incredible job!", + "2": "You rode the winds perfectly... Great battle!" + }, + "defeat": { + "1": "Another storm weathered, another victory claimed! Well fought!", + "2": "You got caught in my storm! Better luck next time!", + "2_female": "You got caught in my storm! Better luck next time!" + } + }, + "alder": { + "encounter": { + "1": "Prepare yourself for a match against the strongest Trainer in Unova!" + }, + "victory": { + "1": "Well done! You certainly are an unmatched talent." + }, + "defeat": { + "1": "A fresh wind blows through my heart...\n$What an extraordinary effort!" + } + }, + "kieran": { + "encounter": { + "1": "Through hard work, I become stronger and stronger!\n$I don't lose." + }, + "victory": { + "1": "I don't believe it...\n$What a fun and heart-pounding battle!" + }, + "defeat": { + "1": "Wowzers, what a battle!\n$Time for you to train even harder." + } + }, + "rival": { + "encounter": { + "1": "@c{smile}あっ、ここに いたんだ! 旅に 出る前に 「じゃ またね!」って くらい 聞きたかったよ……$@c{smile_eclosed}やっぱり 夢を 追ってこうと しているんだ? 信じられない ほどね……$@c{serious_smile_fists}じゃあ、 ここまで 来たから バトルしよっか? 覚悟してるかを 確かめたい から!$@c{serious_mopen_fists}遠慮せずに 全力で かかってこいぜ!" + }, + "victory": { + "1": "@c{shock}ウワッ、カンゼンに ぶっ壊したぜ。\n初心者だとは 思えないほど……$@c{smile}たぶん 運が良っかった だけが……\n最後まで 行ける素質が あるかもな!$こっちの アイテムを あげよう、 博士に そう言いつけたから。 結構 スゴそうな もんだ!$@c{serious_smile_fists}ここからも ガンバレ!" + } + }, + "rival_female": { + "encounter": { + "1": "@c{smile_wave}あ、ついに 見つけた! あちこち 探したのよ! \n@c{angry_mopen}だーい親友に お別れを 言うまでも 忘れちゃった?$@c{smile_ehalf}大切な 夢を 追っていくのね?\n やっぱり、この日が 来たのね……$@c{smile}とにかく! アタシを 忘れちゃったのを 許せる 条件は 一つ: \n@c{smile_wave_wink}アタシと バトルだ!$@c{angry_mopen}全力で かかってきなさい!\n冒険が 第一歩で 終わっちゃうと もったいない でしょう?" + }, + "victory": { + "1": "@c{shock}始まった バッカリなのに そんなに強い?!@d{96}\n@c{angry}完全に チートだろう?\n$@c{smile_wave_wink}なんちゃって!@d{64} @c{smile_eclosed}正々堂々と 負けたよ。 冒険 上手く行ける 気がするね!\n$@c{smile}ところで、こっち! 博士からの アイテムを あげるわ。きっと 便利だと 思うよ!\n$@c{smile_wave}いつも通り 頑張ってね! 信じてるから!" + } + }, + "rival_2": { + "encounter": { + "1": "@c{smile}おや、なんと グウゼン。\n@c{smile_eclosed}今までも パーフェクトに 勝った ようだな……\n$@c{serious_mopen_fists}なんか 忍び寄った みたいだとは 分かるけど、 そんなことない… ほとんどはな。\n$@c{serious_smile_fists}ぶっちゃけ言うと、 オレが 負けた時から 再戦したくて ウズウズしてたぜ。\n$張り切って 特訓したから 今は ちゃんと 勢い 見せるんだ。\n$@c{serious_mopen_fists}今回も 遠慮しな!\n行こうぜ!" + }, + "victory": { + "1": "@c{neutral_eclosed}あ。 自信過剰かも。\n$@c{smile}いいけどさ、 こうなるのを 見込んだから。\n@c{serious_mopen_fists}次回まで もっと頑張らなくちゃ ってことだよな!\n\n$@c{smile}きっと 助け 要らないんだが、 もう一つの アイテムが 欲しいかと 思ったから あげるぜ。\n\n$@c{serious_smile_fists}でも これで ラストだ!\n相手に 利点を あげ続けると 行けないんだろう!" + } + }, + "rival_2_female": { + "encounter": { + "1": "@c{smile_wave}あっ、 こんなとこで 偶然だね! まだ 倒れないようだ。@c{angry_mopen}フム、えらいえらい!\n$@c{angry_mopen}考えてるのは 分かる、 つきまとってるワケ じゃないから!@c{smile_eclosed}この辺に いただけよ。\n$@c{smile_ehalf}ここまで 頑張っていて 良かったけど、 時々 負けることも 大丈夫だと 知ってるよね?\n$@c{smile}みんなは 失敗から 学ぶ… いつまでも 成功し続ける よりもね。\n$@c{angry_mopen}とにかく! 再戦の ために 大変 トレーニングしてたから 全・勢・力で 戦おう!" + }, + "victory": { + "1": "@c{neutral}…今回は 負ける はずじゃなかった…\n$@c{smile}しょうがないね。 次回まで もっともっと トレーニングしなくちゃ ってこと!\n$@c{smile_wave}そして! もう二つの アイテム、 どうぞ!\n@c{smile_wave_wink}「ありがと」なんて 必要ない!\n$@c{angry_mopen}でもね、 これで 最後! 今から サービス 一つも あげないよ~" + }, + "defeat": { + "1": "時々 負けることも いいんだよ…" + } + }, + "rival_3": { + "encounter": { + "1": "@c{smile}Hey, look who it is! It's been a while.\n@c{neutral}You're… still undefeated? Huh.\n$@c{neutral_eclosed}Things have been kind of… strange.\nIt's not the same back home without you.\n$@c{serious}I know it's selfish, but I need to get this off my chest.\n@c{neutral_eclosed}I think you're in over your head here.\n$@c{serious}Never losing once is just unrealistic.\nWe need to lose sometimes in order to grow.\n$@c{neutral_eclosed}You've had a great run but there's still so much ahead, and it only gets harder. @c{neutral}Are you prepared for that?\n$@c{serious_mopen_fists}If so, prove it to me." + }, + "victory": { + "1": "@c{angry_mhalf}This is ridiculous… I've hardly stopped training…\nHow are we still so far apart?" + } + }, + "rival_3_female": { + "encounter": { + "1": "@c{smile_wave}ヒサブリ~! まだ 負けてないね。\n@c{angry}だんだん イラッと来る。@c{smile_wave_wink}なんちゃって!\n$@c{smile_ehalf}でもよ、 本当に ふるさとが 恋しくないの? それとも… アタシ…?\nずっと会いたいよ… あの、みんなはね!\n$@c{smile_eclosed}夢を 叶ってるのを 応援してるけど、 実際は やがて 負ける。\n$@c{smile}その時が 来たら アタシは いつも通り そばにいるよ。\n@c{angry_mopen}さあ、 ここまで 頑張ってきた アタシの 力を 見せさせて!" + }, + "victory": { + "1": "@c{shock}もう… 足りなかった…?\nこのままで 決して 帰らない だろう……" + }, + "defeat": { + "1": "ベストを 尽くした。 じゃ、 帰りましょう。" + } + }, + "rival_4": { + "encounter": { + "1": "@c{neutral}Hey.\n$I won't mince words or pleasantries with you.\n@c{neutral_eclosed}I'm here to win, plain and simple.\n$@c{serious_mhalf_fists}I've learned to maximize my potential by putting all my time into training.\n$@c{smile}You get a lot of extra time when you cut out the unnecessary sleep and social interaction.\n$@c{serious_mopen_fists}None of that matters anymore, not until I win.\n$@c{neutral_eclosed}I've even reached the point where I don't lose anymore.\n@c{smile_eclosed}I suppose your philosophy wasn't so wrong after all.\n$@c{angry_mhalf}Losing is for the weak, and I'm not weak anymore.\n$@c{serious_mopen_fists}Prepare yourself." + }, + "victory": { + "1": "@c{neutral}What…@d{64} What are you?" + } + }, + "rival_4_female": { + "encounter": { + "1": "@c{neutral}アタシよ! また 忘れちゃった… のね?\n$@c{smile}こんな 遠くまで 来たのは 鼻が高いことだよ! おめでと~\nしかし、 ここは 終着点だね。\n$@c{smile_eclosed}アタシの 中にある 全然 知らなかった 部分を 目覚めたよ。\n今は、 トレーニングしか してないみたい。\n$@c{smile_ehalf}食べたり 寝たりも しなくて 朝から晩まで ポケモンを 育って、 毎日 昨日より 強くなってる。\n$@c{neutral}実は… もう 自分 認識できない。\n$結局、 峠を越して まるで カミに なった。\n今は 誰にも アタシを 倒せないと 思う。\n$ねえ、分かる? 全ては アンタの お陰で。\n@c{smile_ehalf}お礼を言うか アンタのこと嫌いか どうしたらいいの 分からない。\n$@c{angry_mopen}覚悟しなさい。" + }, + "victory": { + "1": "@c{neutral}一体…@d{64} 何モノか…?" + }, + "defeat": { + "1": "$@c{smile}ここまで 頑張ってたのを 誇りに思ってね。" + } + }, + "rival_5": { + "encounter": { + "1": "@c{neutral}…" + }, + "victory": { + "1": "@c{neutral}…" + } + }, + "rival_5_female": { + "encounter": { + "1": "@c{neutral}…" + }, + "victory": { + "1": "@c{neutral}…" + }, + "defeat": { + "1": "$@c{smile_ehalf}…" + } + }, + "rival_6": { + "encounter": { + "1": "@c{smile_eclosed}We meet again.\n$@c{neutral}I've had some time to reflect on all this.\nThere's a reason this all seems so strange.\n$@c{neutral_eclosed}Your dream, my drive to beat you…\nIt's all a part of something greater.\n$@c{serious}This isn't about me, or about you… This is about the world, @c{serious_mhalf_fists}and it's my purpose to push you to your limits.\n$@c{neutral_eclosed}Whether I've fulfilled that purpose I can't say, but I've done everything in my power.\n$@c{neutral}This place we ended up in is terrifying… Yet somehow I feel unphased, like I've been here before.\n$@c{serious_mhalf_fists}You feel the same, don't you?\n$@c{serious}…and it's like something here is speaking to me.\nThis is all the world's known for a long time now.\n$Those times we cherished together that seem so recent are nothing but a distant memory.\n$@c{neutral_eclosed}Who can say whether they were ever even real in the first place.\n$@c{serious_mopen_fists}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this.\n$@c{serious_smile_fists}I hardly know what any of this means, I just know that it's true.\n$@c{serious_mopen_fists}If you can't defeat me here and now, you won't stand a chance." + }, + "victory": { + "1": "@c{smile_eclosed}It looks like my work is done here.\n$I want you to promise me one thing.\n@c{smile}After you heal the world, please come home." + } + }, + "rival_6_female": { + "encounter": { + "1": "@c{smile_ehalf}また アタシたちだけに なった。\n$@c{smile_eclosed}ねえ、 頭の中に グルグル 巡ることが あってよ。\n$@c{smile_ehalf}アタシとアナタの 間に 起こしたことも, この変な感情も……\n$@c{smile}アナタの夢、 アタシの野心…\n$アタシたちが し続けることも 全ては… より高い 目的が あると思うよ。\n$@c{smile_eclosed}最果ての 限界まで 押すこと… それは アタシの 役割だと思う。\n$@c{smile_ehalf}今まで 役割を 上手く果たせたかは 分からないけど…… 一生懸命 頑張った。\n$こんな奇妙な 恐ろしい場所で なんか… 全てが 明らかに 見られるみたい。\n$昔から… これだけしかは この世界こそ そのもの。\n$@c{smile_eclosed}アタシたちが 大切にした 心にギュッと 抱いた思い出… もう 思い出せない。\n$@c{smile_ehalf}本当は 全部 ウソだったの? 今は 遠い彼方に あるみたい。\n$@c{angry_mopen}アナタは 最後まで 戦い続けなければ 決して 終わらない。 アナタしか できないのよ。\n$@c{smile_ehalf}この全ての 意味、 全然 分からないけど… 真実だと 感じてる。\n$@c{neutral}今ここで アタシを 倒せないと 最後に 勝ち目は ナイ。" + }, + "victory": { + "1": "@c{smile_ehalf}役割… 果たせたと思う。\n$@c{smile_eclosed}ね、 約束して。 この世界を 癒やしたら… お願い 無事に 帰って。\n$@c{smile_ehalf}……ありがとう。" + } + } +} diff --git a/src/locales/ja/egg.json b/src/locales/ja/egg.json index b0cb7b7de61..91b1442c56c 100644 --- a/src/locales/ja/egg.json +++ b/src/locales/ja/egg.json @@ -4,23 +4,23 @@ "ultraTier": "超レア", "masterTier": "伝説", "defaultTier": "ふつう", - "hatchWavesMessageSoon": "なかから おとが きこえてくる! もうすぐ うまれそう!", - "hatchWavesMessageClose": "ときどき うごいている みたい。 うまれるまで もう ちょっとかな?", - "hatchWavesMessageNotClose": "なにが うまれてくるのかな? うまれるまで まだまだ じかんが かかりそう。", - "hatchWavesMessageLongTime": "この タマゴは うまれるまで かなり じかんが かかりそう。", + "hatchWavesMessageSoon": "中から 音が 聞こえてくる! もうすぐ 生まれそう!", + "hatchWavesMessageClose": "時々 動いている みたい。生まれるまで もう ちょっとかな?", + "hatchWavesMessageNotClose": "なにが 生まれてくるのかな? 生まれるまで まだまだ 時間が かかりそう。", + "hatchWavesMessageLongTime": "この タマゴは 生まれるまで かなり 時間が かかりそう。", "gachaTypeLegendary": "伝説確率アップ", - "gachaTypeMove": "レアなタマゴわざ確率アップ", + "gachaTypeMove": "レアなタマゴ技確率アップ", "gachaTypeShiny": "色違い確率アップ", "selectMachine": "ガチャマシンを選択", "notEnoughVouchers": "タマゴクーポンが足りません!", "tooManyEggs": "タマゴが一杯です!", "pull": "回引く", "pulls": "回引く", - "sameSpeciesEgg": "{{species}}は このタマゴから うまれる!", - "hatchFromTheEgg": "{{pokemonName}}は タマゴから うまれた!", - "eggMoveUnlock": "タマゴわざ {{moveName}}を おぼえた!", - "rareEggMoveUnlock": "レアなタマゴわざ {{moveName}}を おぼえた!!", - "moveUPGacha": "わざ UP!", + "sameSpeciesEgg": "{{species}}は このタマゴから 生まれる!", + "hatchFromTheEgg": "{{pokemonName}}は タマゴから 生まれた!", + "eggMoveUnlock": "タマゴ技: {{moveName}}を 覚えた!", + "rareEggMoveUnlock": "レアなタマゴ技: {{moveName}}を 覚えた!!", + "moveUPGacha": "技 UP!", "shinyUPGacha": "色違い UP!", "legendaryUPGacha": "UP!" -} \ No newline at end of file +} diff --git a/src/locales/ja/fight-ui-handler.json b/src/locales/ja/fight-ui-handler.json index 2318cebd2d3..72cf32f1cd5 100644 --- a/src/locales/ja/fight-ui-handler.json +++ b/src/locales/ja/fight-ui-handler.json @@ -1,7 +1,8 @@ { "pp": "PP", - "power": "いりょく", - "accuracy": "めいちゅう", - "abilityFlyInText": " {{pokemonName}}の {{passive}}{{abilityName}}", - "passive": "Passive " -} \ No newline at end of file + "power": "威力", + "accuracy": "命中", + "abilityFlyInText": " {{pokemonName}}の\n{{passive}}{{abilityName}}", + "passive": "パッシブ ", + "teraHover": "{{type}}テラスタル" +} diff --git a/src/locales/ja/filter-bar.json b/src/locales/ja/filter-bar.json index c09705d9b50..891b7d87674 100644 --- a/src/locales/ja/filter-bar.json +++ b/src/locales/ja/filter-bar.json @@ -32,7 +32,7 @@ "noPokerus": "ポケルス - なし", "sortByNumber": "No.", "sortByCost": "ポイント", - "sortByCandies": "飴の数", + "sortByCandies": "アメの数", "sortByIVs": "個体値", "sortByName": "名前" -} \ No newline at end of file +} diff --git a/src/locales/ja/game-mode.json b/src/locales/ja/game-mode.json index 36559e5cce3..dc04b36932f 100644 --- a/src/locales/ja/game-mode.json +++ b/src/locales/ja/game-mode.json @@ -1,8 +1,8 @@ { "classic": "クラシック", "endless": "エンドレス", - "endlessSpliced": "エンドレス (Spliced)", + "endlessSpliced": "エンドレス(吸収合体)", "dailyRun": "デイリーラン", - "unknown": "Unknown", + "unknown": "???", "challenge": "チャレンジ" -} \ No newline at end of file +} diff --git a/src/locales/ja/game-stats-ui-handler.json b/src/locales/ja/game-stats-ui-handler.json index 25bb21f701a..25301aa4297 100644 --- a/src/locales/ja/game-stats-ui-handler.json +++ b/src/locales/ja/game-stats-ui-handler.json @@ -1,6 +1,6 @@ { "stats": "統計", - "playTime": "プレー時間", + "playTime": "プレイ時間", "totalBattles": "合計バトル数", "starters": "スターター数", "shinyStarters": "色違いスターター数", @@ -12,7 +12,7 @@ "dailyRunAttempts": "デイリーラン", "dailyRunWins": "デイリーラン勝利", "endlessRuns": "エンドレスラン", - "highestWaveEndless": "エンドレス最高ウェーブ", + "highestWaveEndless": "エンドレス最高ラウンド", "highestMoney": "最大貯金", "highestDamage": "最大ダメージ", "highestHPHealed": "最大HP回復", @@ -20,23 +20,23 @@ "pokemonDefeated": "倒したポケモン", "pokemonCaught": "捕まえたポケモン", "eggsHatched": "ふかしたタマゴ", - "subLegendsSeen": "見つけた順伝説", - "subLegendsCaught": "捕まえた順伝説", - "subLegendsHatched": "ふかした順伝説", - "legendsSeen": "見つけた伝説", - "legendsCaught": "捕まえた伝説", - "legendsHatched": "ふかした伝説", + "subLegendsSeen": "見つけた順伝説ポケモン", + "subLegendsCaught": "捕まえた準伝説ポケモン", + "subLegendsHatched": "ふかした準伝説ポケモン", + "legendsSeen": "見つけた伝説ポケモン", + "legendsCaught": "捕まえた伝説ポケモン", + "legendsHatched": "ふかした伝説ポケモン", "mythicalsSeen": "見つけた幻ポケモン", "mythicalsCaught": "捕まえた幻ポケモン", "mythicalsHatched": "ふかした幻ポケモン", - "shiniesSeen": "見つけた色違い", - "shiniesCaught": "捕まえた色違い", - "shiniesHatched": "ふかした色違い", - "pokemonFused": "合体したポケモン", + "shiniesSeen": "見つけた色違いポケモン", + "shiniesCaught": "捕まえた色違いポケモン", + "shiniesHatched": "ふかした色違いポケモン", + "pokemonFused": "吸収合体したポケモン", "trainersDefeated": "倒したトレーナー", "eggsPulled": "引いたタマゴ", "rareEggsPulled": "引いたレアタマゴ", "epicEggsPulled": "引いた超レアタマゴ", "legendaryEggsPulled": "引いた伝説タマゴ", "manaphyEggsPulled": "引いたマナフィタマゴ" -} \ No newline at end of file +} diff --git a/src/locales/ja/growth.json b/src/locales/ja/growth.json index 3d23fa1f46d..e5c1317632f 100644 --- a/src/locales/ja/growth.json +++ b/src/locales/ja/growth.json @@ -1,8 +1,8 @@ { - "Erratic": "60まんタイプ", - "Fast": "80まんタイプ", - "Medium_Fast": "100まんタイプ", - "Medium_Slow": "105まんタイプ", - "Slow": "125まんタイプ", - "Fluctuating": "164まんタイプ" -} \ No newline at end of file + "Erratic": "60万タイプ", + "Fast": "80万タイプ", + "Medium_Fast": "100万タイプ", + "Medium_Slow": "105万タイプ", + "Slow": "125万タイプ", + "Fluctuating": "164万タイプ" +} diff --git a/src/locales/ja/menu-ui-handler.json b/src/locales/ja/menu-ui-handler.json index beb014b84a9..851c8478e9d 100644 --- a/src/locales/ja/menu-ui-handler.json +++ b/src/locales/ja/menu-ui-handler.json @@ -2,25 +2,28 @@ "GAME_SETTINGS": "設定", "ACHIEVEMENTS": "実績", "STATS": "統計", + "RUN_HISTORY": "ラン歴", "EGG_LIST": "タマゴリスト", "EGG_GACHA": "タマゴガチャ", "MANAGE_DATA": "データ管理", "COMMUNITY": "コミュニティ", - "SAVE_AND_QUIT": "保存して終了", + "SAVE_AND_QUIT": "セーブして終了", "LOG_OUT": "ログアウト", "slot": "スロット {{slotNumber}}", - "importSession": "セッションのインポート", + "importSession": "セッションをインポート", "importSlotSelect": "インポート先の スロットを 選んでください", - "exportSession": "セッションのエクスポート", + "exportSession": "セッションをエクスポート", "exportSlotSelect": "エクスポート元の スロットを 選んでください", - "importData": "データのインポート", - "exportData": "データのエクスポート", + "importRunHistory": "ラン歴をインポート", + "exportRunHistory": "ラン歴をエクスポート", + "importData": "データをインポート", + "exportData": "データをエクスポート", "consentPreferences": "同意設定", "linkDiscord": "Discord連携", "unlinkDiscord": "Discord連携解除", "linkGoogle": "Google連携", "unlinkGoogle": "Google連携解除", "cancel": "キャンセル", - "losingProgressionWarning": "戦闘開始からの データが 保存されません。\nよろしいですか?", - "noEggs": "現在 タマゴを ふかしていません!" -} \ No newline at end of file + "losingProgressionWarning": "戦闘開始からの データが セーブされません。\nよろしいですか?", + "noEggs": "現在は タマゴを ふかしていません!" +} diff --git a/src/locales/ja/menu.json b/src/locales/ja/menu.json index ce6f9ae0672..f0914a7941c 100644 --- a/src/locales/ja/menu.json +++ b/src/locales/ja/menu.json @@ -1,38 +1,57 @@ { "cancel": "キャンセル", "continue": "つづきから", - "loadGame": "ロードセーブ", + "dailyRun": "日替わりラン(ベータ版)", + "loadGame": "セーブを読み込む", "newGame": "はじめから", - "username": "ユーザーめい", + "settings": "設定", + "selectGameMode": "ゲームモードを 選んでください。", + "logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要が ありません!", + "username": "ユーザー名", "password": "パスワード", "login": "ログイン", - "orUse": "Or use", - "register": "かいいん とうろく", - "emptyUsername": "ユーザー名は空にできません", - "invalidLoginUsername": "入力したユーザー名は無効です", - "invalidRegisterUsername": "ユーザー名には英文字、数字、アンダースコアのみを含める必要があります", + "orUse": "他の\nログイン方法", + "register": "登録", + "emptyUsername": "ユーザー名を 空にする ことは できません", + "invalidLoginUsername": "入力されたユーザー名は無効です", + "invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむ必要が あります", "invalidLoginPassword": "入力したパスワードは無効です", - "invalidRegisterPassword": "パスワードは6文字以上でなければなりません", - "usernameAlreadyUsed": "ユーザー名は既に使用されています", - "accountNonExistent": "ユーザーは存在しません", - "unmatchingPassword": "パスワードが一致しません", - "passwordNotMatchingConfirmPassword": "パスワードは確認パスワードと一致する必要があります", + "invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません", + "usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています", + "accountNonExistent": "入力したユーザーは 存在しません", + "unmatchingPassword": "入力したパスワードが 一致しません", + "passwordNotMatchingConfirmPassword": "パスワードは パスワード確認と 一致する 必要があります", "confirmPassword": "パスワード確認", - "registrationAgeWarning": "登録することで、あなたが13歳以上であることを確認します。", + "registrationAgeWarning": "登録では 13歳以上 であることを 確認します。", "backToLogin": "ログインへ", - "failedToLoadSaveData": "保存データの読み込みに失敗しました。ページを再読み込みしてください。\nこれが続く場合は、管理者に連絡してください。", - "sessionSuccess": "セッションが正常に読み込まれました。", - "failedToLoadSession": "セッションデータを読み込むことができませんでした。\nデータが破損している可能性があります。", - "boyOrGirl": "おとこのこ?\nそれとも おんなのこ?", - "evolving": "…おや!?\n{{pokemonName}}のようすが…!", - "stoppedEvolving": "{{pokemonName}}のへんかがとまった", - "evolutionDone": "おめでとう!\n{{pokemonName}}は{{evolvedPokemonName}}にしんかした!", - "dailyRankings": "ほんじつのランキング", - "weeklyRankings": "しゅうのランキング", + "failedToLoadSaveData": "セーブデータの 読み込みは 不可能でした。ページを 再読み込み してください。\n長い間に続く 場合は 管理者に 連絡してください。", + "sessionSuccess": "セッションが 正常に 読み込まれました。", + "failedToLoadSession": "セッションデータを 読み込むことが できませんでした。\nデータが 破損している 可能性が あります。", + "boyOrGirl": "男の子?\nそれとも 女の子?", + "evolving": "…おや!?\n{{pokemonName}}の 様子が…!", + "stoppedEvolving": "あれ…? {{pokemonName}}の 変化が 止まった!", + "pauseEvolutionsQuestion": "{{pokemonName}}の 進化を 休止しますか?\n後で 手持ち画面から 進化を また 可能にできます。", + "evolutionsPaused": "{{pokemonName}}の 進化を 休止しました。", + "evolutionDone": "おめでとう!\n{{pokemonName}}は {{evolvedPokemonName}}に 進化した!", + "dailyRankings": "今日のランキング", + "weeklyRankings": "今週のランキング", "noRankings": "ランキングなし", "positionIcon": "#", - "loading": "よみこみちゅう…", + "usernameScoreboard": "ユーザー名", + "score": "スコア", + "wave": "波", + "loading": "読み込み中…", + "loadingAsset": "読み込み中:{{assetName}}", "playersOnline": "オンラインのプレイヤー", "yes": "はい", - "no": "いいえ" -} \ No newline at end of file + "no": "いいえ", + "disclaimer": "免責", + "disclaimerDescription": "このゲームは 未完成作品です。\nセーブデータの 損失を含める ゲーム性に関する 問題が 起きる可能性が あります。\nなお、ゲームは 予告なく変更される 可能性もあり、\nさらに更新され、完成されるとも 限りません。", + "choosePokemon": "ポケモンを選ぶ", + "renamePokemon": "ニックネームを変える", + "rename": "名前を変える", + "nickname": "ニックネーム", + "errorServerDown": "おや!\nサーバーとの 接続中に 問題が 発生しました。\nゲームは 自動的に 再接続されます から\nウィンドウは 開いたままに しておいても よろしいです。", + "noSaves": "何の セーブファイルも ありません!", + "tooManySaves": "セーブファイルが いっぱいです!" +} diff --git a/src/locales/ja/modifier-select-ui-handler.json b/src/locales/ja/modifier-select-ui-handler.json index 9370f01491e..d7428c8e373 100644 --- a/src/locales/ja/modifier-select-ui-handler.json +++ b/src/locales/ja/modifier-select-ui-handler.json @@ -1,12 +1,12 @@ { "transfer": "アイテム移行", "reroll": "選択肢変更", - "lockRarities": "レア度の固定", - "checkTeam": "チームを確認", - "transferDesc": "ポケモンの 手持ちアイテムを 移行する", + "lockRarities": "レア度を固定", + "checkTeam": "手持ちを確認", + "transferDesc": "手持ちポケモンの 持たせるアイテムを 移行する", "rerollDesc": "お金を 使って アイテムの 選択肢を 変更する", - "lockRaritiesDesc": "選択肢を 変更するときの レア度を 固定する\n(選択肢変更金額を影響する)", - "checkTeamDesc": "チームの 状態を 確認する\nフォルムチェンジアイテムを 有効・無効にする", + "lockRaritiesDesc": "選択肢を 変更するときの レア度を 固定する\n(選択肢変更の価格は変わる)", + "checkTeamDesc": "手持ちポケモンの 状態を 確認する\nフォルムチェンジアイテムを 有効・無効にする", "rerollCost": "{{formattedMoney}}円", "itemCost": "{{formattedMoney}}円" } diff --git a/src/locales/ja/modifier-type.json b/src/locales/ja/modifier-type.json index 6effb1d9d82..a2e62bd941e 100644 --- a/src/locales/ja/modifier-type.json +++ b/src/locales/ja/modifier-type.json @@ -2,434 +2,266 @@ "ModifierType": { "AddPokeballModifierType": { "name": "{{modifierCount}}x {{pokeballName}}", - "description": "{{pokeballName}} x{{modifierCount}}こ てにいれる (インベントリ: {{pokeballAmount}}) \nほそくりつ: {{catchRate}}" + "description": "{{pokeballName}}を {{modifierCount}}個 手に入れる (所有: {{pokeballAmount}})\n捕捉率:{{catchRate}}" }, "AddVoucherModifierType": { "name": "{{modifierCount}}x {{voucherTypeName}}", - "description": "{{voucherTypeName}} x{{modifierCount}}こ てにいれる" + "description": "{{voucherTypeName}}を {{modifierCount}}個 手に入れる" }, "PokemonHeldItemModifierType": { "extra": { - "inoperable": "{{pokemonName}} はこのアイテムを\nもつことができません!", - "tooMany": "{{pokemonName}} はこのアイテムを\nもちすぎています!" + "inoperable": "{{pokemonName}}は このアイテムを\n持つ ことが できません!", + "tooMany": "{{pokemonName}}は このアイテムを\n持ちすぎています!" } }, "PokemonHpRestoreModifierType": { - "description": "ポケモンの HPを {{restorePoints}} または {{restorePercent}}%のどちらか たかいほうを かいふくする", + "description": "ポケモン 一匹の {{restorePoints}}HP、または {{restorePercent}}% のどちらか 高い方を 回復する", "extra": { - "fully": "ポケモンのHPをすべてかいふくする", - "fullyWithStatus": "ポケモンの HPと じょうたいいじょうを かいふくする" + "fully": "ポケモン 1匹の HPを すべて 回復する", + "fullyWithStatus": "ポケモン 1匹の HPと 状態異常を すべて 回復する" } }, "PokemonReviveModifierType": { - "description": "ひんしになってしまったポケモンの HP {{restorePercent}}%を かいふくする" + "description": "ひんしに なった ポケモン 1匹を 元気にした上で\nHPを {{restorePercent}}% 回復する" }, "PokemonStatusHealModifierType": { - "description": "すべてのじょうたいいじょうを なおす" + "description": "ポケモン 1匹の 状態の 異常を すべて 回復する" }, "PokemonPpRestoreModifierType": { - "description": "ポケモンが おぼえている わざの PPを {{restorePoints}}ずつ かいふくする", + "description": "ポケモンが 覚えている 技のうち\n1つの PPを 10だけ 回復する", "extra": { - "fully": "ポケモンが おぼえている わざの PPを すべて かいふくする" + "fully": "ポケモンが 覚えている 技のうち\n1つの PPを すべて 回復する" } }, "PokemonAllMovePpRestoreModifierType": { - "description": "ポケモンが おぼえている 4つの わざの PPを {{restorePoints}}ずつ かいふくする", + "description": "ポケモンが 覚えている 4つの 技の PPを {{restorePoints}}ずつ 回復する", "extra": { - "fully": "ポケモンが おぼえている 4つの わざの PPを すべて かいふくする" + "fully": "ポケモンが 覚えている 4つの 技の PPを すべて 回復する" } }, "PokemonPpUpModifierType": { - "description": "ポケモンのわざのさいだいPPを さいだいPP 5ごとに {{upPoints}} ポイントずつ ふやします(さいだい3)" + "description": "ポケモンが 覚えている 技のうち 1つの PPの 最大値を 5ごとに {{upPoints}}ポイントずつ 上げる(最大3)" }, "PokemonNatureChangeModifierType": { - "name": "{{natureName}} Mint", - "description": "ポケモンのせいかくを {{natureName}}にかえて スターターのせいかくをえいきゅうにかいじょする" + "name": "{{natureName}}ミント", + "description": "ポケモン 1匹の 性格を 「{{natureName}}」に 変える。\nその上、スターター画面でも {{natureName}}が 選べるように なる。" }, "DoubleBattleChanceBoosterModifierType": { - "description": "バトル{{battleCount}}かいのあいだ ダブルバトルになるかくりつを2ばいにする" + "description": "バトル{{battleCount}}回の間  ダブルバトルになる  確率を 4倍に する" }, - "TempBattleStatBoosterModifierType": { - "description": "すべてのパーティメンバーの {{tempBattleStatName}}を5かいのバトルのあいだ 1だんかいあげる" + "TempStatStageBoosterModifierType": { + "description": "全員の 手持ちポケモンの {{stat}}を 最大5回の バトルの間に {{amount}} 上げる", + "extra": { + "stage": "1段階", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { - "description": "ポケモンの {{moveType}}タイプのわざのいりょくを20パーセントあげる" + "description": "ポケモンの {{moveType}}タイプの 技の 威力を 20% 上げる" }, "PokemonLevelIncrementModifierType": { - "description": "ポケモンのレベルを1あげる" + "description": "ポケモンの レベルを {{levels}} 上げる" }, "AllPokemonLevelIncrementModifierType": { - "description": "すべてのパーティメンバーのレベルを1あげる" + "description": "手持ちポケモンの 全員のレベルを {{levels}} 上げる" }, - "PokemonBaseStatBoosterModifierType": { - "description": "ポケモンの{{statName}}のきほんステータスを10パーセントあげる。こたいちがたかいほどスタックのげんかいもたかくなる。" + "BaseStatBoosterModifierType": { + "description": "ポケモンの 基本の{{stat}}を 10% あげる。\n個体値が 高けば高いほど 持てる限界が 上がる" }, "AllPokemonFullHpRestoreModifierType": { - "description": "すべてのポケモンのHPを100パーセントかいふくする" + "description": "手持ちポケモン 全員の HPを すべて 回復する" }, "AllPokemonFullReviveModifierType": { - "description": "ひんしになったすべてのポケモンをふっかつさせ HPをぜんかいふくする" + "description": "ひんしに なってしまった ポケモン 全員の HPを すべて 回復する" }, "MoneyRewardModifierType": { - "description": "{{moneyMultiplier}}ぶんのきんがくをあたえる (₽{{moneyAmount}})", + "description": "{{moneyMultiplier}}額の 円を 与える({{moneyAmount}}円)", "extra": { - "small": "すくない", - "moderate": "ふつう", - "large": "おおい" + "small": "小", + "moderate": "ある金", + "large": "多" } }, "ExpBoosterModifierType": { - "description": "もらえるけいけんちを {{boostPercent}}パーセントふやす" + "description": "もらえる 経験値を {{boostPercent}}% 増やす" }, "PokemonExpBoosterModifierType": { - "description": "もっているポケモンのけいけんちを {{boostPercent}}パーセントふやす" + "description": "持っているポケモンの もらう経験値を {{boostPercent}}% 増やす" }, "PokemonFriendshipBoosterModifierType": { - "description": "しょうりごとに 50%パーセント なかよく なりやすくなる" + "description": "持っているポケモンの なかよし度の収穫が 勝利ごとに 50% 上がる" }, "PokemonMoveAccuracyBoosterModifierType": { - "description": "わざのめいちゅうりつを{{accuracyAmount}}ふやす (さいだい100)" + "description": "技の 命中率を {{accuracyAmount}} 増やす(最大 100)" }, "PokemonMultiHitModifierType": { - "description": "こうげきがもういちどあたる。そのたびにいりょくがそれぞれ60/75/82.5%へる" + "description": "持たせると 攻撃が もう一度 当たるが、\n威力が 減る(1個:60%減る/2個:75%減る/3個:82.5%減る)" }, "TmModifierType": { - "name": "TM{{moveId}} - {{moveName}}", - "description": "ポケモンに {{moveName}} をおしえる" + "name": "TM{{moveId}}\n{{moveName}}", + "description": "ポケモンに {{moveName}}を 教える" }, "TmModifierTypeWithInfo": { - "name": "TM{{moveId}} - {{moveName}}", - "description": "ポケモンに {{moveName}} をおしえる\n(Hold C or Shift for more info)" + "name": "TM{{moveId}}\n{{moveName}}", + "description": "ポケモンに {{moveName}}を 教える\n(Cキー/Shiftキーを押すと 技情報が見える)" }, "EvolutionItemModifierType": { - "description": "とくていのポケモンをしんかさせる" + "description": "ある特定の ポケモンを 進化させる" }, "FormChangeItemModifierType": { - "description": "とくていのポケモンをフォームチェンジさせる" + "description": "ある特定の ポケモンを フォームチェンジさせる" }, "FusePokemonModifierType": { - "description": "2匹のポケモンをけつごうする (とくせいをいどうし、きほんステータスとタイプをわけ、わざプールをきょうゆうする)" + "description": "2匹の ポケモンを 吸収合体する(特性が移動し、基本能力とタイプを分け、覚える技を共有する)" }, "TerastallizeModifierType": { - "name": "{{teraType}} Tera Shard", - "description": "ポケモンを{{teraType}}タイプにテラスタル(10かいのバトルまで)" + "name": "テラピース{{teraType}}", + "description": "ポケモンを {{teraType}}タイプに テラスタルさせる(最大10回のバトルの間)" }, "ContactHeldItemTransferChanceModifierType": { - "description": "こうげきするとき あいてがもっているアイテムを {{chancePercent}}パーセントのかくりつでぬすむ" + "description": "持っているポケモンが 攻撃すると 相手の持っている\nアイテムを {{chancePercent}}%の 確率で 盗む" }, "TurnHeldItemTransferModifierType": { - "description": "まいターン あいてからひとつのもちものをてにいれる" + "description": "毎ターン 相手から 一つの 持っている\nアイテム を吸い込んで 盗む" }, "EnemyAttackStatusEffectChanceModifierType": { - "description": "こうげきわざに {{chancePercent}}パーセントのかくりつで {{statusEffect}}をあたえる" + "description": "攻撃技に {{chancePercent}}%の 確率で {{statusEffect}}を 与える" }, "EnemyEndureChanceModifierType": { - "description": "こうげきをこらえるかくりつを{{chancePercent}}パーセントふやす" - }, - "RARE_CANDY": { - "name": "ふしぎなアメ" - }, - "RARER_CANDY": { - "name": "もっとふしぎなアメ" - }, - "MEGA_BRACELET": { - "name": "メガバングル", - "description": "メガストーンがつかえるようになる" - }, - "DYNAMAX_BAND": { - "name": "ダイマックスバンド", - "description": "ダイスープがつかえるようになる" - }, - "TERA_ORB": { - "name": "テラスタルオーブ", - "description": "テラピースがつかえるようになる" - }, - "MAP": { - "name": "ちず", - "description": "わかれみちでいきさきをえらべるようになる" - }, - "POTION": { - "name": "キズぐすり" - }, - "SUPER_POTION": { - "name": "いいキズぐすり" - }, - "HYPER_POTION": { - "name": "すごいキズぐすり" - }, - "MAX_POTION": { - "name": "まんたんのくすり" - }, - "FULL_RESTORE": { - "name": "かいふくのくすり" - }, - "REVIVE": { - "name": "げんきのかけら" - }, - "MAX_REVIVE": { - "name": "げんきのかたまり" - }, - "FULL_HEAL": { - "name": "なんでもなおし" - }, - "SACRED_ASH": { - "name": "せいなるはい" - }, - "REVIVER_SEED": { - "name": "ふっかつのタネ", - "description": "ひんしになったときもっているポケモンをHPはんぶんでふっかつさせる" - }, - "WHITE_HERB": { - "name": "White Herb", - "description": "An item to be held by a Pokémon. It will restore any lowered stat in battle." - }, - "ETHER": { - "name": "ピーピーエイド" - }, - "MAX_ETHER": { - "name": "ピーピーリカバー" - }, - "ELIXIR": { - "name": "ピーピーエイダー" - }, - "MAX_ELIXIR": { - "name": "ピーピーマックス" - }, - "PP_UP": { - "name": "ポイントアップ" - }, - "PP_MAX": { - "name": "ポイントマックス" - }, - "LURE": { - "name": "ダブルバトルコロン" - }, - "SUPER_LURE": { - "name": "シルバーコロン" - }, - "MAX_LURE": { - "name": "ゴールドコロン" - }, - "MEMORY_MUSHROOM": { - "name": "きおくキノコ", - "description": "ポケモンのわすれたわざをおぼえさせる" - }, - "EXP_SHARE": { - "name": "がくしゅうそうち", - "description": "バトルにさんかしていないポケモンが けいけんちの20パーセントをもらう" - }, - "EXP_BALANCE": { - "name": "バランスそうち", - "description": "レベルがひくいパーティメンバーがもらうけいけんちがふえる" - }, - "OVAL_CHARM": { - "name": "まるいおまもり", - "description": "バトルにふくすうのポケモンがさんかするとけいけんちが10パーセントふえる" - }, - "EXP_CHARM": { - "name": "けいけんちおまもり" - }, - "SUPER_EXP_CHARM": { - "name": "いいけいけんちおまもり" - }, - "GOLDEN_EXP_CHARM": { - "name": "ゴールドけいけんちおまもり" - }, - "LUCKY_EGG": { - "name": "しあわせタマゴ" - }, - "GOLDEN_EGG": { - "name": "おうごんタマゴ" - }, - "SOOTHE_BELL": { - "name": "やすらぎのすず" - }, - "SCOPE_LENS": { - "name": "ピントレンズ", - "description": "弱点が 見える レンズ。持たせた ポケモンの技が 急所に 当たりやすくなる。" - }, - "LEEK": { - "name": "ながねぎ", - "description": "とても長くて 硬いクキ。カモネギに 持たせると 技が 急所に 当たりやすくなる。" - }, - "EVIOLITE": { - "name": "しんかのきせき", - "description": "進化の不思議な かたまり。持たせると 進化前ポケモンの 防御と 特防が あがる。" - }, - "SOUL_DEW": { - "name": "こころのしずく", - "description": "ポケモンのせいかくがステータスにあたえるえいきょうを10%ふやす(合算)" - }, - "NUGGET": { - "name": "きんのたま" - }, - "BIG_NUGGET": { - "name": "でかいきんのたま" - }, - "RELIC_GOLD": { - "name": "こだいのきんか" - }, - "AMULET_COIN": { - "name": "おまもりこばん", - "description": "もらえる おかねが 20パーセント ふえる" - }, - "GOLDEN_PUNCH": { - "name": "ゴールドパンチ", - "description": "あたえたちょくせつダメージの50パーセントをおかねとしてもらえる" - }, - "COIN_CASE": { - "name": "コインケース", - "description": "10かいのバトルごとにもちきんの10パーセントをりしとしてうけとる" - }, - "LOCK_CAPSULE": { - "name": "ロックカプセル", - "description": "リロールするときにアイテムのレアリティをロックできる" - }, - "GRIP_CLAW": { - "name": "ねばりのかぎづめ" - }, - "WIDE_LENS": { - "name": "こうかくレンズ" - }, - "MULTI_LENS": { - "name": "マルチレンズ" - }, - "HEALING_CHARM": { - "name": "ヒーリングチャーム", - "description": "HPをかいふくするわざとアイテムのこうかを10パーセントあげる (ふっかつはのぞく)" - }, - "CANDY_JAR": { - "name": "アメボトル", - "description": "ふしぎなアメのアイテムでふえるレベルが1ふえる" - }, - "BERRY_POUCH": { - "name": "きのみぶくろ", - "description": "つかったきのみがつかわれないかくりつを30パーセントふやす" - }, - "FOCUS_BAND": { - "name": "きあいのハチマキ", - "description": "ひんしになるダメージをうけてもHP1でたえるかくりつを10パーセントふやす" - }, - "QUICK_CLAW": { - "name": "せんせいのツメ", - "description": "すばやさにかかわらず さきにこうどうするかくりつを10パーセントふやす (ゆうせんどのあと)" - }, - "KINGS_ROCK": { - "name": "おうじゃのしるし", - "description": "こうげきわざがあいてをひるませるかくりつを10パーセントふやす" - }, - "LEFTOVERS": { - "name": "たべのこし", - "description": "ポケモンのさいだいHPの1/16をまいターンかいふくする" - }, - "SHELL_BELL": { - "name": "かいがらのすず", - "description": "ポケモンがあたえたダメージの1/8をかいふくする" - }, - "TOXIC_ORB": { - "name": "どくどくだま", - "description": "ターンの終わりに すでに じょうたいじょうしょうが なければ もうどくの じょうたいに なる" - }, - "FLAME_ORB": { - "name": "かえんだま", - "description": "ターンの終わりに すでに じょうたいじょうしょうが なければ やけどの じょうたいに なる" - }, - "BATON": { - "name": "バトン", - "description": "ポケモンをこうたいするときにこうかをひきつぎ わなをかいひすることもできる" - }, - "SHINY_CHARM": { - "name": "ひかるおまもり", - "description": "やせいのポケモンがいろちがいポケモンであるかくりつをおおきくふやす" - }, - "ABILITY_CHARM": { - "name": "とくせいおまもり", - "description": "やせいのポケモンがかくれとくせいをもつかくりつをおおきくふやす" - }, - "IV_SCANNER": { - "name": "こたいち たんちき", - "description": "やせいのポケモンのこたいちをスキャンできる。スタックごとに2つのこたいちがあきらかになる。もっともたかいこたいちがさいしょにひょうじされる" - }, - "DNA_SPLICERS": { - "name": "いでんしのくさび" - }, - "MINI_BLACK_HOLE": { - "name": "ミニブラックホール" - }, - "GOLDEN_POKEBALL": { - "name": "ゴールドモンスターボール", - "description": "バトルごとに1つのアイテムオプションをふやす" - }, - "ENEMY_DAMAGE_BOOSTER": { - "name": "ダメージトークン", - "description": "ダメージを5%ふやす" - }, - "ENEMY_DAMAGE_REDUCTION": { - "name": "プロテクショントークン", - "description": "うけるダメージを2.5%へらす" - }, - "ENEMY_HEAL": { - "name": "かいふくトークン", - "description": "まいターンさいだいHPの2%をかいふくする" - }, - "ENEMY_ATTACK_POISON_CHANCE": { - "name": "どくトークン" - }, - "ENEMY_ATTACK_PARALYZE_CHANCE": { - "name": "まひトークン" - }, - "ENEMY_ATTACK_BURN_CHANCE": { - "name": "やけどトークン" - }, - "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { - "name": "なおしトークン", - "description": "まいターン2.5%のかくりつでじょうたいじょうしょうをかいふくする" - }, - "ENEMY_ENDURE_CHANCE": { - "name": "こらえるトークン" - }, - "ENEMY_FUSED_CHANCE": { - "name": "フュージョントークン", - "description": "やせいのポケモンがフュージョンするかくりつを1%ふやす" - } + "description": "ひんしに なりそうな 技を 受けても\n{{chancePercent}}%の 確率で HPを 1だけ 残して 耐える" + }, + + "RARE_CANDY": { "name": "ふしぎなアメ" }, + "RARER_CANDY": { "name": "ふかしぎなアメ" }, + + "MEGA_BRACELET": { "name": "メガバングル", "description": "メガストーンが 見つけられる ように なる" }, + "DYNAMAX_BAND": { "name": "ダイマックスバンド", "description": "ダイキノコが 見つけられる ように なる" }, + "TERA_ORB": { "name": "テラスタルオーブ", "description": "テラピースが 見つけられる ように なる" }, + + "MAP": { "name": "ちず", "description": "分かれ道で 行き先が 選べる よう になる" }, + + "POTION": { "name": "キズぐすり" }, + "SUPER_POTION": { "name": "いいキズぐすり" }, + "HYPER_POTION": { "name": "すごいキズぐすり" }, + "MAX_POTION": { "name": "まんたんのくすり" }, + "FULL_RESTORE": { "name": "かいふくのくすり" }, + + "REVIVE": { "name": "げんきのかけら" }, + "MAX_REVIVE": { "name": "げんきのかたまり" }, + + "FULL_HEAL": { "name": "なんでもなおし" }, + + "SACRED_ASH": { "name": "せいなるはい" }, + + "REVIVER_SEED": { "name": "ふっかつのタネ", "description": "持たせると 直接攻撃から ひんしに なれば\n復活して HPを 50% 回復する" }, + + "WHITE_HERB":{ "name": "しろいハーブ", "description": "持たせた ポケモンの能力が さがったとき 1度だけ 元の状態に 戻す" }, + + "ETHER": { "name": "ピーピーエイド" }, + "MAX_ETHER": { "name": "ピーピーリカバー" }, + + "ELIXIR": { "name": "ピーピーエイダー" }, + "MAX_ELIXIR": { "name": "ピーピーマックス" }, + + "PP_UP": { "name": "ポイントアップ" }, + "PP_MAX": { "name": "ポイントマックス" }, + + "LURE": { "name": "むしよせコロン" }, + "SUPER_LURE": { "name": "シルバーコロン" }, + "MAX_LURE": { "name": "ゴールドコロン" }, + + "MEMORY_MUSHROOM": { "name": "きおくのキノコ", "description": "1匹の ポケモンの 忘れた技を 1つ 覚えさせる" }, + + "EXP_SHARE": { "name": "がくしゅうそうち", "description": "バトルに 参加していない ポケモンが 参加したポケモンの 経験値を 20% もらう" }, + "EXP_BALANCE": { "name": "バランスそうち", "description": "レベルが低い 手持ちポケモンの もらう 経験値が 増える" }, + + "OVAL_CHARM": { "name": "まるいおまもり", "description": "バトルに 複数の ポケモンが 参加すると、\n経験値が 参加したポケモンずつ 10% 増える" }, + + "EXP_CHARM": { "name": "けいけんおまもり" }, + "SUPER_EXP_CHARM": { "name": "いいけいけんおまもり" }, + "GOLDEN_EXP_CHARM": { "name": "ゴールドけいけんちおまもり" }, + + "LUCKY_EGG": { "name": "しあわせタマゴ" }, + "GOLDEN_EGG": { "name": "ゴールドタマゴ" }, + + "SOOTHE_BELL": { "name": "やすらぎのすず" }, + + "SCOPE_LENS": { "name": "ピントレンズ", "description": "弱点が 見える レンズ。\n持たせた ポケモンの技が 急所に 当たりやすくなる"}, + "DIRE_HIT": { "name": "クリティカット", "extra": { "raises": "急所率" } }, + "LEEK": { "name": "ながねぎ", "description": "とても長くて 硬いクキ。\nカモネギに 持たせると 技が 急所に 当たりやすくなる"}, + + "EVIOLITE": { "name": "しんかのきせき", "description": "進化の不思議な かたまり。\n持たせると 進化前ポケモンの 防御と 特防が あがる" }, + + "SOUL_DEW": { "name": "こころのしずく", "description": "持たせると ポケモンの 性格が 能力に与える 影響は 10% 増える(合算)" }, + + "NUGGET": { "name": "きんのたま" }, + "BIG_NUGGET": { "name": "でかいきんのたま" }, + "RELIC_GOLD": { "name": "こだいのきんか" }, + + "AMULET_COIN": { "name": "おまもりこばん", "description": "もらえる お金が 20% 増える" }, + "GOLDEN_PUNCH": { "name": "ゴールドパンチ", "description": "持たせると 与える 直接なダメージの 50%が お金として もらえる" }, + "COIN_CASE": { "name": "コインケース", "description": "10回の バトルごとに 持ち金の 10%を 利子として 受け取れる" }, + + "LOCK_CAPSULE": { "name": "ロックカプセル", "description": "ご褒美の 選択肢変更するとき アイテムの レア度を固定できる ように なる" }, + + "GRIP_CLAW": { "name": "ねばりのかぎづめ" }, + "WIDE_LENS": { "name": "こうかくレンズ" }, + + "MULTI_LENS": { "name": "マルチレンズ" }, + + "HEALING_CHARM": { "name": "かいふくおまもり", "description": "回復する 技や アイテムの 効果を 10% あげる(復活アイテムは除く)" }, + "CANDY_JAR": { "name": "アメボトル", "description": "ふしぎなアメや ふかしぎなアメで あげるレベルを 1 増える" }, + + "BERRY_POUCH": { "name": "きのみぶくろ", "description": "使ったきのみは 無くならない 30%の可能性を 加える" }, + + "FOCUS_BAND": { "name": "きあいのハチマキ", "description": "持たせると ひんしに なりそうな 技を 受けても\n10%の可能性で HPを 1だけ 残して 耐える" }, + + "QUICK_CLAW": { "name": "せんせいのツメ", "description": "持たせると 10%の可能性で 相手より 先に 行動できる (優先技のあと)" }, + + "KINGS_ROCK": { "name": "おうじゃのしるし", "description": "持たせると 攻撃して ダメージを 与えたときに\n10%の可能性で 相手を ひるませる" }, + + "LEFTOVERS": { "name": "たべのこし", "description": "持たせると 毎ターン 最大HPの 1/16を 回復する" }, + "SHELL_BELL": { "name": "かいがらのすず", "description": "持たせると ポケモンが 相手に 与えたダメージの 1/8をHPとして 回復する." }, + + "TOXIC_ORB": { "name": "どくどくだま", "description": "触ると 毒をだす 不思議な玉。\n持たせると 戦闘中に 猛毒の状態に なる" }, + "FLAME_ORB": { "name": "かえんだま", "description": "触ると 熱をだす 不思議な玉。\n持たせると 戦闘中に やけどの状態に なる。" }, + + "BATON": { "name": "バトン", "description": "持たせると 入れ替えるとき 控えのポケモンが\n能力変化を 受けつげる (逃げられなくする 技や 特性も 回避する)" }, + + "SHINY_CHARM": { "name": "ひかるおまもり", "description": "色違いの ポケモンと 大きく 出会いやすくなる" }, + "ABILITY_CHARM": { "name": "とくせいおまもり", "description": "隠れ特性がある ポケモンと 大きく 出会いやすくなる" }, + + "IV_SCANNER": { "name": "こたいちスキャナー", "description": "野生ポケモンの 個体値を 検査できる。 一つのスキャナーあたり\n個体値が 2つ 見える。 最高の 個体値が 最初に 見える。" }, + + "DNA_SPLICERS": { "name": "いでんしのくさび" }, + + "MINI_BLACK_HOLE": { "name": "ミニブラックホール" }, + + "GOLDEN_POKEBALL": { "name": "ゴールドモンスターボール", "description": "バトル後に もう 一つの ご褒美の 選択肢を 加える" }, + + "ENEMY_DAMAGE_BOOSTER": { "name": "ダメージトークン", "description": "ダメージを 5% あげる" }, + "ENEMY_DAMAGE_REDUCTION": { "name": "ぼうごトークン", "description": "受けたダメージを 2.5% さげる" }, + "ENEMY_HEAL": { "name": "かいふくトークン", "description": "毎ターン 最大HPの 2%を 回復する" }, + "ENEMY_ATTACK_POISON_CHANCE": { "name": "どくトークン" }, + "ENEMY_ATTACK_PARALYZE_CHANCE": { "name": "まひトークン" }, + "ENEMY_ATTACK_BURN_CHANCE": { "name": "やけどトークン" }, + "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { "name": "なんでもなおしトークン", "description": "毎ターン 状態異常を 治せる 2.5%の可能性を 加える" }, + "ENEMY_ENDURE_CHANCE": { "name": "こらえるトークン" }, + "ENEMY_FUSED_CHANCE": { "name": "がったいトークン", "description": "野生ポケモンは 吸収合体している 1%の可能性を 加える" } }, "SpeciesBoosterItem": { - "LIGHT_BALL": { - "name": "でんきだま", - "description": "ピカチュウに 持たせると 攻撃と 特攻が あがる 不思議な玉。" - }, - "THICK_CLUB": { - "name": "ふといホネ", - "description": "なにかの 硬いホネ。カラカラ または ガラガラに 持たせると 攻撃が あがる。" - }, - "METAL_POWDER": { - "name": "メタルパウダー", - "description": "メタモンに 持たせると 防御が あがる 不思議な粉。とても こまかくて 硬い。" - }, - "QUICK_POWDER": { - "name": "スピードパウダー", - "description": "メタモンに 持たせると 素早さが あがる 不思議 粉。とても こまかくて 硬い。" - } + "LIGHT_BALL": { "name": "でんきだま", "description": "ピカチュウに 持たせると 攻撃と 特攻が あがる 不思議な玉" }, + "THICK_CLUB": { "name": "ふといホネ", "description": "なにかの 硬いホネ。 カラカラ または ガラガラに 持たせると 攻撃が あがる" }, + "METAL_POWDER": { "name": "メタルパウダー", "description": "メタモンに 持たせると 防御が あがる 不思議な粉。 とても こまかくて 硬い" }, + "QUICK_POWDER": { "name": "スピードパウダー", "description": "メタモンに 持たせると 素早さが あがる 不思議な粉。 とても こまかくて 硬い" } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "プラスパワー", "x_defense": "ディフェンダー", "x_sp_atk": "スペシャルアップ", "x_sp_def": "スペシャルガード", "x_speed": "スピーダー", - "x_accuracy": "ヨクアタール", - "dire_hit": "クリティカット" - }, - "TempBattleStatBoosterStatName": { - "ATK": "こうげき", - "DEF": "ぼうぎょ", - "SPATK": "とくこう", - "SPDEF": "とくぼう", - "SPD": "すばやさ", - "ACC": "めいちゅう", - "CRIT": "きゅうしょりつ", - "EVA": "かいひ", - "DEFAULT": "???" + "x_accuracy": "ヨクアタール" }, "AttackTypeBoosterItem": { "silk_scarf": "シルクのスカーフ", @@ -460,7 +292,8 @@ "carbos": "インドメタシン" }, "EvolutionItem": { - "NONE": "None", + "NONE": "なし", + "LINKING_CORD": "つながりのヒモ", "SUN_STONE": "たいようのいし", "MOON_STONE": "つきのいし", @@ -477,6 +310,7 @@ "TART_APPLE": "すっぱいりんご", "STRAWBERRY_SWEET": "いちごアメざいく", "UNREMARKABLE_TEACUP": "ボンサクのちゃわん", + "CHIPPED_POT": "かけたポット", "BLACK_AUGURITE": "くろのきせき", "GALARICA_CUFF": "ガラナツブレス", @@ -491,7 +325,8 @@ "SYRUPY_APPLE": "みついりりんご" }, "FormChangeItem": { - "NONE": "None", + "NONE": "なし", + "ABOMASITE": "ユキノオナイト", "ABSOLITE": "アブソルナイト", "AERODACTYLITE": "プテラナイト", @@ -540,6 +375,7 @@ "SWAMPERTITE": "ラグラージナイト", "TYRANITARITE": "バンギラスナイト", "VENUSAURITE": "フシギバナイト", + "BLUE_ORB": "あいいろのたま", "RED_ORB": "べにいろのたま", "SHARP_METEORITE": "シャープなうんせき", @@ -567,6 +403,44 @@ "BURN_DRIVE": "ブレイズカセット", "CHILL_DRIVE": "フリーズカセット", "DOUSE_DRIVE": "アクアカセット", - "ULTRANECROZIUM_Z": "ウルトラネクロZ" + "ULTRANECROZIUM_Z": "ウルトラネクロZ", + + "FIST_PLATE": "こぶしのプレート", + "SKY_PLATE": "あおぞらプレート", + "TOXIC_PLATE": "もうどくプレート", + "EARTH_PLATE": "だいちのプレート", + "STONE_PLATE": "がんせきプレート", + "INSECT_PLATE": "たまむしプレート", + "SPOOKY_PLATE": "もののけプレート", + "IRON_PLATE": "こうてつプレート", + "FLAME_PLATE": "ひのたまプレート", + "SPLASH_PLATE": "しずくプレート", + "MEADOW_PLATE": "みどりのプレート", + "ZAP_PLATE": "いかずちプレート", + "MIND_PLATE": "ふしぎのプレート", + "ICICLE_PLATE": "つららのプレート", + "DRACO_PLATE": "りゅうのプレート", + "DREAD_PLATE": "こわもてプレート", + "PIXIE_PLATE": "せいれいプレート", + "BLANK_PLATE": "まっさらプレート", + "LEGEND_PLATE": "レジェンドプレート", + "FIGHTING_MEMORY": "ファイトメモリ", + "FLYING_MEMORY": "フライングメモリ", + "POISON_MEMORY": "ポイズンメモリ", + "GROUND_MEMORY": "グラウンドメモリ", + "ROCK_MEMORY": "ロックメモリ", + "BUG_MEMORY": "バグメモリ", + "GHOST_MEMORY": "ゴーストメモリ", + "STEEL_MEMORY": "スチールメモリ", + "FIRE_MEMORY": "ファイヤーメモリ", + "WATER_MEMORY": "ウオーターメモリ", + "GRASS_MEMORY": "グラスメモリ", + "ELECTRIC_MEMORY": "エレクトロメモリ", + "PSYCHIC_MEMORY": "サイキックメモリ", + "ICE_MEMORY": "アイスメモリ", + "DRAGON_MEMORY": "ドラゴンメモリ", + "DARK_MEMORY": "ダークメモリ", + "FAIRY_MEMORY": "フェアリーメモリ", + "NORMAL_MEMORY": "ノーマルメモリ" } -} \ No newline at end of file +} diff --git a/src/locales/ja/modifier.json b/src/locales/ja/modifier.json index 30d5270d94f..c33bb9be151 100644 --- a/src/locales/ja/modifier.json +++ b/src/locales/ja/modifier.json @@ -1,12 +1,12 @@ { "surviveDamageApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で もちこたえた!", - "turnHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 回復!", - "hitHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 回復!", + "turnHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 体力を 回復した!", + "hitHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 体力を 回復した!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 復活した!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}は {{typeName}}で\n下がった能力が 元に戻った!", + "resetNegativeStatStageApply": "{{pokemonNameWithAffix}}は {{typeName}}で\n下がった能力が 元に戻った!", "moneyInterestApply": "{{typeName}}から {{moneyAmount}}円 取得した!", - "turnHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 吸い取った!", - "contactHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を うばい取った!", + "turnHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 吸い取った!", + "contactHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 奪い取った!", "enemyTurnHealApply": "{{pokemonNameWithAffix}}は\n体力を 回復!", "bypassSpeedChanceApply": "{{pokemonName}}は {{itemName}}で\n行動が はやくなった!" -} \ No newline at end of file +} diff --git a/src/locales/ja/move-trigger.json b/src/locales/ja/move-trigger.json index 0c404feeed6..fbefe883836 100644 --- a/src/locales/ja/move-trigger.json +++ b/src/locales/ja/move-trigger.json @@ -1,62 +1,70 @@ { - "hitWithRecoil": "{{pokemonName}}は\nはんどうによる ダメージを うけた!", - "cutHpPowerUpMove": "{{pokemonName}}は\nたいりょくを けずって パワーぜんかい!", - "absorbedElectricity": "{{pokemonName}}は\n でんきを きゅうしゅうした!", - "switchedStatChanges": "{{pokemonName}}は あいてと じぶんのn\nのうりょくへんかを いれかえた!", - "goingAllOutForAttack": "{{pokemonName}}は\nほんきを だした!", - "regainedHealth": "{{pokemonName}}は\nたいりょくを かいふくした!", - "keptGoingAndCrashed": "いきおいあまって {{pokemonName}}は\nじめんに ぶつかった!", - "fled": "{{pokemonName}}は にげだした!", - "cannotBeSwitchedOut": "{{pokemonName}}を\nもどすことが できない!", - "swappedAbilitiesWithTarget": "{{pokemonName}}は\nおたがいの とくせいを いれかえた!", - "coinsScatteredEverywhere": "こばんが あたりに ちらばった!", + "hitWithRecoil": "{{pokemonName}}は\n反動による ダメージを 受けた!", + "cutHpPowerUpMove": "{{pokemonName}}は\n体力を 削って 技の 威力を 上がった!", + "absorbedElectricity": "{{pokemonName}}は\n 電気を 吸収した!", + "switchedStatChanges": "{{pokemonName}}は 相手と 自分の\n能力変化を 入れ替えた!", + "switchedTwoStatChanges": "{{pokemonName}}は 相手と 自分の {{firstStat}}と\n{{secondStat}}の 能力変化を 入れ替えた!", + "switchedStat": "{{pokemonName}}は 相手と {{stat}}を 入れ替えた!", + "sharedGuard": "{{pokemonName}}は\nお互いのガードを シェアした!", + "sharedPower": "{{pokemonName}}は\nお互いのパワーを シェアした!", + "goingAllOutForAttack": "{{pokemonName}}は\n本気を 出した!", + "regainedHealth": "{{pokemonName}}は\n体力を 回復した!", + "keptGoingAndCrashed": "勢い余って {{pokemonName}}は\n地面に ぶつかった!", + "fled": "{{pokemonName}}は 逃げ出した!", + "cannotBeSwitchedOut": "{{pokemonName}}を\n戻すことが できない!", + "swappedAbilitiesWithTarget": "{{pokemonName}}は\nお互いの 特性を 入れ替えた!", + "coinsScatteredEverywhere": "小判が 辺りに 散らばった!", "attackedByItem": "{{pokemonName}}に\n{{itemName}}が おそいかかる!", - "whippedUpAWhirlwind": "{{pokemonName}}の まわりで\nくうきが うずをまく!", - "flewUpHigh": "{{pokemonName}}は\nそらたかく とびあがった!", - "tookInSunlight": "{{pokemonName}}は\nひかりを きゅうしゅうした!", - "dugAHole": "{{pokemonName}}は\nじめんに もぐった!", - "loweredItsHead": "{{pokemonName}}は\nくびを ひっこめた!", - "isGlowing": "{{pokemonName}}を\nはげしいひかりが つつむ!", - "bellChimed": "すずのおとが ひびきわたった!", - "foresawAnAttack": "{{pokemonName}}は\nみらいに こうげきを よちした!", - "hidUnderwater": "{{pokemonName}}は\nすいちゅうに みをひそめた!", - "soothingAromaWaftedThroughArea": "ここちよい かおりが ひろがった!", - "sprangUp": "{{pokemonName}}は\nたかく とびはねた!", - "choseDoomDesireAsDestiny": "{{pokemonName}}は\nはめつのねがいを みらいに たくした!", - "vanishedInstantly": "{{pokemonName}}の すがたが\nいっしゅんにして きえた!", - "tookTargetIntoSky": "{{pokemonName}}は {{targetName}}を\nじょうくうに つれさった!", - "becameCloakedInFreezingLight": "{{pokemonName}}は\nつめたいひかりに つつまれた!", - "becameCloakedInFreezingAir": "{{pokemonName}}は\nこごえるくうきに つつまれた!", - "isChargingPower": "{{pokemonName}}は\nパワーを ためこんでいる!", - "burnedItselfOut": "{{pokemonName}}の ほのうは\nもえつきた!", - "startedHeatingUpBeak": "{{pokemonName}}は\nクチバシを かねつしはじめた!", + "whippedUpAWhirlwind": "{{pokemonName}}の 周りで\n空気が 渦を巻く!", + "flewUpHigh": "{{pokemonName}}は\n空高く 飛び上がった!", + "tookInSunlight": "{{pokemonName}}は\n光を 吸収した!", + "dugAHole": "{{pokemonName}}は\n地面に 潜った!", + "loweredItsHead": "{{pokemonName}}は\n首を 引っ込めた!", + "isGlowing": "{{pokemonName}}を\n激しい光が 包む!", + "bellChimed": "鈴の音が 響き渡った!", + "foresawAnAttack": "{{pokemonName}}は\n未来に 攻撃を 予知した!", + "isTighteningFocus": "{{pokemonName}}は\n集中力を 高めている!", + "hidUnderwater": "{{pokemonName}}は\n水中に 身を潜めた!", + "soothingAromaWaftedThroughArea": "心地よい 香りが 広がった!", + "sprangUp": "{{pokemonName}}は\n高く 飛び跳ねた!", + "choseDoomDesireAsDestiny": "{{pokemonName}}は\nはめつのねがいを 未来に 託した!", + "vanishedInstantly": "{{pokemonName}}の 姿が\n一瞬にして 消えた!", + "tookTargetIntoSky": "{{pokemonName}}は {{targetName}}を\n上空に 連れ去った!", + "becameCloakedInFreezingLight": "{{pokemonName}}は\n冷たい光に 包まれた!", + "becameCloakedInFreezingAir": "{{pokemonName}}は\n凍える空気に 包まれた!", + "isChargingPower": "{{pokemonName}}は\nパワーを 溜め込んでいる!", + "burnedItselfOut": "{{pokemonName}}の 炎は 燃え尽きた!", + "startedHeatingUpBeak": "{{pokemonName}}は\nクチバシを 加熱し始めた!", "setUpShellTrap": "{{pokemonName}}は\nトラップシェルを 仕掛けた!", - "isOverflowingWithSpacePower": "{{pokemonName}}に\nうちゅうの ちからが あふれだす!", - "usedUpAllElectricity": "{{pokemonName}}は\nでんきを つかいきった!", - "stoleItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を ぬすんだ!", - "incineratedItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を もやした!", - "knockedOffItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を はたきおとした!", - "tookMoveAttack": "{{pokemonName}}は\n{{moveName}}の こうげきを うけた!", - "cutOwnHpAndMaximizedStat": "{{pokemonName}}は\nたいりょくを けずって {{statName}}ぜんかい!", - "copiedStatChanges": "{{pokemonName}}は {{targetName}}の\nのうりょくへんかを コピーした!", + "isOverflowingWithSpacePower": "{{pokemonName}}に\n宇宙の 力が 溢れ出す!", + "usedUpAllElectricity": "{{pokemonName}}は\n電気を 使い切った!", + "stoleItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を 盗んだ!", + "incineratedItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を 燃やした!", + "knockedOffItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を はたき落とした!", + "tookMoveAttack": "{{pokemonName}}は\n{{moveName}}の 攻撃を 受けた!", + "cutOwnHpAndMaximizedStat": "{{pokemonName}}は\n体力を 削って {{statName}}全開!", + "copiedStatChanges": "{{pokemonName}}は {{targetName}}の\n能力変化を コピーした!", "magnitudeMessage": "マグニチュード{{magnitude}}!", - "tookAimAtTarget": "{{pokemonName}}は {{targetName}}に\nねらいを さだめた!", + "tookAimAtTarget": "{{pokemonName}}は {{targetName}}に\n狙いを 定めた!", "transformedIntoType": "{{pokemonName}}は\n{{typeName}}タイプに なった!", "copiedMove": "{{pokemonName}}は\n{{moveName}}を コピーした!", "sketchedMove": "{{pokemonName}}は\n{{moveName}}を スケッチした!", - "acquiredAbility": "{{pokemonName}}の とくせいが\n{{abilityName}}に なった!", + "acquiredAbility": "{{pokemonName}}の 特性が\n{{abilityName}}に なった!", "copiedTargetAbility": "{{pokemonName}}は\n{{targetName}}の {{abilityName}}を コピーした!", - "transformedIntoTarget": "{{pokemonName}}は\n{{targetName}}に へんしんした!", - "tryingToTakeFoeDown": "{{pokemonName}}は あいてを\nみちづれに しようとしている!", - "addType": "{{pokemonName}}に\n{{typeName}}タイプが ついかされた!", - "cannotUseMove": "{{pokemonName}}は\n{{moveName}}を つかえなかった!", - "healHp": "{{pokemonName}}の\nたいりょくが かいふくした!", - "sacrificialFullRestore": "{{pokemonName}}の\nねがいごとが かなった!", - "invertStats": "{{pokemonName}}の\nのうりょくへんかが ぎゃくてんした!", - "resetStats": "{{pokemonName}}の\nのうりょくへんかが もとにもどった!", - "faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターンごに ほろびてしまう!", + "transformedIntoTarget": "{{pokemonName}}は\n{{targetName}}に 変身した!", + "tryingToTakeFoeDown": "{{pokemonName}}は 相手を\nみちづれに しようとしている!", + "addType": "{{pokemonName}}に\n{{typeName}}タイプが 追加された!", + "cannotUseMove": "{{pokemonName}}は\n{{moveName}}を 使えなかった!", + "healHp": "{{pokemonName}}の\n体力が 回復した!", + "sacrificialFullRestore": "{{pokemonName}}の\nいやしのねがいが 叶った!", + "invertStats": "{{pokemonName}}は\n能力変化が ひっくり返った!", + "resetStats": "{{pokemonName}}の\n能力変化が 元に戻った!", + "statEliminated": "全ての 能力変化が 元に戻った!", + "faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターン後に 滅びてしまう!", "copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!", - "suppressAbilities": "{{pokemonName}}の とくせいが きかなくなった!", + "suppressAbilities": "{{pokemonName}}の 特性が 効かなくなった!", "revivalBlessing": "{{pokemonName}}は\n復活して 戦えるようになった!", - "swapArenaTags": "{{pokemonName}}は\nおたがいの ばのこうかを いれかえた!" -} \ No newline at end of file + "swapArenaTags": "{{pokemonName}}は\nお互いの 場の 効果を 入れ替えた!", + "exposedMove": "{{pokemonName}}は {{targetPokemonName}}の\n正体を 見破った!", + "afterYou": "{{pokemonName}}は\nお言葉に 甘えることにした!" +} diff --git a/src/locales/ja/move.json b/src/locales/ja/move.json index fbeb2132d23..2e602407902 100644 --- a/src/locales/ja/move.json +++ b/src/locales/ja/move.json @@ -1,2491 +1,2491 @@ { "pound": { "name": "はたく", - "effect": "長い しっぽや 手などを 使って 相手を はたいて 攻撃する。" + "effect": "長い しっぽや 手などを 使って 相手を はたいて 攻撃する。" }, "karateChop": { "name": "からてチョップ", - "effect": "鋭い チョップで 相手を たたいて 攻撃する。 急所に 当たりやすい。" + "effect": "鋭い チョップで 相手を たたいて 攻撃する。 急所に 当たりやすい。" }, "doubleSlap": { "name": "おうふくビンタ", - "effect": "おうふく ビンタで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "おうふく ビンタで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" }, "cometPunch": { "name": "れんぞくパンチ", - "effect": "どとうの パンチで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "どとうの パンチで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" }, "megaPunch": { "name": "メガトンパンチ", - "effect": "力を こめた パンチで 相手を 攻撃する。" + "effect": "力を こめた パンチで 相手を 攻撃する。" }, "payDay": { "name": "ネコにこばん", - "effect": "相手の 体に 小判を 投げつけて 攻撃する。 戦闘の あとで お金が もらえる。" + "effect": "相手の 体に 小判を 投げつけて 攻撃する。 戦闘の あとで お金が もらえる。" }, "firePunch": { "name": "ほのおのパンチ", - "effect": "炎を こめた パンチで 相手を 攻撃する。 やけど状態に することが ある。" + "effect": "炎を こめた パンチで 相手を 攻撃する。 やけど状態に することが ある。" }, "icePunch": { "name": "れいとうパンチ", - "effect": "冷気を こめた パンチで 相手を 攻撃する。 こおり状態に することが ある。" + "effect": "冷気を こめた パンチで 相手を 攻撃する。 こおり状態に することが ある。" }, "thunderPunch": { "name": "かみなりパンチ", - "effect": "電撃を こめた パンチで 相手を 攻撃する。 まひ状態に することが ある。" + "effect": "電撃を こめた パンチで 相手を 攻撃する。 まひ状態に することが ある。" }, "scratch": { "name": "ひっかく", - "effect": "硬く とがった 鋭い ツメで 相手を ひっかいて 攻撃する。" + "effect": "硬く とがった 鋭い ツメで 相手を ひっかいて 攻撃する。" }, "viseGrip": { "name": "はさむ", - "effect": "相手を 両側から はさんで ダメージを あたえる。" + "effect": "相手を 両側から はさんで ダメージを あたえる。" }, "guillotine": { "name": "ハサミギロチン", - "effect": "大きな ハサミで 相手を 切り裂いて 攻撃する。 当たれば 一撃で ひんしに する。" + "effect": "大きな ハサミで 相手を 切り裂いて 攻撃する。 当たれば 一撃で ひんしに する。" }, "razorWind": { "name": "かまいたち", - "effect": "風の 刃を つくり 2ターン目に 相手を 攻撃する。 急所に 当たりやすい。" + "effect": "風の 刃を つくり 2ターン目に 相手を 攻撃する。 急所に 当たりやすい。" }, "swordsDance": { "name": "つるぎのまい", - "effect": "戦いの舞を 激しく おどって 気合を 高める。 自分の 攻撃を ぐーんと あげる。" + "effect": "戦いの舞を 激しく おどって 気合を 高める。 自分の 攻撃を ぐーんと あげる。" }, "cut": { "name": "いあいぎり", - "effect": "カマや ツメなどで 相手を 切りつけて 攻撃する。" + "effect": "カマや ツメなどで 相手を 切りつけて 攻撃する。" }, "gust": { "name": "かぜおこし", - "effect": "翼で おこした 激しい 風を 相手に ぶつけて 攻撃する。" + "effect": "翼で おこした 激しい 風を 相手に ぶつけて 攻撃する。" }, "wingAttack": { "name": "つばさでうつ", - "effect": "大きく ひろげた りっぱな 翼を 相手に ぶつけて 攻撃する。" + "effect": "大きく ひろげた りっぱな 翼を 相手に ぶつけて 攻撃する。" }, "whirlwind": { "name": "ふきとばし", - "effect": "相手を 吹きとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + "effect": "相手を 吹きとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" }, "fly": { "name": "そらをとぶ", - "effect": "1ターン目で 空へ 飛び 2ターン目に 相手を 攻撃する。" + "effect": "1ターン目で 空へ 飛び 2ターン目に 相手を 攻撃する。" }, "bind": { "name": "しめつける", - "effect": "長い 体や つるなどを 使い 4ー5ターンの 間 相手を 締めつけて 攻撃する。" + "effect": "長い 体や つるなどを 使い 4ー5ターンの 間 相手を 締めつけて 攻撃する。" }, "slam": { "name": "たたきつける", - "effect": "長い しっぽや つるなどを 使い 相手を たたきつけて 攻撃する。" + "effect": "長い しっぽや つるなどを 使い 相手を たたきつけて 攻撃する。" }, "vineWhip": { "name": "つるのムチ", - "effect": "ムチのように しなる 細長い つるで 相手を たたきつけて 攻撃する。" + "effect": "ムチのように しなる 細長い つるで 相手を たたきつけて 攻撃する。" }, "stomp": { "name": "ふみつけ", - "effect": "大きな 足で 相手を 踏みつけて 攻撃する。 相手を ひるませることが ある。" + "effect": "大きな 足で 相手を 踏みつけて 攻撃する。 相手を ひるませることが ある。" }, "doubleKick": { "name": "にどげり", - "effect": "2本の 足で 相手を けとばして 攻撃する。 2回連続で ダメージを 与える。" + "effect": "2本の 足で 相手を けとばして 攻撃する。 2回連続で ダメージを 与える。" }, "megaKick": { "name": "メガトンキック", - "effect": "ものすごい 力を こめた キックで 相手を けとばして 攻撃する。" + "effect": "ものすごい 力を こめた キックで 相手を けとばして 攻撃する。" }, "jumpKick": { "name": "とびげり", - "effect": "高い ジャンプからの キックで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" + "effect": "高い ジャンプからの キックで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" }, "rollingKick": { "name": "まわしげり", - "effect": "体を 素早く 回転させながら けとばして 攻撃する。 相手を ひるませる ことが ある。" + "effect": "体を 素早く 回転させながら けとばして 攻撃する。 相手を ひるませる ことが ある。" }, "sandAttack": { "name": "すなかけ", - "effect": "相手の 顔に 砂を かけて 命中率を さげる。" + "effect": "相手の 顔に 砂を かけて 命中率を さげる。" }, "headbutt": { "name": "ずつき", - "effect": "頭を 突きだして まっすぐ つっこんで 攻撃する。 相手を ひるませることが ある。" + "effect": "頭を 突きだして まっすぐ つっこんで 攻撃する。 相手を ひるませることが ある。" }, "hornAttack": { "name": "つのでつく", - "effect": "鋭く とがった つので 相手を 攻撃する。" + "effect": "鋭く とがった つので 相手を 攻撃する。" }, "furyAttack": { "name": "みだれづき", - "effect": "つのや くちばしで 相手を つついて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "つのや くちばしで 相手を つついて 攻撃する。 2ー5回の 間 連続で だす。" }, "hornDrill": { "name": "つのドリル", - "effect": "回転する つのを 相手に 突き刺して 攻撃する。 当たれば 一撃で ひんしに する。" + "effect": "回転する つのを 相手に 突き刺して 攻撃する。 当たれば 一撃で ひんしに する。" }, "tackle": { "name": "たいあたり", - "effect": "相手に むかって 全身で ぶつかっていき 攻撃する。" + "effect": "相手に むかって 全身で ぶつかっていき 攻撃する。" }, "bodySlam": { "name": "のしかかり", - "effect": "全身で 相手に のしかかり 攻撃する。 まひ状態に することが ある。" + "effect": "全身で 相手に のしかかり 攻撃する。 まひ状態に することが ある。" }, "wrap": { "name": "まきつく", - "effect": "長い 体や つるなどを 使って 4ー5ターンの 間 相手に まきついて 攻撃する。" + "effect": "長い 体や つるなどを 使って 4ー5ターンの 間 相手に まきついて 攻撃する。" }, "takeDown": { "name": "とっしん", - "effect": "すごい 勢いで 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" + "effect": "すごい 勢いで 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" }, "thrash": { "name": "あばれる", - "effect": "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" + "effect": "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" }, "doubleEdge": { "name": "すてみタックル", - "effect": "命を 懸けて 相手に 突進して 攻撃する。 自分も かなり ダメージを 受ける。" + "effect": "命を 懸けて 相手に 突進して 攻撃する。 自分も かなり ダメージを 受ける。" }, "tailWhip": { "name": "しっぽをふる", - "effect": "しっぽを 左右に かわいく ふって 油断を 誘う。 相手の 防御を さげる。" + "effect": "しっぽを 左右に かわいく ふって 油断を 誘う。 相手の 防御を さげる。" }, "poisonSting": { "name": "どくばり", - "effect": "毒の ある ハリを 相手に 突き刺して 攻撃する。 毒状態に することが ある。" + "effect": "毒の ある ハリを 相手に 突き刺して 攻撃する。 毒状態に することが ある。" }, "twineedle": { "name": "ダブルニードル", - "effect": "2本の ハリを 相手に 突き刺し 2回連続で ダメージ。 毒状態に することが ある。" + "effect": "2本の ハリを 相手に 突き刺し 2回連続で ダメージ。 毒状態に することが ある。" }, "pinMissile": { "name": "ミサイルばり", - "effect": "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" }, "leer": { "name": "にらみつける", - "effect": "鋭い 目つきで おびえさせて 相手の 防御を さげる。" + "effect": "鋭い 目つきで おびえさせて 相手の 防御を さげる。" }, "bite": { "name": "かみつく", - "effect": "鋭く とがった 歯で かみついて 攻撃する。 相手を ひるませることが ある。" + "effect": "鋭く とがった 歯で かみついて 攻撃する。 相手を ひるませることが ある。" }, "growl": { "name": "なきごえ", - "effect": "かわいい なきごえを 聞かせて 気を ひき 油断を させて 相手の 攻撃を さげる。" + "effect": "かわいい なきごえを 聞かせて 気を ひき 油断を させて 相手の 攻撃を さげる。" }, "roar": { "name": "ほえる", - "effect": "相手を 逃がして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + "effect": "相手を 逃がして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" }, "sing": { "name": "うたう", - "effect": "心地好い きれいな 歌声を 聞かせて 相手を 眠り状態に する。" + "effect": "心地好い きれいな 歌声を 聞かせて 相手を 眠り状態に する。" }, "supersonic": { "name": "ちょうおんぱ", - "effect": "特殊な 音波を 体から 発して 相手を 混乱させる。" + "effect": "特殊な 音波を 体から 発して 相手を 混乱させる。" }, "sonicBoom": { "name": "ソニックブーム", - "effect": "衝撃波を 相手に ぶつけて 攻撃する。 20の ダメージを 決まって 与える。" + "effect": "衝撃波を 相手に ぶつけて 攻撃する。 20の ダメージを 決まって 与える。" }, "disable": { "name": "かなしばり", - "effect": "相手の 動きを とめて 直前に だしていた 技を 4ターンの 間 使えなくする。" + "effect": "相手の 動きを とめて 直前に だしていた 技を 4ターンの 間 使えなくする。" }, "acid": { "name": "ようかいえき", - "effect": "強い 酸を 相手に かけて 攻撃する。 相手の 特防を さげることが ある。" + "effect": "強い 酸を 相手に かけて 攻撃する。 相手の 特防を さげることが ある。" }, "ember": { "name": "ひのこ", - "effect": "小さな 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + "effect": "小さな 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" }, "flamethrower": { "name": "かえんほうしゃ", - "effect": "激しい 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + "effect": "激しい 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" }, "mist": { "name": "しろいきり", - "effect": "白い霧で 体を おおう。 5ターンの 間 相手に 能力を さげられなく なる。" + "effect": "白い霧で 体を おおう。 5ターンの 間 相手に 能力を さげられなく なる。" }, "waterGun": { "name": "みずでっぽう", - "effect": "水を 勢いよく 相手に 発射して 攻撃する。" + "effect": "水を 勢いよく 相手に 発射して 攻撃する。" }, "hydroPump": { "name": "ハイドロポンプ", - "effect": "大量の 水を 激しい 勢いで 相手に 発射して 攻撃する。" + "effect": "大量の 水を 激しい 勢いで 相手に 発射して 攻撃する。" }, "surf": { "name": "なみのり", - "effect": "大きな 波で 自分の 周りに いるものを 攻撃する。" + "effect": "大きな 波で 自分の 周りに いるものを 攻撃する。" }, "iceBeam": { "name": "れいとうビーム", - "effect": "凍える ビームを 相手に 発射して 攻撃する。 こおり状態に することが ある。" + "effect": "凍える ビームを 相手に 発射して 攻撃する。 こおり状態に することが ある。" }, "blizzard": { "name": "ふぶき", - "effect": "激しい 吹雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" + "effect": "激しい 吹雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" }, "psybeam": { "name": "サイケこうせん", - "effect": "不思議な 光線を 相手に 発射して 攻撃する。 混乱させることが ある。" + "effect": "不思議な 光線を 相手に 発射して 攻撃する。 混乱させることが ある。" }, "bubbleBeam": { "name": "バブルこうせん", - "effect": "泡を 勢いよく 相手に 発射して 攻撃する。 素早さを さげる ことが ある。" + "effect": "泡を 勢いよく 相手に 発射して 攻撃する。 素早さを さげる ことが ある。" }, "auroraBeam": { "name": "オーロラビーム", - "effect": "にじいろの ビームを 相手に 発射して 攻撃する。 攻撃を さげる ことが ある。" + "effect": "にじいろの ビームを 相手に 発射して 攻撃する。 攻撃を さげる ことが ある。" }, "hyperBeam": { "name": "はかいこうせん", - "effect": "強い 光線を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + "effect": "強い 光線を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" }, "peck": { "name": "つつく", - "effect": "鋭く とがった くちばしや つので 相手を 突いて 攻撃する。" + "effect": "鋭く とがった くちばしや つので 相手を 突いて 攻撃する。" }, "drillPeck": { "name": "ドリルくちばし", - "effect": "回転しながら とがった くちばしを 相手に 突き刺して 攻撃する。" + "effect": "回転しながら とがった くちばしを 相手に 突き刺して 攻撃する。" }, "submission": { "name": "じごくぐるま", - "effect": "地面に 自分ごと 相手を 投げつけて 攻撃する。 自分も 少し ダメージを 受ける。" + "effect": "地面に 自分ごと 相手を 投げつけて 攻撃する。 自分も 少し ダメージを 受ける。" }, "lowKick": { "name": "けたぐり", - "effect": "足を 強く けり 相手を 転ばせて 攻撃する。 相手が 重いほど 威力が あがる。" + "effect": "足を 強く けり 相手を 転ばせて 攻撃する。 相手が 重いほど 威力が あがる。" }, "counter": { "name": "カウンター", - "effect": "相手から 受けた 物理攻撃の ダメージを 2倍に して 同じ 相手に 返す。" + "effect": "相手から 受けた 物理攻撃の ダメージを 2倍に して 同じ 相手に 返す。" }, "seismicToss": { "name": "ちきゅうなげ", - "effect": "引力を 使い 投げとばす。 自分の レベルと 同じ ダメージを 相手に 与える。" + "effect": "引力を 使い 投げとばす。 自分の レベルと 同じ ダメージを 相手に 与える。" }, "strength": { "name": "かいりき", - "effect": "こん身の 力で 相手を なぐりつけて 攻撃する。" + "effect": "こん身の 力で 相手を なぐりつけて 攻撃する。" }, "absorb": { "name": "すいとる", - "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" }, "megaDrain": { "name": "メガドレイン", - "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" }, "leechSeed": { "name": "やどりぎのタネ", - "effect": "植えつけた 相手の HPを 毎ターン 少しだけ 吸い取り 自分の HPを 回復する。" + "effect": "植えつけた 相手の HPを 毎ターン 少しだけ 吸い取り 自分の HPを 回復する。" }, "growth": { "name": "せいちょう", - "effect": "体を 一気に 大きく 生長させて 攻撃と 特攻を あげる。" + "effect": "体を 一気に 大きく 生長させて 攻撃と 特攻を あげる。" }, "razorLeaf": { "name": "はっぱカッター", - "effect": "はっぱを とばして 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + "effect": "はっぱを とばして 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" }, "solarBeam": { "name": "ソーラービーム", - "effect": "1ターン目に 光を いっぱいに 集め 2ターン目に 光の 束を 発射して 攻撃する。" + "effect": "1ターン目に 光を いっぱいに 集め 2ターン目に 光の 束を 発射して 攻撃する。" }, "poisonPowder": { "name": "どくのこな", - "effect": "毒の ある 粉を たくさん ふりまいて 相手を 毒状態に する。" + "effect": "毒の ある 粉を たくさん ふりまいて 相手を 毒状態に する。" }, "stunSpore": { "name": "しびれごな", - "effect": "しびれる 粉を たくさん ふりまいて 相手を まひ状態に する。" + "effect": "しびれる 粉を たくさん ふりまいて 相手を まひ状態に する。" }, "sleepPowder": { "name": "ねむりごな", - "effect": "眠くなる 粉を たくさん ふりまいて 相手を 眠り状態に する。" + "effect": "眠くなる 粉を たくさん ふりまいて 相手を 眠り状態に する。" }, "petalDance": { "name": "はなびらのまい", - "effect": "2ー3ターンの 間 花を まきちらして 相手を 攻撃する。 まきちらした あとは 混乱する。" + "effect": "2ー3ターンの 間 花を まきちらして 相手を 攻撃する。 まきちらした あとは 混乱する。" }, "stringShot": { "name": "いとをはく", - "effect": "口から 吹きだした 糸を まきつけて 相手の 素早さを がくっと さげる。" + "effect": "口から 吹きだした 糸を まきつけて 相手の 素早さを がくっと さげる。" }, "dragonRage": { "name": "りゅうのいかり", - "effect": "怒りの 衝撃波を 相手に ぶつけて 攻撃する。 40の ダメージを 決まって 与える。" + "effect": "怒りの 衝撃波を 相手に ぶつけて 攻撃する。 40の ダメージを 決まって 与える。" }, "fireSpin": { "name": "ほのおのうず", - "effect": "激しく 渦をまく 炎の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + "effect": "激しく 渦をまく 炎の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" }, "thunderShock": { "name": "でんきショック", - "effect": "電気の 刺激を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" + "effect": "電気の 刺激を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" }, "thunderbolt": { "name": "10まんボルト", - "effect": "強い 電撃を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" + "effect": "強い 電撃を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" }, "thunderWave": { "name": "でんじは", - "effect": "弱い 電撃を 浴びせることで 相手を まひ状態に する。" + "effect": "弱い 電撃を 浴びせることで 相手を まひ状態に する。" }, "thunder": { "name": "かみなり", - "effect": "激しい 雷を 相手に 落として 攻撃する。 まひ状態に することが ある。" + "effect": "激しい 雷を 相手に 落として 攻撃する。 まひ状態に することが ある。" }, "rockThrow": { "name": "いわおとし", - "effect": "小さな 岩を 持ちあげて 相手に 投げつけて 攻撃する。" + "effect": "小さな 岩を 持ちあげて 相手に 投げつけて 攻撃する。" }, "earthquake": { "name": "じしん", - "effect": "地震の 衝撃で 自分の 周りに いるものを 攻撃する。" + "effect": "地震の 衝撃で 自分の 周りに いるものを 攻撃する。" }, "fissure": { "name": "じわれ", - "effect": "地割れの 裂け目に 相手を 落として 攻撃する。 当たれば 一撃で ひんしに する。" + "effect": "地割れの 裂け目に 相手を 落として 攻撃する。 当たれば 一撃で ひんしに する。" }, "dig": { "name": "あなをほる", - "effect": "1ターン目に 潜り 2ターン目で 相手を 攻撃する。" + "effect": "1ターン目に 潜り 2ターン目で 相手を 攻撃する。" }, "toxic": { "name": "どくどく", - "effect": "相手を 猛毒の 状態に する。 ターンが すすむほど 毒の ダメージが 増えていく。" + "effect": "相手を 猛毒の 状態に する。 ターンが すすむほど 毒の ダメージが 増えていく。" }, "confusion": { "name": "ねんりき", - "effect": "弱い 念力を 相手に 送って 攻撃する。 相手を 混乱させることが ある。" + "effect": "弱い 念力を 相手に 送って 攻撃する。 相手を 混乱させることが ある。" }, "psychic": { "name": "サイコキネシス", - "effect": "強い 念力を 相手に 送って 攻撃する。 相手の 特防を さげることが ある。" + "effect": "強い 念力を 相手に 送って 攻撃する。 相手の 特防を さげることが ある。" }, "hypnosis": { "name": "さいみんじゅつ", - "effect": "眠気を 誘う 暗示を かけて 相手を 眠り状態に する。" + "effect": "眠気を 誘う 暗示を かけて 相手を 眠り状態に する。" }, "meditate": { "name": "ヨガのポーズ", - "effect": "眠っている 力を 体の 奥から ひきだして 自分の 攻撃を あげる。" + "effect": "眠っている 力を 体の 奥から ひきだして 自分の 攻撃を あげる。" }, "agility": { "name": "こうそくいどう", - "effect": "力を ぬいて 体を 軽くして 高速で 動く。 自分の 素早さを ぐーんと あげる。" + "effect": "力を ぬいて 体を 軽くして 高速で 動く。 自分の 素早さを ぐーんと あげる。" }, "quickAttack": { "name": "でんこうせっか", - "effect": "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" + "effect": "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" }, "rage": { "name": "いかり", - "effect": "技を だしたときに 攻撃を 受けると 怒りの 力で 攻撃が あがる。" + "effect": "技を だしたときに 攻撃を 受けると 怒りの 力で 攻撃が あがる。" }, "teleport": { "name": "テレポート", - "effect": "ひかえの ポケモンが いるときに 使うと 入れ替わる。 野生の ポケモンは 逃げてしまう。" + "effect": "ひかえの ポケモンが いるときに 使うと 入れ替わる。 野生の ポケモンは 逃げてしまう。" }, "nightShade": { "name": "ナイトヘッド", - "effect": "恐ろしい 幻を みせて 自分の レベルと 同じだけの ダメージを 相手に 与える。" + "effect": "恐ろしい 幻を みせて 自分の レベルと 同じだけの ダメージを 相手に 与える。" }, "mimic": { "name": "ものまね", - "effect": "相手が 最後に 使った 技を 戦闘の あいだ 自分の 技に することが できる。" + "effect": "相手が 最後に 使った 技を 戦闘の あいだ 自分の 技に することが できる。" }, "screech": { "name": "いやなおと", - "effect": "おもわず 耳を ふさぎたくなる いやなおとを だして 相手の 防御を がくっと さげる。" + "effect": "おもわず 耳を ふさぎたくなる いやなおとを だして 相手の 防御を がくっと さげる。" }, "doubleTeam": { "name": "かげぶんしん", - "effect": "素早い 動きで 分身を つくり 相手を まどわせて 回避率を あげる。" + "effect": "素早い 動きで 分身を つくり 相手を まどわせて 回避率を あげる。" }, "recover": { "name": "じこさいせい", - "effect": "細胞を 再生させて 自分の 最大HPの 半分の HPを 回復する。" + "effect": "細胞を 再生させて 自分の 最大HPの 半分の HPを 回復する。" }, "harden": { "name": "かたくなる", - "effect": "全身に 力を こめて 体を 硬くして 自分の 防御を あげる。" + "effect": "全身に 力を こめて 体を 硬くして 自分の 防御を あげる。" }, "minimize": { "name": "ちいさくなる", - "effect": "体を ちぢめて 小さく みせて 自分の 回避率を ぐーんと あげる。" + "effect": "体を ちぢめて 小さく みせて 自分の 回避率を ぐーんと あげる。" }, "smokescreen": { "name": "えんまく", - "effect": "煙や 墨などを 吹きかけて 相手の 命中率を さげる。" + "effect": "煙や 墨などを 吹きかけて 相手の 命中率を さげる。" }, "confuseRay": { "name": "あやしいひかり", - "effect": "怪しい 光を 相手に みせて まどわせる。 相手を 混乱させる。" + "effect": "怪しい 光を 相手に みせて まどわせる。 相手を 混乱させる。" }, "withdraw": { "name": "からにこもる", - "effect": "殻に 潜りこんで 身を守り 自分の 防御を あげる。" + "effect": "殻に 潜りこんで 身を守り 自分の 防御を あげる。" }, "defenseCurl": { "name": "まるくなる", - "effect": "体を まるめて ちぢこまり 自分の 防御を あげる。" + "effect": "体を まるめて ちぢこまり 自分の 防御を あげる。" }, "barrier": { "name": "バリアー", - "effect": "頑丈な 壁を つくって 自分の 防御を ぐーんと あげる。" + "effect": "頑丈な 壁を つくって 自分の 防御を ぐーんと あげる。" }, "lightScreen": { "name": "ひかりのかべ", - "effect": "5ターンの 間 不思議な かべで 相手から 受ける 特殊攻撃の ダメージを 弱める。" + "effect": "5ターンの 間 不思議な かべで 相手から 受ける 特殊攻撃の ダメージを 弱める。" }, "haze": { "name": "くろいきり", - "effect": "黒い霧を だして 戦闘に でている ポケモン 全員の 能力変化を もとに もどす。" + "effect": "黒い霧を だして 戦闘に でている ポケモン 全員の 能力変化を もとに もどす。" }, "reflect": { "name": "リフレクター", - "effect": "5ターンの 間 不思議な かべで 相手から 受ける 物理攻撃の ダメージを 弱める。" + "effect": "5ターンの 間 不思議な かべで 相手から 受ける 物理攻撃の ダメージを 弱める。" }, "focusEnergy": { "name": "きあいだめ", - "effect": "深く 息を 吸い 気合を こめる。 自分の 攻撃が 急所に 当たりやすくなる。" + "effect": "深く 息を 吸い 気合を こめる。 自分の 攻撃が 急所に 当たりやすくなる。" }, "bide": { "name": "がまん", - "effect": "2ターンの 間 攻撃に たえて 受けた ダメージを 2倍にして 相手に 返す。" + "effect": "2ターンの 間 攻撃に たえて 受けた ダメージを 2倍にして 相手に 返す。" }, "metronome": { "name": "ゆびをふる", - "effect": "指をふり 自分の 脳を 刺激して すべての 技の なかから どれか 1つを くりだす。" + "effect": "指をふり 自分の 脳を 刺激して すべての 技の なかから どれか 1つを くりだす。" }, "mirrorMove": { "name": "オウムがえし", - "effect": "相手の 使った 技を まねして 自分も 同じ技を 使う。" + "effect": "相手の 使った 技を まねして 自分も 同じ技を 使う。" }, "selfDestruct": { "name": "じばく", - "effect": "爆発を おこして 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" + "effect": "爆発を おこして 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" }, "eggBomb": { "name": "タマゴばくだん", - "effect": "大きな タマゴを 力いっぱい 相手に 投げつけて 攻撃する。" + "effect": "大きな タマゴを 力いっぱい 相手に 投げつけて 攻撃する。" }, "lick": { "name": "したでなめる", - "effect": "長い 舌で 相手を なめまわして 攻撃する。 まひ状態に することが ある。" + "effect": "長い 舌で 相手を なめまわして 攻撃する。 まひ状態に することが ある。" }, "smog": { "name": "スモッグ", - "effect": "汚れた ガスを 相手に 吹きつけて 攻撃する。 毒状態に することが ある。" + "effect": "汚れた ガスを 相手に 吹きつけて 攻撃する。 毒状態に することが ある。" }, "sludge": { "name": "ヘドロこうげき", - "effect": "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" + "effect": "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" }, "boneClub": { "name": "ホネこんぼう", - "effect": "手に 持った ホネで 相手を なぐりつけて 攻撃する。 相手を ひるませることが ある。" + "effect": "手に 持った ホネで 相手を なぐりつけて 攻撃する。 相手を ひるませることが ある。" }, "fireBlast": { "name": "だいもんじ", - "effect": "大の字の 炎で 相手を 焼きつくす。 やけど状態に することが ある。" + "effect": "大の字の 炎で 相手を 焼きつくす。 やけど状態に することが ある。" }, "waterfall": { "name": "たきのぼり", - "effect": "すごい 勢いで 相手に つっこむ。 相手を ひるませることが ある。" + "effect": "すごい 勢いで 相手に つっこむ。 相手を ひるませることが ある。" }, "clamp": { "name": "からではさむ", - "effect": "とても 頑丈な ぶあつい 殻に 4ー5ターンの 間 相手を はさんで 攻撃する。" + "effect": "とても 頑丈な ぶあつい 殻に 4ー5ターンの 間 相手を はさんで 攻撃する。" }, "swift": { "name": "スピードスター", - "effect": "星型の 光を 発射して 相手を 攻撃する。 攻撃は 必ず 命中する。" + "effect": "星型の 光を 発射して 相手を 攻撃する。 攻撃は 必ず 命中する。" }, "skullBash": { "name": "ロケットずつき", - "effect": "1ターン目に 頭を ひっこめて 防御を あげる。 2ターン目に 相手を 攻撃する。" + "effect": "1ターン目に 頭を ひっこめて 防御を あげる。 2ターン目に 相手を 攻撃する。" }, "spikeCannon": { "name": "とげキャノン", - "effect": "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" }, "constrict": { "name": "からみつく", - "effect": "触手や ツタなどを からみつけて 攻撃する。相手の 素早さを さげることが ある。" + "effect": "触手や ツタなどを からみつけて 攻撃する。相手の 素早さを さげることが ある。" }, "amnesia": { "name": "ドわすれ", - "effect": "頭を からにして 一瞬 なにかを 忘れることで 自分の 特防を ぐーんと あげる。" + "effect": "頭を からにして 一瞬 なにかを 忘れることで 自分の 特防を ぐーんと あげる。" }, "kinesis": { "name": "スプーンまげ", - "effect": "スプーンを まげて 注意を ひき 相手の 命中率を さげる。" + "effect": "スプーンを まげて 注意を ひき 相手の 命中率を さげる。" }, "softBoiled": { "name": "タマゴうみ", - "effect": "最大HPの 半分 自分の HPを 回復する。" + "effect": "最大HPの 半分 自分の HPを 回復する。" }, "highJumpKick": { "name": "とびひざげり", - "effect": "ジャンプからの ひざげりで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" + "effect": "ジャンプからの ひざげりで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" }, "glare": { "name": "へびにらみ", - "effect": "おなかの 模様で おびえさせて 相手を まひの 状態に する。" + "effect": "おなかの 模様で おびえさせて 相手を まひの 状態に する。" }, "dreamEater": { "name": "ゆめくい", - "effect": "寝ている 相手の 夢を 食べて 攻撃する。 ダメージの 半分の HPを 回復する。" + "effect": "寝ている 相手の 夢を 食べて 攻撃する。 ダメージの 半分の HPを 回復する。" }, "poisonGas": { "name": "どくガス", - "effect": "毒ガスを 相手の 顔に 吹きかけて 毒の 状態に する。" + "effect": "毒ガスを 相手の 顔に 吹きかけて 毒の 状態に する。" }, "barrage": { "name": "たまなげ", - "effect": "まるい ものを 相手に 投げつけて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "まるい ものを 相手に 投げつけて 攻撃する。 2ー5回の 間 連続で だす。" }, "leechLife": { "name": "きゅうけつ", - "effect": "血を 吸い取って 相手を 攻撃する。 与えた ダメージの 半分の HPを 回復できる。" + "effect": "血を 吸い取って 相手を 攻撃する。 与えた ダメージの 半分の HPを 回復できる。" }, "lovelyKiss": { "name": "あくまのキッス", - "effect": "恐ろしい 顔で キスを せまる。 相手を 眠り状態に する。" + "effect": "恐ろしい 顔で キスを せまる。 相手を 眠り状態に する。" }, "skyAttack": { "name": "ゴッドバード", - "effect": "2ターン目に 相手を 攻撃する。 たまに ひるませる。 急所にも 当たりやすい。" + "effect": "2ターン目に 相手を 攻撃する。 たまに ひるませる。 急所にも 当たりやすい。" }, "transform": { "name": "へんしん", - "effect": "相手の ポケモンに 変身することで 相手と まったく 同じ 技が 使える。" + "effect": "相手の ポケモンに 変身することで 相手と まったく 同じ 技が 使える。" }, "bubble": { "name": "あわ", - "effect": "無数の 泡を 相手に 吹きかけて 攻撃する。 相手の 素早さを さげることが ある。" + "effect": "無数の 泡を 相手に 吹きかけて 攻撃する。 相手の 素早さを さげることが ある。" }, "dizzyPunch": { "name": "ピヨピヨパンチ", - "effect": "リズミカルに パンチを くりだして 相手を 攻撃する。 混乱させることが ある。" + "effect": "リズミカルに パンチを くりだして 相手を 攻撃する。 混乱させることが ある。" }, "spore": { "name": "キノコのほうし", - "effect": "催眠効果の ある 胞子を パラパラと ふりまき 相手を 眠り状態に する。" + "effect": "催眠効果の ある 胞子を パラパラと ふりまき 相手を 眠り状態に する。" }, "flash": { "name": "フラッシュ", - "effect": "まぶしい 光で 相手の 命中率を さげる。" + "effect": "まぶしい 光で 相手の 命中率を さげる。" }, "psywave": { "name": "サイコウェーブ", - "effect": "不思議な 念波を 相手に 発射して 攻撃する。 使うたびに ダメージが 変わる。" + "effect": "不思議な 念波を 相手に 発射して 攻撃する。 使うたびに ダメージが 変わる。" }, "splash": { "name": "はねる", - "effect": "攻撃もせずに ピョン ピョンと 跳ねるだけで なにも おこらない……。" + "effect": "攻撃もせずに ピョン ピョンと 跳ねるだけで なにも おこらない……。" }, "acidArmor": { "name": "とける", - "effect": "細胞の 変化で 液状に なり 自分の 防御を ぐーんと あげる。" + "effect": "細胞の 変化で 液状に なり 自分の 防御を ぐーんと あげる。" }, "crabhammer": { "name": "クラブハンマー", - "effect": "大きな ハサミを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" + "effect": "大きな ハサミを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" }, "explosion": { "name": "だいばくはつ", - "effect": "大きな 爆発で 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" + "effect": "大きな 爆発で 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" }, "furySwipes": { "name": "みだれひっかき", - "effect": "ツメや カマなどで 相手を ひっかいて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "ツメや カマなどで 相手を ひっかいて 攻撃する。 2ー5回の 間 連続で だす。" }, "bonemerang": { "name": "ホネブーメラン", - "effect": "手に 持った ホネを 相手に 投げつけ 行きと 帰りの 2回連続で ダメージを 与える。" + "effect": "手に 持った ホネを 相手に 投げつけ 行きと 帰りの 2回連続で ダメージを 与える。" }, "rest": { "name": "ねむる", - "effect": "2ターンの 間 眠り続ける。 自分の HPと 状態異常を すべて 回復する。" + "effect": "2ターンの 間 眠り続ける。 自分の HPと 状態異常を すべて 回復する。" }, "rockSlide": { "name": "いわなだれ", - "effect": "大きな 岩を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" + "effect": "大きな 岩を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" }, "hyperFang": { "name": "ひっさつまえば", - "effect": "鋭い 前歯で 強く かみついて 攻撃する。 相手を ひるませることが ある。" + "effect": "鋭い 前歯で 強く かみついて 攻撃する。 相手を ひるませることが ある。" }, "sharpen": { "name": "かくばる", - "effect": "体の かどを 増やして カクカクに なることで 自分の 攻撃を あげる。" + "effect": "体の かどを 増やして カクカクに なることで 自分の 攻撃を あげる。" }, "conversion": { "name": "テクスチャー", - "effect": "自分の タイプを おぼえている 技で 一番 上の 技と 同じ タイプに する。" + "effect": "自分の タイプを おぼえている 技で 一番 上の 技と 同じ タイプに する。" }, "triAttack": { "name": "トライアタック", - "effect": "3つの 光線で 攻撃する。 まひか やけどか こおり状態の どれかに することが ある。" + "effect": "3つの 光線で 攻撃する。 まひか やけどか こおり状態の どれかに することが ある。" }, "superFang": { "name": "いかりのまえば", - "effect": "鋭い 前歯で 激しく かみついて 攻撃する。 相手の HPは 半分に なる。" + "effect": "鋭い 前歯で 激しく かみついて 攻撃する。 相手の HPは 半分に なる。" }, "slash": { "name": "きりさく", - "effect": "ツメや カマなどで 相手を 切り裂いて 攻撃する。 急所に 当たりやすい。" + "effect": "ツメや カマなどで 相手を 切り裂いて 攻撃する。 急所に 当たりやすい。" }, "substitute": { "name": "みがわり", - "effect": "自分の HPを 少し 削って 分身を だす。 分身は 自分の 身代わりに なる。" + "effect": "自分の HPを 少し 削って 分身を だす。 分身は 自分の 身代わりに なる。" }, "struggle": { "name": "わるあがき", - "effect": "自分の PPが なくなると あがいて 相手を 攻撃する。 自分も 少し ダメージを 受ける。" + "effect": "自分の PPが なくなると あがいて 相手を 攻撃する。 自分も 少し ダメージを 受ける。" }, "sketch": { "name": "スケッチ", - "effect": "相手が 使った 技を 自分の ものに する。 1回 使うと スケッチは 消える。" + "effect": "相手が 使った 技を 自分の ものに する。 1回 使うと スケッチは 消える。" }, "tripleKick": { "name": "トリプルキック", - "effect": "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" + "effect": "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" }, "thief": { "name": "どろぼう", - "effect": "攻撃と 同時に 道具を 盗もうとする。 盗む 可能性は 30%。" + "effect": "攻撃と 同時に 道具を 盗もうとする。 盗む 可能性は 30%。" }, "spiderWeb": { "name": "クモのす", - "effect": "ネバネバした 細い 糸を グルグルと からませて 相手を 戦闘から 逃げられなくする。" + "effect": "ネバネバした 細い 糸を グルグルと からませて 相手を 戦闘から 逃げられなくする。" }, "mindReader": { "name": "こころのめ", - "effect": "相手の 動きを 心で 感じて 次の 攻撃が 必ず 相手に 当たるように する。" + "effect": "相手の 動きを 心で 感じて 次の 攻撃が 必ず 相手に 当たるように する。" }, "nightmare": { "name": "あくむ", - "effect": "眠り状態の 相手に 悪夢を みせて 毎ターン 少しずつ HPを 減らしていく。" + "effect": "眠り状態の 相手に 悪夢を みせて 毎ターン 少しずつ HPを 減らしていく。" }, "flameWheel": { "name": "かえんぐるま", - "effect": "炎を まとい 相手に 突進して 攻撃する。 やけど状態に することが ある。" + "effect": "炎を まとい 相手に 突進して 攻撃する。 やけど状態に することが ある。" }, "snore": { "name": "いびき", - "effect": "自分が 寝ているときに 雑音を だして 攻撃する。 相手を ひるませることが ある。" + "effect": "自分が 寝ているときに 雑音を だして 攻撃する。 相手を ひるませることが ある。" }, "curse": { "name": "のろい", - "effect": "使う ポケモンが ゴーストタイプと それ以外 とでは 効果が 変わる。" + "effect": "使う ポケモンが ゴーストタイプと それ以外 とでは 効果が 変わる。" }, "flail": { "name": "じたばた", - "effect": "じたばた 暴れて 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" + "effect": "じたばた 暴れて 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" }, "conversion2": { "name": "テクスチャー2", - "effect": "相手が 最後に 使った技に 抵抗できる ように 自分の タイプを 変化させる。" + "effect": "相手が 最後に 使った技に 抵抗できる ように 自分の タイプを 変化させる。" }, "aeroblast": { "name": "エアロブラスト", - "effect": "空気の 渦を 発射して 攻撃する。 急所に 当たりやすい。" + "effect": "空気の 渦を 発射して 攻撃する。 急所に 当たりやすい。" }, "cottonSpore": { "name": "わたほうし", - "effect": "綿のような フワフワの 胞子を まとわり つかせて 相手の 素早さを がくっと さげる。" + "effect": "綿のような フワフワの 胞子を まとわり つかせて 相手の 素早さを がくっと さげる。" }, "reversal": { "name": "きしかいせい", - "effect": "力を ふりしぼり 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" + "effect": "力を ふりしぼり 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" }, "spite": { "name": "うらみ", - "effect": "相手が 最後に 使った技に 恨みを 抱いて その技の PPを 4だけ 減らす。" + "effect": "相手が 最後に 使った技に 恨みを 抱いて その技の PPを 4だけ 減らす。" }, "powderSnow": { "name": "こなゆき", - "effect": "冷たい 粉雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" + "effect": "冷たい 粉雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" }, "protect": { "name": "まもる", - "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" }, "machPunch": { "name": "マッハパンチ", - "effect": "目にも 留まらぬ ものすごい 速さで パンチを くりだす。 必ず 先制攻撃 できる。" + "effect": "目にも 留まらぬ ものすごい 速さで パンチを くりだす。 必ず 先制攻撃 できる。" }, "scaryFace": { "name": "こわいかお", - "effect": "恐ろしい 顔で にらみ おびえさせて 相手の 素早さを がくっと さげる。" + "effect": "恐ろしい 顔で にらみ おびえさせて 相手の 素早さを がくっと さげる。" }, "feintAttack": { "name": "だましうち", - "effect": "さりげなく 相手に ちかづき 油断した すきを みて なぐりつける。 攻撃は 必ず 命中する。" + "effect": "さりげなく 相手に ちかづき 油断した すきを みて なぐりつける。 攻撃は 必ず 命中する。" }, "sweetKiss": { "name": "てんしのキッス", - "effect": "天使のように かわいく キスして 相手を 混乱させる。" + "effect": "天使のように かわいく キスして 相手を 混乱させる。" }, "bellyDrum": { "name": "はらだいこ", - "effect": "自分の HPを 最大HPの 半分 減らして 自分の 攻撃を 最大に あげる。" + "effect": "自分の HPを 最大HPの 半分 減らして 自分の 攻撃を 最大に あげる。" }, "sludgeBomb": { "name": "ヘドロばくだん", - "effect": "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" + "effect": "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" }, "mudSlap": { "name": "どろかけ", - "effect": "相手の 顔などに 泥を 投げつけて 攻撃する。 命中率を さげる。" + "effect": "相手の 顔などに 泥を 投げつけて 攻撃する。 命中率を さげる。" }, "octazooka": { "name": "オクタンほう", - "effect": "相手の 顔などに 墨を 吹きかけて 攻撃する。 命中率を さげることが ある。" + "effect": "相手の 顔などに 墨を 吹きかけて 攻撃する。 命中率を さげることが ある。" }, "spikes": { "name": "まきびし", - "effect": "相手の 足下に まきびしを しかける。交代で でてきた 相手の ポケモンに ダメージを 与える。" + "effect": "相手の 足下に まきびしを しかける。交代で でてきた 相手の ポケモンに ダメージを 与える。" }, "zapCannon": { "name": "でんじほう", - "effect": "大砲の ような 電気を 発射して 攻撃する。 相手を まひの 状態に する。" + "effect": "大砲の ような 電気を 発射して 攻撃する。 相手を まひの 状態に する。" }, "foresight": { "name": "みやぶる", - "effect": "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + "effect": "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" }, "destinyBond": { "name": "みちづれ", - "effect": "技のあと 相手の 攻撃で ひんしに なると 攻撃 相手も ひんしにする。 連続して 出すと 失敗する。" + "effect": "技のあと 相手の 攻撃で ひんしに なると 攻撃 相手も ひんしにする。 連続して 出すと 失敗する。" }, "perishSong": { "name": "ほろびのうた", - "effect": "歌を 聴いた ポケモンは 3ターン たつと ひんしに なる。 交代すると 効果は なくなる。" + "effect": "歌を 聴いた ポケモンは 3ターン たつと ひんしに なる。 交代すると 効果は なくなる。" }, "icyWind": { "name": "こごえるかぜ", - "effect": "凍てつく 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" + "effect": "凍てつく 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" }, "detect": { "name": "みきり", - "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" }, "boneRush": { "name": "ボーンラッシュ", - "effect": "硬い ホネで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "硬い ホネで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" }, "lockOn": { "name": "ロックオン", - "effect": "照準を しっかり あわせて 次の 攻撃が 必ず 相手に 当たるように する。" + "effect": "照準を しっかり あわせて 次の 攻撃が 必ず 相手に 当たるように する。" }, "outrage": { "name": "げきりん", - "effect": "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" + "effect": "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" }, "sandstorm": { "name": "すなあらし", - "effect": "5ターンの 間 砂あらしで いわ じめん はがねタイプ 以外に ダメージ。 いわタイプの 特防が あがる。" + "effect": "5ターンの 間 砂あらしで いわ じめん はがねタイプ 以外に ダメージ。 いわタイプの 特防が あがる。" }, "gigaDrain": { "name": "ギガドレイン", - "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + "effect": "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" }, "endure": { "name": "こらえる", - "effect": "攻撃を 受けても HPを 必ず 1だけ 残せる。 連続で だすと 失敗しやすい。" + "effect": "攻撃を 受けても HPを 必ず 1だけ 残せる。 連続で だすと 失敗しやすい。" }, "charm": { "name": "あまえる", - "effect": "かわいく みつめて 油断を 誘い 相手の 攻撃を がくっと さげる。" + "effect": "かわいく みつめて 油断を 誘い 相手の 攻撃を がくっと さげる。" }, "rollout": { "name": "ころがる", - "effect": "5ターンの 間 転がり続けて 攻撃する。 技が 当たるたびに 威力が あがる。" + "effect": "5ターンの 間 転がり続けて 攻撃する。 技が 当たるたびに 威力が あがる。" }, "falseSwipe": { "name": "みねうち", - "effect": "相手の HPが 必ず 1だけ 残るように 手加減して 攻撃する。" + "effect": "相手の HPが 必ず 1だけ 残るように 手加減して 攻撃する。" }, "swagger": { "name": "いばる", - "effect": "相手を 怒らせて 混乱させる。 怒りで 相手の 攻撃は ぐーんと あがってしまう。" + "effect": "相手を 怒らせて 混乱させる。 怒りで 相手の 攻撃は ぐーんと あがってしまう。" }, "milkDrink": { "name": "ミルクのみ", - "effect": "最大HPの 半分 自分の HPを 回復する。" + "effect": "最大HPの 半分 自分の HPを 回復する。" }, "spark": { "name": "スパーク", - "effect": "電気を まとい 相手に 突進して 攻撃する。 まひ状態に することが ある。" + "effect": "電気を まとい 相手に 突進して 攻撃する。 まひ状態に することが ある。" }, "furyCutter": { "name": "れんぞくぎり", - "effect": "カマや ツメなどで 相手を 切りつけて 攻撃する。 連続で 当てると 威力が あがる。" + "effect": "カマや ツメなどで 相手を 切りつけて 攻撃する。 連続で 当てると 威力が あがる。" }, "steelWing": { "name": "はがねのつばさ", - "effect": "硬い 翼を 相手に たたきつけて 攻撃する。 自分の 防御が あがることが ある。" + "effect": "硬い 翼を 相手に たたきつけて 攻撃する。 自分の 防御が あがることが ある。" }, "meanLook": { "name": "くろいまなざし", - "effect": "吸いこまれるような 黒い まなざしで じっと みつめて 相手を 戦闘から 逃げられなくする。" + "effect": "吸いこまれるような 黒い まなざしで じっと みつめて 相手を 戦闘から 逃げられなくする。" }, "attract": { "name": "メロメロ", - "effect": "♂なら♀を ♀なら♂を 誘惑して メロメロに する。 相手は 技が だしにくくなる。" + "effect": "♂なら♀を ♀なら♂を 誘惑して メロメロに する。 相手は 技が だしにくくなる。" }, "sleepTalk": { "name": "ねごと", - "effect": "自分が おぼえている 技の うち どれか 1つを くりだす。 自分が 寝ているときだけ 使える。" + "effect": "自分が おぼえている 技の うち どれか 1つを くりだす。 自分が 寝ているときだけ 使える。" }, "healBell": { "name": "いやしのすず", - "effect": "心地好い 鈴の 音色を 聞かせて 味方 全員の 状態異常を 回復 する。" + "effect": "心地好い 鈴の 音色を 聞かせて 味方 全員の 状態異常を 回復 する。" }, "return": { "name": "おんがえし", - "effect": "トレーナーの ために 全力で 相手を 攻撃する。 なついているほど 威力は あがる。" + "effect": "トレーナーの ために 全力で 相手を 攻撃する。 なついているほど 威力は あがる。" }, "present": { "name": "プレゼント", - "effect": "わなを しかけた 箱を 相手に わたして 攻撃する。HPが 回復して しまうことも ある。" + "effect": "わなを しかけた 箱を 相手に わたして 攻撃する。HPが 回復して しまうことも ある。" }, "frustration": { "name": "やつあたり", - "effect": "不満を はらすため 全力で 相手を 攻撃する。 なついていないほど 威力は あがる。" + "effect": "不満を はらすため 全力で 相手を 攻撃する。 なついていないほど 威力は あがる。" }, "safeguard": { "name": "しんぴのまもり", - "effect": "5ターンの 間 不思議な 力に 守られて 状態異常に ならなくなる。" + "effect": "5ターンの 間 不思議な 力に 守られて 状態異常に ならなくなる。" }, "painSplit": { "name": "いたみわけ", - "effect": "自分の HPと 相手の HPを あわせて それを 自分と 相手で なかよく わける。" + "effect": "自分の HPと 相手の HPを あわせて それを 自分と 相手で なかよく わける。" }, "sacredFire": { "name": "せいなるほのお", - "effect": "神秘の 炎で 相手を 焼きつくして 攻撃する。 やけど状態に することが ある。" + "effect": "神秘の 炎で 相手を 焼きつくして 攻撃する。 やけど状態に することが ある。" }, "magnitude": { "name": "マグニチュード", - "effect": "地面を 揺らして 自分の 周りに いるものを 攻撃する。 技の 威力は いろいろ 変わる。" + "effect": "地面を 揺らして 自分の 周りに いるものを 攻撃する。 技の 威力は いろいろ 変わる。" }, "dynamicPunch": { "name": "ばくれつパンチ", - "effect": "こん身の 力で パンチを くりだして 攻撃する。 相手を 必ず 混乱させる。" + "effect": "こん身の 力で パンチを くりだして 攻撃する。 相手を 必ず 混乱させる。" }, "megahorn": { "name": "メガホーン", - "effect": "硬くて りっぱな つので おもいっきり 相手を 突き刺して 攻撃する。" + "effect": "硬くて りっぱな つので おもいっきり 相手を 突き刺して 攻撃する。" }, "dragonBreath": { "name": "りゅうのいぶき", - "effect": "ものすごい 息を 相手に 吹きつけて 攻撃する。 まひ状態に することが ある。" + "effect": "ものすごい 息を 相手に 吹きつけて 攻撃する。 まひ状態に することが ある。" }, "batonPass": { "name": "バトンタッチ", - "effect": "控えの ポケモンと 入れ替わる。 能力変化は 替わった ポケモンが そのまま 受けつぐ。" + "effect": "控えの ポケモンと 入れ替わる。 能力変化は 替わった ポケモンが そのまま 受けつぐ。" }, "encore": { "name": "アンコール", - "effect": "相手に アンコールした 技を 3回 続けて 出させる。" + "effect": "相手に アンコールした 技を 3回 続けて 出させる。" }, "pursuit": { "name": "おいうち", - "effect": "相手 ポケモンが 入れ替わるときに 技を だしていると 倍の 威力で 攻撃できる。" + "effect": "相手 ポケモンが 入れ替わるときに 技を だしていると 倍の 威力で 攻撃できる。" }, "rapidSpin": { "name": "こうそくスピン", - "effect": "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。自分の 素早さも あがる。" + "effect": "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。自分の 素早さも あがる。" }, "sweetScent": { "name": "あまいかおり", - "effect": "香りで 相手の 回避率を がくっと さげる。" + "effect": "香りで 相手の 回避率を がくっと さげる。" }, "ironTail": { "name": "アイアンテール", - "effect": "硬い しっぽで 相手を たたきつけて 攻撃する。 相手の 防御を さげることが ある。" + "effect": "硬い しっぽで 相手を たたきつけて 攻撃する。 相手の 防御を さげることが ある。" }, "metalClaw": { "name": "メタルクロー", - "effect": "鋼鉄の ツメで 相手を 切り裂いて 攻撃する。 自分の 攻撃が あがることが ある。" + "effect": "鋼鉄の ツメで 相手を 切り裂いて 攻撃する。 自分の 攻撃が あがることが ある。" }, "vitalThrow": { "name": "あてみなげ", - "effect": "相手より あとに 攻撃する。 そのかわり 自分の 攻撃は 必ず 命中する。" + "effect": "相手より あとに 攻撃する。 そのかわり 自分の 攻撃は 必ず 命中する。" }, "morningSun": { "name": "あさのひざし", - "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" }, "synthesis": { "name": "こうごうせい", - "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" }, "moonlight": { "name": "つきのひかり", - "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + "effect": "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" }, "hiddenPower": { "name": "めざめるパワー", - "effect": "技を 使った ポケモンに よって 技の タイプが 変わる。" + "effect": "技を 使った ポケモンに よって 技の タイプが 変わる。" }, "crossChop": { "name": "クロスチョップ", - "effect": "両手チョップを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" + "effect": "両手チョップを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" }, "twister": { "name": "たつまき", - "effect": "竜巻を おこして 相手を まきこみ 攻撃する。 相手を ひるませることが ある。" + "effect": "竜巻を おこして 相手を まきこみ 攻撃する。 相手を ひるませることが ある。" }, "rainDance": { "name": "あまごい", - "effect": "5ターンの 間 雨を 降らせて みずタイプの 威力を あげる。 ほのおタイプの 威力は さがる。" + "effect": "5ターンの 間 雨を 降らせて みずタイプの 威力を あげる。 ほのおタイプの 威力は さがる。" }, "sunnyDay": { "name": "にほんばれ", - "effect": "5ターンの 間 日差しを 強くして ほのおタイプの 威力を あげる。 みずタイプの 威力は さがる。" + "effect": "5ターンの 間 日差しを 強くして ほのおタイプの 威力を あげる。 みずタイプの 威力は さがる。" }, "crunch": { "name": "かみくだく", - "effect": "鋭い 歯で 相手を かみくだいて 攻撃する。 相手の 防御を さげることが ある。" + "effect": "鋭い 歯で 相手を かみくだいて 攻撃する。 相手の 防御を さげることが ある。" }, "mirrorCoat": { "name": "ミラーコート", - "effect": "相手から 受けた 特殊攻撃の ダメージを 2倍に して その相手に 返す。" + "effect": "相手から 受けた 特殊攻撃の ダメージを 2倍に して その相手に 返す。" }, "psychUp": { "name": "じこあんじ", - "effect": "自分に 暗示を かけることで 能力変化の 状態を 相手と 同じにする。" + "effect": "自分に 暗示を かけることで 能力変化の 状態を 相手と 同じにする。" }, "extremeSpeed": { "name": "しんそく", - "effect": "目にも 留まらぬ ものすごい 速さで 相手に 突進して 攻撃する。 必ず 先制攻撃 できる。" + "effect": "目にも 留まらぬ ものすごい 速さで 相手に 突進して 攻撃する。 必ず 先制攻撃 できる。" }, "ancientPower": { "name": "げんしのちから", - "effect": "原始の 力で 攻撃する。 自分の すべての 能力が あがることが ある。" + "effect": "原始の 力で 攻撃する。 自分の すべての 能力が あがることが ある。" }, "shadowBall": { "name": "シャドーボール", - "effect": "黒い影の 塊を 投げつけて 攻撃する。 相手の 特防を さげることが ある。" + "effect": "黒い影の 塊を 投げつけて 攻撃する。 相手の 特防を さげることが ある。" }, "futureSight": { "name": "みらいよち", - "effect": "技を 使った 2ターン後に 相手に 念力の 塊を 送って 攻撃する。" + "effect": "技を 使った 2ターン後に 相手に 念力の 塊を 送って 攻撃する。" }, "rockSmash": { "name": "いわくだき", - "effect": "パンチで 攻撃する。相手の 防御を さげる ことが ある。" + "effect": "パンチで 攻撃する。相手の 防御を さげる ことが ある。" }, "whirlpool": { "name": "うずしお", - "effect": "激しく 渦をまく 水の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + "effect": "激しく 渦をまく 水の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" }, "beatUp": { "name": "ふくろだたき", - "effect": "味方 全員で 攻撃する。 仲間の ポケモンが 多いほど 技の 攻撃回数が 増える。" + "effect": "味方 全員で 攻撃する。 仲間の ポケモンが 多いほど 技の 攻撃回数が 増える。" }, "fakeOut": { "name": "ねこだまし", - "effect": "先制攻撃で 相手を ひるませる。 戦闘に でたら すぐに ださないと 成功しない。" + "effect": "先制攻撃で 相手を ひるませる。 戦闘に でたら すぐに ださないと 成功しない。" }, "uproar": { "name": "さわぐ", - "effect": "3ターンの 間 騒いで 相手を 攻撃する。 そのあいだは だれも 眠れなくなる。" + "effect": "3ターンの 間 騒いで 相手を 攻撃する。 そのあいだは だれも 眠れなくなる。" }, "stockpile": { "name": "たくわえる", - "effect": "力を 蓄えて 自分の 防御と 特防を あげる。 最大 3回まで 蓄えられる。" + "effect": "力を 蓄えて 自分の 防御と 特防を あげる。 最大 3回まで 蓄えられる。" }, "spitUp": { "name": "はきだす", - "effect": "蓄えた 力を 相手に ぶつけて 攻撃する。 蓄えているほど 威力が あがる。" + "effect": "蓄えた 力を 相手に ぶつけて 攻撃する。 蓄えているほど 威力が あがる。" }, "swallow": { "name": "のみこむ", - "effect": "蓄えた 力を のみこんで 自分の HPを 回復する。 蓄えているほど 回復する。" + "effect": "蓄えた 力を のみこんで 自分の HPを 回復する。 蓄えているほど 回復する。" }, "heatWave": { "name": "ねっぷう", - "effect": "熱い 息を 相手に 吹きつけて 攻撃する。 やけど状態に することが ある。" + "effect": "熱い 息を 相手に 吹きつけて 攻撃する。 やけど状態に することが ある。" }, "hail": { "name": "あられ", - "effect": "5ターンの 間 あられを 降らして こおりタイプで ない ポケモン 全員に ダメージを 与える。" + "effect": "5ターンの 間 あられを 降らして こおりタイプで ない ポケモン 全員に ダメージを 与える。" }, "torment": { "name": "いちゃもん", - "effect": "相手に いちゃもんを つけて 同じ 技を 2回連続で だせなくする。" + "effect": "相手に いちゃもんを つけて 同じ 技を 2回連続で だせなくする。" }, "flatter": { "name": "おだてる", - "effect": "相手を おだてて 混乱させる。 同時に 相手の 特攻も あげてしまう。" + "effect": "相手を おだてて 混乱させる。 同時に 相手の 特攻も あげてしまう。" }, "willOWisp": { "name": "おにび", - "effect": "不気味で 怪しい 炎を 放って 相手を やけどの 状態に する。" + "effect": "不気味で 怪しい 炎を 放って 相手を やけどの 状態に する。" }, "memento": { "name": "おきみやげ", - "effect": "自分は ひんしに なるが そのかわりに 相手の 攻撃と 特攻を がくっと さげる。" + "effect": "自分は ひんしに なるが そのかわりに 相手の 攻撃と 特攻を がくっと さげる。" }, "facade": { "name": "からげんき", - "effect": "自分が 毒 まひ やけど 状態のとき 相手に くりだすと 技の 威力が 2倍に なる。" + "effect": "自分が 毒 まひ やけど 状態のとき 相手に くりだすと 技の 威力が 2倍に なる。" }, "focusPunch": { "name": "きあいパンチ", - "effect": "精神を 高めて パンチを くりだす。 技を だすまでに 攻撃を 受けると 失敗する。" + "effect": "精神を 高めて パンチを くりだす。 技を だすまでに 攻撃を 受けると 失敗する。" }, "smellingSalts": { "name": "きつけ", - "effect": "まひ状態の 相手には 威力が 2倍に なるが かわりに 相手の まひが 治る。" + "effect": "まひ状態の 相手には 威力が 2倍に なるが かわりに 相手の まひが 治る。" }, "followMe": { "name": "このゆびとまれ", - "effect": "自分に 注目させて 相手からの 攻撃を すべて 自分に むけさせる。" + "effect": "自分に 注目させて 相手からの 攻撃を すべて 自分に むけさせる。" }, "naturePower": { "name": "しぜんのちから", - "effect": "自然の 力で 攻撃する。 使う 場所で でてくる 技が 変化する。" + "effect": "自然の 力で 攻撃する。 使う 場所で でてくる 技が 変化する。" }, "charge": { "name": "じゅうでん", - "effect": "次の ターンに だす でんきタイプの 技の 威力を あげる。 自分の 特防も あがる。" + "effect": "次の ターンに だす でんきタイプの 技の 威力を あげる。 自分の 特防も あがる。" }, "taunt": { "name": "ちょうはつ", - "effect": "相手を 怒らせる。 3ターンの 間 相手は ダメージを 与える 技しか だせなくなる。" + "effect": "相手を 怒らせる。 3ターンの 間 相手は ダメージを 与える 技しか だせなくなる。" }, "helpingHand": { "name": "てだすけ", - "effect": "仲間を 助ける。 てだすけ された ポケモンの 技の 威力は いつもより 大きくなる。" + "effect": "仲間を 助ける。 てだすけ された ポケモンの 技の 威力は いつもより 大きくなる。" }, "trick": { "name": "トリック", - "effect": "相手の すきを ついて 自分と 相手の 持ち物を 交換する。" + "effect": "相手の すきを ついて 自分と 相手の 持ち物を 交換する。" }, "rolePlay": { "name": "なりきり", - "effect": "相手に なりきって 自分も 相手と 同じ 特性に 変化する。" + "effect": "相手に なりきって 自分も 相手と 同じ 特性に 変化する。" }, "wish": { "name": "ねがいごと", - "effect": "次の ターンに 自分 もしくは 入れ替わった ポケモンの HPを 最大HPの 半分 回復する。" + "effect": "次の ターンに 自分 もしくは 入れ替わった ポケモンの HPを 最大HPの 半分 回復する。" }, "assist": { "name": "ねこのて", - "effect": "大急ぎで 味方の 助けを かりて 味方の ポケモンが おぼえている 技を どれか 1つ 使う。" + "effect": "大急ぎで 味方の 助けを かりて 味方の ポケモンが おぼえている 技を どれか 1つ 使う。" }, "ingrain": { "name": "ねをはる", - "effect": "大地に 根を 張り 毎ターン 自分の HPを 回復する。 根を 張っているので 入れ替えられない。" + "effect": "大地に 根を 張り 毎ターン 自分の HPを 回復する。 根を 張っているので 入れ替えられない。" }, "superpower": { "name": "ばかぢから", - "effect": "すごい 力を 発揮して 相手を 攻撃する。自分の 攻撃と 防御が さがる。" + "effect": "すごい 力を 発揮して 相手を 攻撃する。自分の 攻撃と 防御が さがる。" }, "magicCoat": { "name": "マジックコート", - "effect": "状態異常に なる 技や やどりぎのタネ などを だされたとき 相手に 跳ね返す。" + "effect": "状態異常に なる 技や やどりぎのタネ などを だされたとき 相手に 跳ね返す。" }, "recycle": { "name": "リサイクル", - "effect": "戦闘中に 使って なくなった 自分の 持ち物を 再生させて 使えるように する。" + "effect": "戦闘中に 使って なくなった 自分の 持ち物を 再生させて 使えるように する。" }, "revenge": { "name": "リベンジ", - "effect": "相手から 技を 受けていると その相手に 対して 与える ダメージが 2倍に なる。" + "effect": "相手から 技を 受けていると その相手に 対して 与える ダメージが 2倍に なる。" }, "brickBreak": { "name": "かわらわり", - "effect": "手刀を 勢いよく 振りおろして 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" + "effect": "手刀を 勢いよく 振りおろして 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" }, "yawn": { "name": "あくび", - "effect": "大きな あくびで 眠気を 誘う。 次の ターンに 相手を 眠り状態に する。" + "effect": "大きな あくびで 眠気を 誘う。 次の ターンに 相手を 眠り状態に する。" }, "knockOff": { "name": "はたきおとす", - "effect": "相手の 持ち物を はたき 落として 戦闘が 終わるまで 使えなくする。 物を持つ 相手には ダメージが増す。" + "effect": "相手の 持ち物を はたき 落として 戦闘が 終わるまで 使えなくする。 物を持つ 相手には ダメージが増す。" }, "endeavor": { "name": "がむしゃら", - "effect": "相手の HPが 自分の HPと 同じくらいに なるように ダメージを 与える。" + "effect": "相手の HPが 自分の HPと 同じくらいに なるように ダメージを 与える。" }, "eruption": { "name": "ふんか", - "effect": "怒りを 爆発させて 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + "effect": "怒りを 爆発させて 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" }, "skillSwap": { "name": "スキルスワップ", - "effect": "超能力で 自分の 特性と 相手の 特性を 入れ替える。" + "effect": "超能力で 自分の 特性と 相手の 特性を 入れ替える。" }, "imprison": { "name": "ふういん", - "effect": "相手が 自分と 同じ 技を おぼえていたら 相手だけ その技を 使えなくする。" + "effect": "相手が 自分と 同じ 技を おぼえていたら 相手だけ その技を 使えなくする。" }, "refresh": { "name": "リフレッシュ", - "effect": "体を やすめて 自分が おっている 毒 まひ やけどの 状態異常を 治す。" + "effect": "体を やすめて 自分が おっている 毒 まひ やけどの 状態異常を 治す。" }, "grudge": { "name": "おんねん", - "effect": "相手の 技で ひんしに されたとき おんねんを かけて その技の PPを 0に する。" + "effect": "相手の 技で ひんしに されたとき おんねんを かけて その技の PPを 0に する。" }, "snatch": { "name": "よこどり", - "effect": "相手が 使おうと した 回復技や 能力変化の 技を うばって 自分に 使う。" + "effect": "相手が 使おうと した 回復技や 能力変化の 技を うばって 自分に 使う。" }, "secretPower": { "name": "ひみつのちから", - "effect": "使う場所で 追加効果が 変化する 攻撃。" + "effect": "使う場所で 追加効果が 変化する 攻撃。" }, "dive": { "name": "ダイビング", - "effect": "1ターン目で 潜り 2ターン目に 浮きあがって 攻撃する。" + "effect": "1ターン目で 潜り 2ターン目に 浮きあがって 攻撃する。" }, "armThrust": { "name": "つっぱり", - "effect": "ひらいた 両手で 相手を つっぱって 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "ひらいた 両手で 相手を つっぱって 攻撃する。 2ー5回の 間 連続で だす。" }, "camouflage": { "name": "ほごしょく", - "effect": "水辺や 草むら どうくつなど いる 場所に あわせて 自分の タイプを 変える。" + "effect": "水辺や 草むら どうくつなど いる 場所に あわせて 自分の タイプを 変える。" }, "tailGlow": { "name": "ほたるび", - "effect": "点滅する 光を 眺めて 自分の 精神を 統一し 特攻を ぐぐーんと あげる。" + "effect": "点滅する 光を 眺めて 自分の 精神を 統一し 特攻を ぐぐーんと あげる。" }, "lusterPurge": { "name": "ラスターパージ", - "effect": "まばゆい 光を 解放して 攻撃する。 相手の 特防を さげることが ある。" + "effect": "まばゆい 光を 解放して 攻撃する。 相手の 特防を さげることが ある。" }, "mistBall": { "name": "ミストボール", - "effect": "霧状の 羽毛で 包みこみ 攻撃する。 相手の 特攻を さげることが ある。" + "effect": "霧状の 羽毛で 包みこみ 攻撃する。 相手の 特攻を さげることが ある。" }, "featherDance": { "name": "フェザーダンス", - "effect": "羽毛を ふりまいて 相手の 体に からませる。 相手の 攻撃を がくっと さげる。" + "effect": "羽毛を ふりまいて 相手の 体に からませる。 相手の 攻撃を がくっと さげる。" }, "teeterDance": { "name": "フラフラダンス", - "effect": "フラフラと ダンスを おどって 自分の 周りに いるものを 混乱状態に させる。" + "effect": "フラフラと ダンスを おどって 自分の 周りに いるものを 混乱状態に させる。" }, "blazeKick": { "name": "ブレイズキック", - "effect": "攻撃した 相手を やけど状態に することが ある。 急所にも 当たりやすい。" + "effect": "攻撃した 相手を やけど状態に することが ある。 急所にも 当たりやすい。" }, "mudSport": { "name": "どろあそび", - "effect": "あたりを 泥まみれにする。 5ターンの 間 でんきタイプの 技を 弱める。" + "effect": "あたりを 泥まみれにする。 5ターンの 間 でんきタイプの 技を 弱める。" }, "iceBall": { "name": "アイスボール", - "effect": "5ターンの 間 相手を 攻撃する。 技が 当たるたび 威力が あがる。" + "effect": "5ターンの 間 相手を 攻撃する。 技が 当たるたび 威力が あがる。" }, "needleArm": { "name": "ニードルアーム", - "effect": "トゲの 腕を 激しく ふるって 攻撃する。 相手を ひるませることが ある。" + "effect": "トゲの 腕を 激しく ふるって 攻撃する。 相手を ひるませることが ある。" }, "slackOff": { "name": "なまける", - "effect": "怠けて やすむ。 自分の HPを 最大HPの 半分 回復する。" + "effect": "怠けて やすむ。 自分の HPを 最大HPの 半分 回復する。" }, "hyperVoice": { "name": "ハイパーボイス", - "effect": "うるさく 響く 大きな 振動を 相手に 与えて 攻撃する。" + "effect": "うるさく 響く 大きな 振動を 相手に 与えて 攻撃する。" }, "poisonFang": { "name": "どくどくのキバ", - "effect": "毒の ある キバで 相手に かみついて 攻撃する。 猛毒を おわせる ことが ある。" + "effect": "毒の ある キバで 相手に かみついて 攻撃する。 猛毒を おわせる ことが ある。" }, "crushClaw": { "name": "ブレイククロー", - "effect": "硬く 鋭い ツメで 切り裂いて 攻撃する。 相手の 防御を さげることが ある。" + "effect": "硬く 鋭い ツメで 切り裂いて 攻撃する。 相手の 防御を さげることが ある。" }, "blastBurn": { "name": "ブラストバーン", - "effect": "爆発の 炎で 相手を 焼きつくして 攻撃する。 次の ターンは 動けなくなる。" + "effect": "爆発の 炎で 相手を 焼きつくして 攻撃する。 次の ターンは 動けなくなる。" }, "hydroCannon": { "name": "ハイドロカノン", - "effect": "水の 大砲を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + "effect": "水の 大砲を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" }, "meteorMash": { "name": "コメットパンチ", - "effect": "すい星の ごとく パンチを くりだして 相手を 攻撃する。 自分の 攻撃が あがることが ある。" + "effect": "すい星の ごとく パンチを くりだして 相手を 攻撃する。 自分の 攻撃が あがることが ある。" }, "astonish": { "name": "おどろかす", - "effect": "大きな 声などで 不意に 驚かして 攻撃する。 相手を ひるませることが ある。" + "effect": "大きな 声などで 不意に 驚かして 攻撃する。 相手を ひるませることが ある。" }, "weatherBall": { "name": "ウェザーボール", - "effect": "使ったときの 天気に よって 技の タイプと 威力が 変わる。" + "effect": "使ったときの 天気に よって 技の タイプと 威力が 変わる。" }, "aromatherapy": { "name": "アロマセラピー", - "effect": "心地好い やすらぐ 香りを かがせて 味方全員の 状態異常を 回復する。" + "effect": "心地好い やすらぐ 香りを かがせて 味方全員の 状態異常を 回復する。" }, "fakeTears": { "name": "うそなき", - "effect": "ないた ふりをして 涙を 流す。 こまらせる ことで 相手の 特防を がくっと さげる。" + "effect": "ないた ふりをして 涙を 流す。 こまらせる ことで 相手の 特防を がくっと さげる。" }, "airCutter": { "name": "エアカッター", - "effect": "鋭い 風で 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + "effect": "鋭い 風で 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" }, "overheat": { "name": "オーバーヒート", - "effect": "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" + "effect": "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" }, "odorSleuth": { "name": "かぎわける", - "effect": "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + "effect": "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" }, "rockTomb": { "name": "がんせきふうじ", - "effect": "岩石を 投げつけて 攻撃する。 相手の 動きを 封じることで 素早さを さげる。" + "effect": "岩石を 投げつけて 攻撃する。 相手の 動きを 封じることで 素早さを さげる。" }, "silverWind": { "name": "ぎんいろのかぜ", - "effect": "風に りんぷんを のせて 相手を 攻撃する。自分の すべての 能力が あがることが ある。" + "effect": "風に りんぷんを のせて 相手を 攻撃する。自分の すべての 能力が あがることが ある。" }, "metalSound": { "name": "きんぞくおん", - "effect": "金属を こすって でるような いやな 音を 聞かせる。 相手の 特防を がくっと さげる。" + "effect": "金属を こすって でるような いやな 音を 聞かせる。 相手の 特防を がくっと さげる。" }, "grassWhistle": { "name": "くさぶえ", - "effect": "心地好い 笛の 音色を 聞かせて 相手を 眠りの 状態に する。" + "effect": "心地好い 笛の 音色を 聞かせて 相手を 眠りの 状態に する。" }, "tickle": { "name": "くすぐる", - "effect": "体を くすぐり 笑わせる ことで 相手の 攻撃と 防御を さげる。" + "effect": "体を くすぐり 笑わせる ことで 相手の 攻撃と 防御を さげる。" }, "cosmicPower": { "name": "コスモパワー", - "effect": "宇宙から 神秘の 力を とりこむ ことで 自分の 防御と 特防を あげる。" + "effect": "宇宙から 神秘の 力を とりこむ ことで 自分の 防御と 特防を あげる。" }, "waterSpout": { "name": "しおふき", - "effect": "潮を 吹きつけて 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + "effect": "潮を 吹きつけて 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" }, "signalBeam": { "name": "シグナルビーム", - "effect": "不思議な 光を 発射して 攻撃する。 相手を 混乱させることが ある。" + "effect": "不思議な 光を 発射して 攻撃する。 相手を 混乱させることが ある。" }, "shadowPunch": { "name": "シャドーパンチ", - "effect": "影に まぎれて パンチを くりだす。 攻撃は 必ず 命中する。" + "effect": "影に まぎれて パンチを くりだす。 攻撃は 必ず 命中する。" }, "extrasensory": { "name": "じんつうりき", - "effect": "みえない 不思議な 力を 送って 攻撃する。 相手を ひるませることが ある。" + "effect": "みえない 不思議な 力を 送って 攻撃する。 相手を ひるませることが ある。" }, "skyUppercut": { "name": "スカイアッパー", - "effect": "空に むかうような 高い アッパーで 相手を 突きあげて 攻撃する。" + "effect": "空に むかうような 高い アッパーで 相手を 突きあげて 攻撃する。" }, "sandTomb": { "name": "すなじごく", - "effect": "激しく 吹きあれる 砂あらしの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + "effect": "激しく 吹きあれる 砂あらしの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" }, "sheerCold": { "name": "ぜったいれいど", - "effect": "相手を 一撃で 瀕死に する。 こおりタイプ 以外の ポケモンが 使うと 当たりにくい。" + "effect": "相手を 一撃で 瀕死に する。 こおりタイプ 以外の ポケモンが 使うと 当たりにくい。" }, "muddyWater": { "name": "だくりゅう", - "effect": "濁った 水を 相手に 発射して 攻撃する。 命中率を さげることが ある。" + "effect": "濁った 水を 相手に 発射して 攻撃する。 命中率を さげることが ある。" }, "bulletSeed": { "name": "タネマシンガン", - "effect": "タネを 勢いよく 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "タネを 勢いよく 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" }, "aerialAce": { "name": "つばめがえし", - "effect": "素早い 動きで 相手を ほんろうして 切りつける。 攻撃は 必ず 命中する。" + "effect": "素早い 動きで 相手を ほんろうして 切りつける。 攻撃は 必ず 命中する。" }, "icicleSpear": { "name": "つららばり", - "effect": "鋭い 氷柱を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "鋭い 氷柱を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" }, "ironDefense": { "name": "てっぺき", - "effect": "皮膚を 鉄のように 硬くする ことで 自分の 防御を ぐーんと あげる。" + "effect": "皮膚を 鉄のように 硬くする ことで 自分の 防御を ぐーんと あげる。" }, "block": { "name": "とおせんぼう", - "effect": "両手を ひろげて たちはだかり 相手の 逃げ道を ふさいで 逃げられなくする。" + "effect": "両手を ひろげて たちはだかり 相手の 逃げ道を ふさいで 逃げられなくする。" }, "howl": { "name": "とおぼえ", - "effect": "大声で ほえて 気合を 高め 自分と 味方の 攻撃を あげる。" + "effect": "大声で ほえて 気合を 高め 自分と 味方の 攻撃を あげる。" }, "dragonClaw": { "name": "ドラゴンクロー", - "effect": "鋭く とがった 巨大な ツメで 相手を 切り裂いて 攻撃する。" + "effect": "鋭く とがった 巨大な ツメで 相手を 切り裂いて 攻撃する。" }, "frenzyPlant": { "name": "ハードプラント", - "effect": "大きな 樹木で 相手を たたきつけて 攻撃する。 次の ターンは 動けなくなる。" + "effect": "大きな 樹木で 相手を たたきつけて 攻撃する。 次の ターンは 動けなくなる。" }, "bulkUp": { "name": "ビルドアップ", - "effect": "体に 力を こめて 筋肉を ぶあつく することで 自分の 攻撃と 防御を あげる。" + "effect": "体に 力を こめて 筋肉を ぶあつく することで 自分の 攻撃と 防御を あげる。" }, "bounce": { "name": "とびはねる", - "effect": "空高く 飛び跳ねて 2ターン目に 相手を 攻撃する。 まひ状態に することが ある。" + "effect": "空高く 飛び跳ねて 2ターン目に 相手を 攻撃する。 まひ状態に することが ある。" }, "mudShot": { "name": "マッドショット", - "effect": "泥の 塊を 相手に 投げつけて 攻撃する。 同時に 相手の 素早さを さげる。" + "effect": "泥の 塊を 相手に 投げつけて 攻撃する。 同時に 相手の 素早さを さげる。" }, "poisonTail": { "name": "ポイズンテール", - "effect": "しっぽで たたく。 毒状態に することが あり 急所にも 当たりやすい。" + "effect": "しっぽで たたく。 毒状態に することが あり 急所にも 当たりやすい。" }, "covet": { "name": "ほしがる", - "effect": "かわいく あまえながら 相手に ちかづき 持っている 道具を うばおうとする。 うばう 可能性は 30%。" + "effect": "かわいく あまえながら 相手に ちかづき 持っている 道具を うばおうとする。 うばう 可能性は 30%。" }, "voltTackle": { "name": "ボルテッカー", - "effect": "電気を まとって 突進する。 自分も かなり ダメージを 受ける。 まひ状態に することが ある。" + "effect": "電気を まとって 突進する。 自分も かなり ダメージを 受ける。 まひ状態に することが ある。" }, "magicalLeaf": { "name": "マジカルリーフ", - "effect": "相手を 追跡する 不思議な はっぱを まきちらす。 攻撃は 必ず 命中する。" + "effect": "相手を 追跡する 不思議な はっぱを まきちらす。 攻撃は 必ず 命中する。" }, "waterSport": { "name": "みずあそび", - "effect": "あたりを 水で びしょびしょにする。 5ターンの 間 ほのおタイプの 技を 弱める。" + "effect": "あたりを 水で びしょびしょにする。 5ターンの 間 ほのおタイプの 技を 弱める。" }, "calmMind": { "name": "めいそう", - "effect": "静かに 精神を 統一し 心を 鎮めることで 自分の 特攻と 特防を あげる。" + "effect": "静かに 精神を 統一し 心を 鎮めることで 自分の 特攻と 特防を あげる。" }, "leafBlade": { "name": "リーフブレード", - "effect": "はっぱを 剣のように あやつり 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + "effect": "はっぱを 剣のように あやつり 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" }, "dragonDance": { "name": "りゅうのまい", - "effect": "神秘的で 力強い 舞を 激しく おどる。 自分の 攻撃と 素早さを あげる。" + "effect": "神秘的で 力強い 舞を 激しく おどる。 自分の 攻撃と 素早さを あげる。" }, "rockBlast": { "name": "ロックブラスト", - "effect": "硬い 岩石を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "硬い 岩石を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" }, "shockWave": { "name": "でんげきは", - "effect": "電撃を 素早く 相手に 浴びせる。 攻撃は 必ず 命中する。" + "effect": "電撃を 素早く 相手に 浴びせる。 攻撃は 必ず 命中する。" }, "waterPulse": { "name": "みずのはどう", - "effect": "水の 振動を 相手に 与えて 攻撃する。 相手を 混乱させることが ある。" + "effect": "水の 振動を 相手に 与えて 攻撃する。 相手を 混乱させることが ある。" }, "doomDesire": { "name": "はめつのねがい", - "effect": "技を 使った 2ターン後に 無数の 光の 束で 相手を 攻撃する。" + "effect": "技を 使った 2ターン後に 無数の 光の 束で 相手を 攻撃する。" }, "psychoBoost": { "name": "サイコブースト", - "effect": "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" + "effect": "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" }, "roost": { "name": "はねやすめ", - "effect": "地面に 降りて 体を やすめる。 最大HPの 半分の HPを 回復する。" + "effect": "地面に 降りて 体を やすめる。 最大HPの 半分の HPを 回復する。" }, "gravity": { "name": "じゅうりょく", - "effect": "5ターンの間 ふゆうや ひこうタイプに じめんタイプの 技が 当たるようになる。 空中に 飛ぶ 技も 使えない。" + "effect": "5ターンの間 ふゆうや ひこうタイプに じめんタイプの 技が 当たるようになる。 空中に 飛ぶ 技も 使えない。" }, "miracleEye": { "name": "ミラクルアイ", - "effect": "あくタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + "effect": "あくタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" }, "wakeUpSlap": { "name": "めざましビンタ", - "effect": "眠り状態の 相手に 大きな ダメージを 与える。 かわりに 相手は 眠りから さめる。" + "effect": "眠り状態の 相手に 大きな ダメージを 与える。 かわりに 相手は 眠りから さめる。" }, "hammerArm": { "name": "アームハンマー", - "effect": "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" + "effect": "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" }, "gyroBall": { "name": "ジャイロボール", - "effect": "体を 高速に 回転させて 体当たりする。相手より 素早さが 低いほど 強い。" + "effect": "体を 高速に 回転させて 体当たりする。相手より 素早さが 低いほど 強い。" }, "healingWish": { "name": "いやしのねがい", - "effect": "自分は ひんしに なるが 控えから でてくる ポケモンの 状態異常と HPを 回復する。" + "effect": "自分は ひんしに なるが 控えから でてくる ポケモンの 状態異常と HPを 回復する。" }, "brine": { "name": "しおみず", - "effect": "相手が HPの 半分くらい きずを おっていると 技の 威力が 2倍に なる。" + "effect": "相手が HPの 半分くらい きずを おっていると 技の 威力が 2倍に なる。" }, "naturalGift": { "name": "しぜんのめぐみ", - "effect": "きのみから 力を もらい 攻撃する。持たせた きのみで 技の タイプと 威力が 変わる。" + "effect": "きのみから 力を もらい 攻撃する。持たせた きのみで 技の タイプと 威力が 変わる。" }, "feint": { "name": "フェイント", - "effect": "まもるや みきり などを している 相手に 攻撃が できる。 守りの 効果を 解除させる。" + "effect": "まもるや みきり などを している 相手に 攻撃が できる。 守りの 効果を 解除させる。" }, "pluck": { "name": "ついばむ", - "effect": "くちばしで 攻撃。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" + "effect": "くちばしで 攻撃。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" }, "tailwind": { "name": "おいかぜ", - "effect": "激しく 吹きあれる 風の渦を つくり 4ターンの 間 味方 全員の 素早さを あげる。" + "effect": "激しく 吹きあれる 風の渦を つくり 4ターンの 間 味方 全員の 素早さを あげる。" }, "acupressure": { "name": "つぼをつく", - "effect": "つぼおしで 体を 活性化させる。 能力の どれか 1つを ぐーんと あげる。" + "effect": "つぼおしで 体を 活性化させる。 能力の どれか 1つを ぐーんと あげる。" }, "metalBurst": { "name": "メタルバースト", - "effect": "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" + "effect": "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" }, "uTurn": { "name": "とんぼがえり", - "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" }, "closeCombat": { "name": "インファイト", - "effect": "守りを 捨てて 相手の ふところに 突撃する。 自分の 防御と 特防が さがる。" + "effect": "守りを 捨てて 相手の ふところに 突撃する。 自分の 防御と 特防が さがる。" }, "payback": { "name": "しっぺがえし", - "effect": "ためこんで 攻撃する。 相手より あとに 攻撃できると 技の 威力は 2倍に なる。" + "effect": "ためこんで 攻撃する。 相手より あとに 攻撃できると 技の 威力は 2倍に なる。" }, "assurance": { "name": "ダメおし", - "effect": "そのターンに 相手が すでに ダメージを 受けていたら 技の 威力は 2倍に なる。" + "effect": "そのターンに 相手が すでに ダメージを 受けていたら 技の 威力は 2倍に なる。" }, "embargo": { "name": "さしおさえ", - "effect": "持たせた 道具を 5ターンの 間 使えなくする。 トレーナーも その ポケモンには 道具を 使えない。" + "effect": "持たせた 道具を 5ターンの 間 使えなくする。 トレーナーも その ポケモンには 道具を 使えない。" }, "fling": { "name": "なげつける", - "effect": "持たせた 道具を 素早く 投げつけて 攻撃する。 道具で 威力と 効果が 変わる。" + "effect": "持たせた 道具を 素早く 投げつけて 攻撃する。 道具で 威力と 効果が 変わる。" }, "psychoShift": { "name": "サイコシフト", - "effect": "超能力で 暗示を かけて 自分の 受けている 状態異常を 相手に うつす。" + "effect": "超能力で 暗示を かけて 自分の 受けている 状態異常を 相手に うつす。" }, "trumpCard": { "name": "きりふだ", - "effect": "きりふだの 残り PPが 少なければ 少ないほど 技の 威力が あがる。" + "effect": "きりふだの 残り PPが 少なければ 少ないほど 技の 威力が あがる。" }, "healBlock": { "name": "かいふくふうじ", - "effect": "5ターンの 間 技や 特性や 持っている 道具によって HPを 回復 できなくする。" + "effect": "5ターンの 間 技や 特性や 持っている 道具によって HPを 回復 できなくする。" }, "wringOut": { "name": "しぼりとる", - "effect": "強く 締めあげて 攻撃を する。 相手の HPが 残っているほど 威力は あがる。" + "effect": "強く 締めあげて 攻撃を する。 相手の HPが 残っているほど 威力は あがる。" }, "powerTrick": { "name": "パワートリック", - "effect": "超能力で 自分の 攻撃と 防御の 力を 交換する。" + "effect": "超能力で 自分の 攻撃と 防御の 力を 交換する。" }, "gastroAcid": { "name": "いえき", - "effect": "胃液を 相手の 体に 吐きつける。 ついた 胃液は 相手の 特性の 効果を 消す。" + "effect": "胃液を 相手の 体に 吐きつける。 ついた 胃液は 相手の 特性の 効果を 消す。" }, "luckyChant": { "name": "おまじない", - "effect": "天に むかって おいのりを ささげ 5ターンの 間 相手の 攻撃を 急所に 当たらなくする。" + "effect": "天に むかって おいのりを ささげ 5ターンの 間 相手の 攻撃を 急所に 当たらなくする。" }, "meFirst": { "name": "さきどり", - "effect": "威力を あげて 相手が だそうとする 技を 先にだす。 先に だせないと 失敗する。" + "effect": "威力を あげて 相手が だそうとする 技を 先にだす。 先に だせないと 失敗する。" }, "copycat": { "name": "まねっこ", - "effect": "直前に でた 技を まねして 同じ 技を だす。 技が でていないと 失敗する。" + "effect": "直前に でた 技を まねして 同じ 技を だす。 技が でていないと 失敗する。" }, "powerSwap": { "name": "パワースワップ", - "effect": "超能力で 自分と 相手の 攻撃と 特攻の 能力変化を 入れ替える。" + "effect": "超能力で 自分と 相手の 攻撃と 特攻の 能力変化を 入れ替える。" }, "guardSwap": { "name": "ガードスワップ", - "effect": "超能力で 自分と 相手の 防御と 特防の 能力変化を 入れ替える。" + "effect": "超能力で 自分と 相手の 防御と 特防の 能力変化を 入れ替える。" }, "punishment": { "name": "おしおき", - "effect": "能力変化で 相手が パワーアップ しているほど 技の 威力が あがる。" + "effect": "能力変化で 相手が パワーアップ しているほど 技の 威力が あがる。" }, "lastResort": { "name": "とっておき", - "effect": "戦闘中に おぼえている 技を すべて 使うと はじめて だせる とっておきの 技。" + "effect": "戦闘中に おぼえている 技を すべて 使うと はじめて だせる とっておきの 技。" }, "worrySeed": { "name": "なやみのタネ", - "effect": "心を なやませる タネを 植えつける。 相手を 眠れなくして 特性を ふみんに する。" + "effect": "心を なやませる タネを 植えつける。 相手を 眠れなくして 特性を ふみんに する。" }, "suckerPunch": { "name": "ふいうち", - "effect": "相手より 先に 攻撃 できる。 相手が だす技が 攻撃技でないと 失敗する。" + "effect": "相手より 先に 攻撃 できる。 相手が だす技が 攻撃技でないと 失敗する。" }, "toxicSpikes": { "name": "どくびし", - "effect": "相手の 足下に どくびしを しかける。 交代で でてきた 相手の ポケモンに 毒を おわせる。" + "effect": "相手の 足下に どくびしを しかける。 交代で でてきた 相手の ポケモンに 毒を おわせる。" }, "heartSwap": { "name": "ハートスワップ", - "effect": "超能力で 自分と 相手に かかっている 能力変化を 入れ替える。" + "effect": "超能力で 自分と 相手に かかっている 能力変化を 入れ替える。" }, "aquaRing": { "name": "アクアリング", - "effect": "自分の 体の 周りを 水で つくった ベールで おおう。 毎ターン HPを 回復する。" + "effect": "自分の 体の 周りを 水で つくった ベールで おおう。 毎ターン HPを 回復する。" }, "magnetRise": { "name": "でんじふゆう", - "effect": "電気で つくった 磁力の 力で 宙に 浮かぶ。 5ターンの 間 浮遊できる。" + "effect": "電気で つくった 磁力の 力で 宙に 浮かぶ。 5ターンの 間 浮遊できる。" }, "flareBlitz": { "name": "フレアドライブ", - "effect": "炎を まとって 突進する。 自分も かなり ダメージを 受ける。 やけど状態に することが ある。" + "effect": "炎を まとって 突進する。 自分も かなり ダメージを 受ける。 やけど状態に することが ある。" }, "forcePalm": { "name": "はっけい", - "effect": "相手の 体に 衝撃波を 当てて 攻撃する。 まひ状態に することが ある。" + "effect": "相手の 体に 衝撃波を 当てて 攻撃する。 まひ状態に することが ある。" }, "auraSphere": { "name": "はどうだん", - "effect": "体の 奥から 波導の 力を 相手に うち放つ。 攻撃は 必ず 命中する。" + "effect": "体の 奥から 波導の 力を 相手に うち放つ。 攻撃は 必ず 命中する。" }, "rockPolish": { "name": "ロックカット", - "effect": "自分の 体を 磨いて 空気の 抵抗を 少なくする。素早さを ぐーんと あげることが できる。" + "effect": "自分の 体を 磨いて 空気の 抵抗を 少なくする。素早さを ぐーんと あげることが できる。" }, "poisonJab": { "name": "どくづき", - "effect": "毒に そまった 触手や 腕で 相手を 突き刺す。 毒状態に することが ある。" + "effect": "毒に そまった 触手や 腕で 相手を 突き刺す。 毒状態に することが ある。" }, "darkPulse": { "name": "あくのはどう", - "effect": "体から 悪意に みちた 恐ろしい オーラを 発する。 相手を ひるませることが ある。" + "effect": "体から 悪意に みちた 恐ろしい オーラを 発する。 相手を ひるませることが ある。" }, "nightSlash": { "name": "つじぎり", - "effect": "一瞬の すきを ついて 相手を 切りはらう。 急所に 当たりやすい。" + "effect": "一瞬の すきを ついて 相手を 切りはらう。 急所に 当たりやすい。" }, "aquaTail": { "name": "アクアテール", - "effect": "激しく あれくるう 荒波の ように 大きな しっぽを ふって 相手を 攻撃する。" + "effect": "激しく あれくるう 荒波の ように 大きな しっぽを ふって 相手を 攻撃する。" }, "seedBomb": { "name": "タネばくだん", - "effect": "硬い 殻を もつ 大きな タネを 上から たたきつけて 相手を 攻撃する。" + "effect": "硬い 殻を もつ 大きな タネを 上から たたきつけて 相手を 攻撃する。" }, "airSlash": { "name": "エアスラッシュ", - "effect": "空をも 切り裂く 空気の 刃で 攻撃する。 相手を ひるませることが ある。" + "effect": "空をも 切り裂く 空気の 刃で 攻撃する。 相手を ひるませることが ある。" }, "xScissor": { "name": "シザークロス", - "effect": "カマや ツメを ハサミのように 交差させながら 相手を 切り裂く。" + "effect": "カマや ツメを ハサミのように 交差させながら 相手を 切り裂く。" }, "bugBuzz": { "name": "むしのさざめき", - "effect": "振動で 音波を おこして 攻撃する。相手の 特防を さげることが ある。" + "effect": "振動で 音波を おこして 攻撃する。相手の 特防を さげることが ある。" }, "dragonPulse": { "name": "りゅうのはどう", - "effect": "大きな 口から 衝撃波を まきおこして 相手を 攻撃する。" + "effect": "大きな 口から 衝撃波を まきおこして 相手を 攻撃する。" }, "dragonRush": { "name": "ドラゴンダイブ", - "effect": "すさまじい 殺気で 威圧しながら 体当たりする。 相手を ひるませることが ある。" + "effect": "すさまじい 殺気で 威圧しながら 体当たりする。 相手を ひるませることが ある。" }, "powerGem": { "name": "パワージェム", - "effect": "宝石のように きらめく 光を 発射して 相手を 攻撃する。" + "effect": "宝石のように きらめく 光を 発射して 相手を 攻撃する。" }, "drainPunch": { "name": "ドレインパンチ", - "effect": "こぶしから 相手の 力を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" + "effect": "こぶしから 相手の 力を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" }, "vacuumWave": { "name": "しんくうは", - "effect": "こぶしを ふって 真空の 波を まきおこす。 必ず 先制攻撃できる。" + "effect": "こぶしを ふって 真空の 波を まきおこす。 必ず 先制攻撃できる。" }, "focusBlast": { "name": "きあいだま", - "effect": "気合を 高めて ありったけの 力を 放出する。 相手の 特防を さげることが ある。" + "effect": "気合を 高めて ありったけの 力を 放出する。 相手の 特防を さげることが ある。" }, "energyBall": { "name": "エナジーボール", - "effect": "自然から 集めた 命の力を 発射する。 相手の 特防を さげることがある。" + "effect": "自然から 集めた 命の力を 発射する。 相手の 特防を さげることがある。" }, "braveBird": { "name": "ブレイブバード", - "effect": "はねを おりたたみ 低空飛行で 突撃する。 自分も かなり ダメージを 受ける。" + "effect": "はねを おりたたみ 低空飛行で 突撃する。 自分も かなり ダメージを 受ける。" }, "earthPower": { "name": "だいちのちから", - "effect": "相手の 足下へ 大地の力を 放出する。相手の 特防を さげることが ある。" + "effect": "相手の 足下へ 大地の力を 放出する。相手の 特防を さげることが ある。" }, "switcheroo": { "name": "すりかえ", - "effect": "目にも とまらぬ 速さで 自分と 相手の 持ち物を 交換する。" + "effect": "目にも とまらぬ 速さで 自分と 相手の 持ち物を 交換する。" }, "gigaImpact": { "name": "ギガインパクト", - "effect": "持てる 力を すべて 使って 相手に 突撃する。 次の ターンは 動けなくなる。" + "effect": "持てる 力を すべて 使って 相手に 突撃する。 次の ターンは 動けなくなる。" }, "nastyPlot": { "name": "わるだくみ", - "effect": "悪いことを 考えて 頭を 活性化させる。 自分の 特攻を ぐーんと あげる。" + "effect": "悪いことを 考えて 頭を 活性化させる。 自分の 特攻を ぐーんと あげる。" }, "bulletPunch": { "name": "バレットパンチ", - "effect": "弾丸の ような 速くて 硬い パンチを 相手に くりだす。 必ず 先制攻撃 できる。" + "effect": "弾丸の ような 速くて 硬い パンチを 相手に くりだす。 必ず 先制攻撃 できる。" }, "avalanche": { "name": "ゆきなだれ", - "effect": "相手から 技を 受けていると その 相手に 対して 技の 威力が 2倍に なる。" + "effect": "相手から 技を 受けていると その 相手に 対して 技の 威力が 2倍に なる。" }, "iceShard": { "name": "こおりのつぶて", - "effect": "氷の塊を 一瞬で つくり 相手に 素早く 放つ。 必ず 先制攻撃 できる。" + "effect": "氷の塊を 一瞬で つくり 相手に 素早く 放つ。 必ず 先制攻撃 できる。" }, "shadowClaw": { "name": "シャドークロー", - "effect": "影から つくった 鋭い ツメで 相手を 切り裂く。 急所に 当たりやすい。" + "effect": "影から つくった 鋭い ツメで 相手を 切り裂く。 急所に 当たりやすい。" }, "thunderFang": { "name": "かみなりのキバ", - "effect": "電気を ためた キバで かみつく。 相手を ひるませたり まひ状態に することが ある。" + "effect": "電気を ためた キバで かみつく。 相手を ひるませたり まひ状態に することが ある。" }, "iceFang": { "name": "こおりのキバ", - "effect": "冷気を ひめた キバで かみつく。 相手を ひるませたり こおり状態に することが ある。" + "effect": "冷気を ひめた キバで かみつく。 相手を ひるませたり こおり状態に することが ある。" }, "fireFang": { "name": "ほのおのキバ", - "effect": "炎を まとった キバで かみつく。 相手を ひるませたり やけど状態に することが ある。" + "effect": "炎を まとった キバで かみつく。 相手を ひるませたり やけど状態に することが ある。" }, "shadowSneak": { "name": "かげうち", - "effect": "影を のばして 相手の 背後から 攻撃する。 必ず 先制攻撃 できる。" + "effect": "影を のばして 相手の 背後から 攻撃する。 必ず 先制攻撃 できる。" }, "mudBomb": { "name": "どろばくだん", - "effect": "硬い 泥の 弾を 相手に 発射して 攻撃する。 命中率を さげることが ある。" + "effect": "硬い 泥の 弾を 相手に 発射して 攻撃する。 命中率を さげることが ある。" }, "psychoCut": { "name": "サイコカッター", - "effect": "実体化させた 心の 刃で 相手を 切り裂く。 急所に 当たりやすい。" + "effect": "実体化させた 心の 刃で 相手を 切り裂く。 急所に 当たりやすい。" }, "zenHeadbutt": { "name": "しねんのずつき", - "effect": "思念の 力を 額に 集めて 攻撃する。 相手を ひるませることが ある。" + "effect": "思念の 力を 額に 集めて 攻撃する。 相手を ひるませることが ある。" }, "mirrorShot": { "name": "ミラーショット", - "effect": "磨きあげられた 体から せん光の 力を 相手に 放つ。 命中率を さげることが ある。" + "effect": "磨きあげられた 体から せん光の 力を 相手に 放つ。 命中率を さげることが ある。" }, "flashCannon": { "name": "ラスターカノン", - "effect": "体の 光を 一点に 集めて 力を 放つ。 相手の 特防を さげることが ある。" + "effect": "体の 光を 一点に 集めて 力を 放つ。 相手の 特防を さげることが ある。" }, "rockClimb": { "name": "ロッククライム", - "effect": "すごい 勢いで 相手に つっこみ 攻撃する。 相手を 混乱させることが ある。" + "effect": "すごい 勢いで 相手に つっこみ 攻撃する。 相手を 混乱させることが ある。" }, "defog": { "name": "きりばらい", - "effect": "強い風で 相手の リフレクターや ひかりのかべ などを はらいのける。 回避率も さげる。" + "effect": "強い風で 相手の リフレクターや ひかりのかべ などを はらいのける。 回避率も さげる。" }, "trickRoom": { "name": "トリックルーム", - "effect": "まか不思議な 空間を つくる。 5ターンの 間 遅い ポケモンから 行動できる。" + "effect": "まか不思議な 空間を つくる。 5ターンの 間 遅い ポケモンから 行動できる。" }, "dracoMeteor": { "name": "りゅうせいぐん", - "effect": "天空から 隕石を 相手に 落とす。使うと 反動で 自分の 特攻が がくっと さがる。" + "effect": "天空から 隕石を 相手に 落とす。使うと 反動で 自分の 特攻が がくっと さがる。" }, "discharge": { "name": "ほうでん", - "effect": "まばゆい 電撃で 自分の 周りに いるものを 攻撃する。 まひ状態に することが ある。" + "effect": "まばゆい 電撃で 自分の 周りに いるものを 攻撃する。 まひ状態に することが ある。" }, "lavaPlume": { "name": "ふんえん", - "effect": "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" + "effect": "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" }, "leafStorm": { "name": "リーフストーム", - "effect": "とがった はっぱで 相手に あらしを おこす。使うと 反動で 自分の 特攻が がくっと さがる。" + "effect": "とがった はっぱで 相手に あらしを おこす。使うと 反動で 自分の 特攻が がくっと さがる。" }, "powerWhip": { "name": "パワーウィップ", - "effect": "ツタや 触手を 激しく ふるって 相手を たたきつけ 攻撃する。" + "effect": "ツタや 触手を 激しく ふるって 相手を たたきつけ 攻撃する。" }, "rockWrecker": { "name": "がんせきほう", - "effect": "巨大な 岩を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + "effect": "巨大な 岩を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" }, "crossPoison": { "name": "クロスポイズン", - "effect": "毒の 刃で 相手を 切り裂く。 毒状態に することが あり 急所にも 当たりやすい。" + "effect": "毒の 刃で 相手を 切り裂く。 毒状態に することが あり 急所にも 当たりやすい。" }, "gunkShot": { "name": "ダストシュート", - "effect": "汚い ゴミを 相手に ぶつけて 攻撃する。 毒状態に することが ある。" + "effect": "汚い ゴミを 相手に ぶつけて 攻撃する。 毒状態に することが ある。" }, "ironHead": { "name": "アイアンヘッド", - "effect": "鋼の ような 硬い 頭で 攻撃する。 相手を ひるませることが ある。" + "effect": "鋼の ような 硬い 頭で 攻撃する。 相手を ひるませることが ある。" }, "magnetBomb": { "name": "マグネットボム", - "effect": "相手に 吸いつく 鋼の 爆弾を 発射する。 攻撃は 必ず 命中 する。" + "effect": "相手に 吸いつく 鋼の 爆弾を 発射する。 攻撃は 必ず 命中 する。" }, "stoneEdge": { "name": "ストーンエッジ", - "effect": "とがった 岩を 相手に 突き刺して 攻撃する。 急所に 当たりやすい。" + "effect": "とがった 岩を 相手に 突き刺して 攻撃する。 急所に 当たりやすい。" }, "captivate": { "name": "ゆうわく", - "effect": "♂なら♀を ♀なら♂を 誘惑して 相手の 特攻を がくっと さげる。" + "effect": "♂なら♀を ♀なら♂を 誘惑して 相手の 特攻を がくっと さげる。" }, "stealthRock": { "name": "ステルスロック", - "effect": "相手の 周りに 無数の 岩を 浮かべて 交代で でてきた 相手の ポケモンに ダメージを 与える。" + "effect": "相手の 周りに 無数の 岩を 浮かべて 交代で でてきた 相手の ポケモンに ダメージを 与える。" }, "grassKnot": { "name": "くさむすび", - "effect": "草を からませて 相手を 転ばせる。相手が 重いほど 威力が あがる。" + "effect": "草を からませて 相手を 転ばせる。相手が 重いほど 威力が あがる。" }, "chatter": { "name": "おしゃべり", - "effect": "とても うるさい おしゃべりの 音波で 相手を 攻撃する。 相手を 混乱させる。" + "effect": "とても うるさい おしゃべりの 音波で 相手を 攻撃する。 相手を 混乱させる。" }, "judgment": { "name": "さばきのつぶて", - "effect": "無数の 光弾を 相手に 放出する。 自分の 持つ プレートに より タイプが 変わる。" + "effect": "無数の 光弾を 相手に 放出する。 自分の 持つ プレートに より タイプが 変わる。" }, "bugBite": { "name": "むしくい", - "effect": "かみついて 攻撃する。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" + "effect": "かみついて 攻撃する。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" }, "chargeBeam": { "name": "チャージビーム", - "effect": "電撃の 束を 相手に 発射する。電気を ためて 自分の 特攻を あげることが ある。" + "effect": "電撃の 束を 相手に 発射する。電気を ためて 自分の 特攻を あげることが ある。" }, "woodHammer": { "name": "ウッドハンマー", - "effect": "硬い 胴体を 相手に たたきつけて 攻撃する。 自分も かなり ダメージを 受ける。" + "effect": "硬い 胴体を 相手に たたきつけて 攻撃する。 自分も かなり ダメージを 受ける。" }, "aquaJet": { "name": "アクアジェット", - "effect": "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" + "effect": "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" }, "attackOrder": { "name": "こうげきしれい", - "effect": "しもべを 呼びだして 相手に むかって 攻撃させる。 急所に 当たりやすい。" + "effect": "しもべを 呼びだして 相手に むかって 攻撃させる。 急所に 当たりやすい。" }, "defendOrder": { "name": "ぼうぎょしれい", - "effect": "しもべを 呼びだして 自分の 体に おおい つかせる。防御と 特防を あげることが できる。" + "effect": "しもべを 呼びだして 自分の 体に おおい つかせる。防御と 特防を あげることが できる。" }, "healOrder": { "name": "かいふくしれい", - "effect": "しもべを 呼びだして きずを 治す。 最大HPの 半分 自分の HPを 回復する。" + "effect": "しもべを 呼びだして きずを 治す。 最大HPの 半分 自分の HPを 回復する。" }, "headSmash": { "name": "もろはのずつき", - "effect": "命を 懸けて こん身の 力で 相手に ずつきを する。 自分も ものすごい ダメージを 受ける。" + "effect": "命を 懸けて こん身の 力で 相手に ずつきを する。 自分も ものすごい ダメージを 受ける。" }, "doubleHit": { "name": "ダブルアタック", - "effect": "しっぽなどを 使い 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" + "effect": "しっぽなどを 使い 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" }, "roarOfTime": { "name": "ときのほうこう", - "effect": "時間が ゆがむほどの 力を うちだして 相手を 攻撃する。 次の ターンは 動けなくなる。" + "effect": "時間が ゆがむほどの 力を うちだして 相手を 攻撃する。 次の ターンは 動けなくなる。" }, "spacialRend": { "name": "あくうせつだん", - "effect": "周りの 空間ごと 相手を 引き裂き ダメージを 与える。 急所に 当たりやすい。" + "effect": "周りの 空間ごと 相手を 引き裂き ダメージを 与える。 急所に 当たりやすい。" }, "lunarDance": { "name": "みかづきのまい", - "effect": "自分は ひんしに なるが 控えから でてくる ポケモンの すべての 状態を 回復する。" + "effect": "自分は ひんしに なるが 控えから でてくる ポケモンの すべての 状態を 回復する。" }, "crushGrip": { "name": "にぎりつぶす", - "effect": "すさまじい 力で 相手を にぎりつぶす。 相手の HPが 残っているほど 威力が あがる。" + "effect": "すさまじい 力で 相手を にぎりつぶす。 相手の HPが 残っているほど 威力が あがる。" }, "magmaStorm": { "name": "マグマストーム", - "effect": "激しく 燃えたぎる 炎の なかに 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + "effect": "激しく 燃えたぎる 炎の なかに 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" }, "darkVoid": { "name": "ダークホール", - "effect": "暗黒の 世界に ひきずり 落として 相手を 眠り状態に する。" + "effect": "暗黒の 世界に ひきずり 落として 相手を 眠り状態に する。" }, "seedFlare": { "name": "シードフレア", - "effect": "体の 中から 衝撃波を 発生させる。相手の 特防を がくっと さげることが ある。" + "effect": "体の 中から 衝撃波を 発生させる。相手の 特防を がくっと さげることが ある。" }, "ominousWind": { "name": "あやしいかぜ", - "effect": "みのけも よだつ 突風で 相手を 攻撃する。自分の すべての 能力が あがることが ある。" + "effect": "みのけも よだつ 突風で 相手を 攻撃する。自分の すべての 能力が あがることが ある。" }, "shadowForce": { "name": "シャドーダイブ", - "effect": "1ターン目で 姿を 消して 2ターン目に 相手を 攻撃する。 守っていても 攻撃は 当たる。" + "effect": "1ターン目で 姿を 消して 2ターン目に 相手を 攻撃する。 守っていても 攻撃は 当たる。" }, "honeClaws": { "name": "つめとぎ", - "effect": "ツメを 磨いて 鋭く する。 自分の 攻撃と 命中率を あげる。" + "effect": "ツメを 磨いて 鋭く する。 自分の 攻撃と 命中率を あげる。" }, "wideGuard": { "name": "ワイドガード", - "effect": "味方全員に 当たる 攻撃を 1ターンの 間 防ぐ。" + "effect": "味方全員に 当たる 攻撃を 1ターンの 間 防ぐ。" }, "guardSplit": { "name": "ガードシェア", - "effect": "超能力で 自分と 相手の 防御と 特防を たして 半分に わける。" + "effect": "超能力で 自分と 相手の 防御と 特防を たして 半分に わける。" }, "powerSplit": { "name": "パワーシェア", - "effect": "超能力で 自分と 相手の 攻撃と 特攻を たして 半分に わける。" + "effect": "超能力で 自分と 相手の 攻撃と 特攻を たして 半分に わける。" }, "wonderRoom": { "name": "ワンダールーム", - "effect": "まか不思議な 空間を つくる。 5ターンのあいだ すべてのポケモンの 防御と 特防が 入れ替わる。" + "effect": "まか不思議な 空間を つくる。 5ターンのあいだ すべてのポケモンの 防御と 特防が 入れ替わる。" }, "psyshock": { "name": "サイコショック", - "effect": "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" + "effect": "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" }, "venoshock": { "name": "ベノムショック", - "effect": "特殊な 毒液を 浴びせかける。 毒状態の 相手には 威力が 2倍に なる。" + "effect": "特殊な 毒液を 浴びせかける。 毒状態の 相手には 威力が 2倍に なる。" }, "autotomize": { "name": "ボディパージ", - "effect": "体の ムダな 部分を 削る。 自分の 素早さを ぐーんと あげて 体重も 軽くなる。" + "effect": "体の ムダな 部分を 削る。 自分の 素早さを ぐーんと あげて 体重も 軽くなる。" }, "ragePowder": { "name": "いかりのこな", - "effect": "イライラさせる 粉を 自分に ふりかけて 注意を ひく。 相手の 攻撃を すべて 自分に むける。" + "effect": "イライラさせる 粉を 自分に ふりかけて 注意を ひく。 相手の 攻撃を すべて 自分に むける。" }, "telekinesis": { "name": "テレキネシス", - "effect": "超能力で 相手を 浮かせる。 3ターンの 間 攻撃が 相手に 当たりやすく なる。" + "effect": "超能力で 相手を 浮かせる。 3ターンの 間 攻撃が 相手に 当たりやすく なる。" }, "magicRoom": { "name": "マジックルーム", - "effect": "まか不思議な 空間を つくる。 5ターンの間 すべてのポケモンの 道具の 効果が なくなる。" + "effect": "まか不思議な 空間を つくる。 5ターンの間 すべてのポケモンの 道具の 効果が なくなる。" }, "smackDown": { "name": "うちおとす", - "effect": "石や 弾を 投げて 飛んでいる 相手を 攻撃する。 相手は うち落とされて 地面に 落ちる。" + "effect": "石や 弾を 投げて 飛んでいる 相手を 攻撃する。 相手は うち落とされて 地面に 落ちる。" }, "stormThrow": { "name": "やまあらし", - "effect": "強烈な 一撃を 相手に くりだす。攻撃は 必ず 急所に 当たる。" + "effect": "強烈な 一撃を 相手に くりだす。攻撃は 必ず 急所に 当たる。" }, "flameBurst": { "name": "はじけるほのお", - "effect": "当たると はじける 炎で 相手を 攻撃する。はじけた 炎は 隣の 相手にも ふりかかる。" + "effect": "当たると はじける 炎で 相手を 攻撃する。はじけた 炎は 隣の 相手にも ふりかかる。" }, "sludgeWave": { "name": "ヘドロウェーブ", - "effect": "ヘドロの 波で 自分の 周りに いるものを 攻撃する。 毒状態に することが ある。" + "effect": "ヘドロの 波で 自分の 周りに いるものを 攻撃する。 毒状態に することが ある。" }, "quiverDance": { "name": "ちょうのまい", - "effect": "神秘的で 美しい 舞を 軽やかに おどる。 自分の 特攻と 特防と 素早さを あげる。" + "effect": "神秘的で 美しい 舞を 軽やかに おどる。 自分の 特攻と 特防と 素早さを あげる。" }, "heavySlam": { "name": "ヘビーボンバー", - "effect": "重たい 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" + "effect": "重たい 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" }, "synchronoise": { "name": "シンクロノイズ", - "effect": "不思議な 電波で 周りに いる 自分と 同じ タイプの ポケモンに ダメージを 与える。" + "effect": "不思議な 電波で 周りに いる 自分と 同じ タイプの ポケモンに ダメージを 与える。" }, "electroBall": { "name": "エレキボール", - "effect": "電気の 塊を 相手に ぶつける。相手より 素早さが 速いほど 威力が あがる。" + "effect": "電気の 塊を 相手に ぶつける。相手より 素早さが 速いほど 威力が あがる。" }, "soak": { "name": "みずびたし", - "effect": "たくさんの 水を 浴びせかけて 相手を みずタイプに する。" + "effect": "たくさんの 水を 浴びせかけて 相手を みずタイプに する。" }, "flameCharge": { "name": "ニトロチャージ", - "effect": "炎を まとい 相手を 攻撃する。 力を ためて 自分の 素早さを あげる。" + "effect": "炎を まとい 相手を 攻撃する。 力を ためて 自分の 素早さを あげる。" }, "coil": { "name": "とぐろをまく", - "effect": "とぐろを まいて 集中する。 自分の 攻撃と 防御と 命中率を あげる。" + "effect": "とぐろを まいて 集中する。 自分の 攻撃と 防御と 命中率を あげる。" }, "lowSweep": { "name": "ローキック", - "effect": "素早い 動きで 相手の 足を ねらって 攻撃する。 相手の 素早さを さげる。" + "effect": "素早い 動きで 相手の 足を ねらって 攻撃する。 相手の 素早さを さげる。" }, "acidSpray": { "name": "アシッドボム", - "effect": "相手を とかす 液体を 吐きだして 攻撃する。 相手の 特防を がくっと さげる。" + "effect": "相手を とかす 液体を 吐きだして 攻撃する。 相手の 特防を がくっと さげる。" }, "foulPlay": { "name": "イカサマ", - "effect": "相手の 力を 利用する。 戦っている 相手の 攻撃が 高いほど ダメージが あがる。" + "effect": "相手の 力を 利用する。 戦っている 相手の 攻撃が 高いほど ダメージが あがる。" }, "simpleBeam": { "name": "シンプルビーム", - "effect": "なぞの 念波を 相手に 送る。 念波を 受けとった 相手は 特性が たんじゅんに なる。" + "effect": "なぞの 念波を 相手に 送る。 念波を 受けとった 相手は 特性が たんじゅんに なる。" }, "entrainment": { "name": "なかまづくり", - "effect": "不思議な リズムで おどる。 動きを まねさせて 自分と 相手の 特性を 同じに する。" + "effect": "不思議な リズムで おどる。 動きを まねさせて 自分と 相手の 特性を 同じに する。" }, "afterYou": { "name": "おさきにどうぞ", - "effect": "相手の 行動を サポートして 自分の 行動の あとに 続けて 動けるように する。" + "effect": "相手の 行動を サポートして 自分の 行動の あとに 続けて 動けるように する。" }, "round": { "name": "りんしょう", - "effect": "歌で 相手を 攻撃する。 みんなで 輪唱すると 続けて だすことが でき 威力も あがる。" + "effect": "歌で 相手を 攻撃する。 みんなで 輪唱すると 続けて だすことが でき 威力も あがる。" }, "echoedVoice": { "name": "エコーボイス", - "effect": "響く 声で 相手を 攻撃する。 毎ターン だれかが 技を 使い続けると 威力が あがる。" + "effect": "響く 声で 相手を 攻撃する。 毎ターン だれかが 技を 使い続けると 威力が あがる。" }, "chipAway": { "name": "なしくずし", - "effect": "すきを みて 堅実に 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" + "effect": "すきを みて 堅実に 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" }, "clearSmog": { "name": "クリアスモッグ", - "effect": "特殊な 泥の 塊を 相手に 投げつけて 攻撃する。 能力変化を もとに もどす。" + "effect": "特殊な 泥の 塊を 相手に 投げつけて 攻撃する。 能力変化を もとに もどす。" }, "storedPower": { "name": "アシストパワー", - "effect": "蓄積された パワーで 相手を 攻撃する。自分の 能力が あがっているほど 威力が あがる。" + "effect": "蓄積された パワーで 相手を 攻撃する。自分の 能力が あがっているほど 威力が あがる。" }, "quickGuard": { "name": "ファストガード", - "effect": "自分と 味方を 相手の 先制攻撃から 守る。" + "effect": "自分と 味方を 相手の 先制攻撃から 守る。" }, "allySwitch": { "name": "サイドチェンジ", - "effect": "不思議な 力で テレポートして 自分と 味方の 居場所を 入れ替える。" + "effect": "不思議な 力で テレポートして 自分と 味方の 居場所を 入れ替える。" }, "scald": { "name": "ねっとう", - "effect": "熱く 煮えたぎる 水を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + "effect": "熱く 煮えたぎる 水を 相手に 発射して 攻撃する。 やけど状態に することが ある。" }, "shellSmash": { "name": "からをやぶる", - "effect": "殻を やぶって 自分の 防御 特防を さげるが 攻撃 特攻 素早さを ぐーんと あげる。" + "effect": "殻を やぶって 自分の 防御 特防を さげるが 攻撃 特攻 素早さを ぐーんと あげる。" }, "healPulse": { "name": "いやしのはどう", - "effect": "いやしのはどうを とばして 最大HPの 半分 相手の HPを 回復する。" + "effect": "いやしのはどうを とばして 最大HPの 半分 相手の HPを 回復する。" }, "hex": { "name": "たたりめ", - "effect": "たたみかける ように 攻撃する。 状態異常の 相手に 大きな ダメージを 与える。" + "effect": "たたみかける ように 攻撃する。 状態異常の 相手に 大きな ダメージを 与える。" }, "skyDrop": { "name": "フリーフォール", - "effect": "1ターン目で 相手を 空へ 連れさり 2ターン目に 落として 攻撃する。 連れさられた 相手は 動けない。" + "effect": "1ターン目で 相手を 空へ 連れさり 2ターン目に 落として 攻撃する。 連れさられた 相手は 動けない。" }, "shiftGear": { "name": "ギアチェンジ", - "effect": "歯車を 回して 自分の 攻撃を あげる だけでなく 素早さも ぐーんと あげる。" + "effect": "歯車を 回して 自分の 攻撃を あげる だけでなく 素早さも ぐーんと あげる。" }, "circleThrow": { "name": "ともえなげ", - "effect": "相手を 投げとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + "effect": "相手を 投げとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" }, "incinerate": { "name": "やきつくす", - "effect": "炎で 相手を 攻撃する。 相手が きのみなどを 持っているとき 燃やして 使えなくする。" + "effect": "炎で 相手を 攻撃する。 相手が きのみなどを 持っているとき 燃やして 使えなくする。" }, "quash": { "name": "さきおくり", - "effect": "相手を おさえつけて 行動の 順番を 最後に する。" + "effect": "相手を おさえつけて 行動の 順番を 最後に する。" }, "acrobatics": { "name": "アクロバット", - "effect": "軽やかに 相手を 攻撃する。 自分が 道具を 持っていないとき 大きな ダメージを 与える。" + "effect": "軽やかに 相手を 攻撃する。 自分が 道具を 持っていないとき 大きな ダメージを 与える。" }, "reflectType": { "name": "ミラータイプ", - "effect": "相手の タイプを 反射して 自分も 同じ タイプに なる。" + "effect": "相手の タイプを 反射して 自分も 同じ タイプに なる。" }, "retaliate": { "name": "かたきうち", - "effect": "倒れた 味方の かたきを 討つ。 前の ターンに 味方が 倒されていると 威力が あがる。" + "effect": "倒れた 味方の かたきを 討つ。 前の ターンに 味方が 倒されていると 威力が あがる。" }, "finalGambit": { "name": "いのちがけ", - "effect": "命懸けで 相手を 攻撃する。 自分は ひんしに なるが 相手に HP分の ダメージを 与える。" + "effect": "命懸けで 相手を 攻撃する。 自分は ひんしに なるが 相手に HP分の ダメージを 与える。" }, "bestow": { "name": "ギフトパス", - "effect": "相手が 道具を 持っていないとき 自分が 持っている 道具を 相手に わたす。" + "effect": "相手が 道具を 持っていないとき 自分が 持っている 道具を 相手に わたす。" }, "inferno": { "name": "れんごく", - "effect": "激しい 炎で 相手を 包みこみ 攻撃する。 やけど状態に する。" + "effect": "激しい 炎で 相手を 包みこみ 攻撃する。 やけど状態に する。" }, "waterPledge": { "name": "みずのちかい", - "effect": "水の柱で 攻撃する。 ほのおと 組みあわせると 威力が あがって 空に にじが かかる。" + "effect": "水の柱で 攻撃する。 ほのおと 組みあわせると 威力が あがって 空に にじが かかる。" }, "firePledge": { "name": "ほのおのちかい", - "effect": "炎の柱で 攻撃する。 くさと 組みあわせると 威力が あがって 周りが 火の海に なる。" + "effect": "炎の柱で 攻撃する。 くさと 組みあわせると 威力が あがって 周りが 火の海に なる。" }, "grassPledge": { "name": "くさのちかい", - "effect": "草の柱で 攻撃する。 みずと 組みあわせると 威力が あがって あたりが 湿原に なる。" + "effect": "草の柱で 攻撃する。 みずと 組みあわせると 威力が あがって あたりが 湿原に なる。" }, "voltSwitch": { "name": "ボルトチェンジ", - "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" }, "struggleBug": { "name": "むしのていこう", - "effect": "抵抗して 相手を 攻撃する。 相手の 特攻を さげる。" + "effect": "抵抗して 相手を 攻撃する。 相手の 特攻を さげる。" }, "bulldoze": { "name": "じならし", - "effect": "地面を 踏みならして 自分の 周りに いるものを 攻撃する。 相手の 素早さを さげる。" + "effect": "地面を 踏みならして 自分の 周りに いるものを 攻撃する。 相手の 素早さを さげる。" }, "frostBreath": { "name": "こおりのいぶき", - "effect": "冷たい 息を 相手に 吹きつけて 攻撃する。 必ず 急所に 当たる。" + "effect": "冷たい 息を 相手に 吹きつけて 攻撃する。 必ず 急所に 当たる。" }, "dragonTail": { "name": "ドラゴンテール", - "effect": "相手を はじきとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + "effect": "相手を はじきとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" }, "workUp": { "name": "ふるいたてる", - "effect": "自分を 奮いたてて 攻撃と 特攻を あげる。" + "effect": "自分を 奮いたてて 攻撃と 特攻を あげる。" }, "electroweb": { "name": "エレキネット", - "effect": "電気の ネットで 相手を 捕まえて 攻撃する。 相手の 素早さを さげる。" + "effect": "電気の ネットで 相手を 捕まえて 攻撃する。 相手の 素早さを さげる。" }, "wildCharge": { "name": "ワイルドボルト", - "effect": "電気を まとって 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" + "effect": "電気を まとって 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" }, "drillRun": { "name": "ドリルライナー", - "effect": "ドリルのように 体を 回転しながら 相手に 体当たりする。 急所に 当たりやすい。" + "effect": "ドリルのように 体を 回転しながら 相手に 体当たりする。 急所に 当たりやすい。" }, "dualChop": { "name": "ダブルチョップ", - "effect": "体の 硬い部分で 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" + "effect": "体の 硬い部分で 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" }, "heartStamp": { "name": "ハートスタンプ", - "effect": "かわいい しぐさで 油断させて 強烈な 一撃を 浴びせる。 相手を ひるませることが ある。" + "effect": "かわいい しぐさで 油断させて 強烈な 一撃を 浴びせる。 相手を ひるませることが ある。" }, "hornLeech": { "name": "ウッドホーン", - "effect": "つのを 突き刺して 相手の 養分を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" + "effect": "つのを 突き刺して 相手の 養分を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" }, "sacredSword": { "name": "せいなるつるぎ", - "effect": "長い つので 切りつけ 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" + "effect": "長い つので 切りつけ 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" }, "razorShell": { "name": "シェルブレード", - "effect": "鋭い 貝殻で 切りつけて 攻撃する。 相手の 防御を さげることが ある。" + "effect": "鋭い 貝殻で 切りつけて 攻撃する。 相手の 防御を さげることが ある。" }, "heatCrash": { "name": "ヒートスタンプ", - "effect": "燃える 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" + "effect": "燃える 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" }, "leafTornado": { "name": "グラスミキサー", - "effect": "鋭い はっぱで 相手を 包みこんで 攻撃する。 命中率を さげることが ある。" + "effect": "鋭い はっぱで 相手を 包みこんで 攻撃する。 命中率を さげることが ある。" }, "steamroller": { "name": "ハードローラー", - "effect": "まるめた 体で 回転して 相手を おしつぶす。 相手を ひるませることが ある。" + "effect": "まるめた 体で 回転して 相手を おしつぶす。 相手を ひるませることが ある。" }, "cottonGuard": { "name": "コットンガード", - "effect": "フワフワの 綿毛で 自分の 体を 包みこんで 守る。 防御を ぐぐーんと あげる。" + "effect": "フワフワの 綿毛で 自分の 体を 包みこんで 守る。 防御を ぐぐーんと あげる。" }, "nightDaze": { "name": "ナイトバースト", - "effect": "暗黒の 衝撃波を とばして 相手を 攻撃する。 命中率を さげることが ある。" + "effect": "暗黒の 衝撃波を とばして 相手を 攻撃する。 命中率を さげることが ある。" }, "psystrike": { "name": "サイコブレイク", - "effect": "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" + "effect": "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" }, "tailSlap": { "name": "スイープビンタ", - "effect": "硬い しっぽで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" + "effect": "硬い しっぽで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" }, "hurricane": { "name": "ぼうふう", - "effect": "強烈な 風で 相手を 包みこんで 攻撃する。 相手を 混乱させることが ある。" + "effect": "強烈な 風で 相手を 包みこんで 攻撃する。 相手を 混乱させることが ある。" }, "headCharge": { "name": "アフロブレイク", - "effect": "すごい アフロの 頭で 相手に 突進して 攻撃する。 自分も 少し ダメージを 受ける。" + "effect": "すごい アフロの 頭で 相手に 突進して 攻撃する。 自分も 少し ダメージを 受ける。" }, "gearGrind": { "name": "ギアソーサー", - "effect": "鋼鉄の ギアを 相手に 投げつけて 攻撃する。 2回連続で ダメージを 与える。" + "effect": "鋼鉄の ギアを 相手に 投げつけて 攻撃する。 2回連続で ダメージを 与える。" }, "searingShot": { "name": "かえんだん", - "effect": "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" + "effect": "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" }, "technoBlast": { "name": "テクノバスター", - "effect": "光弾を 相手に 放出する。 自分の 持つ カセットにより タイプが 変わる。" + "effect": "光弾を 相手に 放出する。 自分の 持つ カセットにより タイプが 変わる。" }, "relicSong": { "name": "いにしえのうた", - "effect": "いにしえのうたを 相手に 聞かせて 心に うったえて 攻撃する。 眠り状態に することが ある。" + "effect": "いにしえのうたを 相手に 聞かせて 心に うったえて 攻撃する。 眠り状態に することが ある。" }, "secretSword": { "name": "しんぴのつるぎ", - "effect": "長い つので 切りつけ 攻撃する。 つのが まとった 不思議な 力は 物理的な ダメージを 与える。" + "effect": "長い つので 切りつけ 攻撃する。 つのが まとった 不思議な 力は 物理的な ダメージを 与える。" }, "glaciate": { "name": "こごえるせかい", - "effect": "凍えるような 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" + "effect": "凍えるような 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" }, "boltStrike": { "name": "らいげき", - "effect": "ぼうだいな 電気を 身に まとって 相手に 突進して 攻撃する。 まひ状態に することが ある。" + "effect": "ぼうだいな 電気を 身に まとって 相手に 突進して 攻撃する。 まひ状態に することが ある。" }, "blueFlare": { "name": "あおいほのお", - "effect": "美しくも 激しい 青い炎で 相手を 包みこんで 攻撃する。 やけど状態に することが ある。" + "effect": "美しくも 激しい 青い炎で 相手を 包みこんで 攻撃する。 やけど状態に することが ある。" }, "fieryDance": { "name": "ほのおのまい", - "effect": "炎を まとい はばたいて 相手を 攻撃する。自分の 特攻が あがることが ある。" + "effect": "炎を まとい はばたいて 相手を 攻撃する。自分の 特攻が あがることが ある。" }, "freezeShock": { "name": "フリーズボルト", - "effect": "電気を まとった 氷の 塊で 2ターン目に 相手を たたきつける。 まひ状態に することが ある。" + "effect": "電気を まとった 氷の 塊で 2ターン目に 相手を たたきつける。 まひ状態に することが ある。" }, "iceBurn": { "name": "コールドフレア", - "effect": "すべてを 凍らせる 激しい 冷気で 2ターン目に 相手を 包みこむ。 やけど状態に することが ある。" + "effect": "すべてを 凍らせる 激しい 冷気で 2ターン目に 相手を 包みこむ。 やけど状態に することが ある。" }, "snarl": { "name": "バークアウト", - "effect": "まくしたてる ように 怒鳴りつけて 相手の 特攻を さげる。" + "effect": "まくしたてる ように 怒鳴りつけて 相手の 特攻を さげる。" }, "icicleCrash": { "name": "つららおとし", - "effect": "大きな 氷柱を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" + "effect": "大きな 氷柱を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" }, "vCreate": { "name": "Vジェネレート", - "effect": "灼熱の 炎を 額から 発生させて 捨て身の 体当たり。 防御 特防 素早さが さがる。" + "effect": "灼熱の 炎を 額から 発生させて 捨て身の 体当たり。 防御 特防 素早さが さがる。" }, "fusionFlare": { "name": "クロスフレイム", - "effect": "巨大な 炎を たたきつける。 巨大な 雷の 影響を受け 技の 威力が あがる。" + "effect": "巨大な 炎を たたきつける。 巨大な 雷の 影響を受け 技の 威力が あがる。" }, "fusionBolt": { "name": "クロスサンダー", - "effect": "巨大な 雷を たたきつける。 巨大な 炎の 影響を受け 技の 威力が あがる。" + "effect": "巨大な 雷を たたきつける。 巨大な 炎の 影響を受け 技の 威力が あがる。" }, "flyingPress": { "name": "フライングプレス", - "effect": "空中から 相手に ダイブする。 この技は かくとうタイプと 同時に ひこうタイプでも ある。" + "effect": "空中から 相手に ダイブする。 この技は かくとうタイプと 同時に ひこうタイプでも ある。" }, "matBlock": { "name": "たたみがえし", - "effect": "かえした タタミを 盾にして 自分や 味方への 技の ダメージを 防ぐ。 変化技は 防ぐことが できない。" + "effect": "かえした タタミを 盾にして 自分や 味方への 技の ダメージを 防ぐ。 変化技は 防ぐことが できない。" }, "belch": { "name": "ゲップ", - "effect": "相手に 向かって ゲップを 浴びせて ダメージを 与える。 きのみを 食べないと だせない。" + "effect": "相手に 向かって ゲップを 浴びせて ダメージを 与える。 きのみを 食べないと だせない。" }, "rototiller": { "name": "たがやす", - "effect": "地面を 耕して 草木が 育ちやすいようにする。 くさタイプの 攻撃と 特攻が あがる。" + "effect": "地面を 耕して 草木が 育ちやすいようにする。 くさタイプの 攻撃と 特攻が あがる。" }, "stickyWeb": { "name": "ねばねばネット", - "effect": "相手の 周りに ねばねばした ネットを はりめぐらせ 交代で でてきた 相手の 素早さを さげる。" + "effect": "相手の 周りに ねばねばした ネットを はりめぐらせ 交代で でてきた 相手の 素早さを さげる。" }, "fellStinger": { "name": "とどめばり", - "effect": "この 技を 使って 相手を 倒すと 攻撃が ぐぐーんと あがる。" + "effect": "この 技を 使って 相手を 倒すと 攻撃が ぐぐーんと あがる。" }, "phantomForce": { "name": "ゴーストダイブ", - "effect": "1ターンめで どこかに 消えて 2ターンめに 相手を 攻撃する。 守りを 無視して 攻撃できる。" + "effect": "1ターンめで どこかに 消えて 2ターンめに 相手を 攻撃する。 守りを 無視して 攻撃できる。" }, "trickOrTreat": { "name": "ハロウィン", - "effect": "相手を ハロウィンに 誘う。 相手の タイプに ゴーストタイプが 追加される。" + "effect": "相手を ハロウィンに 誘う。 相手の タイプに ゴーストタイプが 追加される。" }, "nobleRoar": { "name": "おたけび", - "effect": "おたけびを あげて 相手を 威嚇し 相手の 攻撃と 特攻を さげる。" + "effect": "おたけびを あげて 相手を 威嚇し 相手の 攻撃と 特攻を さげる。" }, "ionDeluge": { "name": "プラズマシャワー", - "effect": "電気を 帯びた 粒子を 拡散し ノーマルタイプの 技を でんきタイプに してしまう。" + "effect": "電気を 帯びた 粒子を 拡散し ノーマルタイプの 技を でんきタイプに してしまう。" }, "parabolicCharge": { "name": "パラボラチャージ", - "effect": "周りにいる ポケモン 全員に ダメージ。 与えた ダメージの 半分を 自分が 回復する。" + "effect": "周りにいる ポケモン 全員に ダメージ。 与えた ダメージの 半分を 自分が 回復する。" }, "forestsCurse": { "name": "もりののろい", - "effect": "相手に 森ののろいを かける。 のろいを かけられた 相手は タイプに くさタイプが 追加される。" + "effect": "相手に 森ののろいを かける。 のろいを かけられた 相手は タイプに くさタイプが 追加される。" }, "petalBlizzard": { "name": "はなふぶき", - "effect": "激しい 花吹雪を 起こし 周りに いるものに 攻撃して ダメージを 与える。" + "effect": "激しい 花吹雪を 起こし 周りに いるものに 攻撃して ダメージを 与える。" }, "freezeDry": { "name": "フリーズドライ", - "effect": "相手を 急激に 冷やして こおり 状態に することが ある。 みずタイプにも 効果バツグンになる。" + "effect": "相手を 急激に 冷やして こおり 状態に することが ある。 みずタイプにも 効果バツグンになる。" }, "disarmingVoice": { "name": "チャームボイス", - "effect": "魅惑の 鳴き声を だして 相手に 精神的な ダメージを 与える。 攻撃は 必ず 命中 する。" + "effect": "魅惑の 鳴き声を だして 相手に 精神的な ダメージを 与える。 攻撃は 必ず 命中 する。" }, "partingShot": { "name": "すてゼリフ", - "effect": "すてゼリフで 相手を いかくし 攻撃と 特攻を さげたのち 控えの ポケモンと 入れ替わる。" + "effect": "すてゼリフで 相手を いかくし 攻撃と 特攻を さげたのち 控えの ポケモンと 入れ替わる。" }, "topsyTurvy": { "name": "ひっくりかえす", - "effect": "相手に かかっている すべての 能力変化を ひっくり返して 逆にする。" + "effect": "相手に かかっている すべての 能力変化を ひっくり返して 逆にする。" }, "drainingKiss": { "name": "ドレインキッス", - "effect": "キッスによって 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" + "effect": "キッスによって 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" }, "craftyShield": { "name": "トリックガード", - "effect": "不思議な 力を 使って 味方への 変化技を 防ぐ。 ダメージ技は 受けてしまう。" + "effect": "不思議な 力を 使って 味方への 変化技を 防ぐ。 ダメージ技は 受けてしまう。" }, "flowerShield": { "name": "フラワーガード", - "effect": "不思議な 力を 使って 場にいる くさタイプの ポケモン 全員の 防御を あげる。" + "effect": "不思議な 力を 使って 場にいる くさタイプの ポケモン 全員の 防御を あげる。" }, "grassyTerrain": { "name": "グラスフィールド", - "effect": "5ターンの 間 グラスフィールドにする。 地面にいると 毎ターン 回復する。 くさタイプの 威力が あがる。" + "effect": "5ターンの 間 グラスフィールドにする。 地面にいると 毎ターン 回復する。 くさタイプの 威力が あがる。" }, "mistyTerrain": { "name": "ミストフィールド", - "effect": "5ターンの 間 地面にいると 状態異常に ならず ドラゴン技の ダメージも 半分になる。" + "effect": "5ターンの 間 地面にいると 状態異常に ならず ドラゴン技の ダメージも 半分になる。" }, "electrify": { "name": "そうでん", - "effect": "相手が 技を だす前に そうでん すると そのターン 相手の 技は でんきタイプになる。" + "effect": "相手が 技を だす前に そうでん すると そのターン 相手の 技は でんきタイプになる。" }, "playRough": { "name": "じゃれつく", - "effect": "相手に じゃれついて 攻撃する。 相手の 攻撃を さげる ことがある。" + "effect": "相手に じゃれついて 攻撃する。 相手の 攻撃を さげる ことがある。" }, "fairyWind": { "name": "ようせいのかぜ", - "effect": "ようせいのかぜを 起こし 相手に 吹きつけて 攻撃する。" + "effect": "ようせいのかぜを 起こし 相手に 吹きつけて 攻撃する。" }, "moonblast": { "name": "ムーンフォース", - "effect": "月の パワーを かりて 相手を 攻撃する。 相手の 特攻を さげる ことがある。" + "effect": "月の パワーを かりて 相手を 攻撃する。 相手の 特攻を さげる ことがある。" }, "boomburst": { "name": "ばくおんぱ", - "effect": "すさまじい 爆音の 破壊力に よって 周りに いるものを 攻撃する。" + "effect": "すさまじい 爆音の 破壊力に よって 周りに いるものを 攻撃する。" }, "fairyLock": { "name": "フェアリーロック", - "effect": "ロックを かけることによって 次のターン すべての ポケモンを 逃げられなくする。" + "effect": "ロックを かけることによって 次のターン すべての ポケモンを 逃げられなくする。" }, "kingsShield": { "name": "キングシールド", - "effect": "相手の 攻撃を 防ぐと 同時に 防御態勢になる。 触れた 相手の 攻撃を さげる。" + "effect": "相手の 攻撃を 防ぐと 同時に 防御態勢になる。 触れた 相手の 攻撃を さげる。" }, "playNice": { "name": "なかよくする", - "effect": "相手と なかよくなって 戦う 気力を 失わせ 相手の 攻撃を さげる。" + "effect": "相手と なかよくなって 戦う 気力を 失わせ 相手の 攻撃を さげる。" }, "confide": { "name": "ないしょばなし", - "effect": "ないしょばなしを することで 相手の 集中力を 失わせ 相手の 特攻を さげる。" + "effect": "ないしょばなしを することで 相手の 集中力を 失わせ 相手の 特攻を さげる。" }, "diamondStorm": { "name": "ダイヤストーム", - "effect": "ダイヤの 嵐を 巻き起こし ダメージを 与える。 自分の 防御を ぐーんと あげることが ある。" + "effect": "ダイヤの 嵐を 巻き起こし ダメージを 与える。 自分の 防御を ぐーんと あげることが ある。" }, "steamEruption": { "name": "スチームバースト", - "effect": "ものすごく 熱い 蒸気を 相手に 浴びせる。 相手は やけどする ことがある。" + "effect": "ものすごく 熱い 蒸気を 相手に 浴びせる。 相手は やけどする ことがある。" }, "hyperspaceHole": { "name": "いじげんホール", - "effect": "異次元ホールで 突然 相手の 真横に 現れ 攻撃する。 まもるや みきり なども 無視 できる。" + "effect": "異次元ホールで 突然 相手の 真横に 現れ 攻撃する。 まもるや みきり なども 無視 できる。" }, "waterShuriken": { "name": "みずしゅりけん", - "effect": "粘液で できた 手裏剣を 2ー5回の 間 連続で だす。 必ず 先制攻撃 できる。" + "effect": "粘液で できた 手裏剣を 2ー5回の 間 連続で だす。 必ず 先制攻撃 できる。" }, "mysticalFire": { "name": "マジカルフレイム", - "effect": "口から 吐きだす 特別 熱い 炎で 攻撃する。 相手の 特攻を さげる。" + "effect": "口から 吐きだす 特別 熱い 炎で 攻撃する。 相手の 特攻を さげる。" }, "spikyShield": { "name": "ニードルガード", - "effect": "相手の 攻撃を 防ぐと 同時に 触れた 相手の 体力を 削って しまう。" + "effect": "相手の 攻撃を 防ぐと 同時に 触れた 相手の 体力を 削って しまう。" }, "aromaticMist": { "name": "アロマミスト", - "effect": "不思議な アロマの 香りによって 味方の 特防を あげる。" + "effect": "不思議な アロマの 香りによって 味方の 特防を あげる。" }, "eerieImpulse": { "name": "かいでんぱ", - "effect": "体から かいでんぱを 放ち 相手に 浴びせる ことによって 特攻を がくっと さげる。" + "effect": "体から かいでんぱを 放ち 相手に 浴びせる ことによって 特攻を がくっと さげる。" }, "venomDrench": { "name": "ベノムトラップ", - "effect": "特殊な 毒液を 浴びせかける。 毒状態の 相手は 攻撃 特攻 素早さが さがる。" + "effect": "特殊な 毒液を 浴びせかける。 毒状態の 相手は 攻撃 特攻 素早さが さがる。" }, "powder": { "name": "ふんじん", - "effect": "ふんじんを 浴びせた 相手が ほのお技を 使うと 爆発して ダメージを 与える。" + "effect": "ふんじんを 浴びせた 相手が ほのお技を 使うと 爆発して ダメージを 与える。" }, "geomancy": { "name": "ジオコントロール", - "effect": "1ターン目で エネルギーを 吸収し 2ターン目に 特攻 特防 素早さを ぐーんと あげる。" + "effect": "1ターン目で エネルギーを 吸収し 2ターン目に 特攻 特防 素早さを ぐーんと あげる。" }, "magneticFlux": { "name": "じばそうさ", - "effect": "磁場を 操作 することによって 特性 プラスと マイナスの 防御 特防が あがる。" + "effect": "磁場を 操作 することによって 特性 プラスと マイナスの 防御 特防が あがる。" }, "happyHour": { "name": "ハッピータイム", - "effect": "ハッピータイムの 技を 使うと 戦闘の あとで もらえる お金が 倍になる。" + "effect": "ハッピータイムの 技を 使うと 戦闘の あとで もらえる お金が 倍になる。" }, "electricTerrain": { "name": "エレキフィールド", - "effect": "5ターンの 間 エレキフィールドにする。 地面にいる ポケモンは 眠らない。 でんきタイプの 威力が あがる。" + "effect": "5ターンの 間 エレキフィールドにする。 地面にいる ポケモンは 眠らない。 でんきタイプの 威力が あがる。" }, "dazzlingGleam": { "name": "マジカルシャイン", - "effect": "強力な 光を 放ち 相手に ダメージを 与える。" + "effect": "強力な 光を 放ち 相手に ダメージを 与える。" }, "celebrate": { "name": "おいわい", - "effect": "ポケモンが とっても ハッピーな あなたのことを お祝い してくれる。" + "effect": "ポケモンが とっても ハッピーな あなたのことを お祝い してくれる。" }, "holdHands": { "name": "てをつなぐ", - "effect": "味方の ポケモン 同士が 手をつなぐ。 とっても 幸せな 気持ちに なれる。" + "effect": "味方の ポケモン 同士が 手をつなぐ。 とっても 幸せな 気持ちに なれる。" }, "babyDollEyes": { "name": "つぶらなひとみ", - "effect": "つぶらなひとみで 相手を みつめて 攻撃を さげる。 必ず 先制攻撃 できる。" + "effect": "つぶらなひとみで 相手を みつめて 攻撃を さげる。 必ず 先制攻撃 できる。" }, "nuzzle": { "name": "ほっぺすりすり", - "effect": "電気を 帯びた ほっぺを すりつけて 攻撃。 相手を まひ状態に する。" + "effect": "電気を 帯びた ほっぺを すりつけて 攻撃。 相手を まひ状態に する。" }, "holdBack": { "name": "てかげん", - "effect": "手加減 した 攻撃で 相手の HPを 必ず 1だけ 残す。" + "effect": "手加減 した 攻撃で 相手の HPを 必ず 1だけ 残す。" }, "infestation": { "name": "まとわりつく", - "effect": "4ー5ターンの 間 相手に まとわりついて 攻撃する。 そのあいだ 相手は 逃げられない。" + "effect": "4ー5ターンの 間 相手に まとわりついて 攻撃する。 そのあいだ 相手は 逃げられない。" }, "powerUpPunch": { "name": "グロウパンチ", - "effect": "繰り返し 打つことで だんだん こぶしが 固くなる。 相手に 当てると 攻撃が あがる。" + "effect": "繰り返し 打つことで だんだん こぶしが 固くなる。 相手に 当てると 攻撃が あがる。" }, "oblivionWing": { "name": "デスウイング", - "effect": "ねらいを 定めた 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" + "effect": "ねらいを 定めた 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" }, "thousandArrows": { "name": "サウザンアロー", - "effect": "浮いている ポケモンにも 当たる。 浮いていた 相手は 撃ち落とされて 地面に 落ちる。" + "effect": "浮いている ポケモンにも 当たる。 浮いていた 相手は 撃ち落とされて 地面に 落ちる。" }, "thousandWaves": { "name": "サウザンウェーブ", - "effect": "地をはう 波によって 攻撃。 波に 巻き込まれた 相手は 戦闘から 逃げられなくなる。" + "effect": "地をはう 波によって 攻撃。 波に 巻き込まれた 相手は 戦闘から 逃げられなくなる。" }, "landsWrath": { "name": "グランドフォース", - "effect": "大地の パワーを 集め 力を 相手に 集中させて ダメージを 与える。" + "effect": "大地の パワーを 集め 力を 相手に 集中させて ダメージを 与える。" }, "lightOfRuin": { "name": "はめつのひかり", - "effect": "永遠の花 の パワーを かりて 強力な 光線を 撃ちだす。 自分も かなりの ダメージを 受ける。" + "effect": "永遠の花 の パワーを かりて 強力な 光線を 撃ちだす。 自分も かなりの ダメージを 受ける。" }, "originPulse": { "name": "こんげんのはどう", - "effect": "青白く 輝く 無数の 光線で 相手を 攻撃する。" + "effect": "青白く 輝く 無数の 光線で 相手を 攻撃する。" }, "precipiceBlades": { "name": "だんがいのつるぎ", - "effect": "大地の 力を 刃に 変えて 相手を 攻撃する。" + "effect": "大地の 力を 刃に 変えて 相手を 攻撃する。" }, "dragonAscent": { "name": "ガリョウテンセイ", - "effect": "大空から 急速落下 して 相手を 攻撃する。 自分の 防御と 特防が さがる。" + "effect": "大空から 急速落下 して 相手を 攻撃する。 自分の 防御と 特防が さがる。" }, "hyperspaceFury": { "name": "いじげんラッシュ", - "effect": "たくさんの 腕で まもるや みきり などを 無視した 連続攻撃。 自分の 防御が さがる。" + "effect": "たくさんの 腕で まもるや みきり などを 無視した 連続攻撃。 自分の 防御が さがる。" }, "breakneckBlitzPhysical": { "name": "ウルトラダッシュアタック", - "effect": "Zパワーで 勢いを つけて 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 勢いを つけて 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" }, "breakneckBlitzSpecial": { "name": "ウルトラダッシュアタック", @@ -2493,7 +2493,7 @@ }, "allOutPummelingPhysical": { "name": "ぜんりょくむそうげきれつけん", - "effect": "Zパワーで 作った エネルギーの 弾を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 作った エネルギーの 弾を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" }, "allOutPummelingSpecial": { "name": "ぜんりょくむそうげきれつけん", @@ -2501,7 +2501,7 @@ }, "supersonicSkystrikePhysical": { "name": "ファイナルダイブクラッシュ", - "effect": "Zパワーで 勢いよく 飛びあがり 相手に 向かって 全力で 落下。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 勢いよく 飛びあがり 相手に 向かって 全力で 落下。 元になった 技で 威力が 変わる。" }, "supersonicSkystrikeSpecial": { "name": "ファイナルダイブクラッシュ", @@ -2509,7 +2509,7 @@ }, "acidDownpourPhysical": { "name": "アシッドポイズンデリート", - "effect": "Zパワーで 毒の 沼を 湧きあがらせ 全力で 相手を 沈める。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 毒の 沼を 湧きあがらせ 全力で 相手を 沈める。 元になった 技で 威力が 変わる。" }, "acidDownpourSpecial": { "name": "アシッドポイズンデリート", @@ -2517,7 +2517,7 @@ }, "tectonicRagePhysical": { "name": "ライジングランドオーバー", - "effect": "Zパワーで 地面の 奥深くに 潜り 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 地面の 奥深くに 潜り 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" }, "tectonicRageSpecial": { "name": "ライジングランドオーバー", @@ -2525,7 +2525,7 @@ }, "continentalCrushPhysical": { "name": "ワールズエンドフォール", - "effect": "Zパワーで 大きな 岩山を 呼びだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 大きな 岩山を 呼びだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" }, "continentalCrushSpecial": { "name": "ワールズエンドフォール", @@ -2533,7 +2533,7 @@ }, "savageSpinOutPhysical": { "name": "ぜったいほしょくかいてんざん", - "effect": "Zパワーで 吐きだした 糸が 全力で 相手を 縛りつける。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 吐きだした 糸が 全力で 相手を 縛りつける。 元になった 技で 威力が 変わる。" }, "savageSpinOutSpecial": { "name": "ぜったいほしょくかいてんざん", @@ -2541,7 +2541,7 @@ }, "neverEndingNightmarePhysical": { "name": "むげんあんやへのいざない", - "effect": "Zパワーで 呼びよせた 強い 怨念が 全力で 相手に 降りかかる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 呼びよせた 強い 怨念が 全力で 相手に 降りかかる。 元になった 技で 威力が 変わる。" }, "neverEndingNightmareSpecial": { "name": "むげんあんやへのいざない", @@ -2549,7 +2549,7 @@ }, "corkscrewCrashPhysical": { "name": "ちょうぜつらせんれんげき", - "effect": "Zパワーで 高速回転を おこない 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 高速回転を おこない 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" }, "corkscrewCrashSpecial": { "name": "ちょうぜつらせんれんげき", @@ -2557,7 +2557,7 @@ }, "infernoOverdrivePhysical": { "name": "ダイナミックフルフレイム", - "effect": "Zパワーで 燃えさかる 炎を 吐きだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 燃えさかる 炎を 吐きだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" }, "infernoOverdriveSpecial": { "name": "ダイナミックフルフレイム", @@ -2565,7 +2565,7 @@ }, "hydroVortexPhysical": { "name": "スーパーアクアトルネード", - "effect": "Zパワーで 大きな 渦潮を 作り 全力で 相手を 飲みこむ。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 大きな 渦潮を 作り 全力で 相手を 飲みこむ。 元になった 技で 威力が 変わる。" }, "hydroVortexSpecial": { "name": "スーパーアクアトルネード", @@ -2573,7 +2573,7 @@ }, "bloomDoomPhysical": { "name": "ブルームシャインエクストラ", - "effect": "Zパワーで 草花の エネルギーを 借り 全力で 相手を 攻撃する。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 草花の エネルギーを 借り 全力で 相手を 攻撃する。 元になった 技で 威力が 変わる。" }, "bloomDoomSpecial": { "name": "ブルームシャインエクストラ", @@ -2581,7 +2581,7 @@ }, "gigavoltHavocPhysical": { "name": "スパーキングギガボルト", - "effect": "Zパワーで 溜めた 強い 電気を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 溜めた 強い 電気を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" }, "gigavoltHavocSpecial": { "name": "スパーキングギガボルト", @@ -2589,7 +2589,7 @@ }, "shatteredPsychePhysical": { "name": "マキシマムサイブレイカー", - "effect": "Zパワーで 相手を 操り 全力で 痛い 思いを させる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 相手を 操り 全力で 痛い 思いを させる。 元になった 技で 威力が 変わる。" }, "shatteredPsycheSpecial": { "name": "マキシマムサイブレイカー", @@ -2597,7 +2597,7 @@ }, "subzeroSlammerPhysical": { "name": "レイジングジオフリーズ", - "effect": "Zパワーで 気温を 急激に 下げ 全力で 相手を 凍らせる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 気温を 急激に 下げ 全力で 相手を 凍らせる。 元になった 技で 威力が 変わる。" }, "subzeroSlammerSpecial": { "name": "レイジングジオフリーズ", @@ -2605,7 +2605,7 @@ }, "devastatingDrakePhysical": { "name": "アルティメットドラゴンバーン", - "effect": "Zパワーで オーラを 実体化し 全力で 相手に 襲いかかる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで オーラを 実体化し 全力で 相手に 襲いかかる。 元になった 技で 威力が 変わる。" }, "devastatingDrakeSpecial": { "name": "アルティメットドラゴンバーン", @@ -2613,7 +2613,7 @@ }, "blackHoleEclipsePhysical": { "name": "ブラックホールイクリプス", - "effect": "Zパワーで 悪の エネルギーを 集め 全力で 相手を 吸いよせる。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 悪の エネルギーを 集め 全力で 相手を 吸いよせる。 元になった 技で 威力が 変わる。" }, "blackHoleEclipseSpecial": { "name": "ブラックホールイクリプス", @@ -2621,7 +2621,7 @@ }, "twinkleTacklePhysical": { "name": "ラブリースターインパクト", - "effect": "Zパワーで 魅惑の 空間を 作り 全力で 相手を もてあそぶ。 元になった 技で 威力が 変わる。" + "effect": "Zパワーで 魅惑の 空間を 作り 全力で 相手を もてあそぶ。 元になった 技で 威力が 変わる。" }, "twinkleTackleSpecial": { "name": "ラブリースターインパクト", @@ -2629,1182 +2629,1182 @@ }, "catastropika": { "name": "ひっさつのピカチュート", - "effect": "Zパワーで 最大 電力を 身に まとったピカチュウが 全力で 相手に 飛び掛る。" + "effect": "Zパワーで 最大 電力を 身に まとったピカチュウが 全力で 相手に 飛び掛る。" }, "shoreUp": { "name": "すなあつめ", - "effect": "最大HPの 半分 自分の HPを 回復する。 すなあらしの時は 多く 回復。" + "effect": "最大HPの 半分 自分の HPを 回復する。 すなあらしの時は 多く 回復。" }, "firstImpression": { "name": "であいがしら", - "effect": "威力が 高い 技 だが 戦闘に 出たら すぐに 出さないと 成功 しない。" + "effect": "威力が 高い 技 だが 戦闘に 出たら すぐに 出さないと 成功 しない。" }, "banefulBunker": { "name": "トーチカ", - "effect": "相手の 攻撃を 防ぐと 同時に 触れた 相手に 毒を 与えてしまう。" + "effect": "相手の 攻撃を 防ぐと 同時に 触れた 相手に 毒を 与えてしまう。" }, "spiritShackle": { "name": "かげぬい", - "effect": "攻撃と 同時に 相手の 影を 縫い付けて 逃げられなくする。" + "effect": "攻撃と 同時に 相手の 影を 縫い付けて 逃げられなくする。" }, "darkestLariat": { "name": "DDラリアット", - "effect": "両腕を 回し 相手に 当てる。 相手の 能力変化に 関係なく ダメージを 与える。" + "effect": "両腕を 回し 相手に 当てる。 相手の 能力変化に 関係なく ダメージを 与える。" }, "sparklingAria": { "name": "うたかたのアリア", - "effect": "歌うことによって たくさんの バルーンを 放出する。 技を 受けると やけどが 治る。" + "effect": "歌うことによって たくさんの バルーンを 放出する。 技を 受けると やけどが 治る。" }, "iceHammer": { "name": "アイスハンマー", - "effect": "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" + "effect": "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" }, "floralHealing": { "name": "フラワーヒール", - "effect": "最大HPの 半分 相手の HPを 回復する。 グラスフィールドの時 効果が あがる。" + "effect": "最大HPの 半分 相手の HPを 回復する。 グラスフィールドの時 効果が あがる。" }, "highHorsepower": { "name": "10まんばりき", - "effect": "全身を 使って 相手に 猛アタックする。" + "effect": "全身を 使って 相手に 猛アタックする。" }, "strengthSap": { "name": "ちからをすいとる", - "effect": "相手の 攻撃力と 同じだけ 自分の HPを 回復する。 そして 相手の 攻撃を さげる。" + "effect": "相手の 攻撃力と 同じだけ 自分の HPを 回復する。 そして 相手の 攻撃を さげる。" }, "solarBlade": { "name": "ソーラーブレード", - "effect": "1ターン目に 光を いっぱいに 集め 2ターン目に その 力を 剣に 込めて 攻撃する。" + "effect": "1ターン目に 光を いっぱいに 集め 2ターン目に その 力を 剣に 込めて 攻撃する。" }, "leafage": { "name": "このは", - "effect": "はっぱを 相手に 当てて 攻撃する。" + "effect": "はっぱを 相手に 当てて 攻撃する。" }, "spotlight": { "name": "スポットライト", - "effect": "ポケモンに スポットライトを 当て そのターンに そのポケモンしか 狙えない ようにする。" + "effect": "ポケモンに スポットライトを 当て そのターンに そのポケモンしか 狙えない ようにする。" }, "toxicThread": { "name": "どくのいと", - "effect": "毒の 混じった 糸を 吹き付ける。 相手を 毒にして 素早さを さげる。" + "effect": "毒の 混じった 糸を 吹き付ける。 相手を 毒にして 素早さを さげる。" }, "laserFocus": { "name": "とぎすます", - "effect": "精神を 集中して 次の 攻撃を 必ず 急所に 当てる。" + "effect": "精神を 集中して 次の 攻撃を 必ず 急所に 当てる。" }, "gearUp": { "name": "アシストギア", - "effect": "ギアを 入れる ことによって 特性 プラスと マイナスの 攻撃と 特攻が あがる。" + "effect": "ギアを 入れる ことによって 特性 プラスと マイナスの 攻撃と 特攻が あがる。" }, "throatChop": { "name": "じごくづき", - "effect": "この 技を 受けた 相手は 地獄の 苦しみから 2ターンの間 音の 技を 出すことが できなくなる。" + "effect": "この 技を 受けた 相手は 地獄の 苦しみから 2ターンの間 音の 技を 出すことが できなくなる。" }, "pollenPuff": { "name": "かふんだんご", - "effect": "敵には 爆発する だんごを 使って 攻撃。 味方には 回復する だんごを 与える。" + "effect": "敵には 爆発する だんごを 使って 攻撃。 味方には 回復する だんごを 与える。" }, "anchorShot": { "name": "アンカーショット", - "effect": "アンカーを 相手に からませて 攻撃する。 相手は 逃げることが できなくなる。" + "effect": "アンカーを 相手に からませて 攻撃する。 相手は 逃げることが できなくなる。" }, "psychicTerrain": { "name": "サイコフィールド", - "effect": "5ターンの間 地面にいると 先制技を 受けない。 エスパータイプの 威力が あがる。" + "effect": "5ターンの間 地面にいると 先制技を 受けない。 エスパータイプの 威力が あがる。" }, "lunge": { "name": "とびかかる", - "effect": "全力で 相手に 飛びかかって 攻撃。 相手の 攻撃を さげる。" + "effect": "全力で 相手に 飛びかかって 攻撃。 相手の 攻撃を さげる。" }, "fireLash": { "name": "ほのおのムチ", - "effect": "焼けたムチで 相手を 打ちつける。 攻撃を 受けた 相手は 防御が さがる。" + "effect": "焼けたムチで 相手を 打ちつける。 攻撃を 受けた 相手は 防御が さがる。" }, "powerTrip": { "name": "つけあがる", - "effect": "自分の 強さを 鼻高々に 攻撃する。自分の 能力が あがって いるほど 威力が あがる。" + "effect": "自分の 強さを 鼻高々に 攻撃する。自分の 能力が あがって いるほど 威力が あがる。" }, "burnUp": { "name": "もえつきる", - "effect": "全身の ほのおを すべて 燃やして 大ダメージを 与える。 自分の ほのおタイプが なくなる。" + "effect": "全身の ほのおを すべて 燃やして 大ダメージを 与える。 自分の ほのおタイプが なくなる。" }, "speedSwap": { "name": "スピードスワップ", - "effect": "相手の 素早さと 自分の 素早さを 入れ替えてしまう。" + "effect": "相手の 素早さと 自分の 素早さを 入れ替えてしまう。" }, "smartStrike": { "name": "スマートホーン", - "effect": "とがった つので 相手を 突き刺して 攻撃する。 攻撃は 必ず 命中する。" + "effect": "とがった つので 相手を 突き刺して 攻撃する。 攻撃は 必ず 命中する。" }, "purify": { "name": "じょうか", - "effect": "相手の 状態異常を 治す。 治すと 自分は HPを 回復 することが できる。" + "effect": "相手の 状態異常を 治す。 治すと 自分は HPを 回復 することが できる。" }, "revelationDance": { "name": "めざめるダンス", - "effect": "全力で 踊って 攻撃する。 この 技の タイプは 自分の タイプと 同じになる。" + "effect": "全力で 踊って 攻撃する。 この 技の タイプは 自分の タイプと 同じになる。" }, "coreEnforcer": { "name": "コアパニッシャー", - "effect": "ダメージを 与えた 相手が すでに 行動を 終えていたら 相手の 特性を 消してしまう。" + "effect": "ダメージを 与えた 相手が すでに 行動を 終えていたら 相手の 特性を 消してしまう。" }, "tropKick": { "name": "トロピカルキック", - "effect": "南国 由来の 熱い キックを 相手に 浴びせる。 相手の 攻撃を さげる。" + "effect": "南国 由来の 熱い キックを 相手に 浴びせる。 相手の 攻撃を さげる。" }, "instruct": { "name": "さいはい", - "effect": "相手が 出した 技を 指示して もう一度 出させることが できる。" + "effect": "相手が 出した 技を 指示して もう一度 出させることが できる。" }, "beakBlast": { "name": "くちばしキャノン", - "effect": "最初に クチバシを 加熱してから 攻撃を くりだす。 加熱中に さわると やけどする。" + "effect": "最初に クチバシを 加熱してから 攻撃を くりだす。 加熱中に さわると やけどする。" }, "clangingScales": { "name": "スケイルノイズ", - "effect": "全身の うろこを こすり 大きな 音を 出して 攻撃する。 攻撃後 自分の 防御が さがる。" + "effect": "全身の うろこを こすり 大きな 音を 出して 攻撃する。 攻撃後 自分の 防御が さがる。" }, "dragonHammer": { "name": "ドラゴンハンマー", - "effect": "体を ハンマーのように 使って 相手に 襲いかかり ダメージを 与える。" + "effect": "体を ハンマーのように 使って 相手に 襲いかかり ダメージを 与える。" }, "brutalSwing": { "name": "ぶんまわす", - "effect": "自分の 体を ぶんまわして 相手に ダメージを 与える。" + "effect": "自分の 体を ぶんまわして 相手に ダメージを 与える。" }, "auroraVeil": { "name": "オーロラベール", - "effect": "5ターンの 間 物理と 特殊の ダメージを 弱める。 ゆきの 時しか 出すことが できない。" + "effect": "5ターンの 間 物理と 特殊の ダメージを 弱める。 ゆきの 時しか 出すことが できない。" }, "sinisterArrowRaid": { "name": "シャドーアローズストライク", - "effect": "Zパワーで 無数の 矢を 作りだした ジュナイパーが 全力で 相手を 射抜く 攻撃。" + "effect": "Zパワーで 無数の 矢を 作りだした ジュナイパーが 全力で 相手を 射抜く 攻撃。" }, "maliciousMoonsault": { "name": "ハイパーダーククラッシャー", - "effect": "Zパワーで タフな 肉体を 得た ガオガエンが 全力で 相手に ぶつかって 攻撃する。" + "effect": "Zパワーで タフな 肉体を 得た ガオガエンが 全力で 相手に ぶつかって 攻撃する。" }, "oceanicOperetta": { "name": "わだつみのシンフォニア", - "effect": "Zパワーで 大量の 水を 呼んだ アシレーヌが 全力で 相手を 攻撃する。" + "effect": "Zパワーで 大量の 水を 呼んだ アシレーヌが 全力で 相手を 攻撃する。" }, "guardianOfAlola": { "name": "ガーディアン・デ・アローラ", - "effect": "Zパワーで アローラの 力を 得た とちがみポケモン 全力の 攻撃。 相手の 残りHPを たくさん 減らす。" + "effect": "Zパワーで アローラの 力を 得た とちがみポケモン 全力の 攻撃。 相手の 残りHPを たくさん 減らす。" }, "soulStealing7StarStrike": { "name": "しちせいだっこんたい", - "effect": "Zパワーを 得た マーシャドーが パンチと キックの 連続技を 全力で 相手に 叩き込む。" + "effect": "Zパワーを 得た マーシャドーが パンチと キックの 連続技を 全力で 相手に 叩き込む。" }, "stokedSparksurfer": { "name": "ライトニングサーフライド", - "effect": "Zパワーを 得た アローラ地方の ライチュウが 全力で 攻撃する。 相手を まひ 状態に する。" + "effect": "Zパワーを 得た アローラ地方の ライチュウが 全力で 攻撃する。 相手を まひ 状態に する。" }, "pulverizingPancake": { - "name": "ほんきをだす こうげき", - "effect": "Zパワーで 本気を 出した カビゴンが 巨体を 躍動させて 全力で 相手に 襲いかかる。" + "name": "ほんきをだす こうげき", + "effect": "Zパワーで 本気を 出した カビゴンが 巨体を 躍動させて 全力で 相手に 襲いかかる。" }, "extremeEvoboost": { "name": "ナインエボルブースト", - "effect": "Zパワーを 得た イーブイが 進化した 仲間たちの 力を 借りて 能力を ぐーんと 上げる。" + "effect": "Zパワーを 得た イーブイが 進化した 仲間たちの 力を 借りて 能力を ぐーんと 上げる。" }, "genesisSupernova": { "name": "オリジンズスーパーノヴァ", - "effect": "Zパワーを 得た ミュウが 全力で 相手を 攻撃する。 足元が サイコフィールドになる。" + "effect": "Zパワーを 得た ミュウが 全力で 相手を 攻撃する。 足元が サイコフィールドになる。" }, "shellTrap": { "name": "トラップシェル", - "effect": "こうらの トラップを しかける。 相手が 物理技を 出すと 爆発して ダメージを 与える。" + "effect": "こうらの トラップを しかける。 相手が 物理技を 出すと 爆発して ダメージを 与える。" }, "fleurCannon": { "name": "フルールカノン", - "effect": "強力な ビームを 放ったあと 自分の 特攻が がくっと さがる。" + "effect": "強力な ビームを 放ったあと 自分の 特攻が がくっと さがる。" }, "psychicFangs": { "name": "サイコファング", - "effect": "サイコパワーで かみついて 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" + "effect": "サイコパワーで かみついて 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" }, "stompingTantrum": { "name": "じだんだ", - "effect": "悔しさを バネにして 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" + "effect": "悔しさを バネにして 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" }, "shadowBone": { "name": "シャドーボーン", - "effect": "魂の 宿った ホネで 相手を なぐりつけて 攻撃する。 相手の 防御を さげる ことがある。" + "effect": "魂の 宿った ホネで 相手を なぐりつけて 攻撃する。 相手の 防御を さげる ことがある。" }, "accelerock": { "name": "アクセルロック", - "effect": "素早い スピードで 相手に ぶつかって 攻撃する。 必ず 先制攻撃 できる。" + "effect": "素早い スピードで 相手に ぶつかって 攻撃する。 必ず 先制攻撃 できる。" }, "liquidation": { "name": "アクアブレイク", - "effect": "水の 力で 相手に ぶつかって 攻撃する。 相手の 防御を さげる ことがある。" + "effect": "水の 力で 相手に ぶつかって 攻撃する。 相手の 防御を さげる ことがある。" }, "prismaticLaser": { "name": "プリズムレーザー", - "effect": "プリズムの 力で 強力な 光線を 発射する。 次の ターンは 動けなくなる。" + "effect": "プリズムの 力で 強力な 光線を 発射する。 次の ターンは 動けなくなる。" }, "spectralThief": { "name": "シャドースチール", - "effect": "相手の 影に 潜り込み 相手の 能力アップを 奪って 攻撃する。" + "effect": "相手の 影に 潜り込み 相手の 能力アップを 奪って 攻撃する。" }, "sunsteelStrike": { "name": "メテオドライブ", - "effect": "流星の ような 勢いで 突進する。 相手の 特性を 無視して 攻撃 することが できる。" + "effect": "流星の ような 勢いで 突進する。 相手の 特性を 無視して 攻撃 することが できる。" }, "moongeistBeam": { "name": "シャドーレイ", - "effect": "怪しい 光線を 放って 攻撃する。相手の 特性を 無視して 攻撃 することが できる。" + "effect": "怪しい 光線を 放って 攻撃する。相手の 特性を 無視して 攻撃 することが できる。" }, "tearfulLook": { "name": "なみだめ", - "effect": "なみだめに なって 相手の 戦力を 喪失させる。 相手の 攻撃と 特攻が さがる。" + "effect": "なみだめに なって 相手の 戦力を 喪失させる。 相手の 攻撃と 特攻が さがる。" }, "zingZap": { "name": "びりびりちくちく", - "effect": "相手に ぶつかって 強力な 電気を浴びせ びりびりちくちく させる。 相手を ひるませる ことが ある。" + "effect": "相手に ぶつかって 強力な 電気を浴びせ びりびりちくちく させる。 相手を ひるませる ことが ある。" }, "naturesMadness": { "name": "しぜんのいかり", - "effect": "自然の 怒りを 相手に ぶつける。 相手の HPは 半分に なる。" + "effect": "自然の 怒りを 相手に ぶつける。 相手の HPは 半分に なる。" }, "multiAttack": { "name": "マルチアタック", - "effect": "高い エネルギーを まといつつ 相手に ぶつかって 攻撃する。 メモリに より タイプが 変わる。" + "effect": "高い エネルギーを まといつつ 相手に ぶつかって 攻撃する。 メモリに より タイプが 変わる。" }, "tenMillionVoltThunderbolt": { "name": "1000まんボルト", - "effect": "帽子を かぶった ピカチュウが Zパワーで パワーアップした 電撃を 放つ。 急所に 当たりやすい。" + "effect": "帽子を かぶった ピカチュウが Zパワーで パワーアップした 電撃を 放つ。 急所に 当たりやすい。" }, "mindBlown": { "name": "ビックリヘッド", - "effect": "自分の 頭を 爆発 させて 周りの すべてを 攻撃する。 自分も ダメージを 受けてしまう。" + "effect": "自分の 頭を 爆発 させて 周りの すべてを 攻撃する。 自分も ダメージを 受けてしまう。" }, "plasmaFists": { "name": "プラズマフィスト", - "effect": "電気を まとった こぶしで 攻撃。 ノーマルタイプの 技を でんきタイプに してしまう。" + "effect": "電気を まとった こぶしで 攻撃。 ノーマルタイプの 技を でんきタイプに してしまう。" }, "photonGeyser": { "name": "フォトンゲイザー", - "effect": "光の 柱で 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" + "effect": "光の 柱で 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" }, "lightThatBurnsTheSky": { "name": "てんこがすめつぼうのひかり", - "effect": "ネクロズマが 相手の 特性の 効果を 無視して 攻撃と 特攻の 高い方で ダメージを 与える。" + "effect": "ネクロズマが 相手の 特性の 効果を 無視して 攻撃と 特攻の 高い方で ダメージを 与える。" }, "searingSunrazeSmash": { "name": "サンシャインスマッシャー", - "effect": "Zパワーを 得た ソルガレオが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" + "effect": "Zパワーを 得た ソルガレオが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" }, "menacingMoonrazeMaelstrom": { "name": "ムーンライトブラスター", - "effect": "Zパワーを 得た ルナアーラが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" + "effect": "Zパワーを 得た ルナアーラが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" }, "letsSnuggleForever": { "name": "ぽかぼかフレンドタイム", - "effect": "Zパワーを 得た ミミッキュが 全力で ぽかぽか 攻撃。" + "effect": "Zパワーを 得た ミミッキュが 全力で ぽかぽか 攻撃。" }, "splinteredStormshards": { "name": "ラジアルエッジストーム", - "effect": "Zパワーを 得た ルガルガンが 全力で 攻撃する。 追加で フィールド状態を 打ち消す。" + "effect": "Zパワーを 得た ルガルガンが 全力で 攻撃する。 追加で フィールド状態を 打ち消す。" }, "clangorousSoulblaze": { "name": "ブレイジングソウルビート", - "effect": "Zパワーを 得た ジャラランガが 全力で 相手を 攻撃する。 追加で 自分の 能力が 上がる。" + "effect": "Zパワーを 得た ジャラランガが 全力で 相手を 攻撃する。 追加で 自分の 能力が 上がる。" }, "zippyZap": { "name": "ばちばちアクセル", - "effect": "猛スピードの 電撃 アタック。 必ず 先制攻撃 できて 急所に あたる。" + "effect": "猛スピードの 電撃 アタック。 必ず 先制攻撃 できて 急所に あたる。" }, "splishySplash": { "name": "ざぶざぶサーフ", - "effect": "大きな 波に 電気を あびせ 相手に ぶつけて 攻撃する。 まひ状態に することが ある。" + "effect": "大きな 波に 電気を あびせ 相手に ぶつけて 攻撃する。 まひ状態に することが ある。" }, "floatyFall": { "name": "ふわふわフォール", - "effect": "ふんわりと 浮かび あがり 一気に 急降下して 攻撃。 相手を ひるませることが ある。" + "effect": "ふんわりと 浮かび あがり 一気に 急降下して 攻撃。 相手を ひるませることが ある。" }, "pikaPapow": { "name": "ピカピカサンダー", - "effect": "トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 電撃。 必ず 命中する。" + "effect": "トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 電撃。 必ず 命中する。" }, "bouncyBubble": { "name": "いきいきバブル", - "effect": "水のかたまりを ぶつけて 攻撃。 水を 吸いとり ダメージの 半分の HPを 回復する。" + "effect": "水のかたまりを ぶつけて 攻撃。 水を 吸いとり ダメージの 半分の HPを 回復する。" }, "buzzyBuzz": { "name": "びりびりエレキ", - "effect": "電気を 飛ばし 相手に あびせて 攻撃する。 相手を まひ状態に する。" + "effect": "電気を 飛ばし 相手に あびせて 攻撃する。 相手を まひ状態に する。" }, "sizzlySlide": { "name": "めらめらバーン", - "effect": "炎を まとった 体で 勢いよく 相手に ぶつかる。 相手を やけど状態に する。" + "effect": "炎を まとった 体で 勢いよく 相手に ぶつかる。 相手を やけど状態に する。" }, "glitzyGlow": { "name": "どばどばオーラ", - "effect": "念力を これでもかと あびせる。 相手の 特殊攻撃を 弱める 不思議な かべを つくりだす。" + "effect": "念力を これでもかと あびせる。 相手の 特殊攻撃を 弱める 不思議な かべを つくりだす。" }, "baddyBad": { "name": "わるわるゾーン", - "effect": "わるさを アピールして 攻撃。 相手の 物理攻撃を 弱める 不思議な かべを つくりだす。" + "effect": "わるさを アピールして 攻撃。 相手の 物理攻撃を 弱める 不思議な かべを つくりだす。" }, "sappySeed": { "name": "すくすくボンバー", - "effect": "巨大な ツルを 生やし タネを 撒きちらかして 攻撃する。 タネは 毎ターン 相手の HPを 吸いとる。" + "effect": "巨大な ツルを 生やし タネを 撒きちらかして 攻撃する。 タネは 毎ターン 相手の HPを 吸いとる。" }, "freezyFrost": { "name": "こちこちフロスト", - "effect": "冷たく 凍った くろいきりの 結晶で 攻撃。 全員の 能力変化を もとに もどす。" + "effect": "冷たく 凍った くろいきりの 結晶で 攻撃。 全員の 能力変化を もとに もどす。" }, "sparklySwirl": { "name": "きらきらストーム", - "effect": "むせかえる ような 香りの 竜巻で 相手を つつんで 攻撃。 味方の 状態異常を 回復する。" + "effect": "むせかえる ような 香りの 竜巻で 相手を つつんで 攻撃。 味方の 状態異常を 回復する。" }, "veeveeVolley": { "name": "ブイブイブレイク", - "effect": "イーブイの トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 体当たり。 必ず 命中する。" + "effect": "イーブイの トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 体当たり。 必ず 命中する。" }, "doubleIronBash": { "name": "ダブルパンツァー", - "effect": "胸の ナットを 軸に 回転して 2回 続けて うでを たたきつける。 相手を ひるませる ことが ある。" + "effect": "胸の ナットを 軸に 回転して 2回 続けて うでを たたきつける。 相手を ひるませる ことが ある。" }, "maxGuard": { "name": "ダイウォール", - "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" }, "dynamaxCannon": { "name": "ダイマックスほう", - "effect": "コアから ビームを 放つ。相手の レベルが 過度に 上がっている 場合は 与える ダメージが 最大 2倍に 増える。" + "effect": "コアから ビームを 放つ。相手の レベルが 過度に 上がっている 場合は 与える ダメージが 最大 2倍に 増える。" }, "snipeShot": { "name": "ねらいうち", - "effect": "相手の 技を 引き受ける 特性や 技の 影響を 無視して 選んだ 相手を 攻撃 できる。" + "effect": "相手の 技を 引き受ける 特性や 技の 影響を 無視して 選んだ 相手を 攻撃 できる。" }, "jawLock": { "name": "くらいつく", - "effect": "お互い ひんしに なるまで 交代が できなくなる。 どちらかの ポケモンが いなくなると 効果は消える。" + "effect": "お互い ひんしに なるまで 交代が できなくなる。 どちらかの ポケモンが いなくなると 効果は消える。" }, "stuffCheeks": { "name": "ほおばる", - "effect": "持っている きのみを 食べて 防御を ぐーんと あげる。" + "effect": "持っている きのみを 食べて 防御を ぐーんと あげる。" }, "noRetreat": { "name": "はいすいのじん", - "effect": "自分の すべての 能力が 上がるが 交代 したり 逃げることが できなくなる。" + "effect": "自分の すべての 能力が 上がるが 交代 したり 逃げることが できなくなる。" }, "tarShot": { "name": "タールショット", - "effect": "ねばねばの タールを 浴びせて 相手の 素早さを 下げる。 相手は ほのおが 弱点に なる。" + "effect": "ねばねばの タールを 浴びせて 相手の 素早さを 下げる。 相手は ほのおが 弱点に なる。" }, "magicPowder": { "name": "まほうのこな", - "effect": "まほうのこなを 浴びせて 相手を エスパータイプに 変化させる。" + "effect": "まほうのこなを 浴びせて 相手を エスパータイプに 変化させる。" }, "dragonDarts": { "name": "ドラゴンアロー", - "effect": "ドラメシヤで 2回 攻撃。 相手が 2匹 いるときは それぞれに 1回ずつ 攻撃する。" + "effect": "ドラメシヤで 2回 攻撃。 相手が 2匹 いるときは それぞれに 1回ずつ 攻撃する。" }, "teatime": { "name": "おちゃかい", - "effect": "おちゃかいを ひらいて 場にいる ポケモンが それぞれ 持っている きのみを 食べる。" + "effect": "おちゃかいを ひらいて 場にいる ポケモンが それぞれ 持っている きのみを 食べる。" }, "octolock": { "name": "たこがため", - "effect": "相手を 逃げられなくする。 かためられた 相手は 毎ターン 防御と 特防が 下がる。" + "effect": "相手を 逃げられなくする。 かためられた 相手は 毎ターン 防御と 特防が 下がる。" }, "boltBeak": { "name": "でんげきくちばし", - "effect": "電気を まとった くちばしで 刺す。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" + "effect": "電気を まとった くちばしで 刺す。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" }, "fishiousRend": { "name": "エラがみ", - "effect": "かたい エラで かみつく。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" + "effect": "かたい エラで かみつく。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" }, "courtChange": { "name": "コートチェンジ", - "effect": "不思議な 力で お互いの 場の 効果を 入れ替える。" + "effect": "不思議な 力で お互いの 場の 効果を 入れ替える。" }, "maxFlare": { "name": "ダイバーン", - "effect": "ダイマックスした ポケモンが 繰りだす ほのおタイプの 攻撃。 5ターンの 間 日差しを 強くする。" + "effect": "ダイマックスした ポケモンが 繰りだす ほのおタイプの 攻撃。 5ターンの 間 日差しを 強くする。" }, "maxFlutterby": { "name": "ダイワーム", - "effect": "ダイマックスした ポケモンが 繰りだす むしタイプの 攻撃。 相手の 特攻を 下げる。" + "effect": "ダイマックスした ポケモンが 繰りだす むしタイプの 攻撃。 相手の 特攻を 下げる。" }, "maxLightning": { "name": "ダイサンダー", - "effect": "ダイマックスした ポケモンが 繰りだす でんきタイプの 攻撃。 5ターンの 間 エレキフィールドにする。" + "effect": "ダイマックスした ポケモンが 繰りだす でんきタイプの 攻撃。 5ターンの 間 エレキフィールドにする。" }, "maxStrike": { "name": "ダイアタック", - "effect": "ダイマックスした ポケモンが 繰りだす ノーマルタイプの 攻撃。 相手の 素早さを 下げる。" + "effect": "ダイマックスした ポケモンが 繰りだす ノーマルタイプの 攻撃。 相手の 素早さを 下げる。" }, "maxKnuckle": { "name": "ダイナックル", - "effect": "ダイマックスした ポケモンが 繰りだす かくとうタイプの 攻撃。 味方の 攻撃を 上げる。" + "effect": "ダイマックスした ポケモンが 繰りだす かくとうタイプの 攻撃。 味方の 攻撃を 上げる。" }, "maxPhantasm": { "name": "ダイホロウ", - "effect": "ダイマックスした ポケモンが 繰りだす ゴーストタイプの 攻撃。 相手の 防御を 下げる。" + "effect": "ダイマックスした ポケモンが 繰りだす ゴーストタイプの 攻撃。 相手の 防御を 下げる。" }, "maxHailstorm": { "name": "ダイアイス", - "effect": "ダイマックスした ポケモンが 繰りだす こおりタイプの 攻撃。 5ターンの 間 あられを 降らす。" + "effect": "ダイマックスした ポケモンが 繰りだす こおりタイプの 攻撃。 5ターンの 間 あられを 降らす。" }, "maxOoze": { "name": "ダイアシッド", - "effect": "ダイマックスした ポケモンが 繰りだす どくタイプの 攻撃。 味方の 特攻を 上げる。" + "effect": "ダイマックスした ポケモンが 繰りだす どくタイプの 攻撃。 味方の 特攻を 上げる。" }, "maxGeyser": { "name": "ダイストリーム", - "effect": "ダイマックスした ポケモンが 繰りだす みずタイプの 攻撃。 5ターンの 間 雨を 降らせる。" + "effect": "ダイマックスした ポケモンが 繰りだす みずタイプの 攻撃。 5ターンの 間 雨を 降らせる。" }, "maxAirstream": { "name": "ダイジェット", - "effect": "ダイマックスした ポケモンが 繰りだす ひこうタイプの 攻撃。 味方の 素早さを 上げる。" + "effect": "ダイマックスした ポケモンが 繰りだす ひこうタイプの 攻撃。 味方の 素早さを 上げる。" }, "maxStarfall": { "name": "ダイフェアリー", - "effect": "ダイマックスした ポケモンが 繰りだす フェアリータイプの 攻撃。 5ターンの 間 ミストフィールドにする。" + "effect": "ダイマックスした ポケモンが 繰りだす フェアリータイプの 攻撃。 5ターンの 間 ミストフィールドにする。" }, "maxWyrmwind": { "name": "ダイドラグーン", - "effect": "ダイマックスした ポケモンが 繰りだす ドラゴンタイプの 攻撃。 相手の 攻撃を 下げる。" + "effect": "ダイマックスした ポケモンが 繰りだす ドラゴンタイプの 攻撃。 相手の 攻撃を 下げる。" }, "maxMindstorm": { "name": "ダイサイコ", - "effect": "ダイマックスした ポケモンが 繰りだす エスパータイプの 攻撃。 5ターンの 間 サイコフィールドにする。" + "effect": "ダイマックスした ポケモンが 繰りだす エスパータイプの 攻撃。 5ターンの 間 サイコフィールドにする。" }, "maxRockfall": { "name": "ダイロック", - "effect": "ダイマックスした ポケモンが 繰りだす いわタイプの 攻撃。 5ターンの 間 砂あらしにする。" + "effect": "ダイマックスした ポケモンが 繰りだす いわタイプの 攻撃。 5ターンの 間 砂あらしにする。" }, "maxQuake": { "name": "ダイアース", - "effect": "ダイマックスした ポケモンが 繰りだす じめんタイプの 攻撃。 味方の 特防を 上げる。" + "effect": "ダイマックスした ポケモンが 繰りだす じめんタイプの 攻撃。 味方の 特防を 上げる。" }, "maxDarkness": { "name": "ダイアーク", - "effect": "ダイマックスした ポケモンが 繰りだす あくタイプの 攻撃。 相手の 特防を 下げる。" + "effect": "ダイマックスした ポケモンが 繰りだす あくタイプの 攻撃。 相手の 特防を 下げる。" }, "maxOvergrowth": { "name": "ダイソウゲン", - "effect": "ダイマックスした ポケモンが 繰りだす くさタイプの 攻撃。 5ターンの 間 グラスフィールドにする。" + "effect": "ダイマックスした ポケモンが 繰りだす くさタイプの 攻撃。 5ターンの 間 グラスフィールドにする。" }, "maxSteelspike": { "name": "ダイスチル", - "effect": "ダイマックスした ポケモンが 繰りだす はがねタイプの 攻撃。 味方の 防御を 上げる。" + "effect": "ダイマックスした ポケモンが 繰りだす はがねタイプの 攻撃。 味方の 防御を 上げる。" }, "clangorousSoul": { "name": "ソウルビート", - "effect": "自分の HPを 少し 削って すべての 能力を 上げる。" + "effect": "自分の HPを 少し 削って すべての 能力を 上げる。" }, "bodyPress": { "name": "ボディプレス", - "effect": "体を ぶつけて 攻撃。 防御が 高いほど 与える ダメージが 増える。" + "effect": "体を ぶつけて 攻撃。 防御が 高いほど 与える ダメージが 増える。" }, "decorate": { "name": "デコレーション", - "effect": "かざりつけを することで 相手の 攻撃と 特攻を ぐーんと 上げる。" + "effect": "かざりつけを することで 相手の 攻撃と 特攻を ぐーんと 上げる。" }, "drumBeating": { "name": "ドラムアタック", - "effect": "ドラムの 根っこを ドラミングで コントロールして こうげき することで 相手の 素早さを 下げる。" + "effect": "ドラムの 根っこを ドラミングで コントロールして こうげき することで 相手の 素早さを 下げる。" }, "snapTrap": { "name": "トラバサミ", - "effect": "トラバサミで 捕らえて 4-5ターンの 間 相手を はさんで 攻撃する。" + "effect": "トラバサミで 捕らえて 4-5ターンの 間 相手を はさんで 攻撃する。" }, "pyroBall": { "name": "かえんボール", - "effect": "小石を 燃やした 炎の ボールで 相手を 攻撃する。 やけど 状態に することが ある。" + "effect": "小石を 燃やした 炎の ボールで 相手を 攻撃する。 やけど 状態に することが ある。" }, "behemothBlade": { "name": "きょじゅうざん", - "effect": "全身で 強大な剣を 振りかざし 勢いよく 切りかかって 攻撃する。" + "effect": "全身で 強大な剣を 振りかざし 勢いよく 切りかかって 攻撃する。" }, "behemothBash": { "name": "きょじゅうだん", - "effect": "全身を 強固な盾へと 変化させ 勢いよく ぶつかって 攻撃する。" + "effect": "全身を 強固な盾へと 変化させ 勢いよく ぶつかって 攻撃する。" }, "auraWheel": { "name": "オーラぐるま", - "effect": "ほほぶくろに 溜めた エネルギーで 攻撃し 自分の 素早さを あげる。 モルペコの 姿で タイプが 変わる。" + "effect": "ほほぶくろに 溜めた エネルギーで 攻撃し 自分の 素早さを あげる。 モルペコの 姿で タイプが 変わる。" }, "breakingSwipe": { "name": "ワイドブレイカー", - "effect": "きょうじんな しっぽを 激しく ふりはらって 相手を 攻撃する。 相手の 攻撃を 下げる。" + "effect": "きょうじんな しっぽを 激しく ふりはらって 相手を 攻撃する。 相手の 攻撃を 下げる。" }, "branchPoke": { "name": "えだづき", - "effect": "するどく とがった 枝で 相手を 突いて 攻撃する。" + "effect": "するどく とがった 枝で 相手を 突いて 攻撃する。" }, "overdrive": { "name": "オーバードライブ", - "effect": "ギターや ベースを かきならして 激しく 響く 大きな 振動を 相手に 与えて 攻撃する。" + "effect": "ギターや ベースを かきならして 激しく 響く 大きな 振動を 相手に 与えて 攻撃する。" }, "appleAcid": { "name": "りんごさん", - "effect": "すっぱい りんごから つくりだした 酸性の 液体で 攻撃。 相手の 特防を 下げる。" + "effect": "すっぱい りんごから つくりだした 酸性の 液体で 攻撃。 相手の 特防を 下げる。" }, "gravApple": { "name": "Gのちから", - "effect": "高いところから りんごを 落として ダメージを 与える。 相手の 防御を 下げる。" + "effect": "高いところから りんごを 落として ダメージを 与える。 相手の 防御を 下げる。" }, "spiritBreak": { "name": "ソウルクラッシュ", - "effect": "食らうと くじけるほどの 勢いで 攻撃。 相手の 特攻を 下げる。" + "effect": "食らうと くじけるほどの 勢いで 攻撃。 相手の 特攻を 下げる。" }, "strangeSteam": { "name": "ワンダースチーム", - "effect": "煙を 噴出して 相手を 攻撃。 混乱 させることが ある。" + "effect": "煙を 噴出して 相手を 攻撃。 混乱 させることが ある。" }, "lifeDew": { "name": "いのちのしずく", - "effect": "不思議な 水を ふりまいて 自分と 場にいる 味方の HPを 回復する。" + "effect": "不思議な 水を ふりまいて 自分と 場にいる 味方の HPを 回復する。" }, "obstruct": { "name": "ブロッキング", - "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。 触れると 防御が がくっと 下がる。" + "effect": "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。 触れると 防御が がくっと 下がる。" }, "falseSurrender": { "name": "どげざつき", - "effect": "頭を 下げる ふりを しながら 振りみだした 髪の毛を 突き刺す。 攻撃は 必ず 命中する。" + "effect": "頭を 下げる ふりを しながら 振りみだした 髪の毛を 突き刺す。 攻撃は 必ず 命中する。" }, "meteorAssault": { "name": "スターアサルト", - "effect": "太い クキを ふりまわして 攻撃。 ただし 自分も よろめいてしまうため 次の ターンは 動けなくなる。" + "effect": "太い クキを ふりまわして 攻撃。 ただし 自分も よろめいてしまうため 次の ターンは 動けなくなる。" }, "eternabeam": { "name": "ムゲンダイビーム", - "effect": "本来の 姿と なった ムゲンダイナ 最大の 攻撃。 次の ターンは 動けなくなる。" + "effect": "本来の 姿と なった ムゲンダイナ 最大の 攻撃。 次の ターンは 動けなくなる。" }, "steelBeam": { "name": "てっていこうせん", - "effect": "全身から 集めた はがねを ビームとして 激しく 撃ちだす。 自分も ダメージを 受けてしまう。" + "effect": "全身から 集めた はがねを ビームとして 激しく 撃ちだす。 自分も ダメージを 受けてしまう。" }, "expandingForce": { "name": "ワイドフォース", - "effect": "サイコパワーで 相手を 攻撃する。 サイコフィールドの時 威力が あがり すべての 相手に ダメージを 与える。" + "effect": "サイコパワーで 相手を 攻撃する。 サイコフィールドの時 威力が あがり すべての 相手に ダメージを 与える。" }, "steelRoller": { "name": "アイアンローラー", - "effect": "フィールドを 破壊しながら 攻撃。 なんらかの フィールド状態に 変わっていないと 技は 失敗する。" + "effect": "フィールドを 破壊しながら 攻撃。 なんらかの フィールド状態に 変わっていないと 技は 失敗する。" }, "scaleShot": { "name": "スケイルショット", - "effect": "ウロコを 撃ちだして 攻撃する。 2ー5回の 間 連続で だす。 素早さが あがるが 防御が さがる。" + "effect": "ウロコを 撃ちだして 攻撃する。 2ー5回の 間 連続で だす。 素早さが あがるが 防御が さがる。" }, "meteorBeam": { "name": "メテオビーム", - "effect": "1ターン目に 宇宙の 力を 集めることで 特攻が あがり 2ターン目に 相手を 攻撃する。" + "effect": "1ターン目に 宇宙の 力を 集めることで 特攻が あがり 2ターン目に 相手を 攻撃する。" }, "shellSideArm": { "name": "シェルアームズ", - "effect": "物理か 特殊か より多く ダメージを 与えられる 能力で 攻撃する。 毒状態に することが ある。" + "effect": "物理か 特殊か より多く ダメージを 与えられる 能力で 攻撃する。 毒状態に することが ある。" }, "mistyExplosion": { "name": "ミストバースト", - "effect": "自分の 周りに いる すべてを 攻撃するが 使うと 瀕死になる。 ミストフィールドで 威力が あがる。" + "effect": "自分の 周りに いる すべてを 攻撃するが 使うと 瀕死になる。 ミストフィールドで 威力が あがる。" }, "grassyGlide": { "name": "グラススライダー", - "effect": "地面を 滑るように 相手を 攻撃。 グラスフィールドの時 必ず 先制攻撃 できる。" + "effect": "地面を 滑るように 相手を 攻撃。 グラスフィールドの時 必ず 先制攻撃 できる。" }, "risingVoltage": { "name": "ライジングボルト", - "effect": "地面から 立ちのぼる 電撃で 攻撃。 相手が エレキフィールドに いる時 技の 威力が 2倍に なる。" + "effect": "地面から 立ちのぼる 電撃で 攻撃。 相手が エレキフィールドに いる時 技の 威力が 2倍に なる。" }, "terrainPulse": { "name": "だいちのはどう", - "effect": "フィールドの力を 借りて 攻撃。 使った時の フィールドの状態に よって 技の タイプと 威力が 変わる。" + "effect": "フィールドの力を 借りて 攻撃。 使った時の フィールドの状態に よって 技の タイプと 威力が 変わる。" }, "skitterSmack": { "name": "はいよるいちげき", - "effect": "背後から はいより 攻撃する。 相手の 特攻を さげる。" + "effect": "背後から はいより 攻撃する。 相手の 特攻を さげる。" }, "burningJealousy": { "name": "しっとのほのお", - "effect": "しっとの エネルギーで 相手を 攻撃。 そのターン 能力が あがった ポケモンを やけどの 状態に する。" + "effect": "しっとの エネルギーで 相手を 攻撃。 そのターン 能力が あがった ポケモンを やけどの 状態に する。" }, "lashOut": { "name": "うっぷんばらし", - "effect": "相手への いらだちを ぶつけて 攻撃。 そのターンに 能力を さげられていると 技の 威力が 2倍に なる。" + "effect": "相手への いらだちを ぶつけて 攻撃。 そのターンに 能力を さげられていると 技の 威力が 2倍に なる。" }, "poltergeist": { "name": "ポルターガイスト", - "effect": "相手の 持ち物を あやつって 攻撃。 相手が 道具を 持っていない 場合は 失敗する。" + "effect": "相手の 持ち物を あやつって 攻撃。 相手が 道具を 持っていない 場合は 失敗する。" }, "corrosiveGas": { "name": "ふしょくガス", - "effect": "強い 酸性の ガスで 周りに いるものを 包みこみ 持っている 道具を 溶かしてしまう。" + "effect": "強い 酸性の ガスで 周りに いるものを 包みこみ 持っている 道具を 溶かしてしまう。" }, "coaching": { "name": "コーチング", - "effect": "的確な 指導を おこなうことで 味方 全員の 攻撃と 防御を 上げる。" + "effect": "的確な 指導を おこなうことで 味方 全員の 攻撃と 防御を 上げる。" }, "flipTurn": { "name": "クイックターン", - "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + "effect": "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" }, "tripleAxel": { "name": "トリプルアクセル", - "effect": "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" + "effect": "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" }, "dualWingbeat": { "name": "ダブルウイング", - "effect": "翼を 相手に ぶつけて 攻撃する。 2回連続で ダメージを 与える。" + "effect": "翼を 相手に ぶつけて 攻撃する。 2回連続で ダメージを 与える。" }, "scorchingSands": { "name": "ねっさのだいち", - "effect": "熱く 焼けた 砂を 相手に ぶつけて 攻撃する。 やけど状態に することが ある。" + "effect": "熱く 焼けた 砂を 相手に ぶつけて 攻撃する。 やけど状態に することが ある。" }, "jungleHealing": { "name": "ジャングルヒール", - "effect": "ジャングルと 一体化して 自分と 場にいる 味方の HPと 状態を 回復する。" + "effect": "ジャングルと 一体化して 自分と 場にいる 味方の HPと 状態を 回復する。" }, "wickedBlow": { "name": "あんこくきょうだ", - "effect": "あくの型を 極めし 強烈な 一撃。 必ず 急所に 当たる。" + "effect": "あくの型を 極めし 強烈な 一撃。 必ず 急所に 当たる。" }, "surgingStrikes": { "name": "すいりゅうれんだ", - "effect": "みずの型を 極めし 流れるような 3回の 連撃。 必ず 急所に 当たる。" + "effect": "みずの型を 極めし 流れるような 3回の 連撃。 必ず 急所に 当たる。" }, "thunderCage": { "name": "サンダープリズン", - "effect": "ほとばしる 電気の おりの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + "effect": "ほとばしる 電気の おりの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" }, "dragonEnergy": { "name": "ドラゴンエナジー", - "effect": "生命力を パワーに 変え 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + "effect": "生命力を パワーに 変え 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" }, "freezingGlare": { "name": "いてつくしせん", - "effect": "両目から サイコパワーを 撃ちだして 攻撃する。 こおり状態に することが ある。" + "effect": "両目から サイコパワーを 撃ちだして 攻撃する。 こおり状態に することが ある。" }, "fieryWrath": { "name": "もえあがるいかり", - "effect": "怒りを 炎の ような オーラに 変えて 攻撃する。 相手を ひるませることが ある。" + "effect": "怒りを 炎の ような オーラに 変えて 攻撃する。 相手を ひるませることが ある。" }, "thunderousKick": { "name": "らいめいげり", - "effect": "雷の ような 動きで 相手を 翻弄しながら キックする。 相手の 防御を さげる。" + "effect": "雷の ような 動きで 相手を 翻弄しながら キックする。 相手の 防御を さげる。" }, "glacialLance": { "name": "ブリザードランス", - "effect": "吹雪を まとった 氷の 槍を 相手に 投げつけて 攻撃する。" + "effect": "吹雪を まとった 氷の 槍を 相手に 投げつけて 攻撃する。" }, "astralBarrage": { "name": "アストラルビット", - "effect": "たくさんの 小さな 霊体を 相手に ぶつけて 攻撃する。" + "effect": "たくさんの 小さな 霊体を 相手に ぶつけて 攻撃する。" }, "eerieSpell": { "name": "ぶきみなじゅもん", - "effect": "強力な サイコパワーで 攻撃。 相手が 最後に 使った技の PPを 3だけ 減らす。" + "effect": "強力な サイコパワーで 攻撃。 相手が 最後に 使った技の PPを 3だけ 減らす。" }, "direClaw": { "name": "フェイタルクロー", - "effect": "破滅的なツメで 攻撃する。 相手を どく まひ ねむりの いずれかの状態に することも ある。" + "effect": "破滅的なツメで 攻撃する。 相手を どく まひ ねむりの いずれかの状態に することも ある。" }, "psyshieldBash": { "name": "バリアーラッシュ", - "effect": "思念のエネルギーを まといながら 相手に ぶつかっていく。 自分の 防御を あげる。" + "effect": "思念のエネルギーを まといながら 相手に ぶつかっていく。 自分の 防御を あげる。" }, "powerShift": { "name": "パワーシフト", - "effect": "自分の 攻撃と防御を 入れ替える。" + "effect": "自分の 攻撃と防御を 入れ替える。" }, "stoneAxe": { "name": "がんせきアックス", - "effect": "岩の斧で 攻撃する。 ばらまかれた 岩の破片が 相手の 周りに 浮かぶ。" + "effect": "岩の斧で 攻撃する。 ばらまかれた 岩の破片が 相手の 周りに 浮かぶ。" }, "springtideStorm": { "name": "はるのあらし", - "effect": "愛憎 入りまじった 強烈な風で 相手を 包みこんで 攻撃する。 相手の 攻撃を さげることが ある。" + "effect": "愛憎 入りまじった 強烈な風で 相手を 包みこんで 攻撃する。 相手の 攻撃を さげることが ある。" }, "mysticalPower": { "name": "しんぴのちから", - "effect": "不思議な力を 放出して 攻撃する。 自分の 特攻が あがる。" + "effect": "不思議な力を 放出して 攻撃する。 自分の 特攻が あがる。" }, "ragingFury": { "name": "だいふんげき", - "effect": "2-3ターンの 間 炎を 放ちながら 暴れまわる。 暴れたあとは 混乱する。" + "effect": "2-3ターンの 間 炎を 放ちながら 暴れまわる。 暴れたあとは 混乱する。" }, "waveCrash": { "name": "ウェーブタックル", - "effect": "水を まといつつ 全身で 相手に ぶつかるが 自分も かなりの ダメージ を受ける。" + "effect": "水を まといつつ 全身で 相手に ぶつかるが 自分も かなりの ダメージ を受ける。" }, "chloroblast": { "name": "クロロブラスト", - "effect": "自身の 葉緑素を 集約し 放出して 攻撃する。 自分も ダメージを 受けてしまう。" + "effect": "自身の 葉緑素を 集約し 放出して 攻撃する。 自分も ダメージを 受けてしまう。" }, "mountainGale": { "name": "ひょうざんおろし", - "effect": "氷山のような 大きな 氷塊を ぶつけて 攻撃する。 相手を ひるませることが ある。" + "effect": "氷山のような 大きな 氷塊を ぶつけて 攻撃する。 相手を ひるませることが ある。" }, "victoryDance": { "name": "しょうりのまい", - "effect": "勝利を 呼びこむ 舞を 激しく 踊って 自分の 攻撃と 防御と 素早さを あげる。" + "effect": "勝利を 呼びこむ 舞を 激しく 踊って 自分の 攻撃と 防御と 素早さを あげる。" }, "headlongRush": { "name": "ぶちかまし", - "effect": "全身全霊の たいあたりを くらわせる。 自分の 防御と 特防が さがる。" + "effect": "全身全霊の たいあたりを くらわせる。 自分の 防御と 特防が さがる。" }, "barbBarrage": { "name": "どくばりセンボン", - "effect": "無数の毒針で 相手を 毒状態に することもある。 相手が 毒状態だと 威力は 2倍になる。" + "effect": "無数の毒針で 相手を 毒状態に することもある。 相手が 毒状態だと 威力は 2倍になる。" }, "esperWing": { "name": "オーラウイング", - "effect": "オーラで 強化した翼で 切り裂く。 急所に 当たりやすい。 自分の 素早さを あげる。" + "effect": "オーラで 強化した翼で 切り裂く。 急所に 当たりやすい。 自分の 素早さを あげる。" }, "bitterMalice": { "name": "うらみつらみ", - "effect": "背筋が 凍るような 怨念で 攻撃して 相手の 攻撃を さげる。" + "effect": "背筋が 凍るような 怨念で 攻撃して 相手の 攻撃を さげる。" }, "shelter": { "name": "たてこもる", - "effect": "皮膚を 鉄の盾のように 硬くすることで 自分の 防御を ぐーんと あげる。" + "effect": "皮膚を 鉄の盾のように 硬くすることで 自分の 防御を ぐーんと あげる。" }, "tripleArrows": { "name": "3ぼんのや", - "effect": "足技のあと 3本の矢を 同時に放つ。 相手の 防御を さげたり ひるませることが ある。 急所に 当たりやすい。" + "effect": "足技のあと 3本の矢を 同時に放つ。 相手の 防御を さげたり ひるませることが ある。 急所に 当たりやすい。" }, "infernalParade": { "name": "ひゃっきやこう", - "effect": "無数の火の玉で 攻撃して やけど状態に することが ある。 相手が 状態異常だと 威力は 2倍。" + "effect": "無数の火の玉で 攻撃して やけど状態に することが ある。 相手が 状態異常だと 威力は 2倍。" }, "ceaselessEdge": { "name": "ひけん・ちえなみ", - "effect": "貝殻の剣で 攻撃する。 ばらまかれた 貝殻の破片は 相手の 足下に まきびし となって 散らばる。" + "effect": "貝殻の剣で 攻撃する。 ばらまかれた 貝殻の破片は 相手の 足下に まきびし となって 散らばる。" }, "bleakwindStorm": { "name": "こがらしあらし", - "effect": "身も心も 震える 冷たく 激しい風で 攻撃する。 相手の 素早さを さげることが ある。" + "effect": "身も心も 震える 冷たく 激しい風で 攻撃する。 相手の 素早さを さげることが ある。" }, "wildboltStorm": { "name": "かみなりあらし", - "effect": "嵐を 起こし 雷雲を 呼びよせ 雷と風で 激しく 攻撃をする。 相手を まひ状態に することもある。" + "effect": "嵐を 起こし 雷雲を 呼びよせ 雷と風で 激しく 攻撃をする。 相手を まひ状態に することもある。" }, "sandsearStorm": { "name": "ねっさのあらし", - "effect": "熱く焼けた砂と 強烈な風で 包みこんで 攻撃する。 相手を やけど状態に することがある。" + "effect": "熱く焼けた砂と 強烈な風で 包みこんで 攻撃する。 相手を やけど状態に することがある。" }, "lunarBlessing": { "name": "みかづきのいのり", - "effect": "みかづきに いのりを ささげて 自分と 場にいる 味方の HPと 状態を 回復する。" + "effect": "みかづきに いのりを ささげて 自分と 場にいる 味方の HPと 状態を 回復する。" }, "takeHeart": { "name": "ブレイブチャージ", - "effect": "心を 奮わせて 自分の 状態異常を 治し さらには 特攻と 特防を あげる。" + "effect": "心を 奮わせて 自分の 状態異常を 治し さらには 特攻と 特防を あげる。" }, "gMaxWildfire": { "name": "キョダイゴクエン", - "effect": "キョダイマックスした リザードンが 繰りだす ほのおタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + "effect": "キョダイマックスした リザードンが 繰りだす ほのおタイプの 攻撃。 4ターンの 間 ダメージを 与える。" }, "gMaxBefuddle": { "name": "キョダイコワク", - "effect": "キョダイマックスした バタフリーが 繰り出す むしタイプの 攻撃。 毒・まひ・眠りの どれかに する。" + "effect": "キョダイマックスした バタフリーが 繰り出す むしタイプの 攻撃。 毒・まひ・眠りの どれかに する。" }, "gMaxVoltCrash": { "name": "キョダイバンライ", - "effect": "キョダイマックスした ピカチュウが 繰りだす でんきタイプの 攻撃。 相手を まひ状態に する。" + "effect": "キョダイマックスした ピカチュウが 繰りだす でんきタイプの 攻撃。 相手を まひ状態に する。" }, "gMaxGoldRush": { "name": "キョダイコバン", - "effect": "キョダイマックスした ニャースが 繰り出す ノーマルタイプの 攻撃。 相手を 混乱させ お金も もらえる。" + "effect": "キョダイマックスした ニャースが 繰り出す ノーマルタイプの 攻撃。 相手を 混乱させ お金も もらえる。" }, "gMaxChiStrike": { "name": "キョダイシンゲキ", - "effect": "キョダイマックスした カイリキーが 繰りだす かくとうタイプの 攻撃。 急所に 当たりやすく なる。" + "effect": "キョダイマックスした カイリキーが 繰りだす かくとうタイプの 攻撃。 急所に 当たりやすく なる。" }, "gMaxTerror": { "name": "キョダイゲンエイ", - "effect": "キョダイマックスした ゲンガーが 繰りだす ゴーストタイプの 攻撃。 影を 踏み 交代 できなくする。" + "effect": "キョダイマックスした ゲンガーが 繰りだす ゴーストタイプの 攻撃。 影を 踏み 交代 できなくする。" }, "gMaxResonance": { "name": "キョダイセンリツ", - "effect": "キョダイマックスした ラプラスが 繰りだす こおりタイプの 攻撃。 5ターンの 間 ダメージを 弱める。" + "effect": "キョダイマックスした ラプラスが 繰りだす こおりタイプの 攻撃。 5ターンの 間 ダメージを 弱める。" }, "gMaxCuddle": { "name": "キョダイホーヨー", - "effect": "キョダイマックスした イーブイが 繰りだす ノーマルタイプの 攻撃。 相手を メロメロに する。" + "effect": "キョダイマックスした イーブイが 繰りだす ノーマルタイプの 攻撃。 相手を メロメロに する。" }, "gMaxReplenish": { "name": "キョダイサイセイ", - "effect": "キョダイマックスした カビゴンが 繰りだす ノーマルタイプの 攻撃。 食べた きのみを 再生する。" + "effect": "キョダイマックスした カビゴンが 繰りだす ノーマルタイプの 攻撃。 食べた きのみを 再生する。" }, "gMaxMalodor": { "name": "キョダイシュウキ", - "effect": "キョダイマックスした ダストダスが 繰りだす どくタイプの 攻撃。 相手を 毒 状態に する。" + "effect": "キョダイマックスした ダストダスが 繰りだす どくタイプの 攻撃。 相手を 毒 状態に する。" }, "gMaxStonesurge": { "name": "キョダイガンジン", - "effect": "キョダイマックスした カジリガメが 繰りだす みずタイプの 攻撃。 鋭い 無数の 岩を ばらまく。" + "effect": "キョダイマックスした カジリガメが 繰りだす みずタイプの 攻撃。 鋭い 無数の 岩を ばらまく。" }, "gMaxWindRage": { "name": "キョダイフウゲキ", - "effect": "キョダイマックスした アーマーガアが 繰りだす ひこうタイプの 攻撃。 リフレクターや ひかりのかべを 消し去る。" + "effect": "キョダイマックスした アーマーガアが 繰りだす ひこうタイプの 攻撃。 リフレクターや ひかりのかべを 消し去る。" }, "gMaxStunShock": { "name": "キョダイカンデン", - "effect": "キョダイマックスした ストリンダーが 繰り出す でんきタイプの 攻撃。 相手を 毒 か まひ どちらかにする。" + "effect": "キョダイマックスした ストリンダーが 繰り出す でんきタイプの 攻撃。 相手を 毒 か まひ どちらかにする。" }, "gMaxFinale": { "name": "キョダイダンエン", - "effect": "キョダイマックスした マホイップが 繰りだす フェアリータイプの 攻撃。 味方の HPを 回復する。" + "effect": "キョダイマックスした マホイップが 繰りだす フェアリータイプの 攻撃。 味方の HPを 回復する。" }, "gMaxDepletion": { "name": "キョダイゲンスイ", - "effect": "キョダイマックスした ジュラルドンが 繰りだす ドラゴンタイプの 攻撃。 最後に 使われた わざPPを 減らす。" + "effect": "キョダイマックスした ジュラルドンが 繰りだす ドラゴンタイプの 攻撃。 最後に 使われた わざPPを 減らす。" }, "gMaxGravitas": { "name": "キョダイテンドウ", - "effect": "キョダイマックスした イオルブが 繰りだす エスパータイプの 攻撃。 5ターンの 間 重力が 変わる。" + "effect": "キョダイマックスした イオルブが 繰りだす エスパータイプの 攻撃。 5ターンの 間 重力が 変わる。" }, "gMaxVolcalith": { "name": "キョダイフンセキ", - "effect": "キョダイマックスした セキタンザンが 繰りだす いわタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + "effect": "キョダイマックスした セキタンザンが 繰りだす いわタイプの 攻撃。 4ターンの 間 ダメージを 与える。" }, "gMaxSandblast": { "name": "キョダイサジン", - "effect": "キョダイマックスした サダイジャが 繰りだす じめんタイプの 攻撃。 4-5ターンの間 砂が 吹き荒れる。" + "effect": "キョダイマックスした サダイジャが 繰りだす じめんタイプの 攻撃。 4-5ターンの間 砂が 吹き荒れる。" }, "gMaxSnooze": { "name": "キョダイスイマ", - "effect": "キョダイマックスした オーロンゲが 繰りだす あくタイプの 攻撃。 大きな あくびで 眠気を 誘う。" + "effect": "キョダイマックスした オーロンゲが 繰りだす あくタイプの 攻撃。 大きな あくびで 眠気を 誘う。" }, "gMaxTartness": { "name": "キョダイサンゲキ", - "effect": "キョダイマックスした アップリューが 繰りだす くさタイプの 攻撃。 相手の 回避率を 下げる。" + "effect": "キョダイマックスした アップリューが 繰りだす くさタイプの 攻撃。 相手の 回避率を 下げる。" }, "gMaxSweetness": { "name": "キョダイカンロ", - "effect": "キョダイマックスした タルップルが 繰りだす くさタイプの 攻撃。 味方の 状態異常を 回復する。" + "effect": "キョダイマックスした タルップルが 繰りだす くさタイプの 攻撃。 味方の 状態異常を 回復する。" }, "gMaxSmite": { "name": "キョダイテンバツ", - "effect": "キョダイマックスした ブリムオンが 繰りだす フェアリータイプの 攻撃。 相手を 混乱させる。" + "effect": "キョダイマックスした ブリムオンが 繰りだす フェアリータイプの 攻撃。 相手を 混乱させる。" }, "gMaxSteelsurge": { "name": "キョダイコウジン", - "effect": "キョダイマックスした ダイオウドウが 繰りだす タイプの 攻撃。 鋭い 無数の とげを ばらまく。" + "effect": "キョダイマックスした ダイオウドウが 繰りだす タイプの 攻撃。 鋭い 無数の とげを ばらまく。" }, "gMaxMeltdown": { "name": "キョダイユウゲキ", - "effect": "キョダイマックスした メルメタルが 繰りだす はがねタイプの 攻撃。 同じ 技を 連続で 出せなくする。" + "effect": "キョダイマックスした メルメタルが 繰りだす はがねタイプの 攻撃。 同じ 技を 連続で 出せなくする。" }, "gMaxFoamBurst": { "name": "キョダイホウマツ", - "effect": "キョダイマックスした キングラーが 繰りだす みずタイプの 攻撃。 相手の 素早さを がくっと さげる。" + "effect": "キョダイマックスした キングラーが 繰りだす みずタイプの 攻撃。 相手の 素早さを がくっと さげる。" }, "gMaxCentiferno": { "name": "キョダイヒャッカ", - "effect": "キョダイマックスした マルヤクデが 繰りだす ほのおタイプの 攻撃。 4-5ターンの間 炎に 閉じこめる。" + "effect": "キョダイマックスした マルヤクデが 繰りだす ほのおタイプの 攻撃。 4-5ターンの間 炎に 閉じこめる。" }, "gMaxVineLash": { "name": "キョダイベンタツ", - "effect": "キョダイマックスした フシギバナが 繰りだす くさタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + "effect": "キョダイマックスした フシギバナが 繰りだす くさタイプの 攻撃。 4ターンの 間 ダメージを 与える。" }, "gMaxCannonade": { "name": "キョダイホウゲキ", - "effect": "キョダイマックスした カメックスが 繰りだす みずタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + "effect": "キョダイマックスした カメックスが 繰りだす みずタイプの 攻撃。 4ターンの 間 ダメージを 与える。" }, "gMaxDrumSolo": { "name": "キョダイコランダ", - "effect": "キョダイマックスした ゴリランダーが 繰りだす くさタイプの 攻撃。 相手の 特性に ジャマされない。" + "effect": "キョダイマックスした ゴリランダーが 繰りだす くさタイプの 攻撃。 相手の 特性に ジャマされない。" }, "gMaxFireball": { "name": "キョダイカキュウ", - "effect": "キョダイマックスした エースバーンが 繰りだす ほのおタイプの 攻撃。 相手の 特性に ジャマされない。" + "effect": "キョダイマックスした エースバーンが 繰りだす ほのおタイプの 攻撃。 相手の 特性に ジャマされない。" }, "gMaxHydrosnipe": { "name": "キョダイソゲキ", - "effect": "キョダイマックスした インテレオンが 繰りだす みずタイプの 攻撃。 相手の 特性に ジャマされない。" + "effect": "キョダイマックスした インテレオンが 繰りだす みずタイプの 攻撃。 相手の 特性に ジャマされない。" }, "gMaxOneBlow": { "name": "キョダイイチゲキ", - "effect": "キョダイマックスした ウーラオスが 繰りだす あくタイプの 攻撃。 ダイウォールを 無視できる 一撃。" + "effect": "キョダイマックスした ウーラオスが 繰りだす あくタイプの 攻撃。 ダイウォールを 無視できる 一撃。" }, "gMaxRapidFlow": { "name": "キョダイレンゲキ", - "effect": "キョダイマックスした ウーラオスが 繰りだす みずタイプの 攻撃。 ダイウォールを 無視できる 連撃。" + "effect": "キョダイマックスした ウーラオスが 繰りだす みずタイプの 攻撃。 ダイウォールを 無視できる 連撃。" }, "teraBlast": { "name": "テラバースト", - "effect": "テラスタルだと テラスタイプの エネルギーを 放出して 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" + "effect": "テラスタルだと テラスタイプの エネルギーを 放出して 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" }, "silkTrap": { "name": "スレッドトラップ", - "effect": "糸の罠を はりめぐらせる。 相手の 攻撃を 防ぐと 同時に 触れた 相手の 素早さを さげる。" + "effect": "糸の罠を はりめぐらせる。 相手の 攻撃を 防ぐと 同時に 触れた 相手の 素早さを さげる。" }, "axeKick": { "name": "かかとおとし", - "effect": "蹴りあげた かかとを 落として 攻撃する。 相手を 混乱させることが ある。 はずすと 自分が ダメージを 受ける。" + "effect": "蹴りあげた かかとを 落として 攻撃する。 相手を 混乱させることが ある。 はずすと 自分が ダメージを 受ける。" }, "lastRespects": { "name": "おはかまいり", - "effect": "仲間の 無念を 晴らすため 攻撃する。 倒された 味方のポケモンが 多いほど 技の 威力が 増える。" + "effect": "仲間の 無念を 晴らすため 攻撃する。 倒された 味方のポケモンが 多いほど 技の 威力が 増える。" }, "luminaCrash": { "name": "ルミナコリジョン", - "effect": "精神にも 作用する 奇妙な光を 放って 攻撃する。 相手の 特防を がくっと さげる。" + "effect": "精神にも 作用する 奇妙な光を 放って 攻撃する。 相手の 特防を がくっと さげる。" }, "orderUp": { "name": "いっちょうあがり", - "effect": "いなせな 身のこなしで 攻撃。 口の中に シャリタツが いると そのすがたによって 能力が あがる。" + "effect": "いなせな 身のこなしで 攻撃。 口の中に シャリタツが いると そのすがたによって 能力が あがる。" }, "jetPunch": { "name": "ジェットパンチ", - "effect": "激流を こぶしに まとって 目にも 留まらぬ パンチを くりだす。 必ず 先制攻撃 できる。" + "effect": "激流を こぶしに まとって 目にも 留まらぬ パンチを くりだす。 必ず 先制攻撃 できる。" }, "spicyExtract": { "name": "ハバネロエキス", - "effect": "とんでもなく 辛いエキスを 出す。 相手の 攻撃が ぐーんと あがり 防御が がくっと さがる。" + "effect": "とんでもなく 辛いエキスを 出す。 相手の 攻撃が ぐーんと あがり 防御が がくっと さがる。" }, "spinOut": { "name": "ホイールスピン", - "effect": "足に 負荷を かけることにより 激しく 回転して ダメージを 与える。 自分の 素早さが がくっと さがる。" + "effect": "足に 負荷を かけることにより 激しく 回転して ダメージを 与える。 自分の 素早さが がくっと さがる。" }, "populationBomb": { "name": "ネズミざん", - "effect": "仲間たちが わらわらと 集まって コンビネーションで 攻撃を 与えていく。 1-10回の 間 連続で あたる。" + "effect": "仲間たちが わらわらと 集まって コンビネーションで 攻撃を 与えていく。 1-10回の 間 連続で あたる。" }, "iceSpinner": { "name": "アイススピナー", - "effect": "足に 薄い氷を まとい クルクルと 回りながら ぶつかる。 回転の 動きによって フィールドを 壊す。" + "effect": "足に 薄い氷を まとい クルクルと 回りながら ぶつかる。 回転の 動きによって フィールドを 壊す。" }, "glaiveRush": { "name": "きょけんとつげき", - "effect": "体を 投げだす 無謀な突撃。 技のあと 相手からの 攻撃は 必ず 命中し ダメージが 2倍に なってしまう。" + "effect": "体を 投げだす 無謀な突撃。 技のあと 相手からの 攻撃は 必ず 命中し ダメージが 2倍に なってしまう。" }, "revivalBlessing": { "name": "さいきのいのり", - "effect": "慈愛の心で いのることにより 控えにいる ひんしの ポケモンを HPを 半分の状態で 復活させる。" + "effect": "慈愛の心で いのることにより 控えにいる ひんしの ポケモンを HPを 半分の状態で 復活させる。" }, "saltCure": { "name": "しおづけ", - "effect": "相手を しおづけ状態に して 毎ターン ダメージを 与える。 はがね みずタイプは より 苦しむ。" + "effect": "相手を しおづけ状態に して 毎ターン ダメージを 与える。 はがね みずタイプは より 苦しむ。" }, "tripleDive": { "name": "トリプルダイブ", - "effect": "息のあった 飛びこみを することで 相手に 水しぶきを あてる。 3回連続で ダメージを 与える。" + "effect": "息のあった 飛びこみを することで 相手に 水しぶきを あてる。 3回連続で ダメージを 与える。" }, "mortalSpin": { "name": "キラースピン", - "effect": "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。 相手を 毒状態に する。" + "effect": "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。 相手を 毒状態に する。" }, "doodle": { "name": "うつしえ", - "effect": "相手の本質を とらえて うつしだし 自分と 味方を 相手と 同じ 特性に 変化させる。" + "effect": "相手の本質を とらえて うつしだし 自分と 味方を 相手と 同じ 特性に 変化させる。" }, "filletAway": { "name": "みをけずる", - "effect": "自分の HPを けずって 自分の 攻撃と 特攻と 素早さを ぐーんと あげる。" + "effect": "自分の HPを けずって 自分の 攻撃と 特攻と 素早さを ぐーんと あげる。" }, "kowtowCleave": { "name": "ドゲザン", - "effect": "土下座して 相手を 油断させておいて 切りかかる。 攻撃は 必ず 命中する。" + "effect": "土下座して 相手を 油断させておいて 切りかかる。 攻撃は 必ず 命中する。" }, "flowerTrick": { "name": "トリックフラワー", - "effect": "細工がある 花たばを 相手に 投げて 攻撃する。 必ず 命中して 急所にも 当たる。" + "effect": "細工がある 花たばを 相手に 投げて 攻撃する。 必ず 命中して 急所にも 当たる。" }, "torchSong": { "name": "フレアソング", - "effect": "燃えたぎる 火炎を 歌うように 吹きつけて 相手を 焦がす。 自分の 特攻を あげる。" + "effect": "燃えたぎる 火炎を 歌うように 吹きつけて 相手を 焦がす。 自分の 特攻を あげる。" }, "aquaStep": { "name": "アクアステップ", - "effect": "水もしたたる かろやかな 足どりで 相手を 翻弄し ダメージを 与える。 自分の 素早さを あげる。" + "effect": "水もしたたる かろやかな 足どりで 相手を 翻弄し ダメージを 与える。 自分の 素早さを あげる。" }, "ragingBull": { "name": "レイジングブル", - "effect": "怒り狂う あばれうしの 猛烈な タックル。 フォルムで 技のタイプが 変わり ひかりのかべや リフレクターなども 破壊できる。" + "effect": "怒り狂う あばれうしの 猛烈な タックル。 フォルムで 技のタイプが 変わり ひかりのかべや リフレクターなども 破壊できる。" }, "makeItRain": { "name": "ゴールドラッシュ", - "effect": "大量のコインを ぶちまけて 攻撃。 自分の 特攻が さがる。 戦闘の あとで お金も もらえる。" + "effect": "大量のコインを ぶちまけて 攻撃。 自分の 特攻が さがる。 戦闘の あとで お金も もらえる。" }, "psyblade": { "name": "サイコブレイド", - "effect": "実体のない刃で 相手を 切り裂く。 エレキフィールドに いるとき 技の威力が 1.5倍に なる。" + "effect": "実体のない刃で 相手を 切り裂く。 エレキフィールドに いるとき 技の威力が 1.5倍に なる。" }, "hydroSteam": { "name": "ハイドロスチーム", - "effect": "煮えたぎる水を 勢いよく 浴びせる。 日差しが 強いとき 技の威力が さがるどころか 1.5倍になる。" + "effect": "煮えたぎる水を 勢いよく 浴びせる。 日差しが 強いとき 技の威力が さがるどころか 1.5倍になる。" }, "ruination": { "name": "カタストロフィ", - "effect": "破滅的な 災厄を 巻き起こし 相手の HPを 半分に する。" + "effect": "破滅的な 災厄を 巻き起こし 相手の HPを 半分に する。" }, "collisionCourse": { "name": "アクセルブレイク", - "effect": "変形しながら 荒々しく 落下し いにしえの 大爆発を 引き起こす。 弱点をつくと さらに 威力が 増す。" + "effect": "変形しながら 荒々しく 落下し いにしえの 大爆発を 引き起こす。 弱点をつくと さらに 威力が 増す。" }, "electroDrift": { "name": "イナズマドライブ", - "effect": "変形しながら 超高速で 走行し 未知なる 電撃が 相手を つらぬく。 弱点をつくと さらに 威力が 増す。" + "effect": "変形しながら 超高速で 走行し 未知なる 電撃が 相手を つらぬく。 弱点をつくと さらに 威力が 増す。" }, "shedTail": { "name": "しっぽきり", - "effect": "自分の HPを 削って 分身を だしたあと もどってきて 控えの ポケモンと 入れ替わる。" + "effect": "自分の HPを 削って 分身を だしたあと もどってきて 控えの ポケモンと 入れ替わる。" }, "chillyReception": { "name": "さむいギャグ", - "effect": "場を 凍らせる ギャグを 言い残し 控えの ポケモンと 入れ替わる。 5ターンの 間 ゆきを 降らす。" + "effect": "場を 凍らせる ギャグを 言い残し 控えの ポケモンと 入れ替わる。 5ターンの 間 ゆきを 降らす。" }, "tidyUp": { "name": "おかたづけ", - "effect": "まきびし ステルスロック ねばねばネット どくびし みがわりを すべて かたづける。 自分の 攻撃と 素早さが あがる。" + "effect": "まきびし ステルスロック ねばねばネット どくびし みがわりを すべて かたづける。 自分の 攻撃と 素早さが あがる。" }, "snowscape": { "name": "ゆきげしき", - "effect": "5ターンの 間 ゆきを 降らせる。 こおりタイプの 防御が あがる。" + "effect": "5ターンの 間 ゆきを 降らせる。 こおりタイプの 防御が あがる。" }, "pounce": { "name": "とびつく", - "effect": "相手に 飛びついて 攻撃する。 相手の 素早さを さげる。" + "effect": "相手に 飛びついて 攻撃する。 相手の 素早さを さげる。" }, "trailblaze": { "name": "くさわけ", - "effect": "草むらから 飛びだすように 攻撃する。 軽快な 足どりに よって 自分の 素早さを あげる。" + "effect": "草むらから 飛びだすように 攻撃する。 軽快な 足どりに よって 自分の 素早さを あげる。" }, "chillingWater": { "name": "ひやみず", - "effect": "相手の 元気を 失わせるくらい 冷たい水を 浴びせて 攻撃する。 相手の 攻撃を さげる。" + "effect": "相手の 元気を 失わせるくらい 冷たい水を 浴びせて 攻撃する。 相手の 攻撃を さげる。" }, "hyperDrill": { "name": "ハイパードリル", - "effect": "とがった 体の部位を 急速に 回転させ つらぬく。 まもるや みきり なども 無視 できる。" + "effect": "とがった 体の部位を 急速に 回転させ つらぬく。 まもるや みきり なども 無視 できる。" }, "twinBeam": { "name": "ツインビーム", - "effect": "両目から 不可思議な 光線を 発射して 攻撃する。 2回連続で ダメージを 与える。" + "effect": "両目から 不可思議な 光線を 発射して 攻撃する。 2回連続で ダメージを 与える。" }, "rageFist": { "name": "ふんどのこぶし", - "effect": "怒りを エネルギーに 変えて 攻撃。 受けた 攻撃の 回数が 多いほど 技の 威力が あがる。" + "effect": "怒りを エネルギーに 変えて 攻撃。 受けた 攻撃の 回数が 多いほど 技の 威力が あがる。" }, "armorCannon": { "name": "アーマーキャノン", - "effect": "みずからの ヨロイを 燃えたぎる 弾として 撃ち出して 攻撃する。 自分の 防御と 特防が さがる。" + "effect": "みずからの ヨロイを 燃えたぎる 弾として 撃ち出して 攻撃する。 自分の 防御と 特防が さがる。" }, "bitterBlade": { "name": "むねんのつるぎ", - "effect": "この世への 未練を 剣先に こめて 切りつける。 与えた ダメージの 半分の HPを 回復できる。" + "effect": "この世への 未練を 剣先に こめて 切りつける。 与えた ダメージの 半分の HPを 回復できる。" }, "doubleShock": { "name": "でんこうそうげき", - "effect": "全身の でんきを すべて 放って 大ダメージを 与える。 自分の でんきタイプが なくなる。" + "effect": "全身の でんきを すべて 放って 大ダメージを 与える。 自分の でんきタイプが なくなる。" }, "gigatonHammer": { "name": "デカハンマー", - "effect": "大きな ハンマーを 体ごと ぶんまわして 攻撃する。 この技は 2回連続で だせない。" + "effect": "大きな ハンマーを 体ごと ぶんまわして 攻撃する。 この技は 2回連続で だせない。" }, "comeuppance": { "name": "ほうふく", - "effect": "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" + "effect": "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" }, "aquaCutter": { "name": "アクアカッター", - "effect": "加圧された 水を 刃のように 噴射して 相手を 切り裂く。 急所に 当たりやすい。" + "effect": "加圧された 水を 刃のように 噴射して 相手を 切り裂く。 急所に 当たりやすい。" }, "blazingTorque": { "name": "バーンアクセル", - "effect": "メラメラの エンジンを 吹かして 相手に ぶつかる。やけど状態に することが ある。" + "effect": "メラメラの エンジンを 吹かして 相手に ぶつかる。やけど状態に することが ある。" }, "wickedTorque": { "name": "ダークアクセル", - "effect": "悪意で エンジンを 吹かして 相手に ぶつかる。眠り状態に することが ある。" + "effect": "悪意で エンジンを 吹かして 相手に ぶつかる。眠り状態に することが ある。" }, "noxiousTorque": { "name": "ポイズンアクセル", - "effect": "有毒な エンジンを 吹かして 相手に ぶつかる。毒状態に することが ある。" + "effect": "有毒な エンジンを 吹かして 相手に ぶつかる。毒状態に することが ある。" }, "combatTorque": { "name": "ファイトアクセル", - "effect": "力いっぱい エンジンを 吹かして 相手に ぶつかる。まひ状態に することが ある。" + "effect": "力いっぱい エンジンを 吹かして 相手に ぶつかる。まひ状態に することが ある。" }, "magicalTorque": { "name": "マジカルアクセル", - "effect": "幻想的な エンジンを 吹かして 相手に ぶつかる。混乱させることが ある。" + "effect": "幻想的な エンジンを 吹かして 相手に ぶつかる。混乱させることが ある。" }, "bloodMoon": { "name": "ブラッドムーン", - "effect": "血のように 赤い満月から ありったけの 気迫を 撃ちだす。 この技は 2回連続で だせない。" + "effect": "血のように 赤い満月から ありったけの 気迫を 撃ちだす。 この技は 2回連続で だせない。" }, "matchaGotcha": { "name": "シャカシャカほう", - "effect": "かきまぜた お茶の 大砲は 与えた ダメージの 半分を 回復して やけど状態に することも ある。" + "effect": "かきまぜた お茶の 大砲は 与えた ダメージの 半分を 回復して やけど状態に することも ある。" }, "syrupBomb": { "name": "みずあめボム", - "effect": "ねっとりした みずあめを 爆発させ 相手を あめまみれ 状態にして 3ターンの間 素早さを さげ続ける。" + "effect": "ねっとりした みずあめを 爆発させ 相手を あめまみれ 状態にして 3ターンの間 素早さを さげ続ける。" }, "ivyCudgel": { "name": "ツタこんぼう", - "effect": "ツタを まきつけた こん棒で なぐる。 かぶっている お面で タイプが 変わる。 急所に 当たりやすい。" + "effect": "ツタを まきつけた こん棒で なぐる。 かぶっている お面で タイプが 変わる。 急所に 当たりやすい。" }, "electroShot": { "name": "エレクトロビーム", - "effect": "1ターン目に 電気を 集めて 特攻が あがり 2ターン目に 高圧の 電気を 発射する。 天気が 雨のときは すぐに 発射できる。" + "effect": "1ターン目に 電気を 集めて 特攻が あがり 2ターン目に 高圧の 電気を 発射する。 天気が 雨のときは すぐに 発射できる。" }, "teraStarstorm": { "name": "テラクラスター", - "effect": "結晶の力を 照射し 敵を 排除する。 テラパゴスが ステラフォルムで 放つと すべての 相手に ダメージを 与える。" + "effect": "結晶の力を 照射し 敵を 排除する。 テラパゴスが ステラフォルムで 放つと すべての 相手に ダメージを 与える。" }, "fickleBeam": { "name": "きまぐレーザー", - "effect": "光線を 発射して 攻撃する。 ときどき ほかの首も 協力して レーザーを 放ち 威力が 2倍に なる。" + "effect": "光線を 発射して 攻撃する。 ときどき ほかの首も 協力して レーザーを 放ち 威力が 2倍に なる。" }, "burningBulwark": { "name": "かえんのまもり", - "effect": "相手の 攻撃を 超高熱の 体毛で 防ぎ 同時に 触れた 相手に やけどを 与えてしまう。" + "effect": "相手の 攻撃を 超高熱の 体毛で 防ぎ 同時に 触れた 相手に やけどを 与えてしまう。" }, "thunderclap": { "name": "じんらい", - "effect": "相手より 先に 電撃を 浴びせる。 相手が だす技が 攻撃技でないと 失敗する。" + "effect": "相手より 先に 電撃を 浴びせる。 相手が だす技が 攻撃技でないと 失敗する。" }, "mightyCleave": { "name": "パワフルエッジ", - "effect": "頭部に 蓄積した 光で 切断する。 守りを 無視して 攻撃できる。" + "effect": "頭部に 蓄積した 光で 切断する。 守りを 無視して 攻撃できる。" }, "tachyonCutter": { "name": "タキオンカッター", - "effect": "粒子の刃を たて続けに 発射して 2回連続で ダメージを 与える。 攻撃は 必ず 命中する。" + "effect": "粒子の刃を たて続けに 発射して 2回連続で ダメージを 与える。 攻撃は 必ず 命中する。" }, "hardPress": { "name": "ハードプレス", - "effect": "腕やハサミで 相手を 圧迫する。 相手の HPが 残っているほど 威力が あがる。" + "effect": "腕やハサミで 相手を 圧迫する。 相手の HPが 残っているほど 威力が あがる。" }, "dragonCheer": { "name": "ドラゴンエール", - "effect": "竜の鼓舞で 士気を 上げて 味方の技が 急所に 当たりやすくなる。 ドラゴンタイプだと より 鼓舞される。" + "effect": "竜の鼓舞で 士気を 上げて 味方の技が 急所に 当たりやすくなる。 ドラゴンタイプだと より 鼓舞される。" }, "alluringVoice": { "name": "みわくのボイス", - "effect": "天使のような 歌声で 相手を 攻撃。 そのターン 能力が あがった ポケモンを 混乱の 状態に する。" + "effect": "天使のような 歌声で 相手を 攻撃。 そのターン 能力が あがった ポケモンを 混乱の 状態に する。" }, "temperFlare": { "name": "やけっぱち", - "effect": "自棄になった 勢いで 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" + "effect": "自棄になった 勢いで 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" }, "supercellSlam": { "name": "サンダーダイブ", - "effect": "体を 帯電させ て相手に のしかかる。 はずすと 自分が ダメージを 受ける。" + "effect": "体を 帯電させ て相手に のしかかる。 はずすと 自分が ダメージを 受ける。" }, "psychicNoise": { "name": "サイコノイズ", - "effect": "不快な音波を 相手に 浴びせて 攻撃。 2ターンの間 技や 特性や 持っている 道具によって HPを 回復できなくなる。" + "effect": "不快な音波を 相手に 浴びせて 攻撃。 2ターンの間 技や 特性や 持っている 道具によって HPを 回復できなくなる。" }, "upperHand": { "name": "はやてがえし", - "effect": "動きに 反応して 掌底を 打ちこみ 相手を ひるませる。 相手が だす技が 先制攻撃でないと 失敗する。" + "effect": "動きに 反応して 掌底を 打ちこみ 相手を ひるませる。 相手が だす技が 先制攻撃でないと 失敗する。" }, "malignantChain": { "name": "じゃどくのくさり", - "effect": "毒でできた鎖を 相手に 巻きつけ 毒素を 流しこんで 蝕む。 猛毒の 状態に することが ある。" + "effect": "毒でできた鎖を 相手に 巻きつけ 毒素を 流しこんで 蝕む。 猛毒の 状態に することが ある。" } -} \ No newline at end of file +} diff --git a/src/locales/ja/party-ui-handler.json b/src/locales/ja/party-ui-handler.json index a3a88b2dd6e..096d8d5bcba 100644 --- a/src/locales/ja/party-ui-handler.json +++ b/src/locales/ja/party-ui-handler.json @@ -1,8 +1,47 @@ { - "SEND_OUT": "いれかえる", - "SUMMARY": "つよさをみる", + "SEND_OUT": "入れ替える", + "SUMMARY": "強さを見る", "CANCEL": "やめる", - "RELEASE": "にがす", - "APPLY": "つかう", - "TEACH": "おしえる" -} \ No newline at end of file + "RELEASE": "逃がす", + "APPLY": "使う", + "TEACH": "教える", + "SPLICE": "吸収合体", + "UNSPLICE": "合体を分離", + "ACTIVATE": "有効にする", + "DEACTIVATE": "無効にする", + "TRANSFER": "アイテムを移動", + "ALL": "全部", + "PASS_BATON": "バトンタッチ", + "UNPAUSE_EVOLUTION": "進化を有効にする", + "REVIVE": "復活する", + "RENAME": "名前を変える", + "choosePokemon": "ポケモンを 選んで ください。", + "doWhatWithThisPokemon": "このポケモンを どうする?", + "noEnergy": "{{pokemonName}}は 戦うための\n元気が 残っていません!", + "hasEnergy": "{{pokemonName}}は まだまだ 元気だ!", + "cantBeUsed": "{{pokemonName}}は このチャレンジで\n使えられません!", + "tooManyItems": "{{pokemonName}}は このアイテムが\nこれ以上 持ちきれない!", + "anyEffect": "使っても 効果がないよ", + "unpausedEvolutions": "{{pokemonName}}は また 進化できる。", + "unspliceConfirmation": "本当に {{pokemonName}}を {{fusionName}}から\n分離しますか? {{fusionName}}は なくなる。", + "wasReverted": "{{fusionName}}は {{pokemonName}}に 回帰した。", + "releaseConfirmation": "本当に {{pokemonName}}を 逃がしますか?", + "releaseInBattle": "戦闘中の ポケモンを\n逃がすことは できません!", + "selectAMove": "技を 選んでください。", + "changeQuantity": "移動する アイテムを 選んでください。\n< と > で 数量が 変えられる。", + "selectAnotherPokemonToSplice": "もう一つの ポケモンを 選んで 合体する。", + "cancel": "キャンセル", + "able": "可能", + "notAble": "不可能", + "learned": "覚えている", + "goodbye": "グッバイ {{pokemonName}}!", + "byebye": "ばいばい {{pokemonName}}!", + "farewell": "さようなら {{pokemonName}}!", + "soLong": "じゃあね {{pokemonName}}!", + "thisIsWhereWePart": "これでお別れだね {{pokemonName}}!", + "illMissYou": "恋しく思うよ {{pokemonName}}!", + "illNeverForgetYou": "一生忘れない {{pokemonName}}!", + "untilWeMeetAgain": "また出会える日まで、{{pokemonName}}!", + "sayonara": "さらば {{pokemonName}}!", + "smellYaLater": "そんじゃ あばよ {{pokemonName}}!" +} diff --git a/src/locales/ja/pokemon-info-container.json b/src/locales/ja/pokemon-info-container.json index 796ac706731..287eed23c74 100644 --- a/src/locales/ja/pokemon-info-container.json +++ b/src/locales/ja/pokemon-info-container.json @@ -1,7 +1,7 @@ { - "moveset": "わざ", - "gender": "せいべつ:", - "ability": "とくせい:", - "nature": "せいかく:", - "form": "すがた:" -} \ No newline at end of file + "moveset": "技", + "gender": "性別:", + "ability": "特性:", + "nature": "性格:", + "form": "姿:" +} diff --git a/src/locales/ja/pokemon-info.json b/src/locales/ja/pokemon-info.json index 9b7a7506953..456b4949839 100644 --- a/src/locales/ja/pokemon-info.json +++ b/src/locales/ja/pokemon-info.json @@ -2,21 +2,22 @@ "Stat": { "HP": "HP", "HPshortened": "HP", - "ATK": "こうげき", - "ATKshortened": "こうげき", - "DEF": "ぼうぎょ", - "DEFshortened": "ぼうぎょ", - "SPATK": "とくこう", - "SPATKshortened": "とくこう", - "SPDEF": "とくぼう", - "SPDEFshortened": "とくぼう", - "SPD": "すばやさ", - "SPDshortened": "すばやさ", - "ACC": "めいちゅう", - "EVA": "かいひ" + "ATK": "攻撃", + "ATKshortened": "攻撃", + "DEF": "防御", + "DEFshortened": "防御", + "SPATK": "特攻", + "SPATKshortened": "特攻", + "SPDEF": "特防", + "SPDEFshortened": "特防", + "SPD": "素早さ", + "SPDshortened": "素早さ", + "ACC": "命中", + "EVA": "回避", + "HPStat": "HP" }, "Type": { - "UNKNOWN": "Unknown", + "UNKNOWN": "???", "NORMAL": "ノーマル", "FIGHTING": "かくとう", "FLYING": "ひこう", @@ -37,4 +38,4 @@ "FAIRY": "フェアリー", "STELLAR": "ステラ" } -} \ No newline at end of file +} diff --git a/src/locales/ja/pokemon-summary.json b/src/locales/ja/pokemon-summary.json index 9e26dfeeb6e..cf35befe6fd 100644 --- a/src/locales/ja/pokemon-summary.json +++ b/src/locales/ja/pokemon-summary.json @@ -1 +1,44 @@ -{} \ No newline at end of file +{ + "pokemonInfo": "ポケモン情報", + "status": "ステータス", + "powerAccuracyCategory": "威力\n命中\n分類", + "type": "タイプ", + "unknownTrainer": "???", + "ot": "親", + "nature": "性格", + "expPoints": "経験値", + "nextLv": "次のレベルまで", + "cancel": "キャンセル", + "memoString": "{{natureFragment}}な性格。\n{{metFragment}}", + "metFragment": { + "normal": "{{biome}}で\nLv.{{level}}の時に出会った。", + "apparently": "{{biome}}で\nLv.{{level}}の時に出会ったようだ。" + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" + } +} diff --git a/src/locales/ja/pokemon.json b/src/locales/ja/pokemon.json index 6c182c09f86..e6fcd02a750 100644 --- a/src/locales/ja/pokemon.json +++ b/src/locales/ja/pokemon.json @@ -437,7 +437,7 @@ "bronzor": "ドーミラー", "bronzong": "ドータクン", "bonsly": "ウソハチ", - "mime_jr.": "マネネ", + "mime_jr": "マネネ", "happiny": "ピンプク", "chatot": "ペラップ", "spiritomb": "ミカルゲ", @@ -770,7 +770,7 @@ "sandygast": "スナバァ", "palossand": "シロデスナ", "pyukumuku": "ナマコブシ", - "type:_null": "タイプ:ヌル", + "type_null": "タイプ:ヌル", "silvally": "シルヴァディ", "minior": "メテノ", "komala": "ネッコアラ", @@ -863,7 +863,7 @@ "obstagoon": "タチフサグマ", "perrserker": "ニャイキング", "cursola": "サニゴーン", - "sirfetch_d": "ネギガナイト", + "sirfetchd": "ネギガナイト", "mr_rime": "バリコオル", "runerigus": "デスバーン", "milcery": "マホミル", @@ -1081,4 +1081,4 @@ "paldea_tauros": "ケンタロス", "paldea_wooper": "ウパー", "bloodmoon_ursaluna": "ガチグマ" -} \ No newline at end of file +} diff --git a/src/locales/ja/run-history.json b/src/locales/ja/run-history.json index 222f7de728d..3bf9de32c68 100644 --- a/src/locales/ja/run-history.json +++ b/src/locales/ja/run-history.json @@ -28,10 +28,10 @@ "SPDshortened": "速さ", "runInfo": "ラン情報", "money": "お金", - "runLength": "ラン最高ウェーブ", - "viewHeldItems": "手持ちアイテム", - "hallofFameText": "殿堂へようこそ!", - "hallofFameText_female": "殿堂へようこそ!", + "runLength": "時間", + "viewHeldItems": "持たせたアイテム", + "hallofFameText": "殿堂入り おめでとう!", + "hallofFameText_female": "殿堂入り おめでとう!", "viewHallOfFame": "殿堂登録を見る!", "viewEndingSplash": "クリア後のアートを見る!" -} \ No newline at end of file +} diff --git a/src/locales/ja/save-slot-select-ui-handler.json b/src/locales/ja/save-slot-select-ui-handler.json index a84e3aca23d..73250a08f4b 100644 --- a/src/locales/ja/save-slot-select-ui-handler.json +++ b/src/locales/ja/save-slot-select-ui-handler.json @@ -1,7 +1,7 @@ { "overwriteData": "選択した スロットに データを 上書きします?", "loading": "読込中…", - "wave": "波", + "wave": "ラウンド", "lv": "Lv", "empty": "なし" -} \ No newline at end of file +} diff --git a/src/locales/ja/settings.json b/src/locales/ja/settings.json index c88792979f6..afb2f94a047 100644 --- a/src/locales/ja/settings.json +++ b/src/locales/ja/settings.json @@ -6,19 +6,21 @@ "audio": "音声", "gamepad": "コントローラー", "keyboard": "キーボード", - "gameSpeed": "ゲームスピード", - "hpBarSpeed": "HPバーの増減スピード", - "expGainsSpeed": "EXPバーの増加スピード", - "expPartyDisplay": "パーティの経験値取得表示", + "gameSpeed": "ゲームの速さ", + "hpBarSpeed": "HPバー増減の速さ", + "expGainsSpeed": "経験値バー増加の速さ", + "expPartyDisplay": "手持ちの経験値取得表示", + "skipSeenDialogues": "もう見た話をスキップ", "battleStyle": "試合のルール", - "enableRetries": "リトライを有効にする", + "enableRetries": "再挑戦を有効にする", + "hideIvs": "個体値スキャナーを隠す", "tutorials": "チュートリアル", "touchControls": "タッチ操作", "vibrations": "振動", "normal": "普通", "fast": "早い", "faster": "とても早い", - "skip": "スキップ", + "skip": "飛ばす", "levelUpNotifications": "レベルアップ時のみ", "on": "オン", "off": "オフ", @@ -35,33 +37,71 @@ "moneyFormat": "お金の表示形式", "damageNumbers": "ダメージ表示", "simple": "シンプル", - "fancy": "Fancy", + "fancy": "オシャレ", "abbreviated": "省略", - "moveAnimations": "戦闘アニメ", + "moveAnimations": "戦闘アニメーション", "showStatsOnLevelUp": "レベルアップ時のステータス表示", + "candyUpgradeNotification": "飴アプグレ通知", "passivesOnly": "パッシブのみ", + "candyUpgradeDisplay": "飴アプグレ表示", "icon": "アイコン", "animation": "アニメーション", - "moveInfo": "技の情報表示", + "moveInfo": "技情報", + "showMovesetFlyout": "技情報表示", + "showArenaFlyout": "戦場情報表示", + "showTimeOfDayWidget": "時刻指標", + "timeOfDayAnimation": "時刻指標アニメーション", + "bounce": "跳ねる", + "timeOfDay_back": "跳ね返る", + "spriteSet": "スプライト設定", + "consistent": "一貫", + "mixedAnimated": "アニメーションミックス", + "fusionPaletteSwaps": "吸収合体ポケモンの色違い", "playerGender": "プレイヤーの性別", - "typeHints": "相性のヒント", + "typeHints": "タイプ相性ヒント", "masterVolume": "マスターボリューム", - "bgmVolume": "BGMのボリューム", - "seVolume": "SEのボリューム", + "bgmVolume": "BGMボリューム", + "fieldVolume": "フィールドボリューム", + "seVolume": "SEボリューム", + "uiVolume": "UIボリューム", + "musicPreference": "BGM設定", + "mixed": "ミックス", + "gamepadPleasePlug": "コントローラーを 接続してください\nまたは、ボタンを 押してください", + "delete": "削除", + "keyboardPleasePress": "キーを押してください", "reset": "リセット", "requireReload": "再読み込みが必要", "action": "決定", "back": "戻る", + "pressToBind": "押下でキーバインド", + "pressButton": "ボタンを押してください", "buttonUp": "上", "buttonDown": "下", "buttonLeft": "左", "buttonRight": "右", "buttonAction": "決定", "buttonMenu": "メニュー", - "buttonSubmit": "Submit", + "buttonSubmit": "提出", "buttonCancel": "キャンセル", - "alt": " (代替)", + "buttonStats": "能力変化表示", + "buttonCycleForm": "フォルム変更", + "buttonCycleShiny": "色違い変更", + "buttonCycleGender": "性別変更", + "buttonCycleAbility": "特性変更", + "buttonCycleNature": "性格変更", + "buttonCycleVariant": "色変更", + "buttonSpeedUp": "速さを上げる", + "buttonSlowDown": "速さを下げる", + "alt": "(代替)", "mute": "ミュート", "controller": "コントローラー", - "gamepadSupport": "コントローラーサポート" + "gamepadSupport": "コントローラーサポート", + "showBgmBar": "BGMの名前を表示", + "moveTouchControls": "タッチ移動操作", + "shopOverlayOpacity": "ショップオーバレイ不透明度", + "shopCursorTarget": "ショップカーソル初位置", + "rewards": "ご褒美", + "reroll": "選択肢変更", + "shop": "ショップ", + "checkTeam": "手持ちを確認" } diff --git a/src/locales/ja/splash-messages.json b/src/locales/ja/splash-messages.json index 9e26dfeeb6e..b7378e7a916 100644 --- a/src/locales/ja/splash-messages.json +++ b/src/locales/ja/splash-messages.json @@ -1 +1,36 @@ -{} \ No newline at end of file +{ + "battlesWon": "Battles Won!", + "joinTheDiscord": "Join the Discord!", + "infiniteLevels": "Infinite Levels!", + "everythingStacks": "Everything Stacks!", + "optionalSaveScumming": "Optional Save Scumming!", + "biomes": "35 Biomes!", + "openSource": "Open Source!", + "playWithSpeed": "Play with 5x Speed!", + "liveBugTesting": "Live Bug Testing!", + "heavyInfluence": "Heavy RoR2 Influence!", + "pokemonRiskAndPokemonRain": "Pokémon Risk and Pokémon Rain!", + "nowWithMoreSalt": "Now with 33% More Salt!", + "infiniteFusionAtHome": "Infinite Fusion at Home!", + "brokenEggMoves": "Broken Egg Moves!", + "magnificent": "Magnificent!", + "mubstitute": "Mubstitute!", + "thatsCrazy": "That's Crazy!", + "oranceJuice": "Orance Juice!", + "questionableBalancing": "Questionable Balancing!", + "coolShaders": "Cool Shaders!", + "aiFree": "AI-Free!", + "suddenDifficultySpikes": "Sudden Difficulty Spikes!", + "basedOnAnUnfinishedFlashGame": "Based on an Unfinished Flash Game!", + "moreAddictiveThanIntended": "More Addictive than Intended!", + "mostlyConsistentSeeds": "Mostly Consistent Seeds!", + "achievementPointsDontDoAnything": "Achievement Points Don't Do Anything!", + "youDoNotStartAtLevel": "You Do Not Start at Level 2000!", + "dontTalkAboutTheManaphyEggIncident": "Don't Talk About the Manaphy Egg Incident!", + "alsoTryPokengine": "Also Try Pokéngine!", + "alsoTryEmeraldRogue": "Also Try Emerald Rogue!", + "alsoTryRadicalRed": "Also Try Radical Red!", + "eeveeExpo": "Eevee Expo!", + "ynoproject": "YNOproject!", + "breedersInSpace": "Breeders in space!" +} diff --git a/src/locales/ja/starter-select-ui-handler.json b/src/locales/ja/starter-select-ui-handler.json index 84eaa8598e9..cefc5322385 100644 --- a/src/locales/ja/starter-select-ui-handler.json +++ b/src/locales/ja/starter-select-ui-handler.json @@ -1,5 +1,5 @@ { - "confirmStartTeam": "この条件で チャレンジを 始めますか?", + "confirmStartTeam": "この手持ちで 始めますか?", "confirmExit": "終了しますか?", "invalidParty": "手持ちは チャレンジの 条件で 認められない!", "gen1": "1世代", @@ -16,14 +16,14 @@ "passive": "パッシブ:", "nature": "性格:", "eggMoves": "タマゴ技", - "start": "始める", "addToParty": "手持ちに入れる", + "removeFromParty": "手持ちから除く", "toggleIVs": "個体値を表示", "manageMoves": "技を並び替える", "manageNature": "性格を変える", "addToFavorites": "お気に入りにする", "removeFromFavorites": "お気に入りから除く", - "useCandies": "飴を使う", + "useCandies": "アメを使う", "selectNature": "性格を選んでください。", "selectMoveSwapOut": "入れ替えたい技を選んでください。", "selectMoveSwapWith": "他の技と交換してください。", @@ -36,9 +36,10 @@ "cycleAbility": ": 特性変更", "cycleNature": ": 性格変更", "cycleVariant": ": 色変更", + "goFilter": ": フィルタ へ ", "enablePassive": "パッシブ - オン", "disablePassive": "パッシブ - オフ", - "locked": "開放されていない", + "locked": "非開放", "disabled": "無効", "uncaught": "捕まっていない" -} \ No newline at end of file +} diff --git a/src/locales/ja/trainer-classes.json b/src/locales/ja/trainer-classes.json index 9e26dfeeb6e..aba294fbbbd 100644 --- a/src/locales/ja/trainer-classes.json +++ b/src/locales/ja/trainer-classes.json @@ -1 +1,130 @@ -{} \ No newline at end of file +{ + "ace_trainer": "エリートトレーナー", + "ace_trainer_female": "エリートトレーナー", + "ace_duo": "エリートコンビ", + "artist": "芸術家", + "artist_female": "芸術家", + "backers": "ファンクラブ", + "backpacker": "バックパッカー", + "backpacker_female": "バックパッカー", + "backpackers": "バックパッカーズ", + "baker": "ベーカリー", + "battle_girl": "バトルガール", + "beauty": "大人のおねえさん", + "beginners": "初心者", + "biker": "暴走族", + "black_belt": "カラテ王", + "breeder": "ポケモンブリーダー", + "breeder_female": "ポケモンブリーダー", + "breeders": "ブリーダーコンビ", + "clerk": "ビジネスマン", + "clerk_female": "OL", + "colleagues": "ビジネスパートナー", + "crush_kin": "格闘兄妹", + "cyclist": "サイクリング", + "cyclist_female": "サイクリング", + "cyclists": "サイクリングチーム", + "dancer": "ダンサー", + "dancer_female": "ダンサー", + "depot_agent": "鉄道員", + "doctor": "ドクター", + "doctor_female": "ドクター", + "firebreather": "火吹きやろう", + "fisherman": "釣り人", + "fisherman_female": "釣り人", + "gentleman": "ジェントルマン", + "guitarist": "ギタリスト", + "guitarist_female": "ギタリスト", + "harlequin": "クラウン", + "hiker": "山男", + "hooligans": "バッドチーム", + "hoopster": "バスケ選手", + "infielder": "野球選手", + "janitor": "清掃員", + "lady": "お嬢さま", + "lass": "ミニスカート", + "linebacker": "フットボーラー", + "maid": "メイド", + "madame": "マダム", + "medical_team": "医療チーム", + "musician": "ミュージシャン", + "hex_maniac": "オカルトマニア", + "nurse": "ナース", + "nursery_aide": "保育士", + "officer": "お巡りさん", + "parasol_lady": "パラソルおねえさん", + "pilot": "パイロット", + "pokéfan": "大好きクラブ", + "pokéfan_female": "大好きクラブ", + "pokéfan_family": "大好き夫婦", + "preschooler": "園児", + "preschooler_female": "園児", + "preschoolers": "園児たち", + "psychic": "サイキッカー", + "psychic_female": "サイキッカー", + "psychics": "サイキッ家", + "pokémon_ranger": "ポケモンレンジャー", + "pokémon_ranger_female": "ポケモンレンジャー", + "pokémon_rangers": "レンジャーズ", + "ranger": "レンジャー", + "restaurant_staff": "レストランスタッフ", + "rich": "お金持ち", + "rich_female": "お金持ち", + "rich_boy": "お坊っちゃま", + "rich_couple": "お二人さま", + "rich_kid": "ブルジョワ男子", + "rich_kid_female": "ブルジョワ女子", + "rich_kids": "ブルジョワ子達", + "roughneck": "スキンヘッズ", + "sailor": "船乗り", + "scientist": "研究員", + "scientist_female": "研究員", + "scientists": "研究チーム", + "smasher": "テニスプレイヤー", + "snow_worker": "冷凍作業員", + "snow_worker_female": "冷凍作業員", + "striker": "サッカー選手", + "school_kid": "塾帰り", + "school_kid_female": "塾帰り", + "school_kids": "塾生たち", + "swimmer": "海パンやろう", + "swimmer_female": "ビキニのおねえさん", + "swimmers": "水着カップル", + "twins": "双子ちゃん", + "veteran": "ベテラントレーナー", + "veteran_female": "ベテラントレーナー", + "veteran_duo": "ベテランコンビ", + "waiter": "ウエーター", + "waitress": "ウエートレス", + "worker": "作業員", + "worker_female": "作業員", + "workers": "作業班", + "youngster": "短パン小僧", + "rocket_grunt": "ロケット団の下っ端", + "rocket_grunts": " ロケット団の下っ端", + "rocket_grunt_female": "ロケット団の下っ端", + "magma_grunt": "マグマ団の下っ端", + "magma_grunt_female": "マグマ団の下っ端", + "magma_grunts": "マグマ団の下っ端", + "aqua_grunt": "アクア団の下っ端", + "aqua_grunt_female": "アクア団の下っ端", + "aqua_grunts": "アクア団の下っ端", + "galactic_grunt": "ギンガ団の下っ端", + "galactic_grunt_female": "ギンガ団の下っ端", + "galactic_grunts": "ギンガ団の下っ端", + "plasma_grunt": "プラスマ団の下っ端", + "plasma_grunt_female": "プラズマ団の下っ端", + "plasma_grunts": "プラズマ団の下っ端", + "flare_grunt": "フレア団の下っ端", + "flare_grunt_female": "フレア団の下っ端", + "flare_grunts": "フレア団の下っ端", + "aether_grunt": "エーテル財団の職員", + "aether_grunt_female": "エーテル財団の職員", + "aether_grunts": "エーテル財団の職員", + "skull_grunt": "スカル団の下っ端", + "skull_grunt_female": "スカル団の下っ端", + "skull_grunts": "スカル団の下っ端", + "macro_grunt": "マクロコスモスのトレーナ", + "macro_grunt_female": "マクロコスモスのトレーナ", + "macro_grunts": "マクロコスモスのトレーナ" +} diff --git a/src/locales/ja/trainer-names.json b/src/locales/ja/trainer-names.json index 9e26dfeeb6e..70841734b5b 100644 --- a/src/locales/ja/trainer-names.json +++ b/src/locales/ja/trainer-names.json @@ -1 +1,164 @@ -{} \ No newline at end of file +{ + "brock": "タケシ", + "misty": "カスミ", + "lt_surge": "マチス", + "erika": "エリカ", + "janine": "アンズ", + "sabrina": "ナツメ", + "blaine": "カツラ", + "giovanni": "サカキ", + "falkner": "ハヤト", + "bugsy": "ツクシ", + "whitney": "アカネ", + "morty": "マツバ", + "chuck": "シジマ", + "jasmine": "ミカン", + "pryce": "ヤナギ", + "clair": "イブキ", + "roxanne": "ツツジ", + "brawly": "トウキ", + "wattson": "テッセン", + "flannery": "アスナ", + "norman": "センリ", + "winona": "ナギ", + "tate": "フウ", + "liza": "ラン", + "juan": "アダン", + "roark": "ヒョウタ", + "gardenia": "ナタネ", + "maylene": "スモモ", + "crasher_wake": "マキシ", + "fantina": "メリッサ", + "byron": "トウガン", + "candice": "スズナ", + "volkner": "デンジ", + "cilan": "デント", + "chili": "ポッド", + "cress": "コーン", + "cheren": "チェレン", + "lenora": "アロエ", + "roxie": "ホミカ", + "burgh": "アーティ", + "elesa": "カミツレ", + "clay": "ヤーコン", + "skyla": "フウロ", + "brycen": "ハチク", + "drayden": "シャガ", + "marlon": "シズイ", + "viola": "ビオラ", + "grant": "ザクロ", + "korrina": "コルニ", + "ramos": "フクジ", + "clemont": "シトロン", + "valerie": "マーシュ", + "olympia": "ゴジカ", + "wulfric": "ウルップ", + "milo": "ヤロー", + "nessa": "ルリナ", + "kabu": "カブ", + "bea": "サイトウ", + "allister": "オニオン", + "opal": "ポプラ", + "bede": "ビート", + "gordie": "マクワ", + "melony": "メロン", + "piers": "ネズ", + "marnie": "マリィ", + "raihan": "キバナ", + "katy": "カエデ", + "brassius": "コルサ", + "iono": " ナンジャモ", + "kofu": "ハイダイ", + "larry": "アオキ", + "ryme": "ライム", + "tulip": "リップ", + "grusha": "グルーシャ", + "lorelei": "カンナ", + "bruno": "シバ", + "agatha": "キクコ", + "lance": "ワタル", + "will": "イツキ", + "koga": "キョウ", + "karen": "カリン", + "sidney": "カゲツ", + "phoebe": "フヨウ", + "glacia": "プリム", + "drake": "ゲンジ", + "aaron": "リョウ", + "bertha": "キクノ", + "flint": "オーバ", + "lucian": "ゴヨウ", + "shauntal": "シキミ", + "marshal": "レンブ", + "grimsley": "ギーマ", + "caitlin": "カトレア", + "malva": "パキラ", + "siebold": "ズミ", + "wikstrom": "ガンピ", + "drasna": "ドラセナ", + "hala": "ハラ", + "molayne": "マーレイン", + "olivia": "ライチ", + "acerola": "アセロラ", + "kahili": "カヒリ", + "rika": "チリ", + "poppy": "ポピー", + "hassel": "ハッサク", + "crispin": "アカマツ", + "amarys": "ネリネ", + "lacey": "タロ", + "drayton": "カキツバタ", + "blue": "グリーン", + "red": "レッド", + "steven": "ダイゴ", + "wallace": "ミクリ", + "cynthia": "シロナ", + "alder": "アデク", + "iris": "アイリス", + "diantha": "カルネ", + "hau": "ハウ", + "geeta": "オモダカ", + "nemona": "ネモ", + "kieran": "スグリ", + "leon": "ダンデ", + "rival": "フィン", + "rival_female": "アイヴィー", + "archer": "アポロ", + "ariana": "アテナ", + "proton": "ランス", + "petrel": "ラムダ", + "tabitha": "ホムラ", + "courtney": "カガリ", + "shelly": "イズミ", + "matt": "ウシオ", + "mars": "マーズ", + "jupiter": "ジュピター", + "saturn": "サターン", + "zinzolin": "ヴィオ", + "rood": "ロット", + "xerosic": "クセロシキ", + "bryony": "バラ", + "faba": "ザオボー", + "plumeria": "プルメリ", + "oleana": "オリーヴ", + + "maxie": "マツブサ", + "archie": "アオギリ", + "cyrus": "アカギ", + "ghetsis": "ゲーチス", + "lysandre": "フラダリ", + "lusamine": "ルザミーネ", + "guzma": "グズマ", + "rose": "ローズ", + + "blue_red_double": "グリーンとレッド", + "red_blue_double": "レッドとグリーン", + "tate_liza_double": "フウとラン", + "liza_tate_double": "ランとフウ", + "steven_wallace_double": "ダイゴとミクリ", + "wallace_steven_double": "ミクリとダイゴ", + "alder_iris_double": "アデクとアイリス", + "iris_alder_double": "アイリスとアデク", + "marnie_piers_double": "マリィとネズ", + "piers_marnie_double": "ネズとマリィ" +} diff --git a/src/locales/ja/trainer-titles.json b/src/locales/ja/trainer-titles.json index 9e26dfeeb6e..b3829c701e5 100644 --- a/src/locales/ja/trainer-titles.json +++ b/src/locales/ja/trainer-titles.json @@ -1 +1,38 @@ -{} \ No newline at end of file +{ + "elite_four": "四天王", + "elite_four_female": "四天王", + "gym_leader": "ジムリーダー", + "gym_leader_female": "ジムリーダー", + "gym_leader_double": "ジムリーダーコンビ", + "champion": "チャンピオン", + "champion_female": "チャンピオン", + "champion_double": "チャンピオンコンビ", + "rival": "ライバル", + "professor": "ポケモン博士", + "frontier_brain": "フロンティアブレーン", + "rocket_boss": "ロケット団ボス", + "magma_boss": "マグマ団リーダー", + "aqua_boss": "アクア団リーダー", + "galactic_boss": "ギンガ団ボス", + "plasma_boss": "プラズマ団ボス", + "flare_boss": "フレア団ボス", + "aether_boss": "エーテル代表", + "skull_boss": "スカル団ボス", + "macro_boss": "マクロコスモス社長", + + "rocket_admin": "ロケット団幹部", + "rocket_admin_female": "ロケット団幹部", + "magma_admin": "マグマ団幹部", + "magma_admin_female": "マグマロケット団幹部", + "aqua_admin": "アクア団幹部", + "aqua_admin_female": "アクア団幹部", + "galactic_commander": "ギンガ団幹部", + "galactic_commander_female": "ギンガ団幹部", + "plasma_sage": "プラズマ団賢人", + "plasma_admin": "プラズマ団賢人", + "flare_admin": "フレア団幹部", + "flare_admin_female": "フレア団幹部", + "aether_admin": "エーテル支部長", + "skull_admin": "スカル団幹部", + "macro_admin": "マクロコスモス" +} diff --git a/src/locales/ja/tutorial.json b/src/locales/ja/tutorial.json index 21b1b4856c8..99019b03f7f 100644 --- a/src/locales/ja/tutorial.json +++ b/src/locales/ja/tutorial.json @@ -1,10 +1,10 @@ { - "intro": "PokéRogueへようこそ!ログライク要素が\n加わったバトル中心のポケモンファンゲームです。\n$このゲームは収益を上げず、Pokémonおよび使用される\n著作権資産に対する所有権を主張しません。\n$ゲームはまだ作業中ですが、完全にプレイすることができます。\nバグ報告はディスコードコミュニティをご利用ください。\n$ゲームが遅い場合は、ブラウザ設定で「ハードウェア\nアクセラレーション」がオンになっていることを確認してください", - "accessMenu": "メニューにアクセスするには、入力待ちの間にMキーまたはEscを押してください。\nメニューには設定やさまざまな機能が含まれています。", - "menu": "このメニューから設定にアクセスできます。\n$設定ではゲームスピード、ウィンドウスタイル、\nおよびその他のオプションを変更できます。\n$ここにはさまざまな他の機能もありますので、\nぜひ確認してみてください!", - "starterSelect": "この画面でZキーやSpaceを押してポケモンを選択できます。\n選んだポケモンは自分の最初のパーティーになります。\n$最大6匹のパーティーで始めることができますが\nポケモンによってポイントがあり、合計10を超えてはなりません。\n$捕まえたりふかさせたりすることで\n選択できる性別、特性、フォルムなどの幅を広げることができます。\n$個体値も徐々に累積して高くなるので、\n同じポケモンをたくさん捕まえてみてください!", - "pokerus": "毎日ランダムでスターターの\n3種類に紫色の枠が表示されます。\n$登録されたスターターの中にあれば、\nパーティに追加してつよさを確認してみましょう!", - "statChange": "ポケモンは交代しない限り、\n次のバトルでも能力変化が維持されます。\n$その代わりに、トレーナーバトルや新しいバイオームに\n入る直前に自動的にリセットされます。\n$CキーまたはShiftキーを押し続けると、\n現在のポケモンの能力変化を確認できます。\n$Vキーを押すと、\n相手が使用した技も確認できます。\n$ただし、今のバトルで相手ポケモンがすでに\n使った技のみが表示されます。", - "selectItem": "バトルが終わるたびに、\nランダムなアイテム3つの中から1つを選んで獲得します。\n$種類は消耗品、ポケモンの持ち物、\n永続的なパッシブアイテムなど様々です。\n$ほとんどの消耗しない道具は\n効果が累積されます。\n$進化用など一部のアイテムは\n使用できる場合にのみ登場します。\n$持ち物を渡す機能を使用して\nポケモン同士で道具を持たせることもできます。\n$持ち物があれば、アイテム選択画面の\n右下に渡す機能が表示されます。\n$お金で消耗品を購入することもでき、\nウェーブが進むにつれて購入可能な種類が増えます。\n$アイテムを選択すると次のウェーブに進むため、\nまず消耗品の購入を行ってください。", - "eggGacha": "この画面でポケモンのたまごクーポンを\nガチャができます。\n$卵は戦闘を繰り返すうちにふかします。\n珍しいほどもっと長くかかります。\n$ふかさせたポケモンはパーティーに追加されず、\nスターティングに登録されます。\n$卵からふかしたポケモンは一般的に\n野生で捕まえたポケモンよりも高い個体値を持ちます。\n$一部のポケモンは卵からしか手に入りません。\n$各ガチャマシンがそれぞれ異なるボーナスを持っているため、\n好きな方を使ってみてください!," -} \ No newline at end of file + "intro": "PokéRogueへ ようこそ! ローグライク要素が\n加わった バトル中心の ポケモンファンゲームです。\n$このゲームは 収益を上げず、Pokémonおよび 使用される\n著作権資産に 対する所有権を 主張しません。\n$ゲームは まだ開発中ですが、完全に プレイすることが できます。\nバグ報告は ディスコードコミュニティを ご利用ください。\n$ゲームが 遅い場合は、ブラウザ設定で「ハードウェア\nアクセラレーション」が オンになっている ことを 確認してください。", + "accessMenu": "メニューを開くには 入力待ちの間に Mキー/Escを 押してください。\nメニューには 設定や 様々な機能が 含まれています。", + "menu": "このメニューから 設定が 開けます。\n$設定では、ゲームの速さや ウィンドウタイプなどの オプションを 変更できます。\n$ここには 様々な機能が ありますので、\nぜひ 確認してみてください!", + "starterSelect": "この画面では Zキー/空白キーを押して ポケモンが 選択できます。\n選んだポケモンは 最初の手持ちに なります。\n$各ポケモンは ポイントが ある。最大6つを 選べますが\nポケモンのポイントが 合計10を超えては いけません。\n$ポケモンを 捕まえたり タマゴからふかしたり することで\n選択できる 性別、特性、フォルムなどの 幅を広げられます。\n$個体値も 徐々に 累積して 高くなるので、\n同じポケモンを たくさん 捕まえて みてください!", + "pokerus": "毎日、無作為に スターターの\n3種類には 紫色の枠が 表示されます。\n$登録された スターターの 中に いれば、\n手持ちに加えて 強さを 確認してみましょう!", + "statChange": "ポケモンを 入れ替えない限り、\n次のバトルでも 能力変化は なくなりません。\n$その代わりに、トレーナーバトルや 新しいバイオームに\n入る直前に 自動的に 能力変化は 元に戻ります。\n$Cキー/Shiftキーを 押し続けると、\n場にいるポケモンの 能力変化を 確認できます。\n$Vキーを押すと、\n相手が出した技も 確認できます。\n$ただし、現在のバトルでの 相手ポケモンが\nすでに使った 技のみが 表示されます。", + "selectItem": "バトルが 終わるたびには、「ショップ」という\n画面で 3つのご褒美から 1つが選べます。\n$種類は 消耗品、ポケモンの持ち物や道具、\n永続的な パッシブアイテムなど 様々です。\n$ほとんどの 消耗しない 道具は\n効果が 累積されます。\n$例えば 進化アイテムなどの ご褒美は\n使用できる 場合にのみ 登場します。\n$持ち物や道具が\n手持ちポケモン間に 移動できる\n$持ち物や道具が あれば、ショップ画面の\n右下に「アイテム移行」が 表示されます。\n$ショップ画面で お金で 消耗品を 買えます。\nラウンドが 進むにつれて 買えるアイテムが 増えます。\n$ご褒美を 選択すると 次のラウンドに\n進むから、まず 消耗品を 買ってください。", + "eggGacha": "この画面では、「タマゴクーポン」で\nポケモンのタマゴを 取得できます。\n$タマゴは ラウンドが進めるうちに ふかします。\nタマゴのふかは レア度によって 時間が かかります。\n$ふかしたポケモンは 手持ちに 加えられず、\nスターターに 登録されます。\n$ふかしたポケモンは 一般的に\n野生ポケモンよりも 高い個体値があります。\n$あるポケモンは タマゴからしか 手に入りません。\n$各ガチャマシンは 個性的なボーナスが あるますから、\n好きな方から 引いてみてください!," +} diff --git a/src/locales/ja/weather.json b/src/locales/ja/weather.json index 92728b81461..e2d3c6c4e32 100644 --- a/src/locales/ja/weather.json +++ b/src/locales/ja/weather.json @@ -16,17 +16,17 @@ "snowStartMessage": "雪が 降り始めた!", "snowLapseMessage": "雪が 降っている!", "snowClearMessage": "雪が 止んだ!", - "fogStartMessage": "足下に 霧(きり)が立ち込めた!", - "fogLapseMessage": "足下に 霧(きり)が 立ち込めている!", - "fogClearMessage": "足下の 霧(きり)が消え去った!", + "fogStartMessage": "足下に 霧が 立ち込めた!", + "fogLapseMessage": "足下に 霧が 立ち込めている!", + "fogClearMessage": "足下の 霧が 消え去った!", "heavyRainStartMessage": "強い雨が 降り始めた!", "heavyRainLapseMessage": "強い雨が 降っている!", "heavyRainClearMessage": "強い雨が あがった!", "harshSunStartMessage": "日差しが とても強くなった!", "harshSunLapseMessage": "日差しが とても強い!", "harshSunClearMessage": "日差しが 元に戻った!", - "strongWindsStartMessage": "謎(なぞ)の 乱気流(らんきりゅう)が\nひこうポケモンを 護(まも)る!", - "strongWindsLapseMessage": "謎(なぞ)の 乱気流(らんきりゅう)の 勢(いきお)いは 止まらない!", - "strongWindsEffectMessage": "謎(なぞ)の 乱気流(らんきりゅう)が 攻撃(こうげき)を 弱(よわ)めた!", - "strongWindsClearMessage": "謎(なぞ)の 乱気流(らんきりゅう)が おさまった!" -} \ No newline at end of file + "strongWindsStartMessage": "謎の 乱気流が\nひこうポケモンを 護る!", + "strongWindsLapseMessage": "謎の 乱気流の 勢いは 止まらない!", + "strongWindsEffectMessage": "謎の 乱気流が 攻撃を 弱めた!", + "strongWindsClearMessage": "謎の 乱気流が おさまった!" +} diff --git a/src/locales/ko/ability-trigger.json b/src/locales/ko/ability-trigger.json index 5cf94e92235..b1f100c5c36 100644 --- a/src/locales/ko/ability-trigger.json +++ b/src/locales/ko/ability-trigger.json @@ -12,6 +12,7 @@ "blockItemTheft": "{{pokemonNameWithAffix}}의 {{abilityName}}에 의해\n도구를 빼앗기지 않는다!", "typeImmunityHeal": "{{pokemonNameWithAffix}}[[는]]\n{{abilityName}}[[로]] 체력이 회복되었다!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}[[는]] {{abilityName}} 때문에\n데미지를 입지 않는다!", + "fullHpResistType": "{{pokemonNameWithAffix}}[[는]] 등껍질을 빛나게 하여\n타입 상성을 왜곡시켰다!!", "moveImmunity": "{{pokemonNameWithAffix}}에게는\n효과가 없는 것 같다...", "reverseDrain": "{{pokemonNameWithAffix}}[[는]]\n해감액을 흡수했다!", "postDefendTypeChange": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]] 인해\n{{typeName}}타입이 됐다!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}}[[는]]\n세차게 튀는 오라를 발산하고 있다!", "postSummonDarkAura": "{{pokemonNameWithAffix}}[[는]]\n다크오라를 발산하고 있다!", "postSummonFairyAura": "{{pokemonNameWithAffix}}[[는]]\n페어리오라를 발산하고 있다!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}}[[는]]\n모든 오라를 제압한다!", "postSummonNeutralizingGas": "주위가 화학변화가스로 가득 찼다!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}}[[는]]\n두 가지 특성을 겸비한다!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}}[[는]]\n두 가지 특성을 겸비한다!", @@ -59,4 +61,4 @@ "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}의 재앙의목간에 의해\n주위의 {{statName}}[[가]] 약해졌다!", "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}의 재앙의구슬에 의해\n주위의 {{statName}}[[가]] 약해졌다!", "preventBerryUse": "{{pokemonNameWithAffix}}[[는]] 긴장해서\n나무열매를 먹을 수 없게 되었다!" -} \ No newline at end of file +} diff --git a/src/locales/ko/achv.json b/src/locales/ko/achv.json index 8546dff949c..9364c1c55b6 100644 --- a/src/locales/ko/achv.json +++ b/src/locales/ko/achv.json @@ -80,7 +80,7 @@ "100_RIBBONS": { "name": "마스터 리그 챔피언" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "팀워크", "description": "한 개 이상의 능력치가 최대 랭크일 때 배턴터치 사용" }, @@ -225,7 +225,7 @@ "name": "독침붕처럼 쏴라" }, "MONO_GHOST": { - "name": "누굴 부를 거야?" + "name": "무서운 게 딱 좋아!" }, "MONO_STEEL": { "name": "강철 심장" @@ -265,4 +265,4 @@ "name": "상성 전문가(였던 것)", "description": "거꾸로 배틀 챌린지 모드 클리어." } -} \ No newline at end of file +} diff --git a/src/locales/ko/arena-flyout.json b/src/locales/ko/arena-flyout.json index bfd24776cdc..e4b271691a5 100644 --- a/src/locales/ko/arena-flyout.json +++ b/src/locales/ko/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "마룻바닥세워막기", "craftyShield": "트릭가드", "tailwind": "순풍", - "happyHour": "해피타임" -} \ No newline at end of file + "happyHour": "해피타임", + "safeguard": "신비의부적" +} diff --git a/src/locales/ko/arena-tag.json b/src/locales/ko/arena-tag.json index 61586508a94..ce9922ab3bf 100644 --- a/src/locales/ko/arena-tag.json +++ b/src/locales/ko/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "우리 편의\n순풍이 멈췄다!", "tailwindOnRemoveEnemy": "상대의\n순풍이 멈췄다!", "happyHourOnAdd": "모두 행복한 기분에\n휩싸였다!", - "happyHourOnRemove": "기분이 원래대로 돌아왔다." + "happyHourOnRemove": "기분이 원래대로 돌아왔다.", + "safeguardOnAdd": "필드 전체가 신비의 베일에 둘러싸였다!", + "safeguardOnAddPlayer": "우리 편은 신비의 베일에 둘러싸였다!", + "safeguardOnAddEnemy": "상대 편은 신비의 베일에 둘러싸였다!", + "safeguardOnRemove": "필드를 감싸던 신비의 베일이 없어졌다!", + "safeguardOnRemovePlayer": "우리 편을 감싸던 신비의 베일이 없어졌다!", + "safeguardOnRemoveEnemy": "상대 편을 감싸던 신비의 베일이 없어졌다!" } \ No newline at end of file diff --git a/src/locales/ko/battle.json b/src/locales/ko/battle.json index 001720e4e5c..154ca04fd49 100644 --- a/src/locales/ko/battle.json +++ b/src/locales/ko/battle.json @@ -44,6 +44,10 @@ "moveNotImplemented": "{{moveName}}[[는]] 아직 구현되지 않아 사용할 수 없다…", "moveNoPP": "기술의 남은 포인트가 없다!", "moveDisabled": "{{moveName}}[[를]] 쓸 수 없다!", + "canOnlyUseMove": "{{pokemonName}}[[는]]\n{{moveName}}밖에 쓸 수 없다!", + "moveCannotBeSelected": "{{moveName}}[[를]] 쓸 수 없다!", + "disableInterruptedMove": "{{pokemonNameWithAffix}}의 {{moveName}}[[는]]\n사용할 수 없다.", + "throatChopInterruptedMove": "{{pokemonName}}[[는]]\n지옥찌르기 효과로 기술을 쓸 수 없다!", "noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.", "noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!", "noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!", @@ -61,6 +65,7 @@ "skipItemQuestion": "아이템을 받지 않고 넘어가시겠습니까?", "itemStackFull": "{{fullItemName}}의 소지 한도에 도달했습니다.\n{{itemname}}[[를]] 대신 받습니다.", "eggHatching": "어라…?", + "eggSkipPrompt": "알 부화 요약 화면으로 바로 넘어가시겠습니까?", "ivScannerUseQuestion": "{{pokemonName}}에게 개체값탐지기를 사용하시겠습니까?", "wildPokemonWithAffix": "야생 {{pokemonName}}", "foePokemonWithAffix": "상대 {{pokemonName}}", @@ -89,10 +94,10 @@ "statSeverelyFell_other": "{{pokemonNameWithAffix}}의\n{{stats}}[[가]] 매우 크게 떨어졌다!", "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}의\n{{stats}}[[는]] 더 떨어지지 않는다!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}의\n{{stats}}[[는]] 더 떨어지지 않는다!", - "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", + "transformedIntoType": "{{pokemonName}}[[는]]\n{{type}}타입이 됐다!", "retryBattle": "이 배틀의 처음부터 재도전하시겠습니까?", "unlockedSomething": "{{unlockedThing}}[[가]]\n해금되었다.", "congratulations": "축하합니다!", "beatModeFirstTime": "{{speciesName}}[[가]] {{gameMode}} 모드를 처음으로 클리어했다!\n{{newModifier}}[[를]] 손에 넣었다!", "ppReduced": "{{targetName}}의\n{{moveName}}[[를]] {{reduction}} 깎았다!" -} \ No newline at end of file +} diff --git a/src/locales/ko/battler-tags.json b/src/locales/ko/battler-tags.json index 47ddb26c183..21e548a01a6 100644 --- a/src/locales/ko/battler-tags.json +++ b/src/locales/ko/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}}[[는]] 소금절이의\n데미지를 입고 있다.", "cursedOnAdd": "{{pokemonNameWithAffix}}[[는]] 자신의 체력을 깎아서\n{{pokemonName}}에게 저주를 걸었다!", "cursedLapse": "{{pokemonNameWithAffix}}[[는]]\n저주받고 있다!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}}[[는]]\n{{stockpiledCount}}개 비축했다!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}}[[는]]\n{{stockpiledCount}}개 비축했다!", + "disabledOnAdd": "{{pokemonNameWithAffix}}의 {{moveName}}[[는]]\n사용할 수 없다!", + "disabledLapse": "{{pokemonNameWithAffix}}의 {{moveName}}[[는]]\n이제 사용할 수 있다.", + "tarShotOnAdd": "{{pokemonNameWithAffix}}[[는]] 불꽃에 약해졌다!" +} diff --git a/src/locales/ko/challenges.json b/src/locales/ko/challenges.json index 7efdfef3570..07a9052eebf 100644 --- a/src/locales/ko/challenges.json +++ b/src/locales/ko/challenges.json @@ -1,6 +1,7 @@ { "title": "챌린지 조건 설정", "illegalEvolution": "{{pokemon}}[[는]] 현재의 챌린지에\n부적합한 포켓몬이 되었습니다!", + "noneSelected": "미선택", "singleGeneration": { "name": "단일 세대", "desc": "{{gen}}의 포켓몬만 사용할 수 있습니다.", diff --git a/src/locales/ko/dialogue-misc.json b/src/locales/ko/dialogue-misc.json index 8445c5c4810..f24fc79ea99 100644 --- a/src/locales/ko/dialogue-misc.json +++ b/src/locales/ko/dialogue-misc.json @@ -1,6 +1,6 @@ { - "ending": "@c{smile}오? 이긴거야?@d{96} @c{smile_eclosed}진즉 알았어야 했는데.\n아무튼, 돌아왔구나.\n$@c{smile}다 끝난거야.@d{64} 네가 굴레를 끝장냈어.\n$@c{serious_smile_fists}네 꿈도 이뤄졌고말야.\n진짜로 한 번도 안 졌잖아.\n$@c{neutral}기억하는 건 우리들 뿐일 모양이지만.@d{96}\n그래도, 괜찮지?\n$@c{serious_smile_fists}오늘의 일은\n너와 나의 마음 속에 항상 함께할 거야.\n$@c{smile_eclosed}여기 구경도 충분히 했으니\n이제 집에 가자.\n$@c{serious_smile_fists}되돌아가서, 다시 배틀을 할 수도 있지 않을까?\n네가 원한다면 말야.", - "ending_female": "@c{shock}돌아왔구나?@d{32} 그 말은…@d{96} 이겼어?!\n@c{smile_ehalf}그럴 줄 알았다니까.\n$@c{smile_eclosed}물론… 언제나 느껴왔지.\n@c{smile}끝난 거, 맞지? 이 굴레를 말이야.\n$@c{smile_ehalf}네 꿈도 이뤘고 말이야.\n어떻게 한번도 안 졌대?\n$네가 한 일은 나만 기억하게 될 모양이지만.\n@c{angry_mopen}나, 안 까먹어볼 테니까!\n$@c{smile_wave_wink}농담이야!@d{64} @c{smile}절대 안 잊어버릴 거야.@d{32}\n마음 속엔 쭉 남아있을 수 있게.\n$@c{smile_wave}어쨌든,@d{64} 시간이 좀 늦었어…@d{96}\n이런 곳에서 할 말은 아닌가?\n$집에 가자. @c{smile_wave_wink}아마 내일은,\n추억을 되짚어보기 위한 배틀을 해볼 수 있을 거야.", + "ending": "@c{shock}돌아왔구나?@d{32} 그 말은…@d{96} 이겼어?!\n@c{smile_ehalf}그럴 줄 알았다니까.\n$@c{smile_eclosed}물론… 언제나 느껴왔지.\n@c{smile}끝난 거, 맞지? 이 굴레를 말이야.\n$@c{smile_ehalf}네 꿈도 이뤘고 말이야.\n어떻게 한번도 안 졌대?\n$네가 한 일은 나만 기억하게 될 모양이지만.\n@c{angry_mopen}나, 안 까먹어볼 테니까!\n$@c{smile_wave_wink}농담이야!@d{64} @c{smile}절대 안 잊어버릴 거야.@d{32}\n마음 속엔 쭉 남아있을 수 있게.\n$@c{smile_wave}어쨌든,@d{64} 시간이 좀 늦었어…@d{96}\n이런 곳에서 할 말은 아닌가?\n$집에 가자. @c{smile_wave_wink}아마 내일은,\n추억을 되짚어보기 위한 배틀을 해볼 수 있을 거야.", + "ending_female": "@c{smile}오? 이긴거야?@d{96} @c{smile_eclosed}진즉 알았어야 했는데.\n아무튼, 돌아왔구나.\n$@c{smile}다 끝난거야.@d{64} 네가 굴레를 끝장냈어.\n$@c{serious_smile_fists}네 꿈도 이뤄졌고말야.\n진짜로 한 번도 안 졌잖아.\n$@c{neutral}기억하는 건 우리들 뿐일 모양이지만.@d{96}\n그래도, 괜찮지?\n$@c{serious_smile_fists}오늘의 일은\n너와 나의 마음 속에 항상 함께할 거야.\n$@c{smile_eclosed}여기 구경도 충분히 했으니\n이제 집에 가자.\n$@c{serious_smile_fists}되돌아가서, 다시 배틀을 할 수도 있지 않을까?\n네가 원한다면 말야.", "ending_endless": "끝에 도달하신 것을 축하드립니다!\n더 많은 컨텐츠를 기다려주세요.", "ending_name": "Devs" -} \ No newline at end of file +} diff --git a/src/locales/ko/fight-ui-handler.json b/src/locales/ko/fight-ui-handler.json index a5f961095e1..d950e605896 100644 --- a/src/locales/ko/fight-ui-handler.json +++ b/src/locales/ko/fight-ui-handler.json @@ -2,6 +2,7 @@ "pp": "PP", "power": "위력", "accuracy": "명중률", - "abilityFlyInText": " {{pokemonName}}의 {{passive}}{{abilityName}}", - "passive": "패시브 " -} \ No newline at end of file + "abilityFlyInText": " {{pokemonName}}의\n{{passive}}{{abilityName}}", + "passive": "패시브 ", + "teraHover": "{{type}} 테라스탈" +} diff --git a/src/locales/ko/menu.json b/src/locales/ko/menu.json index 7976e0bedcf..a9db630f7a1 100644 --- a/src/locales/ko/menu.json +++ b/src/locales/ko/menu.json @@ -51,5 +51,7 @@ "renamePokemon": "포켓몬의 닉네임은?", "rename": "닉네임 바꾸기", "nickname": "닉네임", - "errorServerDown": "서버 연결 중 문제가 발생했습니다.\n\n이 창을 종료하지 않고 두면,\n게임은 자동으로 재접속됩니다." + "errorServerDown": "서버 연결 중 문제가 발생했습니다.\n\n이 창을 종료하지 않고 두면,\n게임은 자동으로 재접속됩니다.", + "noSaves": "기기에 세이브 파일이 없습니다!", + "tooManySaves": "기기에 세이브 파일이 너무 많습니다!" } diff --git a/src/locales/ko/modifier-type.json b/src/locales/ko/modifier-type.json index 3d282dfffd1..e957fd0d58a 100644 --- a/src/locales/ko/modifier-type.json +++ b/src/locales/ko/modifier-type.json @@ -47,10 +47,14 @@ "description": "포켓몬의 성격을 {{natureName}}[[로]] 바꾸고 스타팅에도 등록한다." }, "DoubleBattleChanceBoosterModifierType": { - "description": "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률이 두 배가 된다." + "description": "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률이 4배가 된다." }, - "TempBattleStatBoosterModifierType": { - "description": "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}[[가]] 한 단계 증가한다." + "TempStatStageBoosterModifierType": { + "description": "자신의 모든 포켓몬이 5번의 배틀 동안 {{stat}}[[가]] {{amount}}단계 증가한다.", + "extra": { + "stage": "1 스테이지", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승한다." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "자신의 모든 포켓몬의 레벨이 {{levels}}만큼 상승한다." }, - "PokemonBaseStatBoosterModifierType": { - "description": "지니게 하면 {{statName}} 종족값을 10% 올려준다. 개체값이 높을수록 더 많이 누적시킬 수 있다." + "BaseStatBoosterModifierType": { + "description": "지니게 하면 {{stat}} 종족값을 10% 올려준다. 개체값이 높을수록 더 많이 누적시킬 수 있다." }, "AllPokemonFullHpRestoreModifierType": { "description": "자신의 포켓몬의 HP를 모두 회복한다." @@ -248,6 +252,12 @@ "name": "초점렌즈", "description": "약점이 보이는 렌즈. 지니게 한 포켓몬의 기술이 급소에 맞기 쉬워진다." }, + "DIRE_HIT": { + "name": "크리티컬커터", + "extra": { + "raises": "급소율" + } + }, "LEEK": { "name": "대파", "description": "매우 길고 단단한 줄기. 파오리에게 지니게 하면 기술이 급소에 맞기 쉬워진다." @@ -411,25 +421,13 @@ "description": "메타몽에게 지니게 하면 스피드가 올라가는 이상한 가루. 매우 잘고 단단하다." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "플러스파워", "x_defense": "디펜드업", "x_sp_atk": "스페셜업", "x_sp_def": "스페셜가드", "x_speed": "스피드업", - "x_accuracy": "잘-맞히기", - "dire_hit": "크리티컬커터" - }, - "TempBattleStatBoosterStatName": { - "ATK": "공격", - "DEF": "방어", - "SPATK": "특수공격", - "SPDEF": "특수방어", - "SPD": "스피드", - "ACC": "명중률", - "CRIT": "급소율", - "EVA": "회피율", - "DEFAULT": "???" + "x_accuracy": "잘-맞히기" }, "AttackTypeBoosterItem": { "silk_scarf": "실크스카프", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "드래곤메모리", "DARK_MEMORY": "다크메모리", "FAIRY_MEMORY": "페어리메모리", - "BLANK_MEMORY": "빈메모리" + "NORMAL_MEMORY": "일반메모리" } -} \ No newline at end of file +} diff --git a/src/locales/ko/modifier.json b/src/locales/ko/modifier.json index 281ecf61943..b04e51f30cb 100644 --- a/src/locales/ko/modifier.json +++ b/src/locales/ko/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{pokemonNameWithAffix}}[[는]]\n{{typeName}}[[로]] 인해 조금 회복했다.", "hitHealApply": "{{pokemonNameWithAffix}}[[는]]\n{{typeName}}[[로]] 인해 조금 회복했다.", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}}[[는]] {{typeName}}[[로]]\n정신을 차려 싸울 수 있게 되었다!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}[[는]] {{typeName}}[[로]]\n상태를 원래대로 되돌렸다!", + "resetNegativeStatStageApply": "{{pokemonNameWithAffix}}[[는]] {{typeName}}[[로]]\n상태를 원래대로 되돌렸다!", "moneyInterestApply": "{{typeName}}[[로]]부터\n₽{{moneyAmount}}[[를]] 받았다!", "turnHeldItemTransferApply": "{{pokemonName}}의 {{typeName}}[[는]]\n{{pokemonNameWithAffix}}의 {{itemName}}[[를]] 흡수했다!", "contactHeldItemTransferApply": "{{pokemonName}}의 {{typeName}}[[는]]\n{{pokemonNameWithAffix}}의 {{itemName}}[[를]] 가로챘다!", diff --git a/src/locales/ko/move-trigger.json b/src/locales/ko/move-trigger.json index 61dffa122a3..a8a6c0cf86f 100644 --- a/src/locales/ko/move-trigger.json +++ b/src/locales/ko/move-trigger.json @@ -3,6 +3,11 @@ "cutHpPowerUpMove": "{{pokemonName}}[[는]]\n체력을 깎아서 자신의 기술을 강화했다!", "absorbedElectricity": "{{pokemonName}}는(은)\n전기를 흡수했다!", "switchedStatChanges": "{{pokemonName}}[[는]] 상대와 자신의\n능력 변화를 바꿨다!", + "switchedTwoStatChanges": "{{pokemonName}} 상대와 자신의 {{firstStat}}과 {{secondStat}}의 능력 변화를 바꿨다!", + "switchedStat": "{{pokemonName}}[[는]] 서로의 {{stat}}[[를]] 교체했다!", + "sharedGuard": "{{pokemonName}}[[는]] 서로의 가드를 셰어했다!", + "sharedPower": "{{pokemonName}}[[는]] 서로의 파워를 셰어했다!", + "shiftedStats": "{{pokemonName}}[[는]] {{statToSwitch}}[[와]] {{statToSwitchWith}}[[를]] 바꿨다!", "goingAllOutForAttack": "{{pokemonName}}[[는]]\n전력을 다하기 시작했다!", "regainedHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!", "keptGoingAndCrashed": "{{pokemonName}}[[는]]\n의욕이 넘쳐서 땅에 부딪쳤다!", @@ -61,5 +66,7 @@ "suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!", "revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!", "swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!", - "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!", + "safeguard": "{{targetName}}[[는]] 신비의 베일이 지켜 주고 있다!", + "afterYou": "{{pokemonName}}[[는]]\n배려를 받아들이기로 했다!" +} diff --git a/src/locales/ko/pokemon-info.json b/src/locales/ko/pokemon-info.json index 1e26af64d0f..c708ee93c6e 100644 --- a/src/locales/ko/pokemon-info.json +++ b/src/locales/ko/pokemon-info.json @@ -13,8 +13,7 @@ "SPD": "스피드", "SPDshortened": "스피드", "ACC": "명중률", - "EVA": "회피율", - "HPStat": "HP" + "EVA": "회피율" }, "Type": { "UNKNOWN": "Unknown", diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index b7fc01cb148..c10046385e1 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -100,7 +100,7 @@ "moveTouchControls": "터치 컨트롤 이동", "shopOverlayOpacity": "상점 오버레이 투명도", "shopCursorTarget": "상점 커서 위치", - "items": "아이템", + "rewards": "아이템", "reroll": "갱신", "shop": "상점", "checkTeam": "파티 확인" diff --git a/src/locales/pt_BR/ability-trigger.json b/src/locales/pt_BR/ability-trigger.json index f6a11267f9d..cd47fd8e3dc 100644 --- a/src/locales/pt_BR/ability-trigger.json +++ b/src/locales/pt_BR/ability-trigger.json @@ -12,6 +12,7 @@ "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nprevine o roubo de itens!", "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaurou um pouco de PS!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evitou dano\ncom {{abilityName}}!", + "fullHpResistType": "{{pokemonNameWithAffix}} fez seu casco brilhar!\nEstá distorcendo o confronte de tipos!", "moveImmunity": "Isso não afeta {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} absorveu a gosma líquida!", "postDefendTypeChange": "{{abilityName}} de {{pokemonNameWithAffix}}\ntransformou-o no tipo {{typeName}}!", diff --git a/src/locales/pt_BR/achv.json b/src/locales/pt_BR/achv.json index acdec1ae306..93e982b60ea 100644 --- a/src/locales/pt_BR/achv.json +++ b/src/locales/pt_BR/achv.json @@ -84,7 +84,7 @@ "100_RIBBONS": { "name": "Fita de Diamante" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "Trabalho em Equipe", "description": "Use Baton Pass com pelo menos um atributo aumentado ao máximo" }, @@ -269,4 +269,4 @@ "name": "A torre da derrotA", "description": "Complete o desafio da Batalha Inversa.\n.asrevnI ahlataB ad oifased o etelpmoC" } -} \ No newline at end of file +} diff --git a/src/locales/pt_BR/arena-flyout.json b/src/locales/pt_BR/arena-flyout.json index e221fa6c0a5..a4be2727b27 100644 --- a/src/locales/pt_BR/arena-flyout.json +++ b/src/locales/pt_BR/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "Mat Block", "craftyShield": "Crafty Shield", "tailwind": "Tailwind", - "happyHour": "Happy Hour" + "happyHour": "Happy Hour", + "safeguard": "Safeguard" } diff --git a/src/locales/pt_BR/arena-tag.json b/src/locales/pt_BR/arena-tag.json index 20ef208c8fc..7ab1ecea721 100644 --- a/src/locales/pt_BR/arena-tag.json +++ b/src/locales/pt_BR/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "O Tailwind de sua equipe acabou!", "tailwindOnRemoveEnemy": "O Tailwind da equipe adversária acabou!", "happyHourOnAdd": "Todos foram envolvidos por uma atmosfera alegre!", - "happyHourOnRemove": "A atmosfera retornou ao normal." + "happyHourOnRemove": "A atmosfera retornou ao normal.", + "safeguardOnAdd": "O campo de batalha está envolto num véu místico!", + "safeguardOnAddPlayer": "Sua equipe se envolveu num véu místico!", + "safeguardOnAddEnemy": "A equipe adversária se envolveu num véu místico!", + "safeguardOnRemove": "O campo não está mais protegido por Safeguard!", + "safeguardOnRemovePlayer": "Sua equipe não está mais protegido por Safeguard!", + "safeguardOnRemoveEnemy": "A equipe adversária não está mais protegido por Safeguard!" } \ No newline at end of file diff --git a/src/locales/pt_BR/battle.json b/src/locales/pt_BR/battle.json index e34ce8efdca..08eeb99e0cd 100644 --- a/src/locales/pt_BR/battle.json +++ b/src/locales/pt_BR/battle.json @@ -44,6 +44,7 @@ "moveNotImplemented": "{{moveName}} ainda não foi implementado e não pode ser usado.", "moveNoPP": "Não há mais PP\npara esse movimento!", "moveDisabled": "Não se pode usar {{moveName}} porque foi desabilitado!", + "disableInterruptedMove": "{{moveName}} de {{pokemonNameWithAffix}}\nestá desabilitado!", "noPokeballForce": "Uma força misteriosa\nte impede de usar Poké Bolas.", "noPokeballTrainer": "Não se pode capturar\nPokémon dos outros!", "noPokeballMulti": "Não se pode lançar Poké Bolas\nquando há mais de um Pokémon!", @@ -61,6 +62,7 @@ "skipItemQuestion": "Tem certeza de que não quer escolher um item?", "itemStackFull": "O estoque de {{fullItemName}} está cheio.\nVocê receberá {{itemName}} no lugar.", "eggHatching": "Opa?", + "eggSkipPrompt": "Pular para súmario de ovos?", "ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?", "wildPokemonWithAffix": "{{pokemonName}} selvagem", "foePokemonWithAffix": "{{pokemonName}} adversário", @@ -89,10 +91,10 @@ "statSeverelyFell_other": "{{stats}} de {{pokemonNameWithAffix}} diminuíram severamente!", "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}} não vai mais diminuir!", "statWontGoAnyLower_other": "{{stats}} de {{pokemonNameWithAffix}} não vão mais diminuir!", - "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", + "transformedIntoType": "{{pokemonName}} se transformou\nno tipo {{type}}!", "ppReduced": "O PP do movimento {{moveName}} de\n{{targetName}} foi reduzido em {{reduction}}!", "retryBattle": "Você gostaria de tentar novamente desde o início da batalha?", "unlockedSomething": "{{unlockedThing}}\nfoi desbloqueado.", "congratulations": "Parabéns!", "beatModeFirstTime": "{{speciesName}} venceu o Modo {{gameMode}} pela primeira vez!\nVocê recebeu {{newModifier}}!" -} \ No newline at end of file +} diff --git a/src/locales/pt_BR/battler-tags.json b/src/locales/pt_BR/battler-tags.json index 560da13cc6f..ec6559e12e5 100644 --- a/src/locales/pt_BR/battler-tags.json +++ b/src/locales/pt_BR/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}} foi ferido pelo {{moveName}}!", "cursedOnAdd": "{{pokemonNameWithAffix}} cortou seus PS pela metade e amaldiçoou {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} foi ferido pelo Curse!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} estocou {{stockpiledCount}}!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}} estocou {{stockpiledCount}}!", + "disabledOnAdd": "{{moveName}} de {{pokemonNameWithAffix}}\nfoi desabilitado!", + "disabledLapse": "{{moveName}} de {{pokemonNameWithAffix}}\nnão está mais desabilitado.", + "tarShotOnAdd": "{{pokemonNameWithAffix}} tornou-se mais fraco ao fogo!" +} diff --git a/src/locales/pt_BR/challenges.json b/src/locales/pt_BR/challenges.json index 6b20a92f4f6..9dc613651a6 100644 --- a/src/locales/pt_BR/challenges.json +++ b/src/locales/pt_BR/challenges.json @@ -1,6 +1,7 @@ { "title": "Desafios", "illegalEvolution": "{{pokemon}} não pode ser escolhido\nnesse desafio!", + "noneSelected": "Nada Selecionado", "singleGeneration": { "name": "Geração Única", "desc": "Você só pode user Pokémon da {{gen}} geração.", diff --git a/src/locales/pt_BR/dialogue-misc.json b/src/locales/pt_BR/dialogue-misc.json index 18eb2ba8c91..10e50aaa7e1 100644 --- a/src/locales/pt_BR/dialogue-misc.json +++ b/src/locales/pt_BR/dialogue-misc.json @@ -1,6 +1,6 @@ { - "ending": "@c{smile}Oh? Você venceu?@d{96} @c{smile_eclosed}Acho que eu deveria saber.\nMas, você está de volta agora.\n$@c{smile}Acabou.@d{64} Você quebrou o ciclo.\n$@c{serious_smile_fists}Você também realizou seu sonho, não é?\nVocê não perdeu nenhuma vez.\n$@c{neutral}Eu sou o único que vai lembrar o que você fez.@d{96}\nAcho que está tudo bem, não é?\n$@c{serious_smile_fists}Sua lenda sempre viverá em nossos corações.\n$@c{smile_eclosed}Enfim, já tive o suficiente deste lugar, não é? Vamos para casa.\n$@c{serious_smile_fists}Talvez quando voltarmos, possamos ter outra batalha?\nSe você estiver disposto.", - "ending_female": "@c{shock}Você está de volta?@d{32} Isso significa que…@d{96} você venceu?!\n@c{smile_ehalf}Eu deveria saber que você conseguiria.\n$@c{smile_eclosed}Claro… Eu sempre tive essa sensação.\n@c{smile}Acabou agora, certo? Você quebrou o ciclo.\n$@c{smile_ehalf}Você também realizou seu sonho, não foi?\nVocê não perdeu nenhuma vez.\n$Eu serei a única a lembrar o que você fez.\n@c{angry_mopen}Eu tentarei não esquecer!\n$@c{smile_wave_wink}Brincadeirinha!@d{64} @c{smile}Eu nunca esqueceria.@d{32}\nSua lenda viverá em nossos corações.\n$@c{smile_wave}De qualquer forma,@d{64} está ficando tarde…@d{96} Eu acho?\nÉ difícil dizer neste lugar.\n$Vamos para casa. @c{smile_wave_wink}Talvez amanhã possamos ter outra batalha, pelos velhos tempos?", + "ending": "@c{shock}Você está de volta?@d{32} Isso significa que…@d{96} você venceu?!\n@c{smile_ehalf}Eu deveria saber que você conseguiria.\n$@c{smile_eclosed}Claro… Eu sempre tive essa sensação.\n@c{smile}Acabou agora, certo? Você quebrou o ciclo.\n$@c{smile_ehalf}Você também realizou seu sonho, não foi?\nVocê não perdeu nenhuma vez.\n$Eu serei a única a lembrar o que você fez.\n@c{angry_mopen}Eu tentarei não esquecer!\n$@c{smile_wave_wink}Brincadeirinha!@d{64} @c{smile}Eu nunca esqueceria.@d{32}\nSua lenda viverá em nossos corações.\n$@c{smile_wave}De qualquer forma,@d{64} está ficando tarde…@d{96} Eu acho?\nÉ difícil dizer neste lugar.\n$Vamos para casa. @c{smile_wave_wink}Talvez amanhã possamos ter outra batalha, pelos velhos tempos?", + "ending_female": "@c{smile}Oh? Você venceu?@d{96} @c{smile_eclosed}Acho que eu deveria saber.\nMas, você está de volta agora.\n$@c{smile}Acabou.@d{64} Você quebrou o ciclo.\n$@c{serious_smile_fists}Você também realizou seu sonho, não é?\nVocê não perdeu nenhuma vez.\n$@c{neutral}Eu sou o único que vai lembrar o que você fez.@d{96}\nAcho que está tudo bem, não é?\n$@c{serious_smile_fists}Sua lenda sempre viverá em nossos corações.\n$@c{smile_eclosed}Enfim, já tive o suficiente deste lugar, não é? Vamos para casa.\n$@c{serious_smile_fists}Talvez quando voltarmos, possamos ter outra batalha?\nSe você estiver disposto.", "ending_endless": "Parabéns por alcançar o final atual!\nMais conteúdo chegará em breve.", "ending_name": "Desenvolvedores" -} \ No newline at end of file +} diff --git a/src/locales/pt_BR/fight-ui-handler.json b/src/locales/pt_BR/fight-ui-handler.json index f5c4379126d..8fc41f523eb 100644 --- a/src/locales/pt_BR/fight-ui-handler.json +++ b/src/locales/pt_BR/fight-ui-handler.json @@ -3,5 +3,6 @@ "power": "Poder", "accuracy": "Precisão", "abilityFlyInText": " {{passive}}{{pokemonName}}\n{{abilityName}}", - "passive": "Passiva de " -} \ No newline at end of file + "passive": "Passiva de ", + "teraHover": "Terastalizado {{type}}" +} diff --git a/src/locales/pt_BR/menu-ui-handler.json b/src/locales/pt_BR/menu-ui-handler.json index cc087c8335e..df654976d68 100644 --- a/src/locales/pt_BR/menu-ui-handler.json +++ b/src/locales/pt_BR/menu-ui-handler.json @@ -14,8 +14,8 @@ "importSlotSelect": "Selecione um slot para importar.", "exportSession": "Exportar sessão", "exportSlotSelect": "Selecione um slot para exportar.", - "importRunHistory":"Importar Histórico de Jogos", - "exportRunHistory":"Exportar Histórico de Jogos", + "importRunHistory": "Importar Histórico de Jogos", + "exportRunHistory": "Exportar Histórico de Jogos", "importData": "Importar dados", "exportData": "Exportar dados", "consentPreferences": "Opções de Privacidade", @@ -25,5 +25,5 @@ "unlinkGoogle": "Desconectar Google", "cancel": "Cancelar", "losingProgressionWarning": "Você vai perder todo o progresso desde o início da batalha. Confirmar?", - "noEggs": "Você não está chocando\nnenhum ovo no momento!" -} \ No newline at end of file + "noEggs": "Você não está chocando nenhum ovo\nno momento!" +} diff --git a/src/locales/pt_BR/modifier-type.json b/src/locales/pt_BR/modifier-type.json index 1787360b51e..f20a4ef3d0f 100644 --- a/src/locales/pt_BR/modifier-type.json +++ b/src/locales/pt_BR/modifier-type.json @@ -47,10 +47,14 @@ "description": "Muda a natureza do Pokémon para {{natureName}} e a desbloqueia permanentemente." }, "DoubleBattleChanceBoosterModifierType": { - "description": "Dobra as chances de encontrar uma batalha em dupla por {{battleCount}} batalhas." + "description": "Quadruplica as chances de encontrar uma batalha em dupla por {{battleCount}} batalhas." }, - "TempBattleStatBoosterModifierType": { - "description": "Aumenta o atributo de {{tempBattleStatName}} para todos os membros da equipe por 5 batalhas." + "TempStatStageBoosterModifierType": { + "description": "Aumenta o atributo de {{stat}} para todos os membros da equipe em {{amount}} por 5 batalhas.", + "extra": { + "stage": "um nível", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "Aumenta o poder dos ataques do tipo {{moveType}} de um Pokémon em 20%." @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "Aumenta em {{levels}} o nível de todos os membros da equipe." }, - "PokemonBaseStatBoosterModifierType": { - "description": "Aumenta o atributo base de {{statName}} em 10%. Quanto maior os IVs, maior o limite de aumento." + "BaseStatBoosterModifierType": { + "description": "Aumenta o atributo base de {{stat}} em 10%. Quanto maior os IVs, maior o limite de aumento." }, "AllPokemonFullHpRestoreModifierType": { "description": "Restaura totalmente os PS de todos os Pokémon." @@ -248,6 +252,12 @@ "name": "Lentes de Mira", "description": "Estas lentes facilitam o foco em pontos fracos. Aumenta a chance de acerto crítico de quem a segurar." }, + "DIRE_HIT": { + "name": "Direto", + "extra": { + "raises": "Chance de Acerto Crítico" + } + }, "LEEK": { "name": "Alho-poró", "description": "Esse talo de alho-poró muito longo e rígido aumenta a taxa de acerto crítico dos movimentos do Farfetch'd." @@ -411,25 +421,13 @@ "description": "Extremamente fino, porém duro, este pó estranho aumenta o atributo de Velocidade de Ditto." } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "Ataque X", "x_defense": "Defesa X", "x_sp_atk": "Ataque Esp. X", "x_sp_def": "Defesa Esp. X", "x_speed": "Velocidade X", - "x_accuracy": "Precisão X", - "dire_hit": "Direto" - }, - "TempBattleStatBoosterStatName": { - "ATK": "Ataque", - "DEF": "Defesa", - "SPATK": "Ataque Esp.", - "SPDEF": "Defesa Esp.", - "SPD": "Velocidade", - "ACC": "Precisão", - "CRIT": "Chance de Acerto Crítico", - "EVA": "Evasão", - "DEFAULT": "???" + "x_accuracy": "Precisão X" }, "AttackTypeBoosterItem": { "silk_scarf": "Lenço de Seda", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "Memória do Dragão", "DARK_MEMORY": "Memória Sombria", "FAIRY_MEMORY": "Memória de Fada", - "BLANK_MEMORY": "Memória Vazia" + "NORMAL_MEMORY": "Memória Normal" } } diff --git a/src/locales/pt_BR/modifier.json b/src/locales/pt_BR/modifier.json index 602a0be3a5b..38622de579e 100644 --- a/src/locales/pt_BR/modifier.json +++ b/src/locales/pt_BR/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsuas {{typeName}}!", "hitHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsua {{typeName}}!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi reanimado\npor sua {{typeName}}!", - "pokemonResetNegativeStatStageApply": "Os atributos diminuídos de {{pokemonNameWithAffix}} foram\nrestaurados por seu(sua) {{typeName}}!", + "resetNegativeStatStageApply": "Os atributos diminuídos de {{pokemonNameWithAffix}} foram\nrestaurados por seu(sua) {{typeName}}!", "moneyInterestApply": "Você recebeu um juros de ₽{{moneyAmount}}\nde sua {{typeName}}!", "turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi absorvido(a)\npelo {{typeName}} de {{pokemonName}}!", "contactHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi pego(a)\npela {{typeName}} de {{pokemonName}}!", diff --git a/src/locales/pt_BR/move-trigger.json b/src/locales/pt_BR/move-trigger.json index 416740dba0d..4549f83cdf1 100644 --- a/src/locales/pt_BR/move-trigger.json +++ b/src/locales/pt_BR/move-trigger.json @@ -61,5 +61,7 @@ "suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!", "revivalBlessing": "{{pokemonName}} foi reanimado!", "swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!", - "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!", + "safeguard": "{{targetName}} está protegido por Safeguard!", + "afterYou": "{{pokemonName}} aceitou a gentil oferta!" +} diff --git a/src/locales/pt_BR/pokemon-summary.json b/src/locales/pt_BR/pokemon-summary.json index 62add589847..4c427dbac4f 100644 --- a/src/locales/pt_BR/pokemon-summary.json +++ b/src/locales/pt_BR/pokemon-summary.json @@ -13,5 +13,32 @@ "metFragment": { "normal": "encontrado no Nv.{{level}},\n{{biome}}.", "apparently": "aparentemente encontrado no Nv.{{level}},\n{{biome}}." + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" } -} \ No newline at end of file +} diff --git a/src/locales/pt_BR/settings.json b/src/locales/pt_BR/settings.json index 58ccb45f86d..74f3918bed8 100644 --- a/src/locales/pt_BR/settings.json +++ b/src/locales/pt_BR/settings.json @@ -100,7 +100,7 @@ "moveTouchControls": "Mover Controles de Toque", "shopOverlayOpacity": "Opacidade da Loja", "shopCursorTarget": "Alvo do Cursor da Loja", - "items": "Itens", + "rewards": "Itens", "reroll": "Atualizar", "shop": "Loja", "checkTeam": "Checar Time" diff --git a/src/locales/pt_BR/tutorial.json b/src/locales/pt_BR/tutorial.json index e347ca6fbee..92ea0dd080f 100644 --- a/src/locales/pt_BR/tutorial.json +++ b/src/locales/pt_BR/tutorial.json @@ -1,10 +1,10 @@ { - "intro": "Bem-vindo ao PokéRogue! Este é um jogo Pokémon feito por fãs focado em batalhas com elementos roguelite.\n$Este jogo não é monetizado e não reivindicamos propriedade de Pokémon nem dos ativos protegidos\n$por direitos autorais usados.\n$O jogo é um trabalho em andamento, mas é totalmente jogável.\n$Para relatórios de bugs, use a comunidade no Discord.\n$Se o jogo estiver rodando lentamente, certifique-se de que a 'Aceleração de hardware' esteja ativada \n$nas configurações do seu navegador.", + "intro": "Bem-vindo ao PokéRogue!\n$Este é um fangame Pokémon focado em batalhas com elementos roguelite.\n$Este jogo não é monetizado e não reivindicamos propriedade do Pokémon nem dos ativos protegidos$por direitos autorais usados.\n$O jogo é um trabalho em andamento,\nmas totalmente jogável.\n$Para relatórios de bugs, use a comunidade do Discord.\n$Se o jogo rodar lentamente, certifique-se de que\na 'Aceleração de Hardware' esteja ativada$nas configurações do seu navegador.", "accessMenu": "Para acessar o menu, pressione M ou Esc.\n$O menu contém configurações e diversas funções.", - "menu": "A partir deste menu, você pode acessar as configurações. \n$Nas configurações, você pode alterar a velocidade do jogo,\n$o estilo da janela, entre outras opções. \n$Existem também vários outros recursos disponíveis aqui.\n$Não deixe de conferir todos eles!", - "starterSelect": "Aqui você pode escolher seus iniciais apertando a tecla Z ou\na Barra de Espaço.\n$Esses serão os primeiro Pokémon da sua equipe.\n$Cada inicial tem seu custo. Sua equipe pode ter até 6\nmembros, desde que a soma dos custos não ultrapasse 10. \n$Você pode escolher o gênero, a habilidade\ne até a forma do seu inicial.\n$Essas opções dependem das variantes dessa\nespécie que você já capturou ou chocou. \n$Os IVs de cada inicial são os melhores de todos os Pokémon\ndaquela espécie que você já capturou ou chocou.\n$Sempre capture vários Pokémon de várias espécies!", + "menu": "A partir deste menu, você pode\\nacessar as configurações.\n$A partir das configurações, você\npode alterar a velocidade do jogo,\n$o estilo da janela e outras opções.\n$Há também vários outros recursos aqui.\nCertifique-se de verificar todos eles!", + "starterSelect": "Nesta tela, você pode selecionar seus iniciais\npressionando Z ou a barra de espaço.\n$Esses serão os primeiros membros da sua equipe.\n$Cada inicial tem um custo. Sua equipe pode ter até 6 membros,\ndesde que desde que o custo total não exceda 10.\n$Você pode escolher o gênero, a habilidade\ne até a forma do seu inicial.\n$Essas opções dependem das variantes dessa\nespécie que você já capturou ou chocou.\n$Os IVs de cada inicial são os melhores de todos os Pokémon\ndaquela espécie que você já capturou ou chocou.\n$Sempre capture vários Pokémon de todas as espécies!", "pokerus": "Todo dia, 3 Pokémon iniciais ficam com uma borda roxa.\n$Caso veja um inicial que você possui com uma dessa, tente\nadicioná-lo a sua equipe. Lembre-se de olhar seu sumário!", "statChange": "As mudanças de atributos se mantém após a batalha desde que o Pokémon não seja trocado.\n$Seus Pokémon voltam a suas Poké Bolas antes de batalhas contra treinadores e de entrar em um novo bioma.\n$Para ver as mudanças de atributos dos Pokémon em campo, mantena C ou Shift pressionado durante a batalha.", "selectItem": "Após cada batalha, você pode escolher entre 3 itens aleatórios.\n$Você pode escolher apenas um deles.\n$Esses itens variam entre consumíveis, itens de segurar e itens passivos permanentes.\n$A maioria dos efeitos de itens não consumíveis podem ser acumulados.\n$Alguns itens só aparecerão se puderem ser usados, como os itens de evolução.\n$Você também pode transferir itens de segurar entre os Pokémon utilizando a opção \"Alterar\".\n$A opção de transferir irá aparecer no canto inferior direito assim que você obter um item de segurar.\n$Você pode comprar itens consumíveis com dinheiro, e sua variedade aumentará conforme você for mais longe.\n$Certifique-se de comprá-los antes de escolher seu item aleatório. Ao escolhê-lo, a próxima batalha começará.", - "eggGacha": "Aqui você pode trocar seus vouchers\npor ovos de Pokémon.\n$Ovos ficam mais próximos de chocar após cada batalha.\nOvos mais raros demoram mais para chocar.\n$Pokémon chocados não serão adicionados a sua equipe,\nmas sim aos seus iniciais.\n$Pokémon chocados geralmente possuem IVs melhores\nque Pokémon selvagens.\n$Alguns Pokémon só podem ser obtidos através de seus ovos.\n$Temos 3 máquinas, cada uma com seu bônus específico,\nentão escolha a que mais lhe convém!" + "eggGacha": "Nesta tela, você pode trocar seus vouchers por ovos\nde Pokémon.\n$Ovos ficam mais próximos de chocar após cada batalha.\nOvos mais raros demoram mais tempo para chocar.\n$Pokémon chocados não serão adicionados a sua equipe,\nmas sim aos seus iniciais.\n$Pokémon chocados de ovos geralmente têm IVs melhores\ndo que Pokémon selvagens.\n$Alguns Pokémon só podem ser obtidos através de ovos.\n$Existem 3 máquinas para usar com diferentes bônus, então\nescolha a que mais lhe convém!" } diff --git a/src/locales/zh_CN/ability-trigger.json b/src/locales/zh_CN/ability-trigger.json index f4289b598a0..0bb9c3a56ef 100644 --- a/src/locales/zh_CN/ability-trigger.json +++ b/src/locales/zh_CN/ability-trigger.json @@ -12,6 +12,7 @@ "blockItemTheft": "{{pokemonNameWithAffix}}的{{abilityName}}\n阻止了对方夺取道具!", "typeImmunityHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回复了少许HP!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n避免了伤害!", + "fullHpResistType": "{{pokemonNameWithAffix}}\n让甲壳发出光辉,使属性相克发生扭曲!", "moveImmunity": "对{{pokemonNameWithAffix}}没有效果!", "reverseDrain": "{{pokemonNameWithAffix}}\n吸到了污泥浆!", "postDefendTypeChange": "{{pokemonNameWithAffix}}因{{abilityName}}\n变成了{{typeName}}属性!", @@ -51,6 +52,7 @@ "postSummonTeravolt": "{{pokemonNameWithAffix}}\n正在释放溅射气场!", "postSummonDarkAura": "{{pokemonNameWithAffix}}\n正在释放暗黑气场!", "postSummonFairyAura": "{{pokemonNameWithAffix}}\n正在释放妖精气场!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}}\n压制了所有气场!", "postSummonNeutralizingGas": "周围充满了\n{{pokemonNameWithAffix}}的化学变化气体!", "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}}\n同时拥有了两种特性!", "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}}\n同时拥有了两种特性!", @@ -59,4 +61,4 @@ "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}的灾祸之简\n令周围的宝可梦的{{statName}}减弱了!", "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}的灾祸之玉\n令周围的宝可梦的{{statName}}减弱了!", "preventBerryUse": "{{pokemonNameWithAffix}}因太紧张\n而无法食用树果!" -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/ability.json b/src/locales/zh_CN/ability.json index 7c70c84f55c..31e3c08161d 100644 --- a/src/locales/zh_CN/ability.json +++ b/src/locales/zh_CN/ability.json @@ -469,7 +469,7 @@ }, "honeyGather": { "name": "采蜜", - "description": "The Pokémon gat\nhers Honey afte\nr a battle. The\n Honey is then \nsold for money." + "description": "战斗结束时,有时候会捡来甜甜蜜。\n甜甜蜜会转换成金钱。" }, "frisk": { "name": "察觉", @@ -1239,4 +1239,4 @@ "name": "毒傀儡", "description": "因桃歹郎的招式而陷入中毒状态的\n对手同时也会陷入混乱状态。" } -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/achv.json b/src/locales/zh_CN/achv.json index 8de0c48a2c3..b93345d876b 100644 --- a/src/locales/zh_CN/achv.json +++ b/src/locales/zh_CN/achv.json @@ -23,7 +23,7 @@ }, "DamageAchv": { - "description": "在单次攻击中造成 {{damageAmount}} 点伤害" + "description": "在单次攻击中造成\n {{damageAmount}} 点伤害" }, "250_DMG": { "name": "重拳出击" @@ -39,7 +39,7 @@ }, "HealAchv": { - "description": "通过技能、能力或携带的道具一次性治疗 {{healAmount}} {{HP}}点" + "description": "通过技能、能力或携带的道具\n一次性治疗 {{healAmount}} {{HP}}点" }, "250_HEAL": { "name": "新手奶妈" @@ -86,13 +86,13 @@ "name": "大师球联盟冠军" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "团队协作", - "description": "在一项属性强化至最大时用接力棒传递给其他宝可梦" + "description": "在一项属性强化至最大时用\n接力棒传递给其他宝可梦" }, "MAX_FRIENDSHIP": { "name": "亲密无间", - "description": "使一只宝可梦的亲密度达到最大值" + "description": "使一只宝可梦的亲密度\n达到最大值" }, "MEGA_EVOLVE": { "name": "大变身", @@ -112,7 +112,7 @@ }, "SPLICE": { "name": "无限融合", - "description": "使用基因之楔将两只宝可梦融合在一起" + "description": "使用基因之楔将两只宝可梦\n融合在一起" }, "MINI_BLACK_HOLE": { "name": "一大洞的道具", @@ -136,7 +136,7 @@ }, "SHINY_PARTY": { "name": "呕心沥血", - "description": "拥有一支由闪光宝可梦组成的满员队伍" + "description": "拥有一支由闪光宝可梦组成\n的满员队伍" }, "HATCH_MYTHICAL": { "name": "幻兽蛋", @@ -144,7 +144,7 @@ }, "HATCH_SUB_LEGENDARY": { "name": "二级传说蛋", - "description": "从蛋中孵化出一只二级传说宝可梦" + "description": "从蛋中孵化出一只二级传说\n宝可梦" }, "HATCH_LEGENDARY": { "name": "传说蛋", @@ -156,19 +156,19 @@ }, "HIDDEN_ABILITY": { "name": "隐藏实力", - "description": "捕捉一只拥有隐藏特性的宝可梦" + "description": "捕捉一只拥有隐藏特性\n的宝可梦" }, "PERFECT_IVS": { "name": "合格证", - "description": "获得一只拥有完美个体值的宝可梦" + "description": "获得一只拥有完美个体值\n的宝可梦" }, "CLASSIC_VICTORY": { "name": "战无不胜", "description": "在经典模式中通关游戏" }, "UNEVOLVED_CLASSIC_VICTORY": { - "name": "带孩子来上班", - "description": "通关经典模式时队伍中至少有一名未进化的宝可梦" + "name": "带孩上班", + "description": "通关经典模式时队伍中至少有\n一名未进化的宝可梦" }, "MONO_GEN_ONE": { diff --git a/src/locales/zh_CN/arena-flyout.json b/src/locales/zh_CN/arena-flyout.json index 7ddc304f404..fbce213c4aa 100644 --- a/src/locales/zh_CN/arena-flyout.json +++ b/src/locales/zh_CN/arena-flyout.json @@ -36,5 +36,6 @@ "matBlock": "掀榻榻米", "craftyShield": "戏法防守", "tailwind": "顺风", - "happyHour": "快乐时光" -} \ No newline at end of file + "happyHour": "快乐时光", + "safeguard": "神秘守护" +} diff --git a/src/locales/zh_CN/arena-tag.json b/src/locales/zh_CN/arena-tag.json index 5a36b3ae1f7..74ad38ba9bf 100644 --- a/src/locales/zh_CN/arena-tag.json +++ b/src/locales/zh_CN/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "我方的顺风停止了!", "tailwindOnRemoveEnemy": "敌方的顺风停止了!", "happyHourOnAdd": "大家被欢乐的\n气氛包围了!", - "happyHourOnRemove": "气氛回复到平常了。" + "happyHourOnRemove": "气氛回复到平常了。", + "safeguardOnAdd": "整个场地被\n神秘之幕包围了!", + "safeguardOnAddPlayer": "我方被\n神秘之幕包围了!", + "safeguardOnAddEnemy": "对手被\n神秘之幕包围了!", + "safeguardOnRemove": "包围整个场地的\n神秘之幕消失了!", + "safeguardOnRemovePlayer": "包围我方的\n神秘之幕消失了!", + "safeguardOnRemoveEnemy": "包围对手的\n神秘之幕消失了!" } \ No newline at end of file diff --git a/src/locales/zh_CN/battle.json b/src/locales/zh_CN/battle.json index 84f468a4a4b..ccf0e560805 100644 --- a/src/locales/zh_CN/battle.json +++ b/src/locales/zh_CN/battle.json @@ -15,7 +15,7 @@ "moneyPickedUp": "捡到了₽{{moneyAmount}}!", "pokemonCaught": "{{pokemonName}}被抓住了!", "addedAsAStarter": "增加了{{pokemonName}}作为\n一个新的基础宝可梦!", - "partyFull": "你的队伍已满员。是否放生其他宝可梦\n为{{pokemonName}}腾出空间?", + "partyFull": "你的队伍已满员。是否放生其他宝可梦\n为{{pokemonName}}腾出空间?", "pokemon": "宝可梦", "sendOutPokemon": "上吧!\n{{pokemonName}}!", "hitResultCriticalHit": "击中了要害!", @@ -44,6 +44,7 @@ "moveNotImplemented": "{{moveName}}尚未实装,无法选择。", "moveNoPP": "这个技能的PP用完了", "moveDisabled": "{{moveName}}被禁用!", + "disableInterruptedMove": "{{pokemonNameWithAffix}}的{{moveName}}\n被无效化了!", "noPokeballForce": "一股无形的力量阻止了你使用精灵球。", "noPokeballTrainer": "你不能捕捉其他训练家的宝可梦!", "noPokeballMulti": "只能在剩下一只宝可梦时才能扔出精灵球!", @@ -87,4 +88,4 @@ "unlockedSomething": "{{unlockedThing}}\n已解锁。", "congratulations": "恭喜!", "beatModeFirstTime": "{{speciesName}}首次击败了{{gameMode}}!\n你获得了{{newModifier}}!" -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/battler-tags.json b/src/locales/zh_CN/battler-tags.json index 81838b5023a..7a01f5dff23 100644 --- a/src/locales/zh_CN/battler-tags.json +++ b/src/locales/zh_CN/battler-tags.json @@ -67,5 +67,8 @@ "saltCuredLapse": "{{pokemonNameWithAffix}}\n受到了{{moveName}}的伤害!", "cursedOnAdd": "{{pokemonNameWithAffix}}削减了自己的体力,\n并诅咒了{{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}}\n正受到诅咒!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}}蓄力了{{stockpiledCount}}次!" -} \ No newline at end of file + "stockpilingOnAdd": "{{pokemonNameWithAffix}}蓄力了{{stockpiledCount}}次!", + "disabledOnAdd": "封住了{{pokemonNameWithAffix}}的\n{{moveName}}!", + "disabledLapse": "{{pokemonNameWithAffix}}的\n定身法解除了!", + "tarShotOnAdd": "{{pokemonNameWithAffix}}\n变得怕火了!" +} diff --git a/src/locales/zh_CN/bgm-name.json b/src/locales/zh_CN/bgm-name.json index d0508a49661..065347e3bb6 100644 --- a/src/locales/zh_CN/bgm-name.json +++ b/src/locales/zh_CN/bgm-name.json @@ -57,7 +57,7 @@ "battle_legendary_dusk_dawn": "究极日月「战斗!日食・月食 奈克洛兹玛」", "battle_legendary_ultra_nec": "究极日月「战斗!究极奈克洛兹玛」", "battle_legendary_zac_zam": "剑盾「战斗!苍响・藏玛然特」", - "battle_legendary_glas_spec": "剑盾「战斗! 雪暴马・灵幽马」", + "battle_legendary_glas_spec": "剑盾「战斗! 雪暴马・灵幽马」", "battle_legendary_calyrex": "剑盾「战斗!蕾冠王」", "battle_legendary_birds_galar": "剑盾「战斗!传说的鸟宝可梦」", "battle_legendary_ruinous": "朱紫「战斗!灾厄宝可梦」", @@ -117,9 +117,9 @@ "plains": "空之探险队「天空顶端(草原)」", "power_plant": "空之探险队「电气平原 深处」", "ruins": "空之探险队「封印岩地 深处」", - "sea": "Andr06 - Marine Mystique", + "sea": "Andr06 - 海洋之秘", "seabed": "Firel - 海底", - "slum": "Andr06 - Sneaky Snom", + "slum": "Andr06 - 狡猾的雪吞虫", "snowy_forest": "空之探险队「天空顶端(雪山)」", "space": "Firel - 太空", "swamp": "空之探险队「封闭之海」", diff --git a/src/locales/zh_CN/dialogue-double-battle.json b/src/locales/zh_CN/dialogue-double-battle.json index 5ab6bf49890..629de3aff3e 100644 --- a/src/locales/zh_CN/dialogue-double-battle.json +++ b/src/locales/zh_CN/dialogue-double-battle.json @@ -9,10 +9,10 @@ }, "red_blue_double": { "encounter": { - "1": "赤红: ...!$青绿: 他人狠话不多。$青绿: 但别被他耍了,\n毕竟他可是个冠军!" + "1": "赤红: ...!$青绿: 他人狠话不多。$青绿: 但别被他耍了,\n毕竟他可是个冠军!" }, "victory": { - "1": "赤红: ...!$青绿: 下次我们一定会赢你!" + "1": "赤红: ...!$青绿: 下次我们一定会赢你!" } }, "tate_liza_double": { @@ -79,4 +79,4 @@ "1": "聂梓:这首歌献给大家!$玛俐:哥哥……" } } -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/dialogue-misc.json b/src/locales/zh_CN/dialogue-misc.json index e9ac66b7955..b26a3a5f208 100644 --- a/src/locales/zh_CN/dialogue-misc.json +++ b/src/locales/zh_CN/dialogue-misc.json @@ -1,4 +1,4 @@ { - "ending": "@c{smile}哦?你赢了?@d{96} @c{smile_eclosed}我应该早猜到了\n你回来了。\n$@c{smile}结束了。@d{64} 你终结了这个循环。\n$@c{serious_smile_fists}你也完成了自己的梦想,不是吗?\n你甚至一次都没失败。\n$@c{neutral}我是唯一能够记得你所作所为的人@d{96}\n我觉得这应该也还行吧?\n$@c{serious_smile_fists}你的传奇将永远留存于我们心中。\n$@c{smile_eclosed}不管了,我真是受够这个地方了,你也一样吗?我们回家吧。\n$@c{serious_smile_fists}可能等我们回家以后,再打一场?\n要是你想的话", - "ending_female": "@c{shock}你回来了?@d{32} 也就是说…@d{96} 你赢了呀!?\n@c{smile_ehalf}我应该早料到了。\n$@c{smile_eclosed}当然…我一直有这种感觉\n@c{smile}一切都结束了,对么? 你打破了循环。\n$@c{smile_ehalf}你也完成了自己的梦想,不是吗?\n你甚至一次都没失败。\n$我是唯一能够记得你所作所为的人\n@c{angry_mopen}我会努力不忘掉哒!\n$@c{smile_wave_wink}开玩笑啦,@d{64} @c{smile}我才不会忘呢。@d{32}\n你的传奇将永远留存于我们心中。\n$@c{smile_wave}不管了,@d{64} 时候不早了@d{96} ,应该吧?\n在这地方还真搞不清楚。\n$一起回家吧。 @c{smile_wave_wink}可能明天,我们再来打一场,为了重温回忆嘛~" -} \ No newline at end of file + "ending": "@c{shock}你回来了?@d{32} 也就是说…@d{96} 你赢了呀!?\n@c{smile_ehalf}我应该早料到了。\n$@c{smile_eclosed}当然…我一直有这种感觉\n@c{smile}一切都结束了,对么? 你打破了循环。\n$@c{smile_ehalf}你也完成了自己的梦想,不是吗?\n你甚至一次都没失败。\n$我是唯一能够记得你所作所为的人\n@c{angry_mopen}我会努力不忘掉哒!\n$@c{smile_wave_wink}开玩笑啦,@d{64} @c{smile}我才不会忘呢。@d{32}\n你的传奇将永远留存于我们心中。\n$@c{smile_wave}不管了,@d{64} 时候不早了@d{96} ,应该吧?\n在这地方还真搞不清楚。\n$一起回家吧。 @c{smile_wave_wink}可能明天,我们再来打一场,为了重温回忆嘛~", + "ending_female": "@c{smile}哦?你赢了?@d{96} @c{smile_eclosed}我应该早猜到了\n你回来了。\n$@c{smile}结束了。@d{64} 你终结了这个循环。\n$@c{serious_smile_fists}你也完成了自己的梦想,不是吗?\n你甚至一次都没失败。\n$@c{neutral}我是唯一能够记得你所作所为的人@d{96}\n我觉得这应该也还行吧?\n$@c{serious_smile_fists}你的传奇将永远留存于我们心中。\n$@c{smile_eclosed}不管了,我真是受够这个地方了,你也一样吗?我们回家吧。\n$@c{serious_smile_fists}可能等我们回家以后,再打一场?\n要是你想的话" +} diff --git a/src/locales/zh_CN/dialogue.json b/src/locales/zh_CN/dialogue.json index 5a4615fac54..dd0fa3fb3cc 100644 --- a/src/locales/zh_CN/dialogue.json +++ b/src/locales/zh_CN/dialogue.json @@ -388,7 +388,7 @@ "victory": { "1": "大失误……", "2": "以我现在的实力,无法胜任我的任务……", - "3": "原……谅我,坂木。\n我竟被一名训练师打败了。." + "3": "原……谅我,坂木。\n我竟被一名训练师打败了。" } }, "ariana": { @@ -1230,14 +1230,14 @@ }, "crasher_wake": { "encounter": { - "1": "极限! 极限! 看好了!$极限假面…就此…登场!", - "2": "极限! 极限! 极限假面!", + "1": "极限! 极限! 看好了!$极限假面…就此…登场!", + "2": "极限! 极限! 极限假面!", "3": "我是滔滔巨浪,将你冲走!" }, "victory": { "1": "我真是笑得合不拢嘴啊!$哈哈哈!那真是太有趣了!", "2": "呼哇!结束收尾了!$我该怎么说呢……$我还想再对战!我还想再继续战斗!", - "3": "啊啊啊啊啊!?" + "3": "啊啊啊啊啊!?" }, "defeat": { "1": "耶!就是这样!", @@ -1254,7 +1254,7 @@ "victory": { "1": "明白了……我会礼貌地退场。", "2": "输了就是输了。你确实很强大。", - "3": "…行吧! 嗯, 我输了。" + "3": "…行吧! 嗯, 我输了。" }, "defeat": { "1": "爸爸!我用你珍爱的飞行宝可梦赢了……", @@ -1576,7 +1576,7 @@ }, "clay": { "encounter": { - "1": "咳咳! 让我好等,不是吗,孩子?$好吧,是时候看看你能做到什么了!" + "1": "咳咳! 让我好等,不是吗,孩子?$好吧,是时候看看你能做到什么了!" }, "victory": { "1": "真是的……我先说好,\n我可没有手下留情。" @@ -1802,7 +1802,7 @@ "1": "够野的!你的想法比我的还要毒!" }, "defeat": { - "1": "嘿,拜托!认真点!\n你要加把劲啊!" + "1": "嘿,拜托!认真点!\n你要加把劲啊!" } }, "olivia": { @@ -1884,7 +1884,7 @@ }, "wikstrom": { "encounter": { - "1": "年轻的挑战者,幸会!\n我乃是著名的钢铁之刃,公爵雁铠! $让我们开始战斗吧!预备!" + "1": "年轻的挑战者,幸会!\n我乃是著名的钢铁之刃,公爵雁铠! $让我们开始战斗吧!预备!" }, "victory": { "1": "辉煌!你与你尊贵的\n宝可梦之间的信任居然胜过了我!" @@ -1921,7 +1921,7 @@ "2": "我知道你能走这么远。让我们开始吧。" }, "victory": { - "1": "被你拿下了啊。你太出色了!", + "1": "被你拿下了啊。你太出色了!", "2": "我从没想到会有另一个训练师打败我……$我很惊讶。" }, "defeat": { @@ -2025,13 +2025,13 @@ }, "red": { "encounter": { - "1": "…!" + "1": "…!" }, "victory": { - "1": "…?" + "1": "…?" }, "defeat": { - "1": "…!" + "1": "…!" } }, "jasmine": { @@ -2303,7 +2303,7 @@ }, "grant": { "encounter": { - "1": "我只期待一件事。. $通过超越彼此,\n我们找到通往更高境界的道路。" + "1": "我只期待一件事。$通过超越彼此,\n我们找到通往更高境界的道路。" }, "victory": { "1": "你是一堵我无法逾越的墙!" @@ -2413,7 +2413,7 @@ }, "marnie": { "encounter": { - "1": "事实上,言而总之… \n人家自己也想当冠军呀! $所以别认为我在针对你!" + "1": "事实上,言而总之… \n人家自己也想当冠军呀! $所以别认为我在针对你!" }, "victory": { "1": "好吧,我还是输了……\n但是我看到了很多你和你宝可梦的优点哦" @@ -2468,13 +2468,13 @@ }, "ryme": { "encounter": { - "1": "宝贝, 一起! \n摇滚摇到骨子里!" + "1": "宝贝, 一起! \n摇滚摇到骨子里!" }, "victory": { "1": "你好酷!我佩服!\n我的灵魂为你哭!" }, "defeat": { - "1": "再会, 宝贝!" + "1": "再会, 宝贝!" } }, "grusha": { @@ -2625,7 +2625,7 @@ }, "rival_3_female": { "encounter": { - "1": "@c{smile_wave}好久不见!还没输过,对吧。$@c{angry}我觉得你点烦了。@c{smile_wave_wink}开玩笑啦!$@c{smile_ehalf}但说真的,你现在不想家吗?\n不想…我吗?$我……我的意思是,我们真的很想你。$@c{smile_eclosed}我支持你的一切,包括你的梦想。\n但现实就是你早晚会经历失败。$@c{smile}当你失败的时候,我想像往常一样陪在你身边。$@c{angry_mopen}现在,给你看看我变得多强了吧!" + "1": "@c{smile_wave}好久不见!还没输过,对吧。$@c{angry}我觉得你点烦了。@c{smile_wave_wink}开玩笑啦!$@c{smile_ehalf}但说真的,你现在不想家吗?\n不想…我吗?$我……我的意思是,我们真的很想你。$@c{smile_eclosed}我支持你的一切,包括你的梦想。\n但现实就是你早晚会经历失败。$@c{smile}当你失败的时候,我想像往常一样陪在你身边。$@c{angry_mopen}现在,给你看看我变得多强了吧!" }, "victory": { "1": "@c{shock}都这样了……还是不够吗?$这样下去,你就永远不会回来了……" diff --git a/src/locales/zh_CN/fight-ui-handler.json b/src/locales/zh_CN/fight-ui-handler.json index 8496bf2c1ea..aa8bf4f77c5 100644 --- a/src/locales/zh_CN/fight-ui-handler.json +++ b/src/locales/zh_CN/fight-ui-handler.json @@ -2,6 +2,7 @@ "pp": "PP", "power": "威力", "accuracy": "命中", - "abilityFlyInText": " {{pokemonName}} 的 {{passive}}{{abilityName}}", - "passive": "被动 " -} \ No newline at end of file + "abilityFlyInText": " {{pokemonName}} 的\n{{passive}}{{abilityName}}", + "passive": "被动 ", + "teraHover": "{{type}} 太晶化" +} diff --git a/src/locales/zh_CN/menu.json b/src/locales/zh_CN/menu.json index 59146d30ee9..8946b5b8df6 100644 --- a/src/locales/zh_CN/menu.json +++ b/src/locales/zh_CN/menu.json @@ -51,5 +51,7 @@ "renamePokemon": "给宝可梦起名", "rename": "起名", "nickname": "昵称", - "errorServerDown": "糟糕!访问服务器时发生了错误。\n\n你可以保持页面开启,\n游戏会自动重新连接。" -} \ No newline at end of file + "errorServerDown": "糟糕!访问服务器时发生了错误。\n\n你可以保持页面开启,\n游戏会自动重新连接。", + "noSaves": "你没有任何记录文件!", + "tooManySaves": "你的记录文件太多了!" +} diff --git a/src/locales/zh_CN/modifier-type.json b/src/locales/zh_CN/modifier-type.json index e9172985092..981f26a1603 100644 --- a/src/locales/zh_CN/modifier-type.json +++ b/src/locales/zh_CN/modifier-type.json @@ -47,10 +47,14 @@ "description": "将一只宝可梦的性格改为{{natureName}}并为\n该宝可梦永久解锁该性格。" }, "DoubleBattleChanceBoosterModifierType": { - "description": "接下来的{{battleCount}}场战斗是双打的概率翻倍。" + "description": "遭遇双打概率提升四倍,持续{{battleCount}}场战斗。" }, - "TempBattleStatBoosterModifierType": { - "description": "为所有成员宝可梦提升一级{{tempBattleStatName}},持续5场战斗。" + "TempStatStageBoosterModifierType": { + "description": "提升全队的{{stat}}{{amount}}级,持续5场战斗。", + "extra": { + "stage": "1阶", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "一只宝可梦的{{moveType}}系招式威力提升20%。" @@ -61,8 +65,8 @@ "AllPokemonLevelIncrementModifierType": { "description": "使一只寶可夢的等級提升{{levels}}級。" }, - "PokemonBaseStatBoosterModifierType": { - "description": "增加10%持有者的{{statName}},\n个体值越高堆叠上限越高。" + "BaseStatBoosterModifierType": { + "description": "增加10%持有者的{{stat}},\n个体值越高堆叠上限越高。" }, "AllPokemonFullHpRestoreModifierType": { "description": "所有宝可梦完全回复HP。" @@ -91,7 +95,7 @@ "description": "招式命中率增加{{accuracyAmount}}(最大100)。" }, "PokemonMultiHitModifierType": { - "description": "攻击以40/25/12.5%的伤害造成2/3/4次伤害" + "description": "攻击以60/75/82.5%的伤害造成2/3/4次伤害。" }, "TmModifierType": { "name": "招式学习器\n{{moveId}} - {{moveName}}", @@ -248,6 +252,12 @@ "name": "焦点镜", "description": "能看见弱点的镜片。携带它的宝可梦的招式\n会变得容易击中要害。" }, + "DIRE_HIT": { + "name": "要害攻击", + "extra": { + "raises": "会心" + } + }, "LEEK": { "name": "大葱", "description": "非常长且坚硬的茎。让大葱鸭携带后,\n招式会变得容易击中要害。" @@ -304,19 +314,19 @@ }, "BERRY_POUCH": { "name": "树果袋", - "description": "使用树果时有30%的几率不会消耗树果。" + "description": "使用树果时增加30%的几率不会消耗树果。" }, "FOCUS_BAND": { "name": "气势头带", - "description": "携带该道具的宝可梦有10%几率在受到攻击\n而将陷入濒死状态时,保留1点HP不陷入濒死状态。" + "description": "携带该道具的宝可梦增加10%几率在受到攻击\n而将陷入濒死状态时,保留1点HP不陷入濒死状态。" }, "QUICK_CLAW": { "name": "先制之爪", - "description": "有10%的几率无视速度优先使出招式\n(先制技能优先)。" + "description": "增加10%的几率无视速度优先使出招式\n(先制技能优先)。" }, "KINGS_ROCK": { "name": "王者之证", - "description": "使用任意原本不会造成畏缩状态的攻击,\n有10%几率使目标陷入畏缩状态。" + "description": "使用任意原本不会造成畏缩状态的攻击,\n增加10%几率使目标陷入畏缩状态。" }, "LEFTOVERS": { "name": "吃剩的东西", @@ -411,25 +421,13 @@ "description": "让百变怪携带后,速度就会提高的神奇粉末。\n非常细腻坚硬。" } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "力量强化", "x_defense": "防御强化", "x_sp_atk": "特攻强化", "x_sp_def": "特防强化", "x_speed": "速度强化", - "x_accuracy": "命中强化", - "dire_hit": "要害攻击" - }, - "TempBattleStatBoosterStatName": { - "ATK": "攻击", - "DEF": "防御", - "SPATK": "特攻", - "SPDEF": "特防", - "SPD": "速度", - "ACC": "命中", - "CRIT": "会心", - "EVA": "闪避", - "DEFAULT": "???" + "x_accuracy": "命中强化" }, "AttackTypeBoosterItem": { "silk_scarf": "丝绸围巾", @@ -604,6 +602,6 @@ "DRAGON_MEMORY": "龙存储碟", "DARK_MEMORY": "黑暗存储碟", "FAIRY_MEMORY": "妖精存储碟", - "BLANK_MEMORY": "空白存储碟" + "NORMAL_MEMORY": "一般存储碟" } -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/modifier.json b/src/locales/zh_CN/modifier.json index 707fab20ecc..a50cdd35bc1 100644 --- a/src/locales/zh_CN/modifier.json +++ b/src/locales/zh_CN/modifier.json @@ -3,7 +3,7 @@ "turnHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回复了体力!", "hitHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回复了体力!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}}用{{typeName}}\n恢复了活力!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}降低的能力被{{typeName}}\n复原了!", + "resetNegativeStatStageApply": "{{pokemonNameWithAffix}}降低的能力被{{typeName}}\n复原了!", "moneyInterestApply": "用{{typeName}}\n获得了 ₽{{moneyAmount}} 利息!", "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}吸收了!", "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}夺取了!", diff --git a/src/locales/zh_CN/move-trigger.json b/src/locales/zh_CN/move-trigger.json index 5a76f402783..436f1805c4e 100644 --- a/src/locales/zh_CN/move-trigger.json +++ b/src/locales/zh_CN/move-trigger.json @@ -3,6 +3,10 @@ "cutHpPowerUpMove": "{{pokemonName}}\n削减了体力并提升了招式威力!", "absorbedElectricity": "{{pokemonName}}\n吸收了电力!", "switchedStatChanges": "{{pokemonName}}和对手互换了\n自己的能力变化!", + "switchedTwoStatChanges": "{{pokemonName}} 和对手互换了自己的{{firstStat}}和{{secondStat}}的能力变化!", + "switchedStat": "{{pokemonName}} 互换了各自的{{stat}}!", + "sharedGuard": "{{pokemonName}} 平分了各自的防守!", + "sharedPower": "{{pokemonName}} 平分了各自的力量!", "goingAllOutForAttack": "{{pokemonName}}拿出全力了!", "regainedHealth": "{{pokemonName}}的\n体力回复了!", "keptGoingAndCrashed": "{{pokemonName}}因势头过猛\n而撞到了地面!", @@ -38,12 +42,12 @@ "incineratedItem": "{{pokemonName}}烧没了\n{{targetName}}的{{itemName}}!", "knockedOffItem": "{{pokemonName}}拍落了\n{{targetName}}的{{itemName}}!", "tookMoveAttack": "{{pokemonName}}\n受到了{{moveName}}的攻击!", - "cutOwnHpAndMaximizedStat": "{{pokemonName}}\n削减了体力并释放了全部{{statName}}!", + "cutOwnHpAndMaximizedStat": "{{pokemonName}}\n削减了体力并释放了全部{{statName}}!", "copiedStatChanges": "{{pokemonName}}复制了\n{{targetName}}的能力变化!", "magnitudeMessage": "震级{{magnitude}}!", "tookAimAtTarget": "{{pokemonName}}将目标对准了\n{{targetName}}!", "transformedIntoType": "{{pokemonName}} \n变成了{{typeName}}属性!", - "copiedMove": "{{pokemonName}}\n复制了{{moveName}}!", + "copiedMove": "{{pokemonName}}\n复制了{{moveName}}!", "sketchedMove": "{{pokemonName}}\n对{{moveName}}进行了写生!", "acquiredAbility": "{{pokemonName}}的特性\n变为{{abilityName}}了!", "copiedTargetAbility": "{{pokemonName}}复制了\n{{targetName}}的{{abilityName}}!", @@ -61,5 +65,7 @@ "suppressAbilities": "{{pokemonName}}的特性\n变得无效了!", "revivalBlessing": "{{pokemonName}}复活了!", "swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!", - "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!", + "safeguard": "{{targetName}}\n正受到神秘之幕的保护!", + "afterYou": "{{pokemonName}}\n接受了对手的好意!" +} diff --git a/src/locales/zh_CN/party-ui-handler.json b/src/locales/zh_CN/party-ui-handler.json index db364d29ab2..8dff1ffb75c 100644 --- a/src/locales/zh_CN/party-ui-handler.json +++ b/src/locales/zh_CN/party-ui-handler.json @@ -23,7 +23,7 @@ "tooManyItems": "{{pokemonName}}拥有\n太多这个道具了!", "anyEffect": "即便使用也无效果哦。", "unpausedEvolutions": "{{pokemonName}}的进化停止了。", - "unspliceConfirmation": "真的要把{{fusionName}}\n从{{pokemonName}}身上分离吗? {{fusionName}}会就此消失。", + "unspliceConfirmation": "真的要把{{fusionName}}\n从{{pokemonName}}身上分离吗?{{fusionName}}会就此消失。", "wasReverted": "{{fusionName}}恢复成了{{pokemonName}}。", "releaseConfirmation": "你真要放生{{pokemonName}}吗?", "releaseInBattle": "你无法放生正在战斗中的宝可梦!", @@ -44,4 +44,4 @@ "untilWeMeetAgain": "下次再见了,{{pokemonName}}!", "sayonara": "撒由那拉,{{pokemonName}}!", "smellYaLater": "拜拜了您嘞,{{pokemonName}}!" -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/pokemon-form.json b/src/locales/zh_CN/pokemon-form.json index 09ec54be804..e77f9bdb9fa 100644 --- a/src/locales/zh_CN/pokemon-form.json +++ b/src/locales/zh_CN/pokemon-form.json @@ -1,5 +1,5 @@ { - "pikachuCosplay": "服装", + "pikachuCosplay": "换装", "pikachuCoolCosplay": "摇滚巨星", "pikachuBeautyCosplay": "贵妇", "pikachuCuteCosplay": "流行偶像", @@ -167,4 +167,4 @@ "paldeaTaurosCombat": "斗战种", "paldeaTaurosBlaze": "火炽种", "paldeaTaurosAqua": "水澜种" -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/pokemon-info.json b/src/locales/zh_CN/pokemon-info.json index 5194189c806..a21a8156e4c 100644 --- a/src/locales/zh_CN/pokemon-info.json +++ b/src/locales/zh_CN/pokemon-info.json @@ -1,7 +1,7 @@ { "Stat": { "HP": "最大HP", - "HPshortened": "最大HP", + "HPshortened": "HP", "ATK": "攻击", "ATKshortened": "攻击", "DEF": "防御", @@ -37,4 +37,4 @@ "FAIRY": "妖精", "STELLAR": "星晶" } -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/settings.json b/src/locales/zh_CN/settings.json index 3ae0fa8204c..dd001213b9e 100644 --- a/src/locales/zh_CN/settings.json +++ b/src/locales/zh_CN/settings.json @@ -99,7 +99,7 @@ "moveTouchControls": "移动触摸控制", "shopOverlayOpacity": "商店显示不透明度", "shopCursorTarget": "商店指针位置", - "items": "道具", + "rewards": "道具", "reroll": "刷新", "shop": "购买", "checkTeam": "检查队伍" diff --git a/src/locales/zh_CN/splash-messages.json b/src/locales/zh_CN/splash-messages.json index f679c43cdb9..4d2d208edfd 100644 --- a/src/locales/zh_CN/splash-messages.json +++ b/src/locales/zh_CN/splash-messages.json @@ -6,7 +6,7 @@ "optionalSaveScumming": "可用SL大法!", "biomes": "35种地区!", "openSource": "开源!", - "playWithSpeed": "请五倍速游玩!", + "playWithSpeed": "请五倍速游玩!", "liveBugTesting": "随时修复BUG!", "heavyInfluence": "深受雨中冒险2影响!", "pokemonRiskAndPokemonRain": "雨中宝可梦冒险!", @@ -33,4 +33,4 @@ "eeveeExpo": "伊布博览会!", "ynoproject": "Yume Nikki 页游项目!", "breedersInSpace": "饲养员也能上太空?" -} \ No newline at end of file +} diff --git a/src/locales/zh_CN/starter-select-ui-handler.json b/src/locales/zh_CN/starter-select-ui-handler.json index d184d5f7c63..4851468ab7c 100644 --- a/src/locales/zh_CN/starter-select-ui-handler.json +++ b/src/locales/zh_CN/starter-select-ui-handler.json @@ -21,8 +21,8 @@ "toggleIVs": "显示个体", "manageMoves": "管理招式", "manageNature": "管理性格", - "addToFavorites": "Add to Favorites", - "removeFromFavorites": "Remove from Favorites", + "addToFavorites": "添加到最爱", + "removeFromFavorites": "移出最爱", "useCandies": "使用糖果", "selectNature": "选择性格", "selectMoveSwapOut": "选择要替换的招式。", @@ -42,4 +42,4 @@ "locked": "未解锁", "disabled": "已禁用", "uncaught": "未捕获" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/ability-trigger.json b/src/locales/zh_TW/ability-trigger.json index 3286d97b10c..c4b53fa5126 100644 --- a/src/locales/zh_TW/ability-trigger.json +++ b/src/locales/zh_TW/ability-trigger.json @@ -3,9 +3,62 @@ "badDreams": "{{pokemonName}} 被折磨着!", "costar": "{{pokemonName}} 複製了 {{allyName}} 的\n能力變化!", "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} 因爲 {{abilityName}}\n避免了傷害!", - "trace": "{{pokemonName}} 複製了 {{targetName}} 的\n{{abilityName}}!", - "windPowerCharged": "受 {{moveName}} 的影響, {{pokemonName}} 提升了能力!", + "perishBody": "因爲{{pokemonName}}的{{abilityName}}\n雙方將在3回合後滅亡!", + "poisonHeal": "{{pokemonName}}因{{abilityName}}\n回複了少許HP!", + "trace": "{{pokemonName}}複制了{{targetName}}的\n{{abilityName}}!", + "windPowerCharged": "受{{moveName}}的影響,{{pokemonName}}提升了能力!", + "quickDraw":"因爲速擊效果發動,\n{{pokemonName}}比平常出招更快了!", "disguiseAvoidedDamage": "{{pokemonNameWithAffix}}的畫皮脫落了!", + "blockItemTheft": "{{pokemonNameWithAffix}}的{{abilityName}}\n阻止了對方奪取道具!", + "typeImmunityHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回複了少許HP!", + "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n避免了傷害!", + "fullHpResistType": "{{pokemonNameWithAffix}}讓甲殼綻放光輝,扭曲了屬性相剋關係!", + "moveImmunity": "對{{pokemonNameWithAffix}}沒有效果!", + "reverseDrain": "{{pokemonNameWithAffix}}\n吸到了汙泥漿!", + "postDefendTypeChange": "{{pokemonNameWithAffix}}因{{abilityName}}\n變成了{{typeName}}屬性!", + "postDefendContactDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使對方受到了傷害!", + "postDefendAbilitySwap": "{{pokemonNameWithAffix}}\n互換了各自的特性!", + "postDefendAbilityGive": "因爲{{pokemonNameWithAffix}}\n對方的特性變成了{{abilityName}}!", + "postDefendMoveDisable": "封住了{{pokemonNameWithAffix}}的\n{{moveName}}!", + "pokemonTypeChange": "{{pokemonNameWithAffix}}\n變成了{{moveType}}屬性!", + "postAttackStealHeldItem": "{{pokemonNameWithAffix}}從{{defenderName}}那裏\n奪取了{{stolenItemType}}!", + "postDefendStealHeldItem": "{{pokemonNameWithAffix}}從{{attackerName}}那裏\n奪取了{{stolenItemType}}!", + "copyFaintedAllyAbility": "繼承了{{pokemonNameWithAffix}}的\n{{abilityName}}!", + "intimidateImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}沒有受到威嚇!", + "postSummonAllyHeal": "{{pokemonNameWithAffix}}喝光了\n{{pokemonName}}泡的茶!", + "postSummonClearAllyStats": "{{pokemonNameWithAffix}}的\n能力變化消失了!", + "postSummonTransform": "{{pokemonNameWithAffix}}\n變身成了{{targetName}}!", + "protectStat": "因{{pokemonNameWithAffix}}的{{abilityName}}\n{{statName}}不會降低!", + "statusEffectImmunityWithName": "{{pokemonNameWithAffix}}因{{abilityName}}\n{{statusEffectName}}沒有效果!", + "statusEffectImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n異常狀態沒有效果!", + "battlerTagImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n而不會{{battlerTagName}}!", + "forewarn": "{{pokemonNameWithAffix}}讀取了\n{{moveName}}!", + "frisk": "{{pokemonNameWithAffix}}察覺到了\n{{opponentName}}的{{opponentAbilityName}}!", + "postWeatherLapseHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回複了少許HP!", + "postWeatherLapseDamage": "{{pokemonNameWithAffix}}\n因{{abilityName}}而受到了傷害!", + "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}}\n收獲了{{berryName}}!", + "postTurnHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回複了少許HP!", + "fetchBall": "{{pokemonNameWithAffix}}\n撿回了{{pokeballName}}!", + "healFromBerryUse": "{{pokemonNameWithAffix}}因{{abilityName}}\n回複了HP!", + "arenaTrap": "因{{pokemonNameWithAffix}}的{{abilityName}}\n而無法進行替換!", + "postBattleLoot": "{{pokemonNameWithAffix}}撿到了\n{{itemName}}!", + "postFaintContactDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使對方受到了傷害!", + "postFaintHpDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使對方受到了傷害!", + "postSummonPressure": "從{{pokemonNameWithAffix}}的身上\n感到了一種壓迫感!", "weatherEffectDisappeared": "天氣的影響消失了!", + "postSummonMoldBreaker": "{{pokemonNameWithAffix}}\n打破了常規!", + "postSummonAnticipation": "{{pokemonNameWithAffix}}\n發抖了!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}}\n正在釋放熾焰氣場!", + "postSummonTeravolt": "{{pokemonNameWithAffix}}\n正在釋放濺射氣場!", + "postSummonDarkAura": "{{pokemonNameWithAffix}}\n正在釋放暗黑氣場!", + "postSummonFairyAura": "{{pokemonNameWithAffix}}\n正在釋放妖精氣場!", + "postSummonAuraBreak": "{{pokemonNameWithAffix}}壓制了所有氣場!", + "postSummonNeutralizingGas": "周圍充滿了\n{{pokemonNameWithAffix}}的化學變化氣體!", + "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}}\n同時擁有了兩種特性!", + "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}}\n同時擁有了兩種特性!", + "postSummonVesselOfRuin": "{{pokemonNameWithAffix}}的災禍之鼎\n令周圍的寶可夢的{{statName}}減弱了!", + "postSummonSwordOfRuin": "{{pokemonNameWithAffix}}的災禍之劍\n令周圍的寶可夢的{{statName}}減弱了!", + "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}的災禍之簡\n令周圍的寶可夢的{{statName}}減弱了!", + "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}的災禍之玉\n令周圍的寶可夢的{{statName}}減弱了!", "preventBerryUse": "{{pokemonNameWithAffix}}因太緊張\n而無法食用樹果!" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/ability.json b/src/locales/zh_TW/ability.json index ddfe2783794..21206c5362a 100644 --- a/src/locales/zh_TW/ability.json +++ b/src/locales/zh_TW/ability.json @@ -469,7 +469,7 @@ }, "honeyGather": { "name": "採蜜", - "description": "The Pokémon gathers Honey after a battle. The Honey is then sold for money." + "description": "戰鬥結束時,有時候會撿來甜甜蜜。\n甜甜蜜會轉換成金錢。" }, "frisk": { "name": "察覺", @@ -1239,4 +1239,4 @@ "name": "毒傀儡", "description": "因為桃歹郎的招式而陷入中\n毒狀態的對手同時也會陷入\n混亂狀態。" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/achv.json b/src/locales/zh_TW/achv.json index 6587394cf41..804ec0c5089 100644 --- a/src/locales/zh_TW/achv.json +++ b/src/locales/zh_TW/achv.json @@ -21,7 +21,7 @@ "name": "暴發戶" }, "DamageAchv": { - "description": "在單次攻擊中造成 {{damageAmount}} 點傷害" + "description": "在單次攻擊中造成\n {{damageAmount}} 點傷害" }, "250_DMG": { "name": "重拳出擊" @@ -36,7 +36,7 @@ "name": "一拳超人" }, "HealAchv": { - "description": "通過技能、能力或攜帶的道具一次性治療 {{healAmount}} {{HP}}點" + "description": "通過技能、能力或攜帶的道具\n一次性治療 {{healAmount}} {{HP}}點" }, "250_HEAL": { "name": "新手奶媽" @@ -80,13 +80,13 @@ "100_RIBBONS": { "name": "大師球聯盟冠軍" }, - "TRANSFER_MAX_BATTLE_STAT": { + "TRANSFER_MAX_STAT_STAGE": { "name": "團隊協作", - "description": "在一項屬性強化至最大時用接力棒傳遞給其他寶可夢" + "description": "在一項屬性強化至最大時用\n接力棒傳遞給其他寶可夢" }, "MAX_FRIENDSHIP": { "name": "親密無間", - "description": "使一隻寶可夢的親密度達到最大值" + "description": "使一隻寶可夢的親密度\n達到最大值" }, "MEGA_EVOLVE": { "name": "大變身", @@ -106,7 +106,7 @@ }, "SPLICE": { "name": "無限融合", - "description": "使用基因之楔將兩隻寶可夢融合在一起" + "description": "使用基因之楔將兩隻寶可夢\n融合在一起" }, "MINI_BLACK_HOLE": { "name": "一大洞的道具", @@ -130,7 +130,7 @@ }, "SHINY_PARTY": { "name": "嘔心瀝血", - "description": "擁有一支由閃光寶可夢組成的滿員隊伍" + "description": "擁有一支由閃光寶可夢組成\n的滿員隊伍" }, "HATCH_MYTHICAL": { "name": "幻獸蛋", @@ -138,7 +138,7 @@ }, "HATCH_SUB_LEGENDARY": { "name": "二級傳說蛋", - "description": "從蛋中孵化出一隻準傳說寶可夢" + "description": "從蛋中孵化出一隻準傳說\n寶可夢" }, "HATCH_LEGENDARY": { "name": "傳說蛋", @@ -150,16 +150,20 @@ }, "HIDDEN_ABILITY": { "name": "隱藏實力", - "description": "捕捉一隻擁有隱藏特性的寶可夢" + "description": "捕捉一隻擁有隱藏特性\n的寶可夢" }, "PERFECT_IVS": { "name": "合格證", - "description": "獲得一隻擁有完美個體值的寶可夢" + "description": "獲得一隻擁有完美個體值\n的寶可夢" }, "CLASSIC_VICTORY": { "name": "戰無不勝", "description": "在經典模式中通關遊戲" }, + "UNEVOLVED_CLASSIC_VICTORY": { + "name": "帶孩上班", + "description": "通關經典模式時隊伍中至少有\n一名未進化的寶可夢" + }, "MONO_GEN_ONE": { "name": "最初的勁敵", "description": "完成僅限第一世代的挑戰." @@ -253,8 +257,12 @@ "MONO_FAIRY": { "name": "林克,醒醒!" }, + "FRESH_START": { + "name": "初次嘗試!", + "description": "完成初次嘗試挑戰" + }, "INVERSE_BATTLE": { "name": "鏡子子鏡", "description": "完成逆轉之戰挑戰\n戰挑戰之轉逆成完" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/arena-flyout.json b/src/locales/zh_TW/arena-flyout.json index 9e26dfeeb6e..e201b683543 100644 --- a/src/locales/zh_TW/arena-flyout.json +++ b/src/locales/zh_TW/arena-flyout.json @@ -1 +1,43 @@ -{} \ No newline at end of file +{ + "activeBattleEffects": "當前戰鬥效果", + "player": "我方", + "neutral": "全局", + "enemy": "敵方", + + "sunny": "大晴天", + "rain": "下雨", + "sandstorm": "沙暴", + "hail": "冰雹", + "snow": "下雪", + "fog": "起霧", + "heavyRain": "大雨", + "harshSun": "大日照", + "strongWinds": "亂流", + + "misty": "薄霧場地", + "electric": "電氣場地", + "grassy": "青草場地", + "psychic": "精神場地", + + "mudSport": "玩泥巴", + "waterSport": "玩水", + "spikes": "撒菱", + "toxicSpikes": "毒菱", + "mist": "白霧", + "futureSight": "預知未來", + "doomDesire": "破滅之願", + "wish": "祈願", + "stealthRock": "隱形岩", + "stickyWeb": "黏黏網", + "trickRoom": "戲法空間", + "gravity": "重力", + "reflect": "反射壁", + "lightScreen": "光牆", + "auroraVeil": "極光幕", + "quickGuard": "快速防守", + "wideGuard": "廣域防守", + "matBlock": "掀榻榻米", + "craftyShield": "戲法防守", + "tailwind": "順風", + "happyHour": "快樂時光" +} diff --git a/src/locales/zh_TW/arena-tag.json b/src/locales/zh_TW/arena-tag.json index b60946a3b77..a6224f300a3 100644 --- a/src/locales/zh_TW/arena-tag.json +++ b/src/locales/zh_TW/arena-tag.json @@ -1,5 +1,57 @@ { + "yourTeam": "我方隊伍", + "opposingTeam": "敵方隊伍", + "arenaOnRemove": "{{moveName}}的效果消失了!", + "arenaOnRemovePlayer": "{{moveName}}在我方的效果消失了!", + "arenaOnRemoveEnemy": "{{moveName}}在敵方的效果消失了!", + "mistOnAdd": "{{pokemonNameWithAffix}}的一方被\n白霧包圍了!", + "mistApply": "正受到白霧的保護\n能力不會被降低!", + "reflectOnAdd": "反射壁使\n物理抗性提高了!", + "reflectOnAddPlayer": "反射壁使我方的\n物理抗性提高了!", + "reflectOnAddEnemy": "反射壁使敵方的\n物理抗性提高了!", + "lightScreenOnAdd": "光牆使\n特殊抗性提高了!", + "lightScreenOnAddPlayer": "光牆使我方的\n特殊抗性提高了!", + "lightScreenOnAddEnemy": "光牆使敵方的\n特殊抗性提高了!", + "auroraVeilOnAdd": "極光幕使\n物理和特殊抗性提高了!", + "auroraVeilOnAddPlayer": "極光幕使我方的\n物理和特殊抗性提高了!", + "auroraVeilOnAddEnemy": "極光幕使敵方的\n物理和特殊抗性提高了!", + "conditionalProtectOnAdd": "{{moveName}}\n保護了!", + "conditionalProtectOnAddPlayer": "{{moveName}}\n保護了我方!", + "conditionalProtectOnAddEnemy": "{{moveName}}\n保護了敵方!", + "conditionalProtectApply": "{{moveName}}\n保護了{{pokemonNameWithAffix}}!", + "matBlockOnAdd": "{{pokemonNameWithAffix}}正在\n伺機使出掀榻榻米!", "noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!", "noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!", - "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!" -} \ No newline at end of file + "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!", + "wishTagOnAdd": "{{pokemonNameWithAffix}}的\n祈願實現了!", + "mudSportOnAdd": "電氣的威力減弱了!", + "mudSportOnRemove": "玩泥巴的效果消失了!", + "waterSportOnAdd": "火焰的威力減弱了!", + "waterSportOnRemove": "玩水的效果消失了!", + "spikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!", + "spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的傷害!", + "toxicSpikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!", + "toxicSpikesActivateTrapPoison": "{{pokemonNameWithAffix}}\n吸收了{{moveName}}!", + "stealthRockOnAdd": "{{opponentDesc}}周圍\n開始浮現出尖銳的岩石!", + "stealthRockActivateTrap": "尖銳的岩石紮進了\n{{pokemonNameWithAffix}}的體內!", + "stickyWebOnAdd": "對方的腳下\n延伸出了{{moveName}}!", + "stickyWebActivateTrap": "{{pokemonName}}\n被黏黏網粘住了!", + "trickRoomOnAdd": "{{pokemonNameWithAffix}}\n扭曲了時空!", + "trickRoomOnRemove": "扭曲的時空複原了!", + "gravityOnAdd": "重力變強了!", + "gravityOnRemove": "重力複原了!", + "tailwindOnAdd": "從身後\n吹起了順風!", + "tailwindOnAddPlayer": "從我方身後\n吹起了順風!", + "tailwindOnAddEnemy": "從敵方身後\n吹起了順風!", + "tailwindOnRemove": "順風停止了!", + "tailwindOnRemovePlayer": "我方的順風停止了!", + "tailwindOnRemoveEnemy": "敵方的順風停止了!", + "happyHourOnAdd": "大家被歡樂的\n氣氛包圍了!", + "happyHourOnRemove": "氣氛回複到平常了。", + "safeguardOnAdd": "整個場地被\n神秘之幕包圍了!", + "safeguardOnAddPlayer": "我方被\n神秘之幕包圍了!", + "safeguardOnAddEnemy": "對手被\n神秘之幕包圍了!", + "safeguardOnRemove": "包圍整個場地的\n神秘之幕消失了!", + "safeguardOnRemovePlayer": "包圍我方的\n神秘之幕消失了!", + "safeguardOnRemoveEnemy": "包圍對手的\n神秘之幕消失了!" +} diff --git a/src/locales/zh_TW/battle.json b/src/locales/zh_TW/battle.json index 97b85a9cd5a..66da1b7cace 100644 --- a/src/locales/zh_TW/battle.json +++ b/src/locales/zh_TW/battle.json @@ -1,68 +1,98 @@ { - "bossAppeared": "{{bossName}} 出現了.", + "bossAppeared": "{{bossName}} 出現了。", "trainerAppeared": "{{trainerName}}\n想要和你對戰!", "trainerAppearedDouble": "{{trainerName}}\n想要和你對戰!", - "singleWildAppeared": "一隻野生的 {{pokemonName}} 出現了!", - "multiWildAppeared": "野生的 {{pokemonName1}}\n和 {{pokemonName2}} 出現了!", - "playerComeBack": "回來吧, {{pokemonName}}!", - "trainerComeBack": "{{trainerName}} 收回了 {{pokemonName}}!", - "playerGo": "去吧! {{pokemonName}}!", - "trainerGo": "{{trainerName}} 派出了 {{pokemonName}}!", + "trainerSendOut": "{{trainerName}}派出了\n{{pokemonName}}!", + "singleWildAppeared": "一只野生的{{pokemonName}}出現了!", + "multiWildAppeared": "野生的{{pokemonName1}}\n和{{pokemonName2}}出現了!", + "playerComeBack": "回來吧,{{pokemonName}}!", + "trainerComeBack": "{{trainerName}}收回了{{pokemonName}}!", + "playerGo": "去吧!{{pokemonName}}!", + "trainerGo": "{{trainerName}}派出了\n{{pokemonName}}!", + "trainerSendOut": "{{trainerName}}派出了\n{{pokemonName}}!", + "singleWildAppeared": "一只野生的{{pokemonName}}出現了!", + "multiWildAppeared": "野生的{{pokemonName1}}\n和{{pokemonName2}}出現了!", + "playerComeBack": "回來吧,{{pokemonName}}!", + "trainerComeBack": "{{trainerName}}收回了{{pokemonName}}!", + "playerGo": "去吧!{{pokemonName}}!", + "trainerGo": "{{trainerName}}派出了\n{{pokemonName}}!", "switchQuestion": "要更換\n{{pokemonName}}嗎?", "trainerDefeated": "你擊敗了\n{{trainerName}}!", - "moneyPickedUp": "撿到了 ₽{{moneyAmount}}!", - "pokemonCaught": "{{pokemonName}} 被抓住了!", + "moneyWon": "你贏得了\n₽{{moneyAmount}}!", + "moneyPickedUp": "撿到了₽{{moneyAmount}}!", + "pokemonCaught": "{{pokemonName}}被抓住了!", + "addedAsAStarter": "增加了{{pokemonName}}作爲\n一個新的基礎寶可夢!", + "partyFull": "你的隊伍已滿員。是否放生其他寶可夢\n爲{{pokemonName}}騰出空間?", "pokemon": "寶可夢", - "sendOutPokemon": "上吧! {{pokemonName}}!", + "sendOutPokemon": "上吧!\n{{pokemonName}}!", "hitResultCriticalHit": "擊中了要害!", "hitResultSuperEffective": "效果拔群!", "hitResultNotVeryEffective": "收效甚微…", - "hitResultNoEffect": "對 {{pokemonName}} 沒有效果!", + "hitResultNoEffect": "對{{pokemonName}}沒有效果!", "hitResultImmune": "對于{{pokemonName}},\n完全沒有效果!", - "hitResultOneHitKO": "一擊切殺!", + "hitResultOneHitKO": "一擊必殺!", "attackFailed": "但是失敗了!", "attackMissed": "沒有命中{{pokemonNameWithAffix}}!", - "attackHitsCount": "擊中 {{count}} 次!", - "rewardGain": "You received\n{{modifierName}}!", - "expGain": "{{pokemonName}} 獲得了 {{exp}} 經驗值!", - "levelUp": "{{pokemonName}} 升級到 Lv. {{level}}!", - "learnMove": "{{pokemonName}} 學會了{{moveName}}!", - "learnMovePrompt": "{{pokemonName}} 想要學習 {{moveName}}.", - "learnMoveLimitReached": "但是, {{pokemonName}} 已經學會了\n四個招式.", - "learnMoveReplaceQuestion": "要忘記一個招式並學習 {{moveName}} 嗎?", - "learnMoveStopTeaching": "不再嘗試學習\n{{moveName}}嗎?", - "learnMoveNotLearned": "{{pokemonName}} 沒有學會 {{moveName}}.", + "attackHitsCount": "擊中{{count}}次!", + "rewardGain": "你獲得了\n{{modifierName}}!", + "expGain": "{{pokemonName}}獲得了{{exp}} 點經驗值!", + "levelUp": "{{pokemonName}}升級到Lv.{{level}}!", + "learnMove": "{{pokemonName}}學會了{{moveName}}!", + "learnMovePrompt": "{{pokemonName}}想要學習{{moveName}}。", + "learnMoveLimitReached": "但是,{{pokemonName}}已經學會了\n四個技能", + "learnMoveReplaceQuestion": "要忘記一個技能並學習{{moveName}}嗎?", + "learnMoveStopTeaching": "不再嘗試學習{{moveName}}?", + "learnMoveNotLearned": "{{pokemonName}}沒有學會{{moveName}}。", "learnMoveForgetQuestion": "要忘記哪個技能?", - "learnMoveForgetSuccess": "{{pokemonName}} 忘記了 {{moveName}}.", - "countdownPoof": "@d{32}1, @d{15}2, 和@d{15}… @d{15}… @d{15}… @d{15}@s{se/pb_bounce_1}噗!", - "learnMoveAnd": "然後…", - "levelCapUp": "等級上限提升到 {{levelCap}}!", - "moveNotImplemented": "{{moveName}} 未實裝,無法選擇。", + "learnMoveForgetSuccess": "{{pokemonName}}忘記了\n如何使用{{moveName}}。", + "countdownPoof": "@d{32}1, @d{15}2 @d{15}… @d{15}… @d{15}@s{pb_bounce_1}空!", + "learnMoveAnd": "然後……", + "levelCapUp": "等級上限提升到{{levelCap}}!", + "moveNotImplemented": "{{moveName}}尚未實裝,無法選擇。", "moveNoPP": "這個技能的PP用完了", - "moveDisabled": "{{moveName}} 被禁用!", + "moveDisabled": "{{moveName}}被禁用!", + "disableInterruptedMove": "{{pokemonNameWithAffix}}的{{moveName}}\n被無效化了!", "noPokeballForce": "一股無形的力量阻止了你使用精靈球。", "noPokeballTrainer": "你不能捕捉其他訓練家的寶可夢!", - "noPokeballMulti": "只能在剩下一隻寶可夢時才能扔出精靈球!", - "noPokeballStrong": "目標寶可夢太強了,無法捕捉!你需要先\n削弱它!", + "noPokeballMulti": "只能在剩下一只寶可夢時才能扔出精靈球!", + "noPokeballStrong": "目標寶可夢太強了,無法捕捉!\n你需要先削弱它!", "noEscapeForce": "一股無形的力量阻止你逃跑。", - "noEscapeTrainer": "你不能從訓練家對戰中逃跑!", - "noEscapePokemon": "{{pokemonName}} 的 {{moveName}} 阻止了你 {{escapeVerb}}!", - "runAwaySuccess": "你成功逃脫了!", - "runAwayCannotEscape": "你無法逃脫!", + "noEscapeTrainer": "你不能從與訓練家的戰鬥中逃跑!", + "noEscapePokemon": "{{pokemonName}}的{{moveName}}\n阻止了你{{escapeVerb}}!", + "runAwaySuccess": "成功逃走了!", + "runAwayCannotEscape": "無法逃走!", "escapeVerbSwitch": "切換", "escapeVerbFlee": "逃跑", - "ppHealBerry": "{{pokemonNameWithAffix}}用{{berryName}}\n回復了{{moveName}}的PP!", - "hpHealBerry": "{{pokemonNameWithAffix}}用{{berryName}}\n回復了體力!", - "notDisabled": "{{moveName}} 不再被禁用!", - "skipItemQuestion": "你要跳過拾取道具嗎?", - "itemStackFull": "{{fullItemName}}持有數已達到上限,\n你獲得了{{itemName}}作爲代替。", + "notDisabled": "{{moveName}}不再被禁用!", + "turnEndHpRestore": "{{pokemonName}}的體力恢複了。", + "hpIsFull": "{{pokemonName}}的體力已滿!", + "skipItemQuestion": "你確定要跳過拾取道具嗎?", + "itemStackFull": "{{fullItemName}}持有數達到上限,\n你獲得了{{itemName}}作爲替代。", "eggHatching": "咦?", - "ivScannerUseQuestion": "對 {{pokemonName}} 使用個體值掃描儀?", - "wildPokemonWithAffix": "野生的 {{pokemonName}}", - "foePokemonWithAffix": "對手 {{pokemonName}}", - "useMove": "{{pokemonNameWithAffix}} 使用了 {{moveName}}!", - "drainMessage": "{{pokemonName}} 吸取了體力!", - "regainHealth": "{{pokemonName}} 回復了體力!", - "fainted": "{{pokemonNameWithAffix}} 倒下了!", - "ppReduced": "降低了 {{targetName}} 的\n{{moveName}} 的PP{{reduction}}點!" -} \ No newline at end of file + "stealEatBerry": "{{pokemonName}}奪取並吃掉了\n{{targetName}}的{{berryName}}!", + "ppHealBerry": "{{pokemonNameWithAffix}}用{{berryName}}\n回複了{{moveName}}的PP!", + "hpHealBerry": "{{pokemonNameWithAffix}}用{{berryName}}\n回複了體力!", + "ivScannerUseQuestion": "對{{pokemonName}}使用個體值掃描儀?", + "wildPokemonWithAffix": "野生的{{pokemonName}}", + "foePokemonWithAffix": "對手的{{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}}使用了\n{{moveName}}!", + "drainMessage": "{{pokemonName}}\n吸取了體力!", + "regainHealth": "{{pokemonName}}\n回複了體力!", + "fainted": "{{pokemonNameWithAffix}}\n倒下了!", + "statsAnd": "和", + "stats": "能力", + "statRose_other": "{{pokemonNameWithAffix}}的{{stats}}提高了!", + "statSharplyRose_other": "{{pokemonNameWithAffix}}的{{stats}}大幅提高了!", + "statRoseDrastically_other": "{{pokemonNameWithAffix}}的{{stats}}極大幅提高了!", + "statWontGoAnyHigher_other": "{{pokemonNameWithAffix}}的{{stats}}已經無法再提高了!", + "statFell_other": "{{pokemonNameWithAffix}}的{{stats}}降低了!", + "statHarshlyFell_other": "{{pokemonNameWithAffix}}的{{stats}}大幅降低了!", + "statSeverelyFell_other": "{{pokemonNameWithAffix}}的{{stats}}極大幅降低了!", + "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}的{{stats}}已經無法再降低了!", + "transformedIntoType": "{{pokemonName}}變成了\n{{type}}屬性!", + "ppReduced": "降低了{{targetName}}的\n{{moveName}}的PP{{reduction}}點!", + "retryBattle": "你要從對戰開始時重試麽?", + "unlockedSomething": "{{unlockedThing}}\n已解鎖。", + "congratulations": "恭喜!", + "beatModeFirstTime": "{{speciesName}}首次擊敗了{{gameMode}}!\n你獲得了{{newModifier}}!" +} diff --git a/src/locales/zh_TW/battler-tags.json b/src/locales/zh_TW/battler-tags.json index 9a35bb0d03f..9653db1077a 100644 --- a/src/locales/zh_TW/battler-tags.json +++ b/src/locales/zh_TW/battler-tags.json @@ -66,5 +66,9 @@ "saltCuredOnAdd": "{{pokemonNameWithAffix}} 陷入了鹽腌狀態!", "saltCuredLapse": "{{pokemonNameWithAffix}} 受到了{{moveName}}的傷害!", "cursedOnAdd": "{{pokemonNameWithAffix}}削減了自己的體力,並詛咒了{{pokemonName}}!", - "cursedLapse": "{{pokemonNameWithAffix}}正受到詛咒!" -} \ No newline at end of file + "cursedLapse": "{{pokemonNameWithAffix}}正受到詛咒!", + "stockpilingOnAdd": "{{pokemonNameWithAffix}}蓄力了{{stockpiledCount}}次!", + "disabledOnAdd": "封住了{{pokemonNameWithAffix}}的\n{moveName}}!", + "disabledLapse": "{{pokemonNameWithAffix}}的\n定身法解除了!", + "tarShotOnAdd": "{{pokemonNameWithAffix}}\n變得怕火了!" +} diff --git a/src/locales/zh_TW/bgm-name.json b/src/locales/zh_TW/bgm-name.json index 9e26dfeeb6e..e8546750977 100644 --- a/src/locales/zh_TW/bgm-name.json +++ b/src/locales/zh_TW/bgm-name.json @@ -1 +1,141 @@ -{} \ No newline at end of file +{ + "music": "Music: ", + "missing_entries" : "{{name}}", + "battle_kanto_champion": "黑2白2「決戰!關都冠軍」", + "battle_johto_champion": "黑2白2「決戰!城都冠軍」", + "battle_hoenn_champion_g5": "黑2白2「決戰!豐緣冠軍」", + "battle_hoenn_champion_g6": "Ω紅寶石α藍寶石「決戰!豐緣冠軍」", + "battle_sinnoh_champion": "黑2白2「決戰!神奧冠軍」", + "battle_champion_alder": "黑白「決戰!合衆冠軍」", + "battle_champion_iris": "黑2白2「決戰!合衆冠軍」", + "battle_kalos_champion": "XY「決戰!卡洛斯冠軍」", + "battle_alola_champion": "究極日月「頂上決戰!」", + "battle_galar_champion": "劍盾「決戰!伽勒爾冠軍」", + "battle_champion_geeta": "朱紫「決戰!帕底亞首席也慈」", + "battle_champion_nemona": "朱紫「決戰!帕底亞冠軍妮莫」", + "battle_champion_kieran": "朱紫「決戰!藍莓學園冠軍烏栗」", + "battle_hoenn_elite": "Ω紅寶石α藍寶石 「戰鬥!豐緣四天王」", + "battle_unova_elite": "黑白 「戰鬥!合衆四天王」", + "battle_kalos_elite": "XY「戰鬥!卡洛斯四天王」", + "battle_alola_elite": "日月「戰鬥!阿羅拉四天王」", + "battle_galar_elite": "劍盾「聯盟錦標賽」", + "battle_paldea_elite": "朱紫「戰鬥!帕底亞四天王」", + "battle_bb_elite": "朱紫「戰鬥!藍之圓盤四天王」", + "battle_final_encounter": "探險隊DX 「裂空座的領域」", + "battle_final": "黑白「戰鬥!魁奇思」", + "battle_kanto_gym": "黑2白2「戰鬥!關都道館主」", + "battle_johto_gym": "黑2白2「戰鬥!城都道館主」", + "battle_hoenn_gym": "黑2白2「戰鬥!合衆道館主」", + "battle_sinnoh_gym": "黑2白2「戰鬥!神奧道館主」", + "battle_unova_gym": "黑白「戰鬥!合衆道館主」", + "battle_kalos_gym": "XY「戰鬥!卡洛斯道館主」", + "battle_galar_gym": "劍盾「戰鬥!伽勒爾道館主」", + "battle_paldea_gym": "朱紫「戰鬥!帕底亞道館主」", + "battle_legendary_kanto": "XY「戰鬥!傳說寶可夢(關都)」", + "battle_legendary_raikou": "心金魂銀「戰鬥!雷公」", + "battle_legendary_entei": "心金魂銀「戰鬥!炎帝」", + "battle_legendary_suicune": "心金魂銀「戰鬥!水君」", + "battle_legendary_lugia": "心金魂銀「戰鬥!洛奇亞」", + "battle_legendary_ho_oh": "心金魂銀「戰鬥!鳳王」", + "battle_legendary_regis_g5": "黑2白2「戰鬥!傳說中的巨人」", + "battle_legendary_regis_g6": "Ω紅寶石α藍寶石「戰鬥!傳說中的巨人」", + "battle_legendary_gro_kyo": "Ω紅寶石α藍寶石「戰鬥!原始回歸」", + "battle_legendary_rayquaza": "Ω紅寶石α藍寶石「戰鬥!超古代寶可夢」", + "battle_legendary_deoxys": "Ω紅寶石α藍寶石「戰鬥!代歐奇希斯」", + "battle_legendary_lake_trio": "Ω紅寶石α藍寶石「戰鬥!由克希・艾姆利多・亞克諾姆」", + "battle_legendary_sinnoh": "Ω紅寶石α藍寶石「戰鬥!傳說的寶可夢」", + "battle_legendary_dia_pal": "Ω紅寶石α藍寶石「戰鬥!帝牙盧卡・帕路奇亞」", + "battle_legendary_giratina": "Ω紅寶石α藍寶石「戰鬥!騎拉帝納」", + "battle_legendary_arceus": "心金魂銀「阿爾宙斯」", + "battle_legendary_unova": "黑白「戰鬥!傳說的寶可夢」", + "battle_legendary_kyurem": "黑白「戰鬥!酋雷姆」", + "battle_legendary_res_zek": "黑白「戰鬥!萊希拉姆・捷克羅姆」", + "battle_legendary_xern_yvel": "XY「戰鬥!哲爾尼亞斯・伊裴爾塔爾」", + "battle_legendary_tapu": "日月「戰鬥!卡璞」", + "battle_legendary_sol_lun": "日月「戰鬥!露奈雅拉・索爾迦雷歐」", + "battle_legendary_ub": "日月「戰鬥!究極異獸」", + "battle_legendary_dusk_dawn": "究極日月「戰鬥!日食・月食 奈克洛茲瑪」", + "battle_legendary_ultra_nec": "究極日月「戰鬥!究極奈克洛茲瑪」", + "battle_legendary_zac_zam": "劍盾「戰鬥!蒼響・藏瑪然特」", + "battle_legendary_glas_spec": "劍盾「戰鬥! 雪暴馬・靈幽馬」", + "battle_legendary_calyrex": "劍盾「戰鬥!蕾冠王」", + "battle_legendary_birds_galar": "劍盾「戰鬥!傳說的鳥寶可夢」", + "battle_legendary_ruinous": "朱紫「戰鬥!災厄寶可夢」", + "battle_legendary_kor_mir": "朱紫「戰鬥!第零區的寶可夢2」", + "battle_legendary_loyal_three": "朱紫「戰鬥!寶伴」", + "battle_legendary_ogerpon": "朱紫「戰鬥!厄鬼椪」", + "battle_legendary_terapagos": "朱紫「戰鬥!太樂巴戈斯」", + "battle_legendary_pecharunt": "朱紫「戰鬥!桃歹郎」", + "battle_rival": "黑白「戰鬥!黑連・貝爾」", + "battle_rival_2": "黑白「戰鬥!N」", + "battle_rival_3": "黑白「決戰!N」", + "battle_trainer": "黑白「戰鬥!訓練師」", + "battle_wild": "黑白「戰鬥!野生寶可夢」", + "battle_wild_strong": "黑白「戰鬥!強大野生寶可夢」", + "end_summit": "探險隊DX 「天空之柱 頂層」", + "battle_rocket_grunt": "心金魂銀「戰鬥!火箭隊」", + "battle_aqua_magma_grunt": "Ω紅寶石α藍寶石「戰鬥!熔岩隊・海洋隊」", + "battle_galactic_grunt": "晶燦鑽石·明亮珍珠「戰鬥!銀河隊」", + "battle_plasma_grunt": "黑白「戰鬥!等離子團」", + "battle_flare_grunt": "XY「戰鬥!閃焰隊」", + "battle_rocket_boss": "究極日月「戰鬥!坂木」", + "battle_aqua_magma_boss": "Ω紅寶石α藍寶石「戰鬥!水梧桐・赤焰松」", + "battle_galactic_boss": "晶燦鑽石·明亮珍珠「戰鬥!赤日」", + "battle_plasma_boss": "黑2白2「戰鬥!魁奇思」", + "battle_flare_boss": "XY「戰鬥!弗拉達利」", + + "abyss": "空之探險隊「黑暗小丘」", + "badlands": "空之探險隊「枯竭之谷」", + "beach": "空之探險隊「潮濕岩地」", + "cave": "空之探險隊「天空頂端(洞窟)」", + "construction_site": "空之探險隊「幻影石室」", + "desert": "空之探險隊「北方沙漠」", + "dojo": "空之探險隊「嘎啦嘎啦道場」", + "end": "探險隊DX「天空之柱」", + "factory": "空之探險隊「隱藏遺迹」", + "fairy_cave": "空之探險隊「星之洞窟」", + "forest": "空之探險隊「黑暗森林」", + "grass": "空之探險隊「蘋果森林」", + "graveyard": "空之探險隊「神秘森林」", + "ice_cave": "空之探險隊「大冰山」", + "island": "空之探險隊「沿岸岩地」", + "jungle": "Lmz - 叢林", + "laboratory": "Firel - 研究所", + "lake": "空之探險隊「水晶洞窟」", + "meadow": "空之探險隊「天空頂端(森林)」", + "metropolis": "Firel - 城市", + "mountain": "空之探險隊「角山」", + "plains": "空之探險隊「天空頂端(草原)」", + "power_plant": "空之探險隊「電氣平原 深處」", + "ruins": "空之探險隊「封印岩地 深處」", + "sea": "Andr06 - 海洋之秘", + "seabed": "Firel - 海底", + "slum": "Andr06 - 狡猾的雪吞蟲", + "snowy_forest": "空之探險隊「天空頂端(雪山)」", + "space": "Firel - 太空", + "swamp": "空之探險隊「封閉之海」", + "tall_grass": "空之探險隊「濃霧森林」", + "temple": "空之探險隊「守護洞穴」", + "town": "空之探險隊「隨機迷宮3」", + "volcano": "空之探險隊「熱水洞窟」", + "wasteland": "空之探險隊「夢幻高原」", + + "encounter_ace_trainer": "黑白 「視線!精英訓練師」", + "encounter_backpacker": "黑白 「視線!背包客」", + "encounter_clerk": "黑白 「視線!上班族」", + "encounter_cyclist": "黑白 「視線!自行車手」", + "encounter_lass": "黑白 「視線!迷你裙」", + "encounter_parasol_lady": "黑白 「視線!陽傘姐姐」", + "encounter_pokefan": "黑白 「視線!寶可夢愛好者」", + "encounter_psychic": "黑白 「視線!超能力者」", + "encounter_rich": "黑白 「視線!紳士」", + "encounter_rival": "黑白「黑連」", + "encounter_roughneck": "黑白 「視線!光頭」", + "encounter_scientist": "黑白 「視線!科學家」", + "encounter_twins": "黑白 「視線!雙胞胎」", + "encounter_youngster": "黑白 「視線!短褲小子」", + + "heal": "黑白「寶可夢回複」", + "menu": "空之探險隊「歡迎來到寶可夢的世界」", + "title": "空之探險隊「主題曲」" +} diff --git a/src/locales/zh_TW/challenges.json b/src/locales/zh_TW/challenges.json index a1fc6b8f50f..f600cc24a84 100644 --- a/src/locales/zh_TW/challenges.json +++ b/src/locales/zh_TW/challenges.json @@ -20,6 +20,12 @@ "desc": "你只能使用{{type}}\n屬性的寶可夢", "desc_default": "你只能使用所選\n屬性的寶可夢" }, + "freshStart": { + "name": "初次嘗試", + "desc": "你只能使用禦三家,就像是你第一次玩寶可夢肉鴿一樣。", + "value.0": "關閉", + "value.1": "開啓" + }, "inverseBattle": { "name": "逆轉之戰", "shortName": "逆轉之戰", @@ -27,4 +33,4 @@ "value.0": "關閉", "value.1": "開啓" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/common.json b/src/locales/zh_TW/common.json index 29ac963ebc3..8b0f91802ee 100644 --- a/src/locales/zh_TW/common.json +++ b/src/locales/zh_TW/common.json @@ -1,8 +1,8 @@ { "start": "開始", - "luckIndicator": "Luck:", - "shinyOnHover": "Shiny", + "luckIndicator": "幸運:", + "shinyOnHover": "閃光", "commonShiny": "常見", "rareShiny": "稀有", "epicShiny": "史詩" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/dialogue-double-battle.json b/src/locales/zh_TW/dialogue-double-battle.json index 684beac5eaf..612bf3d4490 100644 --- a/src/locales/zh_TW/dialogue-double-battle.json +++ b/src/locales/zh_TW/dialogue-double-battle.json @@ -9,10 +9,10 @@ }, "red_blue_double": { "encounter": { - "1": "赤紅: ...!$青綠:他人狠話不多。$青綠: 他人狠話不多。$青綠: 但別被他耍了,\n畢竟他可是個冠軍!" + "1": "赤紅: ...!$青綠:他人狠話不多。$青綠: 他人狠話不多。$青綠: 但別被他耍了,\n畢竟他可是個冠軍!" }, "victory": { - "1": "赤紅: ...!$青綠: 下次我們一定會贏你!" + "1": "赤紅: ...!$青綠: 下次我們一定會贏你!" } }, "tate_liza_double": { @@ -79,4 +79,4 @@ "1": "聶梓:這首歌獻給大家!$瑪俐:哥哥……" } } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/dialogue-misc.json b/src/locales/zh_TW/dialogue-misc.json index 24e2109e5b3..73771e96027 100644 --- a/src/locales/zh_TW/dialogue-misc.json +++ b/src/locales/zh_TW/dialogue-misc.json @@ -1,4 +1,4 @@ { - "ending": "@c{smile}哦?你贏了?@d{96} @c{smile_eclosed}我應該早猜到了\n你回來了。\n$@c{smile}結束了。@d{64} 你終結了這個循環。\n$@c{serious_smile_fists}你也完成了自己的夢想,不是嗎?\n你甚至一次都沒失敗。\n$@c{neutral}我是唯一能夠記得你所作所為的人@d{96}\n我覺得這應該也還行吧?\n$@c{serious_smile_fists}你的傳奇將永遠留存於我們心中。\n$@c{smile_eclosed}不管了,我真是受夠這個地方了,你也一樣嗎?我們回家吧。\n$@c{serious_smile_fists}可能等我們回家以後,再打一場?\n要是你想的話", - "ending_female": "@c{shock}你回來了?@d{32} 也就是說…@d{96} 你贏了呀!?\n@c{smile_ehalf}我應該早料到了。\n$@c{smile_eclosed}當然…我一直有這種感覺\n@c{smile}一切都結束了,對麼? 你打破了循環。\n$@c{smile_ehalf}你也完成了自己的夢想,不是嗎?\n你甚至一次都沒失敗。\n$我是唯一能夠記得你所作所為的人\n@c{angry_mopen}我會努力不忘掉哒!\n$@c{smile_wave_wink}開玩笑啦,@d{64} @c{smile}我才不會忘呢。@d{32}\n你的傳奇將永遠留存於我們心中。\n$@c{smile_wave}不管了,@d{64} 時候不早了@d{96} ,應該吧?\n在這地方還真搞不清楚。\n$一起回家吧。 @c{smile_wave_wink}可能明天,我們再來打一場,為了重溫回憶嘛~" -} \ No newline at end of file + "ending": "@c{shock}你回來了?@d{32} 也就是說…@d{96} 你贏了呀!?\n@c{smile_ehalf}我應該早料到了。\n$@c{smile_eclosed}當然…我一直有這種感覺\n@c{smile}一切都結束了,對麼? 你打破了循環。\n$@c{smile_ehalf}你也完成了自己的夢想,不是嗎?\n你甚至一次都沒失敗。\n$我是唯一能夠記得你所作所為的人\n@c{angry_mopen}我會努力不忘掉哒!\n$@c{smile_wave_wink}開玩笑啦,@d{64} @c{smile}我才不會忘呢。@d{32}\n你的傳奇將永遠留存於我們心中。\n$@c{smile_wave}不管了,@d{64} 時候不早了@d{96} ,應該吧?\n在這地方還真搞不清楚。\n$一起回家吧。 @c{smile_wave_wink}可能明天,我們再來打一場,為了重溫回憶嘛~", + "ending_female": "@c{smile}哦?你贏了?@d{96} @c{smile_eclosed}我應該早猜到了\n你回來了。\n$@c{smile}結束了。@d{64} 你終結了這個循環。\n$@c{serious_smile_fists}你也完成了自己的夢想,不是嗎?\n你甚至一次都沒失敗。\n$@c{neutral}我是唯一能夠記得你所作所為的人@d{96}\n我覺得這應該也還行吧?\n$@c{serious_smile_fists}你的傳奇將永遠留存於我們心中。\n$@c{smile_eclosed}不管了,我真是受夠這個地方了,你也一樣嗎?我們回家吧。\n$@c{serious_smile_fists}可能等我們回家以後,再打一場?\n要是你想的話" +} diff --git a/src/locales/zh_TW/dialogue.json b/src/locales/zh_TW/dialogue.json index 4236e239622..99427ac5f14 100644 --- a/src/locales/zh_TW/dialogue.json +++ b/src/locales/zh_TW/dialogue.json @@ -379,6 +379,414 @@ "3": "好像是我暈船了…" } }, + "archer": { + "encounter": { + "1": "在你繼續前進之前,\n讓我看看你要如何和對付火箭隊。", + "2": "我收到報告說你的實力與衆不同,\n就讓我來看看這是否屬實吧。", + "3": "我是阿波羅,火箭對的幹部。\n我不會對組織的敵人手軟。" + }, + "victory": { + "1": "大失誤……", + "2": "以我現在的實力,無法勝任我的任務……", + "3": "原……諒我,坂木。\n我竟被一名訓練師打敗了。." + } + }, + "ariana": { + "encounter": { + "1": "站住!我們可不能放過你!$這會損傷火箭對的名譽,明白嗎?", + "2": "我不知道也不想知道我的所作所爲正確與否…$我只要遵從坂木老大的指令就可以了!", + "3": "你的旅途到此爲止了,我會把你狠狠扳倒!" + }, + "victory": { + "1": "切,你好強,可惡。$如果你加入火箭隊,肯定能成爲幹部。", + "2": "好……好崩潰……", + "3": "嗯啊啊!這不可能!我使出全力還是輸了!" + } + }, + "proton": { + "encounter": { + "1": "你想幹什麽?如果你要妨礙我們的事業,我可不會手下留情。", + "2": "你在這幹什麽?別人總說我是火箭隊裏最殘忍和恐怖的人…$我強烈推薦你別來礙我們的事!", + "3": "我是蘭斯,火箭隊的幹部。就讓來掃除你對我們的阻撓。" + }, + "victory": { + "1": "我的防線崩潰了……", + "2": "你雖然這次贏了,但是這只是讓火箭隊的怒火繼續燃燒!", + "3": "我輸了…但是我不會忘記的。" + } + }, + + "petrel": { + "encounter": { + "1": "哇哈哈哈,我們一直在等你。我?你不知道我是誰?是我,坂木啊。$偉大的坂木大人本人!哇哈哈哈!…啊?我聽起來不像坂木嗎?$我連看起來都不像?怎麽會呢,我可認真的變裝了!", + "2": "我是拉姆達,火箭隊的幹部。我不會允許你幹涉我們的計劃!", + "3": "火箭隊幹部拉姆達來會會這個入侵者!" + }, + "victory": { + "1": "好好好,我會說他在哪的", + "2": "我……我什麽也做不了……坂木,請原諒我……", + "3": "不,我不能慌了神,必須通知其他人…" + } + }, + "tabitha": { + "encounter": { + "1": "呵呵呵!原來你都一路來到這裏了!但你來晚了!", + "2": "呵呵呵……你終于來了?我們小瞧你了,沒不過事! $我比你見過的所有隊員都要厲害,我可不會拖延時間。$我會把你碾碎!", + "3": "我要讓你嘗嘗痛苦的滋味!認命吧!" + }, + "victory": { + "1": "呵呵呵!雖然你打敗了我,但你根本沒機會打敗老大!$如果你現在輸了,你就不用面對那樣嚴厲的鞭笞了!", + "2": "呵呵呵……所以,我也輸了……", + "3": "啊哈!怎麽會這樣?像我這樣的幹部\n竟然輸給了一個隨處可見的訓練師……" + } + }, + "courtney": { + "encounter": { + "1": "那個東西……你所擁有的那個東西……\n那就是……那就是我們熔岩隊所尋找的東西……", + "2": "……那麽……刪除記憶……", + "3": "……哈……分析中……啊哈♪" + }, + "victory": { + "1": "……改變……世界。", + "2": "如預期。出乎意料。目標鎖定…鎖定你……完成。$開始……實驗。材料是你…永遠…啊哈……♪", + "3": "……又來了?出乎意料……我就知道。你……很有趣!……啊哈哈!♪" + } + }, + "shelly": { + "encounter": { + "1": "啊哈哈哈哈!你要插手海洋隊的事?$你要麽是絕對無畏,要麽就是無知,或者兩者兼有!$你太可愛了,太惡心了!我要把你打倒!", + "2": "怎麽回事?這個小鬼頭是誰?", + "3": "冷靜點,耐心點。我很快就會把你擊潰。" + }, + "victory": { + "1": "啊哈哈哈哈!我們意外地被人幹擾了!我們別無選擇。$不得不撤退了,但這會不是你最後一次面對海洋隊!$我們還有其他計劃!別忘了!", + "2": "啊?!我是不是對你太溫柔了?!", + "3": "唉…難道在對戰中你也一刻不停地在變強嗎?$你真是個前途光明的小鬼……\n我和我的寶可夢已經沒有任何力量去戰鬥了……$繼續吧……准備去被水梧桐摧毀吧。" + } + }, + "matt": { + "encounter": { + "1": "嚯!哈哈哈!怎麽,你是不是腦子不正常了?\n看看你,像個幕下力士!", + "2": "“哦吼!你!你真是個有趣的孩子!", + "3": "你在這裏幹什麽?你跟蹤我們了嗎?" + }, + "victory": { + "1": "好吧,在老大有時間對付你之前,我來成爲你的對手!", + "2": "我能感覺到!我感覺到了,沒錯!你身上散發出的力量!$更多!還想要更多!但看起來我們沒時間了……", + "3": "真有趣!我就知道你會讓我盡興的!\n我期待有一天再次面對你!" + } + }, + "mars": { + "encounter": { + "1": "我是夥星,銀河隊的頂級幹部之一。", + "2": "銀河隊對未來的願景堅定不移。\n反對者將被無情地粉碎!", + "3": "“緊張嗎?你是該感到緊張了!" + }, + "victory": { + "1": "這不可能!我怎麽會輸?!", + "2": "你很有本事,我承認。", + "3": "輸了……犯了一個代價高昂的大錯。" + } + }, + "jupiter": { + "encounter": { + "1": "歲星,銀河隊幹部,爲您效勞。", + "2": "抵抗是徒勞的。銀河隊必將獲勝!", + "3": "你在發抖啊……已經害怕了嗎?" + }, + "victory": { + "1": "不會吧……我輸了?!", + "2": "厲害,你膽子真大!", + "3": "輸成這樣……真丟人。" + } + }, + "saturn": { + "encounter": { + "1": "我是鎮星,銀河隊的幹部。", + "2": "我們的使命是絕對的,任何阻礙都將被消滅!", + "3": "我從你的眼中看到的是恐懼嗎?" + }, + "victory": { + "1": "不可能……被你打敗了?!", + "2": "你證明了自己是一個值得尊敬的對手。", + "3": "失敗的苦澀……難以接受……。" + }}, + "zinzolin": { + "encounter": { + "1": "你可能會對等離子隊構成威脅,所以我們現在就消滅你!", + "2": "哦,天哪……我沒想到要在這麽冷的天氣裏戰鬥!", + "3": "能走到今天這一步,你真是個了不起的訓練師。\n但一切到此結束。" + }, + "victory": { + "1": "魁奇思大人……我讓你失望了……", + "2": "好冷,我不僅發抖,還要遭罪。", + "3": "哼。你比我想象的要聰明,但還不夠。" + } + }, + "rood": { + "encounter": { + "1": "你對等離子隊是個威脅。我們現在不能讓你離開這裏!", + "2": "哦,這寒風……我從沒想過我必須在這裏戰鬥!", + "3": "能走到今天這一步,你是一位了不起的訓練師,但這就是你的結局了。" + }, + "victory": { + "1": "魁奇思大人……我的任務失敗了", + "2": "寒風刺骨。我瑟瑟發抖。我痛苦不堪。", + "3": "嗯,你是很有才。但是要打敗等離子隊還不夠……!" + } + }, + "xerosic": { + "encounter": { + "1": "啊哈哈!我很樂意。\n來吧,小訓練師!讓我們看看你有什麽本事!", + "2": "嗯……你比看上去更強大。\n我想知道你體內有多少能量。", + "3": "我一直在等你!我需要對你做一點研究!\n來吧,我們開始吧!" + }, + "victory": { + "1": "啊,你好強大啊……嗯……確實非常強大。", + "2": "叮叮叮!你成功了!\n戰利品歸勝利者!", + "3": "太棒了!太神奇了!\n你的技巧和勇氣都無與倫比!" + } + }, + "bryony": { + "encounter": { + "1": "我是芭拉,能與你一戰是我的榮幸。\n讓我看看你的實力。", + "2": "令人印象深刻……你比你看上去的還要強大。\n讓我們看看你真正的實力。", + "3": "我預料到了你的到來。\n是時候進行一個小實驗了,我們開始吧?" + }, + "victory": { + "1": "你很強大。哦,嗯嗯!確實非常強大", + "2": "叮叮叮!你做得很好。勝利屬于你。", + "3": "太棒了!了不起!你的技巧和勇氣值得稱贊。" + } + }, + "rocket_grunt": { + "encounter": { + "1": "你要有麻煩了!", + "2": "我們要幹大事了!\n閃一邊去,小子!", + "3": "把你的寶可夢交過來,\n不然就嘗嘗火箭隊的厲害!", + "4": "你准備好感受火箭隊真正的恐怖了嗎!", + "5": "喂,小子!俺可是火箭隊滴!" + }, + "victory": { + "1": "好討厭的感覺啊!", + "2": "哎呀!不小心丟掉電梯鑰匙啦!", + "3": "我搞砸了。", + "4": "我的夥計們不會放過你……!", + "5": "你說啥?俺們火箭隊要玩完了?" + } + }, + "magma_grunt": { + "encounter": { + "1": "如果你擋在熔岩隊路上,那就別指望我們手下留情!", + "2": "你最好別妨礙我們的計劃!\n我們會讓世界變得更美好!", + "3": "少礙事!熔岩隊沒時間理你這樣的小孩!", + "4": "你有棉花糖沒?我來給你好好烤烤!", + "5": "我們會利用火山的力量!\n它馬上要…爆發了!懂嗎?嘿嘿嘿……" + }, + "victory": { + "1": "哈?我輸了?!", + "2": "我怎麽會輸!我爲了訓練飯都不吃了!", + "3": "不會吧,不就是一個小孩!", + "4": "唉啊…我得趕快逃回基地…", + "5": "你打敗我了…你覺得老大會扣我工資嗎?" + } + }, + "aqua_grunt": { + "encounter": { + "1": "即使是小孩,如果要和海洋隊作對,也別指望我們手下留情!", + "2": "嚯…你好大的膽子,敢惹我們海洋隊!", + "3": "不僅是我的水系寶可夢,整片大海即將淹沒你!", + "4": "我們海洋隊,是爲了大義!", + "5": "准備好被我的…唉…我寶可夢的海流沖走吧!" + }, + "victory": { + "1": "你在開玩笑吧?", + "2": "害,沒想到這種小屁孩也要管我的閑事!", + "3": "我輸了?看來我得自己遊回基地了。", + "4": "不是吧,怎麽會,老大要生氣了……", + "5": "你打敗了我…老大不會要讓我上跳板吧……" + } + }, + "galactic_grunt": { + "encounter": { + "1": "別惹銀河隊!", + "2": "見識下我們的科技,和我們所設想的未來!", + "3": "以銀河隊之名,我會掃清一切擋路的人!", + "4": "准備輸的一敗塗地吧!", + "5": "希望你做好被宇宙力量碾壓的准備。" + }, + "victory": { + "1": "停機了…", + "2": "從長遠來看,這次的挫折不用在意。", + "3": "小失敗不會影響我們的宏圖偉業!", + "4": "咋回事!?", + "5": "個人記錄:提升對戰水平,優先級,高……" + } + }, + "plasma_grunt": { + "encounter": { + "1": "異端不共戴天!", + "2": "要是我贏了你!就把你的寶可夢放生!", + "3": "要是敢和等離子隊作對,我來好好關照你!", + "4": "等離子隊會從你們這種自私的人手裏解放寶可夢!", + "5": "我們的發型帥的一批,而我們的戰鬥水平呢,\n馬上讓你見識一下。" + }, + "victory": { + "1": "等離子子子子子子!", + "2": "我怎麽會輸……", + "3": "…沒用的家夥!我得去偷個厲害點的寶可夢!", + "4": "偉大的事業總會被人阻撓…", + "5": "爛完了…爛爛爛爛爛!等離子隊爛了!\n說短點就是,等爛子隊!" + } + }, + "flare_grunt": { + "encounter": { + "1": "你的寶可夢無法與閃焰隊的優雅相提並論", + "2": "帶個墨鏡吧,別被我閃瞎狗眼了!", + "3": "閃焰隊將淨化這個不完美的世界!", + "4": "准備面對閃焰隊的美!", + "5": "時尚對我們來說最重要!" + }, + "victory": { + "1": "我的未來看起來並不明亮…", + "2": "這戰鬥比我想的更難搞,我得重頭訓練了。", + "3": "啊啊?我輸了?!", + "4": "就算是在失敗當中,閃焰隊依舊優雅動人!", + "5": "你雖然打敗了我,但是我輸的也這麽潇灑!" + } + }, + "rocket_boss_giovanni_1": { + "encounter": { + "1": "我不得不說,能來到這裏,你的確很不簡單!" + }, + "victory": { + "1": "什麽!這不可能!" + }, + "defeat": { + "1": "記住我的話。無法衡量自己的力量,說明你還是個孩子。" + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + "1": "我的老夥計還需要我…你要擋我的路嗎?" + }, + "victory": { + "1": "這怎麽可能…?\n火箭隊的夢想…就這麽成爲泡影了…" + }, + "defeat": { + "1": "火箭隊會重生,而我會統治世界!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + "1": "我會親手埋葬你,希望你能喜歡!" + }, + "victory": { + "1": "啊!你…很厲害…我落後了…一點…" + }, + "defeat": { + "1": "熔岩隊必勝!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + "1": "你是我實現目標最後的障礙。\n准備好迎接我最強的一擊吧!哈哈哈哈!" + }, + "victory": { + "1": "這…這不…唉" + }, + "defeat": { + "1": "現在…我要把這個星球變成人類的理想國度!" + } + }, + "aqua_boss_archie_1": { + "encounter": { + "1": "我是海洋隊的老大,所以,你的路大概走到頭了。" + }, + "victory": { + "1": "下次再見吧。我會記住你的臉的。" + }, + "defeat": { + "1": "天才!我的隊伍不會再退縮了!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + "1": "我等這一天很久了。\n這就是我的真實力量!" + }, + "victory": { + "1": "果然很強……啊!" + }, + "defeat": { + "1": "我會讓這世界上的一切回歸到最初的純淨狀態!!" + } + }, + "galactic_boss_cyrus_1": { + "encounter": { + "1": "但在這之前,讓我見識見識你那敢向銀河隊叫板的實力吧。" + }, + "victory": { + "1": "有意思,簡直太有意思了。" + }, + "defeat": { + "1": "我要創造我的新世界…" + } + }, + "galactic_boss_cyrus_2": { + "encounter": { + "1": "是啊,我和你還真是有緣呢。\n不過,這段孽緣…就讓我在此斬斷吧!" + }, + "victory": { + "1": "怎麽可能!怎麽可能!怎麽可能!" + }, + "defeat": { + "1": "永別了。" + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + "1": "無論是誰做了什麽!都無法阻止我!" + }, + "victory": { + "1": "怎麽回事?我可是建立了等離子隊的完美的人啊!\n是要改變世界的完美的統治者!" + }, + "defeat": { + "1": "我是坐擁世界的完美統治者!哇哈哈哈!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + "1": "來吧!讓我看看你徹底絕望時的那張臉!" + }, + "victory": { + "1": "不!我的偉大目標!我要完全支配世界啊!" + }, + "defeat": { + "1": "酋雷姆!融合吧!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + "1": "你想要阻止我?在對戰中展示給我看吧!" + }, + "victory": { + "1": "看來你的確是想要阻止我。但是,先等一下。" + }, + "defeat": { + "1": "寶可夢…不該存在。" + } + }, + "flare_boss_lysandre_2": { + "encounter": { + "1": "你我的未來…究竟哪個才正確,\n就讓我們來問問各自的寶可夢吧!" + }, + "victory": { + "1": "哇啊啊啊!" + }, + "defeat": { + "1": "沒有遠見的蠢貨會繼續玷汙這個美麗的世界。" + } + }, "brock": { "encounter": { "1": "我對岩石屬性寶可夢的專精會擊敗你!來吧!", @@ -676,14 +1084,14 @@ }, "crasher_wake": { "encounter": { - "1": "極限! 極限! 看好了!$極限假面…就此…登場!", - "2": "極限! 極限! 極限假面!", + "1": "極限! 極限! 看好了!$極限假面…就此…登場!", + "2": "極限! 極限! 極限假面!", "3": "我是滔滔巨浪,將你沖走!" }, "victory": { "1": "我真是笑得合不攏嘴啊!$哈哈哈!那真是太有趣了!", "2": "呼哇!結束收尾了!$我該怎麼說呢……$我還想再對戰!我還想再繼續戰鬥!", - "3": "啊啊啊啊啊!?" + "3": "啊啊啊啊啊!?" }, "defeat": { "1": "耶!就是這樣!", @@ -700,7 +1108,7 @@ "victory": { "1": "明白了……我會禮貌地退場。", "2": "輸了就是輸了。你確實很強大。", - "3": "…行吧! 嗯, 我輸了。" + "3": "…行吧! 嗯, 我輸了。" }, "defeat": { "1": "爸爸!我用你珍愛的飛行寶可夢贏了……", @@ -1022,7 +1430,7 @@ }, "clay": { "encounter": { - "1": "咳咳! 讓我好等,不是嗎,孩子?$好吧,是時候看看你能做到什麼了!" + "1": "咳咳! 讓我好等,不是嗎,孩子?$好吧,是時候看看你能做到什麼了!" }, "victory": { "1": "真是的……我先說好,\n我可沒有手下留情。" @@ -1248,7 +1656,7 @@ "1": "夠野的!你的想法比我的還要毒!" }, "defeat": { - "1": "嘿,拜託!認真點!\n你要加把勁啊!" + "1": "嘿,拜託!認真點!\n你要加把勁啊!" } }, "olivia": { @@ -1330,7 +1738,7 @@ }, "wikstrom": { "encounter": { - "1": "年輕的挑戰者,幸會!\n我乃是著名的鋼鐵之刃,公爵雁鎧! $讓我們開始戰鬥吧!預備!" + "1": "年輕的挑戰者,幸會!\n我乃是著名的鋼鐵之刃,公爵雁鎧! $讓我們開始戰鬥吧!預備!" }, "victory": { "1": "輝煌!你與你尊貴的\n寶可夢之間的信任居然勝過了我!" @@ -1367,7 +1775,7 @@ "2": "我知道你能走這麼遠。讓我們開始吧。" }, "victory": { - "1": "被你拿下了啊。你太出色了!", + "1": "被你拿下了啊。你太出色了!", "2": "我從沒想到會有另一個訓練師打敗我……$我很驚訝。" }, "defeat": { @@ -1471,13 +1879,13 @@ }, "red": { "encounter": { - "1": "…!" + "1": "…!" }, "victory": { - "1": "…?" + "1": "…?" }, "defeat": { - "1": "…!" + "1": "…!" } }, "jasmine": { @@ -1859,7 +2267,7 @@ }, "marnie": { "encounter": { - "1": "事實上,言而總之… \n人家自己也想當冠軍呀! $所以別認為我在針對你!" + "1": "事實上,言而總之… \n人家自己也想當冠軍呀! $所以別認為我在針對你!" }, "victory": { "1": "好吧,我還是輸了……\n但是我看到了很多你和你寶可夢的優點哦" @@ -1914,13 +2322,13 @@ }, "ryme": { "encounter": { - "1": "寶貝, 一起! \n搖滾搖到骨子裡!" + "1": "寶貝, 一起! \n搖滾搖到骨子裡!" }, "victory": { "1": "你好酷!我佩服!\n我的靈魂為你哭!" }, "defeat": { - "1": "再會, 寶貝!" + "1": "再會, 寶貝!" } }, "grusha": { @@ -2012,18 +2420,18 @@ "1": "精彩!簡直就是天下無雙!" }, "defeat": { - "1": "戰鬥結束後,我的心像是吹過了溫和的風…\n$真是厲害!" + "1": "戰鬥結束後,我的心像是吹過了溫和的風…$真是厲害!" } }, "kieran": { "encounter": { - "1": "我的努力讓我越來越強!\n$所以我不會輸。" + "1": "我的努力讓我越來越強!$所以我不會輸。" }, "victory": { - "1": "不可能…\n$真是一場有趣又激動人心的戰鬥啊!" + "1": "不可能…$真是一場有趣又激動人心的戰鬥啊!" }, "defeat": { - "1": "哇塞,好一場戰鬥!\n$你得多練練了。" + "1": "哇塞,好一場戰鬥!$你得多練練了。" } }, "rival": { @@ -2031,7 +2439,7 @@ "1": "@c{smile}嘿,我在找你呢!我知道你急著上路,\n但至少說個再見吧…$@c{smile_eclosed}所以你終於要開始追逐夢想了?\n我幾乎不敢相信。$@c{serious_smile_fists}來都來了,來一場對戰怎麼樣?\n畢竟,我想看看你是不是準備周全了。$@c{serious_mopen_fists}不要手下留情,我想讓你全力以赴!" }, "victory": { - "1": "@c{shock}哇…你徹底擊敗了我。\n你是真初學者嗎?$@c{smile}也許是靠點運氣,但是…\n誰知道,你可能真的能一路走下去。$順便說一下,博士讓我給你這些東西。它們看起來可牛了。$@c{serious_smile_fists}祝你好运!" + "1": "@c{shock}哇…你徹底擊敗了我。\n你是真初學者嗎?$@c{smile}也許是靠點運氣,但是…\n誰知道,你可能真的能一路走下去。$順便說一下,博士讓我給你這些東西。它們看起來可牛了。$@c{serious_smile_fists}祝你好运!$@c{smile}哦!我希望你能喜歡這次的活動!" } }, "rival_female": { @@ -2039,7 +2447,7 @@ "1": "@c{smile_wave}你在這兒啊!我到處找你呢!$@c{angry_mopen}你忘了和你最好的朋友說再見了嗎?$@c{smile_ehalf}你要去追逐夢想了,對吧?\n從今天開始,是不是…$@c{smile}不管怎樣,忘了我的事就原諒你吧,\n但有個條件。@c{smile_wave_wink}你必須和我對戰!$@c{angry_mopen}全力以赴!\n你也不想讓你的冒險在開始之前就結束了,對吧?" }, "victory": { - "1": "@c{shock}你剛開始就已經這麼強了?!@d{96}$@c{angry}你是不是開了?$@c{smile_wave_wink}只是開個玩笑啦!@d{64} @c{smile_eclosed}我輸地心服口服了…\n我感覺你出去挺有天賦的。$@c{smile}順便說一下,博士想讓我給你一些東西。\n希望它們能幫上忙!$@c{smile_wave}像往常一樣盡力而為!\n我相信你!" + "1": "@c{shock}你剛開始就已經這麼強了?!@d{96}$@c{angry}你是不是開了?$@c{smile_wave_wink}只是開個玩笑啦!@d{64} @c{smile_eclosed}我輸地心服口服了…\n我感覺你出去挺有天賦的。$@c{smile}順便說一下,博士想讓我給你一些東西。\n希望它們能幫上忙!$@c{smile_wave}像往常一樣盡力而為!\n我相信你!$@c{smile}哦!我希望你能喜歡這次的活動!" } }, "rival_2": { @@ -2047,7 +2455,7 @@ "1": "@c{smile}嘿,你也在這裡嗎?$@c{smile_eclosed}一路過關斬將,是吧?$@c{serious_mopen_fists}我知道看起來好像我尾隨著你來到這裡,\n怎麼可能啦。$@c{serious_smile_fists}說真的,自從你在老家打敗我後,\n我就一直很渴望再比一場。$我自己也進行了很多訓練,\n所以這次我肯定會好好打一場。$@c{serious_mopen_fists}不要手下留情,就像以前一樣!$讓我們開始吧!" }, "victory": { - "1": "@c{neutral_eclosed}哦。我過於自信了。$@c{smile}不過沒關係。我猜到可能會這樣。$@c{serious_mopen_fists}這只意味著我下次需要更努力!$$@c{smile}呃,不是特意幫你,我正好有多餘的這個,\n我覺得你可能想要。$$@c{serious_smile_fists}不過這次之後別指望再有了!$我不能一直給我的對手優勢。$@c{smile}反正,保重!" + "1": "@c{neutral_eclosed}哦。我過於自信了。$@c{smile}不過沒關係。我猜到可能會這樣。$@c{serious_mopen_fists}這只意味著我下次需要更努力!$$@c{smile}呃,不是特意幫你,我正好有多餘的這個,\n我覺得你可能想要。$$@c{serious_smile_fists}不過這次之後別指望再有了!$我不能一直給我的對手優勢。$@c{smile}反正,保重, 要享受活動哦!" } }, "rival_2_female": { @@ -2055,7 +2463,7 @@ "1": "@c{smile_wave}哦,真巧,在這裡遇見你。\n看來你還沒輸過嘛。@c{angry_mopen}哈……好傢伙!$@c{angry_mopen}我知道你在想什麼,\n不,我才不會跟蹤你什麼呢。 @c{smile_eclosed}我只是碰巧在附近。$@c{smile_ehalf}我為你感到高興,但我只想讓你知道\n有時輸了是可以接受的。$@c{smile}我們從錯誤中學到的東西\n往往比我們一直成功時學到的還要多。$@c{angry_mopen}無論如何,我為了我們的複賽已經努力訓練了\n所以你最好全力以赴!" }, "victory": { - "1": "@c{neutral}我……沒打算會輸來著……$@c{smile}嗷……好吧。看來我要再更加努力訓練了!$@c{smile_wave}我還給你帶了個這個$@c{smile_wave_wink}不用謝我哦~.$@c{angry_mopen}不過,這是最後一個啦!\n 你可別想再從我這賺小便宜了~$@c{smile_wave}要保重哦!" + "1": "@c{neutral}我……沒打算會輸來著……$@c{smile}嗷……好吧。看來我要再更加努力訓練了!$@c{smile_wave}我還給你帶了個這個$@c{smile_wave_wink}不用謝我哦~.$@c{angry_mopen}不過,這是最後一個啦!\n 你可別想再從我這賺小便宜了~$@c{smile_wave}要保重哦,要享受活動哦!" }, "defeat": { "1": "輸了有時候也不要緊的…" @@ -2071,7 +2479,7 @@ }, "rival_3_female": { "encounter": { - "1": "@c{smile_wave}好久不見!還沒輸過,對吧。$@c{angry}我覺得你點煩了。@c{smile_wave_wink}開玩笑啦!$@c{smile_ehalf}但說真的,你現在不想家嗎?\n 不想…我嗎?$我……我的意思是,我們真的很想你。$@c{smile_eclosed}我支持你的一切,包括你的夢想。\n但現實就是你早晚會經歷失敗。$@c{smile}當你失敗的時候,我想像往常一樣陪在你身邊。$@c{angry_mopen}現在,給你看看我變得多強了吧!" + "1": "@c{smile_wave}好久不見!還沒輸過,對吧。$@c{angry}我覺得你點煩了。@c{smile_wave_wink}開玩笑啦!$@c{smile_ehalf}但說真的,你現在不想家嗎?\n 不想…我嗎?$我……我的意思是,我們真的很想你。$@c{smile_eclosed}我支持你的一切,包括你的夢想。\n但現實就是你早晚會經歷失敗。$@c{smile}當你失敗的時候,我想像往常一樣陪在你身邊。$@c{angry_mopen}現在,給你看看我變得多強了吧!" }, "victory": { "1": "@c{shock}都這樣了……還是不夠嗎?$這樣下去,你就永遠不會回來了……" @@ -2113,6 +2521,7 @@ }, "victory": { "1": "@c{neutral}…" + }, "defeat": { "1": "$@c{smile_ehalf}…" @@ -2134,4 +2543,4 @@ "1": "@c{smile_ehalf}我……\n我想我完成了我的使命……$@c{smile_eclosed}答應我……在你拯救世界之後\n……要……平安到家。$@c{smile_ehalf}……謝謝你。" } } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/egg.json b/src/locales/zh_TW/egg.json index 28e5e1b9bda..0178848a0d5 100644 --- a/src/locales/zh_TW/egg.json +++ b/src/locales/zh_TW/egg.json @@ -20,7 +20,7 @@ "hatchFromTheEgg": "{{pokemonName}} 從蛋中孵化了!", "eggMoveUnlock": "蛋招式已解鎖: {{moveName}}", "rareEggMoveUnlock": "稀有蛋招式已解鎖: {{moveName}}", - "moveUPGacha": "蛋招式UP!", - "shinyUPGacha": "闪光UP!", - "legendaryUPGacha": "UP!" -} \ No newline at end of file + "moveUPGacha": "蛋招式UP!", + "shinyUPGacha": "闪光UP!", + "legendaryUPGacha": "UP!" +} diff --git a/src/locales/zh_TW/fight-ui-handler.json b/src/locales/zh_TW/fight-ui-handler.json index 3cf1868196a..dda3e443b36 100644 --- a/src/locales/zh_TW/fight-ui-handler.json +++ b/src/locales/zh_TW/fight-ui-handler.json @@ -2,6 +2,7 @@ "pp": "PP", "power": "威力", "accuracy": "命中率", - "abilityFlyInText": " {{pokemonName}} 的 {{passive}}{{abilityName}}", - "passive": "被動能力 " -} \ No newline at end of file + "abilityFlyInText": " {{pokemonName}} 的\n{{passive}}{{abilityName}}", + "passive": "被動能力 ", + "teraHover": "{{type}} 太晶化" +} diff --git a/src/locales/zh_TW/menu-ui-handler.json b/src/locales/zh_TW/menu-ui-handler.json index 33e6b44d73d..cbb5b604d33 100644 --- a/src/locales/zh_TW/menu-ui-handler.json +++ b/src/locales/zh_TW/menu-ui-handler.json @@ -19,6 +19,11 @@ "importData": "導入數據", "exportData": "導出數據", "consentPreferences": "同意偏好", + "linkDiscord": "關聯Discord", + "unlinkDiscord": "解除關聯Discord", + "linkGoogle": "關聯Google", + "unlinkGoogle": "解除關聯Google", "cancel": "取消", - "losingProgressionWarning": "你將失去自戰鬥開始以來的所有進度。是否\n繼續?" -} \ No newline at end of file + "losingProgressionWarning": "你將失去自戰鬥開始以來的所有進度。是否\n繼續?", + "noEggs": "當前沒有任何蛋\n正在孵化中!" +} diff --git a/src/locales/zh_TW/menu.json b/src/locales/zh_TW/menu.json index 1ec0b7124d8..fee9b1be85d 100644 --- a/src/locales/zh_TW/menu.json +++ b/src/locales/zh_TW/menu.json @@ -6,11 +6,11 @@ "newGame": "新遊戲", "settings": "設定", "selectGameMode": "選擇遊戲模式", - "logInOrCreateAccount": "登入或註冊即可開始遊戲,無需郵箱!", + "logInOrCreateAccount": "登入或註冊即可開始遊戲,無需郵箱!", "username": "用戶名", "password": "密碼", "login": "登入", - "orUse": "Or use", + "orUse": "或使用", "register": "注冊", "emptyUsername": "用戶名不能為空", "invalidLoginUsername": "提供的用戶名無效", @@ -27,10 +27,10 @@ "failedToLoadSaveData": "讀取存檔數據失敗。請重新加載頁面。如果\n問題仍然存在,請聯繫管理員。", "sessionSuccess": "工作階段加載成功.", "failedToLoadSession": "無法加載您的工作階段數據。它可能已損壞。", - "boyOrGirl": "你是男孩還是女孩?", - "evolving": "甚麼?\n{{pokemonName}} 要進化了!", + "boyOrGirl": "你是男孩還是女孩?", + "evolving": "甚麼?\n{{pokemonName}} 要進化了!", "stoppedEvolving": "{{pokemonName}} 停止了進化。", - "pauseEvolutionsQuestion": "你確定要停止 {{pokemonName}} 的進化嗎?\n你可以在隊伍畫面中重新啟用進化。", + "pauseEvolutionsQuestion": "你確定要停止 {{pokemonName}} 的進化嗎?\n你可以在隊伍畫面中重新啟用進化。", "evolutionsPaused": "{{pokemonName}}的進化已暫停。", "evolutionDone": "恭喜!\n你的 {{pokemonName}} 進化成了 {{evolvedPokemonName}}!", "dailyRankings": "每日排名", @@ -38,7 +38,17 @@ "noRankings": "無排名", "positionIcon": "#", "loading": "加載中…", + "loadingAsset": "加載資源: {{assetName}}", "playersOnline": "在線玩家", "yes": "是", - "no": "否" -} \ No newline at end of file + "no": "否", + "disclaimer": "免責聲明", + "disclaimerDescription": "這個遊戲尚未完成; 可能存在遊戲性問題(包括潛在的丟檔風險)、\n 不經通知的調整、 未來可能會更新或完成更多內容", + "choosePokemon": "選擇一只寶可夢。", + "renamePokemon": "給寶可夢起名", + "rename": "起名", + "nickname": "昵稱", + "errorServerDown": "糟糕!訪問服務器時發生了錯誤。\n\n你可以保持頁面開啓,\n遊戲會自動重新連接。", + "noSaves": "你沒有任何記錄檔!", + "tooManySaves": "你的記錄檔太多了!" +} diff --git a/src/locales/zh_TW/modifier-select-ui-handler.json b/src/locales/zh_TW/modifier-select-ui-handler.json index 9e26dfeeb6e..84ebbbfef6a 100644 --- a/src/locales/zh_TW/modifier-select-ui-handler.json +++ b/src/locales/zh_TW/modifier-select-ui-handler.json @@ -1 +1,12 @@ -{} \ No newline at end of file +{ + "transfer": "交換道具", + "reroll": "刷新商店", + "lockRarities": "鎖定稀有度", + "checkTeam": "查看隊伍", + "transferDesc": "將寶可夢攜帶的道具交換給其他寶可夢", + "rerollDesc": "花錢刷新道具", + "lockRaritiesDesc": "在刷新時鎖定道具稀有度(影響刷新費用)", + "checkTeamDesc": "檢查隊伍或使用形態改變道具", + "rerollCost": "₽{{formattedMoney}}", + "itemCost": "₽{{formattedMoney}}" +} diff --git a/src/locales/zh_TW/modifier-type.json b/src/locales/zh_TW/modifier-type.json index a7ddaea077b..6ff593ddba7 100644 --- a/src/locales/zh_TW/modifier-type.json +++ b/src/locales/zh_TW/modifier-type.json @@ -47,22 +47,26 @@ "description": "將一隻寶可夢的性格改爲{{natureName}}併爲該寶可\n夢永久解鎖該性格。" }, "DoubleBattleChanceBoosterModifierType": { - "description": "接下來的{{battleCount}}場戰鬥是雙打的概率翻倍。" + "description": "遭遇雙打機率提升四倍,持續{{battleCount}}場戰鬥。" }, - "TempBattleStatBoosterModifierType": { - "description": "爲所有成員寶可夢提升一級{{tempBattleStatName}},持續5場戰鬥。" + "TempStatStageBoosterModifierType": { + "description": "提升全隊的{{stat}}{{amount}}級,持續5場戰鬥。", + "extra": { + "stage": "1階", + "percentage": "30%" + } }, "AttackTypeBoosterModifierType": { "description": "一隻寶可夢的{{moveType}}系招式威力提升20%。" }, "PokemonLevelIncrementModifierType": { - "description": "Increases a Pokémon's level by {{levels}}." + "description": "使一只寶可夢的等級提升{{levels}}級。" }, "AllPokemonLevelIncrementModifierType": { - "description": "Increases all party members' level by {{levels}}." + "description": "使一只寶可夢的等級提升{{levels}}級。" }, - "PokemonBaseStatBoosterModifierType": { - "description": "增加持有者的{{statName}}10%,個體值越高堆疊\n上限越高。" + "BaseStatBoosterModifierType": { + "description": "增加持有者的{{stat}}10%,個體值越高堆疊\n上限越高。" }, "AllPokemonFullHpRestoreModifierType": { "description": "所有寶可夢完全恢復HP。" @@ -91,15 +95,15 @@ "description": "招式命中率增加{{accuracyAmount}} (最大100)。" }, "PokemonMultiHitModifierType": { - "description": "攻擊造成一次額外傷害,每次堆疊額外傷害\n分別衰減60/75/82.5%。" + "description": "攻擊以60/75/82.5%的傷害造成2/3/4次傷害。" }, "TmModifierType": { "name": "招式學習器 {{moveId}} - {{moveName}}", "description": "教會一隻寶可夢{{moveName}}。" }, "TmModifierTypeWithInfo": { - "name": "TM{{moveId}} - {{moveName}}", - "description": "教會一隻寶可夢{{moveName}}\n(Hold C or Shift for more info)。" + "name": "招式學習器{{moveId}} - {{moveName}}", + "description": "教會一隻寶可夢{{moveName}}\n(按住C或者Shift查看更多信息)" }, "EvolutionItemModifierType": { "description": "使某些寶可夢進化。" @@ -179,6 +183,10 @@ "name": "復活種子", "description": "受到技能攻擊傷害瀕死時,\n恢復該瀕死寶可夢的HP至1/2。" }, + "WHITE_HERB": { + "name": "白色香草", + "description": "當攜帶它的寶可夢能力降低時,\n僅能回到之前的狀態1次。" + }, "ETHER": { "name": "PP單項小補劑" }, @@ -244,6 +252,12 @@ "name": "焦點鏡", "description": "能看見弱點的鏡片。攜帶它的寶可夢的招式 會變得容易擊中要害。" }, + "DIRE_HIT": { + "name": "要害攻擊", + "extra": { + "raises": "會心" + } + }, "LEEK": { "name": "大蔥", "description": "非常長且堅硬的莖。讓大蔥鴨攜帶後,招式會 變得容易擊中要害。" @@ -300,19 +314,19 @@ }, "BERRY_POUCH": { "name": "樹果袋", - "description": "使用樹果時有30%的幾率不會消耗樹果。" + "description": "使用樹果時增加30%的幾率不會消耗樹果。" }, "FOCUS_BAND": { "name": "氣勢頭帶", - "description": "攜帶該道具的寶可夢有10%幾率在受到\n攻擊而將陷入瀕死狀態時,保留1點HP不陷入瀕死狀態。" + "description": "攜帶該道具的寶可夢增加10%幾率在受到\n攻擊而將陷入瀕死狀態時,保留1點HP不陷入瀕死狀態。" }, "QUICK_CLAW": { "name": "先制之爪", - "description": "有10%的幾率無視速度優先使出招式\n(先制技能優先)。" + "description": "增加10%的幾率無視速度優先使出招式\n(先制技能優先)。" }, "KINGS_ROCK": { "name": "王者之證", - "description": "攜帶該道具的寶可夢使用任意原本不會造成\n畏縮狀態的攻擊招式並造成傷害時,有\n10%幾率使目標陷入畏縮狀態。" + "description": "攜帶該道具的寶可夢使用任意原本不會造成\n畏縮狀態的攻擊招式並造成傷害時,增加\n10%幾率使目標陷入畏縮狀態。" }, "LEFTOVERS": { "name": "喫剩的東西", @@ -407,25 +421,13 @@ "description": "讓百變怪攜帶後,速度就會提高的神奇粉末。非常細緻堅硬。" } }, - "TempBattleStatBoosterItem": { + "TempStatStageBoosterItem": { "x_attack": "力量強化", "x_defense": "防禦強化", "x_sp_atk": "特攻強化", "x_sp_def": "特防強化", "x_speed": "速度強化", - "x_accuracy": "命中強化", - "dire_hit": "要害攻擊" - }, - "TempBattleStatBoosterStatName": { - "ATK": "攻擊", - "DEF": "防禦", - "SPATK": "特攻", - "SPDEF": "特防", - "SPD": "速度", - "ACC": "命中", - "CRIT": "會心", - "EVA": "閃避", - "DEFAULT": "???" + "x_accuracy": "命中強化" }, "AttackTypeBoosterItem": { "silk_scarf": "絲綢圍巾", @@ -600,6 +602,6 @@ "DRAGON_MEMORY": "龍記憶碟", "DARK_MEMORY": "黑暗記憶碟", "FAIRY_MEMORY": "妖精記憶碟", - "BLANK_MEMORY": "空白記憶碟" + "NORMAL_MEMORY": "一般記憶碟" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/modifier.json b/src/locales/zh_TW/modifier.json index eb4b5107cff..12c436c1783 100644 --- a/src/locales/zh_TW/modifier.json +++ b/src/locales/zh_TW/modifier.json @@ -3,9 +3,10 @@ "turnHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了體力!", "hitHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了體力!", "pokemonInstantReviveApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了活力!", + "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}降低的能力被{{typeName}}\n複原了!!", "moneyInterestApply": "用{{typeName}}\n獲得了 ₽{{moneyAmount}} 利息!", "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}吸收了!", "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}奪取了!", "enemyTurnHealApply": "{{pokemonNameWithAffix}}\n回復了一些體力!", "bypassSpeedChanceApply": "{{pokemonName}}用了{{itemName}}後,行動變快了!" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/move-trigger.json b/src/locales/zh_TW/move-trigger.json index 03ca6841a7f..db88f6df57f 100644 --- a/src/locales/zh_TW/move-trigger.json +++ b/src/locales/zh_TW/move-trigger.json @@ -3,6 +3,10 @@ "cutHpPowerUpMove": "{{pokemonName}}\n削減體力並提升了招式威力!", "absorbedElectricity": "{{pokemonName}}\n吸收了电力!", "switchedStatChanges": "{{pokemonName}}和對手互換了\n自身的能力變化!", + "switchedTwoStatChanges": "{{pokemonName}} 和對手互換了自身的{{firstStat}}和{{secondStat}}的能力變化!", + "switchedStat": "{{pokemonName}} 互換了各自的{{stat}}!", + "sharedGuard": "{{pokemonName}} 平分了各自的防守!", + "sharedPower": "{{pokemonName}} 平分了各自的力量!", "goingAllOutForAttack": "{{pokemonName}}拿出全力了!", "regainedHealth": "{{pokemonName}}的\n體力回復了!", "keptGoingAndCrashed": "{{pokemonName}}因勢頭過猛\n而撞到了地面!", @@ -38,12 +42,12 @@ "incineratedItem": "{{pokemonName}}燒掉了\n{{targetName}}的{{itemName}}!", "knockedOffItem": "{{pokemonName}}拍落了\n{{targetName}}的{{itemName}}!", "tookMoveAttack": "{{pokemonName}}\n受到了{{moveName}}的攻擊!", - "cutOwnHpAndMaximizedStat": "{{pokemonName}}\n削減體力並釋放了全部{{statName}}!", + "cutOwnHpAndMaximizedStat": "{{pokemonName}}\n削減體力並釋放了全部{{statName}}!", "copiedStatChanges": "{{pokemonName}}複製了\n{{targetName}}的能力變化!", "magnitudeMessage": "震級{{magnitude}}!", "tookAimAtTarget": "{{pokemonName}}將目標對準了\n{{targetName}}!", "transformedIntoType": "{{pokemonName}} \n變成了{{typeName}}屬性!", - "copiedMove": "{{pokemonName}}\n複製了{{moveName}}!", + "copiedMove": "{{pokemonName}}\n複製了{{moveName}}!", "sketchedMove": "{{pokemonName}}\n對{{moveName}}進行了寫生!", "acquiredAbility": "{{pokemonName}}的特性\n變为{{abilityName}}了!", "copiedTargetAbility": "{{pokemonName}}複製了\n{{targetName}}的{{abilityName}}!", @@ -61,5 +65,7 @@ "suppressAbilities": "{{pokemonName}}的特性\n變得無效了!", "revivalBlessing": "{{pokemonName}}復活了!", "swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!", - "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!" -} \ No newline at end of file + "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!", + "safeguard": "{{targetName}}\n正受到神秘之幕的保護!", + "afterYou": "{{pokemonName}}\n接受了對手的好意!" +} diff --git a/src/locales/zh_TW/move.json b/src/locales/zh_TW/move.json index 101d6f85e5d..b8c4ec05033 100644 --- a/src/locales/zh_TW/move.json +++ b/src/locales/zh_TW/move.json @@ -2913,7 +2913,7 @@ }, "zippyZap": { "name": "電電加速", - "effect": "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness." + "effect": "迅猛無比的電擊。必定能夠先制攻擊,\n並且提高自己的閃避率" }, "splishySplash": { "name": "滔滔衝浪", @@ -3807,4 +3807,4 @@ "name": "邪毒鎖鏈", "effect": "用由毒形成的鎖鏈纏住對手\n注入毒素加以侵蝕。有時會\n讓對手陷入劇毒狀態" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/party-ui-handler.json b/src/locales/zh_TW/party-ui-handler.json index 975212bbdad..ef82d1a3ddc 100644 --- a/src/locales/zh_TW/party-ui-handler.json +++ b/src/locales/zh_TW/party-ui-handler.json @@ -4,5 +4,44 @@ "CANCEL": "取消", "RELEASE": "放生", "APPLY": "應用", - "TEACH": "教授" -} \ No newline at end of file + "TEACH": "教授", + "SPLICE": "融合", + "UNSPLICE": "分離", + "ACTIVATE": "激活", + "DEACTIVATE": "解除", + "TRANSFER": "交換", + "ALL": "全部道具", + "PASS_BATON": "接棒", + "UNPAUSE_EVOLUTION": "解除進化暫停", + "REVIVE": "複活", + "RENAME": "起名", + "choosePokemon": "選擇一只寶可夢。", + "doWhatWithThisPokemon": "要對寶可夢做什麽?", + "noEnergy": "{{pokemonName}}沒有力氣戰鬥了!", + "hasEnergy": "{{pokemonName}}仍然精神十足!", + "cantBeUsed": "{{pokemonName}}無法在此挑戰中使用!", + "tooManyItems": "{{pokemonName}}擁有\n太多這個道具了!", + "anyEffect": "即便使用也無效果哦。", + "unpausedEvolutions": "{{pokemonName}}的進化停止了。", + "unspliceConfirmation": "真的要把{{fusionName}}\n從{{pokemonName}}身上分離嗎?{{fusionName}}會就此消失。", + "wasReverted": "{{fusionName}}恢複成了{{pokemonName}}。", + "releaseConfirmation": "你真要放生{{pokemonName}}嗎?", + "releaseInBattle": "你無法放生正在戰鬥中的寶可夢!", + "selectAMove": "選擇一個招式。", + "changeQuantity": "選擇一件道具來交換。\n使用 ← 和 → 來指定數量。", + "selectAnotherPokemonToSplice": "選擇另一只寶可夢來融合。", + "cancel": "取消", + "able": "能學會!", + "notAble": "無法學習", + "learned": "已習得", + "goodbye": "再見,{{pokemonName}}!", + "byebye": "拜拜,{{pokemonName}}!", + "farewell": "再會了,{{pokemonName}}!", + "soLong": "告辭了,{{pokemonName}}!", + "thisIsWhereWePart": "就此分別吧,{{pokemonName}}!", + "illMissYou": "我會想你的,{{pokemonName}}!", + "illNeverForgetYou": "我不會忘記你的,{{pokemonName}}!", + "untilWeMeetAgain": "下次再見了,{{pokemonName}}!", + "sayonara": "撒由那拉,{{pokemonName}}!", + "smellYaLater": "拜拜了您勒,{{pokemonName}}!" +} diff --git a/src/locales/zh_TW/pokemon-form-battle.json b/src/locales/zh_TW/pokemon-form-battle.json index 75087aa0669..93a9e65f1f8 100644 --- a/src/locales/zh_TW/pokemon-form-battle.json +++ b/src/locales/zh_TW/pokemon-form-battle.json @@ -2,13 +2,13 @@ "mega": "Mega {{pokemonName}}", "mega-x": "Mega {{pokemonName}} X", "mega-y": "Mega {{pokemonName}} Y", - "primal": "Primal {{pokemonName}}", - "gigantamax": "G-Max {{pokemonName}}", - "eternamax": "E-Max {{pokemonName}}", + "primal": "原始{{pokemonName}}", + "gigantamax": "超極巨{{pokemonName}}", + "eternamax": "無極巨{{pokemonName}}", "megaChange": "{{preName}}超級進化成了\n{{pokemonName}}!", "gigantamaxChange": "{{preName}}超極巨化成了\n{{pokemonName}}!", "eternamaxChange": "{{preName}}無極巨化成了\n{{pokemonName}}!", "revertChange": "{{pokemonName}}變回了\n原本的樣子!", "formChange": "{{preName}}變為其他樣子了。", "disguiseChange": "它的畫皮被當作誘餌使用了!" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/pokemon-form.json b/src/locales/zh_TW/pokemon-form.json index b98a1e946b8..f1fb4dff599 100644 --- a/src/locales/zh_TW/pokemon-form.json +++ b/src/locales/zh_TW/pokemon-form.json @@ -1,3 +1,170 @@ { - "meloettaPirouette": "舞步形態" -} \ No newline at end of file + "pikachuCosplay": "換裝", + "pikachuCoolCosplay": "搖滾巨星", + "pikachuBeautyCosplay": "貴婦", + "pikachuCuteCosplay": "流行偶像", + "pikachuSmartCosplay": "博士", + "pikachuToughCosplay": "面罩摔跤手", + "pikachuPartner": "搭檔", + "eeveePartner": "搭檔", + "pichuSpiky": "刺刺耳", + "unownA": "A", + "unownB": "B", + "unownC": "C", + "unownD": "D", + "unownE": "E", + "unownF": "F", + "unownG": "G", + "unownH": "H", + "unownI": "I", + "unownJ": "J", + "unownK": "K", + "unownL": "L", + "unownM": "M", + "unownN": "N", + "unownO": "O", + "unownP": "P", + "unownQ": "Q", + "unownR": "R", + "unownS": "S", + "unownT": "T", + "unownU": "U", + "unownV": "V", + "unownW": "W", + "unownX": "X", + "unownY": "Y", + "unownZ": "Z", + "unownExclamation": "!", + "unownQuestion": "?", + "castformSunny": "晴天", + "castformRainy": "雨天", + "castformSnowy": "雪天", + "deoxysNormal": "普通", + "burmyPlant": "草木蓑衣", + "burmySandy": "砂土蓑衣", + "burmyTrash": "垃圾蓑衣", + "shellosEast": "東海", + "shellosWest": "西海", + "rotomHeat": "加熱", + "rotomWash": "清洗", + "rotomFrost": "結冰", + "rotomFan": "旋轉", + "rotomMow": "切割", + "giratinaAltered": "別種", + "shayminLand": "陸上", + "basculinRedStriped": "紅條紋", + "basculinBlueStriped": "藍條紋", + "basculinWhiteStriped": "白條紋", + "deerlingSpring": "春天", + "deerlingSummer": "夏天", + "deerlingAutumn": "秋天", + "deerlingWinter": "冬天", + "tornadusIncarnate": "化身", + "thundurusIncarnate": "化身", + "landorusIncarnate": "化身", + "keldeoOrdinary": "通常", + "meloettaAria": "歌聲", + "meloettaPirouette": "舞步形態", + "froakieBattleBond": "牽絆變身", + "scatterbugMeadow": "花園花紋", + "scatterbugIcySnow": "冰雪花紋", + "scatterbugPolar": "雪國花紋", + "scatterbugTundra": "雪原花紋", + "scatterbugContinental": "大陸花紋", + "scatterbugGarden": "庭園花紋", + "scatterbugElegant": "高雅花紋", + "scatterbugModern": "摩登花紋", + "scatterbugMarine": "大海花紋", + "scatterbugArchipelago": "群島花紋", + "scatterbugHighPlains": "荒野花紋", + "scatterbugSandstorm": "沙塵花紋", + "scatterbugRiver": "大河花紋", + "scatterbugMonsoon": "驟雨花紋", + "scatterbugSavanna": "熱帶草原花紋", + "scatterbugSun": "太陽花紋", + "scatterbugOcean": "大洋花紋", + "scatterbugJungle": "熱帶雨林花紋", + "scatterbugFancy": "幻彩花紋", + "scatterbugPokeBall": "球球花紋", + "flabebeRed": "紅花", + "flabebeYellow": "黃花", + "flabebeOrange": "橙花", + "flabebeBlue": "藍花", + "flabebeWhite": "白花", + "furfrouHeart": "心形造型", + "furfrouStar": "星形造型", + "furfrouDiamond": "菱形造型", + "furfrouDebutante": "淑女造型", + "furfrouMatron": "貴婦造型", + "furfrouDandy": "紳士造型", + "furfrouLaReine": "女王造型", + "furfrouKabuki": "歌舞伎造型", + "furfrouPharaoh": "國王造型", + "pumpkabooSmall": "小尺寸", + "pumpkabooLarge": "大尺寸", + "pumpkabooSuper": "特大尺寸", + "xerneasNeutral": "放松模式", + "xerneasActive": "活躍模式", + "zygarde50": "50%形態", + "zygarde10": "10%形態", + "zygarde50Pc": "50%形態 群聚變形", + "zygarde10Pc": "10%形態 群聚變形", + "zygardeComplete": "完全體形態", + "oricorioBaile": "熱辣熱辣風格", + "oricorioPompom": "啪滋啪滋風格", + "oricorioPau": "呼拉呼拉風格", + "oricorioSensu": "輕盈輕盈風格", + "rockruffOwnTempo": "特殊岩狗狗", + "miniorRedMeteor": "紅色核心", + "miniorOrangeMeteor": "橙色核心", + "miniorYellowMeteor": "黃色核心", + "miniorGreenMeteor": "綠色核心", + "miniorBlueMeteor": "淺藍色核心", + "miniorIndigoMeteor": "藍色核心", + "miniorVioletMeteor": "紫色核心", + "miniorRed": "紅色", + "miniorOrange": "橙色", + "miniorYellow": "黃色", + "miniorGreen": "綠色", + "miniorBlue": "淺藍色", + "miniorIndigo": "藍色", + "miniorViolet": "紫色", + "mimikyuDisguised": "化形", + "mimikyuBusted": "現形", + "magearnaOriginal": "500年前的顔色", + "marshadowZenith": "全力", + "sinisteaPhony": "赝品", + "sinisteaAntique": "真品", + "eiscueNoIce": "解凍頭", + "indeedeeMale": "雄性", + "indeedeeFemale": "雌性", + "morpekoFullBelly": "滿腹花紋", + "zacianHeroOfManyBattles": "百戰勇者", + "zamazentaHeroOfManyBattles": "百戰勇者", + "zarudeDada": "老爹", + "enamorusIncarnate": "化身", + "squawkabillyGreenPlumage": "綠羽毛", + "squawkabillyBluePlumage": "藍羽毛", + "squawkabillyYellowPlumage": "黃羽毛", + "squawkabillyWhitePlumage": "白羽毛", + "tatsugiriCurly": "上弓姿勢", + "tatsugiriDroopy": "下垂姿勢", + "tatsugiriStretchy": "平挺姿勢", + "gimmighoulChest": "寶箱形態", + "gimmighoulRoaming": "徒步形態", + "koraidonApexBuild": "頂尖形態", + "koraidonLimitedBuild":"限制形態", + "koraidonSprintingBuild":"沖刺形態", + "koraidonSwimmingBuild":"遊泳形態", + "koraidonGlidingBuild":"滑翔形態", + "miraidonUltimateMode":"極限模式", + "miraidonLowPowerMode":"節能模式", + "miraidonDriveMode":"駕駛模式", + "miraidonAquaticMode":"水上模式", + "miraidonGlideMode":"滑翔模式", + "poltchageistCounterfeit": "冒牌貨", + "poltchageistArtisan": "高檔貨", + "paldeaTaurosCombat": "鬥戰種", + "paldeaTaurosBlaze": "火熾種", + "paldeaTaurosAqua": "水瀾種" +} diff --git a/src/locales/zh_TW/pokemon-info.json b/src/locales/zh_TW/pokemon-info.json index 66a99857a82..f2e29875e75 100644 --- a/src/locales/zh_TW/pokemon-info.json +++ b/src/locales/zh_TW/pokemon-info.json @@ -11,7 +11,9 @@ "SPDEF": "特殊防禦", "SPDEFshortened": "特防", "SPD": "速度", - "SPDshortened": "速度" + "SPDshortened": "速度", + "ACC": "命中率", + "EVA": "回避率" }, "Type": { "UNKNOWN": "未知", @@ -35,4 +37,4 @@ "FAIRY": "妖精", "STELLAR": "星晶" } -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/pokemon-summary.json b/src/locales/zh_TW/pokemon-summary.json index 9e26dfeeb6e..ddbbea63a3a 100644 --- a/src/locales/zh_TW/pokemon-summary.json +++ b/src/locales/zh_TW/pokemon-summary.json @@ -1 +1,45 @@ -{} \ No newline at end of file +{ + "pokemonInfo": "寶可夢信息", + "status": "狀態", + "powerAccuracyCategory": "威力\n命中率\n類別", + "type": "屬性", + "unknownTrainer": "未知", + "ot": "訓練師", + "nature": "性格", + "expPoints": "經驗值", + "nextLv": "下一級", + "cancel": "取消", + + "memoString": "{{natureFragment}} 性格,\n{{metFragment}}", + "metFragment": { + "normal": "met at Lv{{level}},\n{{biome}}.", + "apparently": "命中注定般地相遇于Lv.{{level}},\n{{biome}}。" + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" + } +} diff --git a/src/locales/zh_TW/settings.json b/src/locales/zh_TW/settings.json index b077e0216a3..d9c71df3a0d 100644 --- a/src/locales/zh_TW/settings.json +++ b/src/locales/zh_TW/settings.json @@ -95,5 +95,7 @@ "mute": "靜音", "controller": "控制器", "gamepadSupport": "手柄支持", - "moveTouchControls": "移動觸控控制" + "showBgmBar": "顯示音樂名稱", + "moveTouchControls": "移動觸控控制", + "shopOverlayOpacity": "商店顯示不透明度" } diff --git a/src/locales/zh_TW/splash-messages.json b/src/locales/zh_TW/splash-messages.json index 8535a9acd84..a25e7dab97b 100644 --- a/src/locales/zh_TW/splash-messages.json +++ b/src/locales/zh_TW/splash-messages.json @@ -31,5 +31,6 @@ "alsoTryEmeraldRogue": "也試試翡翠流氓!", "alsoTryRadicalRed": "也試試激進紅!", "eeveeExpo": "伊布博覽會!", - "ynoproject": "YNO專案!" -} \ No newline at end of file + "ynoproject": "YNO專案!", + "breedersInSpace": "飼養員也能上太空?" +} diff --git a/src/locales/zh_TW/starter-select-ui-handler.json b/src/locales/zh_TW/starter-select-ui-handler.json index 2a0db7217da..ad64a8d9743 100644 --- a/src/locales/zh_TW/starter-select-ui-handler.json +++ b/src/locales/zh_TW/starter-select-ui-handler.json @@ -1,6 +1,7 @@ { - "confirmStartTeam": "使用這些寶可夢開始嗎?", - "invalidParty": "此為無效隊伍!", + "confirmStartTeam": "使用這些寶可夢開始嗎?", + "confirmExit": "確定要退出嗎?", + "invalidParty": "此為無效隊伍!", "gen1": "I", "gen2": "II", "gen3": "III", @@ -21,6 +22,8 @@ "toggleIVs": "查看個體值", "manageMoves": "管理技能", "manageNature": "管理性格", + "addToFavorites": "添加到最愛", + "removeFromFavorites": "移出最愛", "useCandies": "使用糖果", "selectNature": "選擇性格", "selectMoveSwapOut": "選擇想要替換走的招式", @@ -40,4 +43,4 @@ "locked": "未解鎖", "disabled": "已禁用", "uncaught": "未捕獲" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/status-effect.json b/src/locales/zh_TW/status-effect.json index 9e26dfeeb6e..5f634a0bddf 100644 --- a/src/locales/zh_TW/status-effect.json +++ b/src/locales/zh_TW/status-effect.json @@ -1 +1,65 @@ -{} \ No newline at end of file +{ + "none": { + "name": "無", + "description": "", + "obtain": "", + "obtainSource": "", + "activation": "", + "overlap": "", + "heal": "" + }, + "poison": { + "name": "中毒", + "description": "中毒", + "obtain": "{{pokemonNameWithAffix}}中毒了!", + "obtainSource": "{{pokemonNameWithAffix}}因{{sourceText}}中毒了!", + "activation": "{{pokemonNameWithAffix}}受到了毒的傷害!", + "overlap": "{{pokemonNameWithAffix}}已經中毒了!", + "heal": "{{pokemonNameWithAffix}}中的毒徹底清除了!" + }, + "toxic": { + "name": "劇毒", + "description": "中毒", + "obtain": "{{pokemonNameWithAffix}}中了劇毒!", + "obtainSource": "{{pokemonNameWithAffix}}因{{sourceText}}中了劇毒!", + "activation": "{{pokemonNameWithAffix}}受到了毒的傷害!", + "overlap": "{{pokemonNameWithAffix}}已經中毒了!", + "heal": "{{pokemonNameWithAffix}}中的毒徹底清除了!" + }, + "paralysis": { + "name": "麻痹", + "description": "麻痹", + "obtain": "{{pokemonNameWithAffix}}麻痹了,很難使出招式!", + "obtainSource": "{{pokemonNameWithAffix}}被{{sourceText}}麻痹了,很難使出招式!", + "activation": "{{pokemonNameWithAffix}}因身體麻痹而無法行動!", + "overlap": "{{pokemonNameWithAffix}}已經麻痹了!", + "heal": "{{pokemonNameWithAffix}}的麻痹治愈了!" + }, + "sleep": { + "name": "睡眠", + "description": "睡眠", + "obtain": "{{pokemonNameWithAffix}}睡著了!", + "obtainSource": "{{pokemonNameWithAffix}}因{{sourceText}}睡著了!", + "activation": "{{pokemonNameWithAffix}}正在呼呼大睡。", + "overlap": "{{pokemonNameWithAffix}}已經睡著了!", + "heal": "{{pokemonNameWithAffix}}醒了!" + }, + "freeze": { + "name": "冰凍", + "description": "冰凍", + "obtain": "{{pokemonNameWithAffix}}凍住了!", + "obtainSource": "{{pokemonNameWithAffix}}因{{sourceText}}凍住了!", + "activation": "{{pokemonNameWithAffix}}因凍住了而無法行動!", + "overlap": "{{pokemonNameWithAffix}}已經凍住了!", + "heal": "{{pokemonNameWithAffix}}治愈了冰凍狀態!" + }, + "burn": { + "name": "灼傷", + "description": "灼傷", + "obtain": "{{pokemonNameWithAffix}}被灼傷了!", + "obtainSource": "{{pokemonNameWithAffix}}因{{sourceText}}被灼傷了!", + "activation": "{{pokemonNameWithAffix}}受到了灼傷的傷害!", + "overlap": "{{pokemonNameWithAffix}}已經被灼傷了!", + "heal": "{{pokemonNameWithAffix}}的灼傷治愈了!" + } +} diff --git a/src/locales/zh_TW/trainer-classes.json b/src/locales/zh_TW/trainer-classes.json index d6ff7238ec1..d0b0fed7e5d 100644 --- a/src/locales/zh_TW/trainer-classes.json +++ b/src/locales/zh_TW/trainer-classes.json @@ -100,6 +100,8 @@ "worker_female": "工人", "workers": "工人組合", "youngster": "短褲小子", + "rocket_grunt": "火箭队手下", + "rocket_grunt_female": "火箭队手下", "rocket_grunts": "火箭队手下們", "magma_grunt": "熔岩队手下", "magma_grunt_female": "熔岩队手下", @@ -116,4 +118,4 @@ "flare_grunt": "闪焰队手下", "flare_grunt_female": "闪焰队手下", "flare_grunts": "闪焰队手下們" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/trainer-names.json b/src/locales/zh_TW/trainer-names.json index 64e8892e64b..04399cf19af 100644 --- a/src/locales/zh_TW/trainer-names.json +++ b/src/locales/zh_TW/trainer-names.json @@ -123,6 +123,34 @@ "kieran": "烏栗", "rival": "芬恩", "rival_female": "艾薇", + "archer": "阿波羅", + "ariana": "雅典娜", + "proton": "蘭斯", + "petrel": "拉姆達", + "tabitha": "火村", + "courtney": "火雁", + "shelly": "阿泉", + "matt": "阿潮", + "mars": "夥星", + "jupiter": "碎星", + "saturn": "鎮星", + "zinzolin": "維奧", + "rood": "羅德", + "xerosic": "庫瑟洛斯奇", + "bryony": "芭拉", + "faba": "扎奧博", + "plumeria": "布爾美麗", + "oleana": "奧利薇", + + "maxie": "赤焰松", + "archie": "水梧桐", + "cyrus": "赤日", + "ghetsis": "魁奇思", + "lysandre": "弗拉達利", + "lusamine": "露莎米奈", + "guzma": "古茲馬", + "rose": "洛茲", + "blue_red_double": "青綠 & 赤紅", "red_blue_double": "赤紅 & 青綠", "tate_liza_double": "小楓 & 小南", @@ -133,4 +161,4 @@ "iris_alder_double": "艾莉絲 & 阿戴克", "marnie_piers_double": "瑪俐 & 聶梓", "piers_marnie_double": "聶梓 & 瑪俐" -} \ No newline at end of file +} diff --git a/src/locales/zh_TW/trainer-titles.json b/src/locales/zh_TW/trainer-titles.json index 54fe48b1464..80b2807e7b5 100644 --- a/src/locales/zh_TW/trainer-titles.json +++ b/src/locales/zh_TW/trainer-titles.json @@ -9,5 +9,24 @@ "champion_double": "冠軍搭檔", "rival": "勁敵", "professor": "博士", - "frontier_brain": "開拓頭腦" -} \ No newline at end of file + "frontier_brain": "開拓頭腦", + "rocket_boss": "火箭隊老大", + "magma_boss": "熔岩隊老大", + "aqua_boss": "海洋隊老大", + "galactic_boss": "銀河隊老大", + "plasma_boss": "等離子隊老大", + "flare_boss": "閃焰隊老大", + + "rocket_admin": "火箭隊幹部", + "rocket_admin_female": "火箭隊幹部", + "magma_admin": "熔岩隊幹部", + "magma_admin_female": "熔岩隊幹部", + "aqua_admin": "海洋隊幹部", + "aqua_admin_female": "海洋隊幹部", + "galactic_commander": "銀河隊幹部", + "galactic_commander_female": "銀河隊幹部", + "plasma_sage": "等離子隊賢人", + "plasma_admin": "等離子隊幹部", + "flare_admin": "閃焰隊幹部", + "flare_admin_female": "閃焰隊幹部" +} diff --git a/src/locales/zh_TW/weather.json b/src/locales/zh_TW/weather.json index b20975374ea..b315d27625d 100644 --- a/src/locales/zh_TW/weather.json +++ b/src/locales/zh_TW/weather.json @@ -27,5 +27,6 @@ "harshSunClearMessage": "日照復原了。", "strongWindsStartMessage": "吹起了神秘的亂流!", "strongWindsLapseMessage": "神秘的亂流勢頭不減。", + "strongWindsEffectMessage": "神秘的亂流減弱了攻擊!", "strongWindsClearMessage": "神秘的亂流停止了。" -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index 8a69d3f1b72..b5f813bdf2f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,8 +4,8 @@ import InvertPostFX from "./pipelines/invert"; import { version } from "../package.json"; import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin"; -import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin.js"; -import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin.js"; +import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin"; +import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin"; import { LoadingScene } from "./loading-scene"; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 8c1beb05a91..fa861271ccb 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -3,12 +3,10 @@ import { AttackMove, allMoves, selfStatLowerMoves } from "../data/move"; import { MAX_PER_TYPE_POKEBALLS, PokeballType, getPokeballCatchMultiplier, getPokeballName } from "../data/pokeball"; import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "../field/pokemon"; import { EvolutionItem, pokemonEvolutions } from "../data/pokemon-evolutions"; -import { Stat, getStatName } from "../data/pokemon-stat"; import { tmPoolTiers, tmSpecies } from "../data/tms"; import { Type } from "../data/type"; import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "../ui/party-ui-handler"; import * as Utils from "../utils"; -import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName } from "../data/temp-battle-stat"; import { getBerryEffectDescription, getBerryName } from "../data/berry"; import { Unlockables } from "../system/unlockables"; import { StatusEffect, getStatusEffectDescriptor } from "../data/status-effect"; @@ -27,7 +25,8 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PermanentStat, TEMP_BATTLE_STATS, TempBattleStat, Stat, getStatKey } from "#app/enums/stat"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -434,39 +433,48 @@ export class RememberMoveModifierType extends PokemonModifierType { } export class DoubleBattleChanceBoosterModifierType extends ModifierType { - public battleCount: integer; + private maxBattles: number; - constructor(localeKey: string, iconImage: string, battleCount: integer) { - super(localeKey, iconImage, (_type, _args) => new Modifiers.DoubleBattleChanceBoosterModifier(this, this.battleCount), "lure"); + constructor(localeKey: string, iconImage: string, maxBattles: number) { + super(localeKey, iconImage, (_type, _args) => new Modifiers.DoubleBattleChanceBoosterModifier(this, maxBattles), "lure"); - this.battleCount = battleCount; + this.maxBattles = maxBattles; } - getDescription(scene: BattleScene): string { - return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", { battleCount: this.battleCount }); + getDescription(_scene: BattleScene): string { + return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", { + battleCount: this.maxBattles + }); } } -export class TempBattleStatBoosterModifierType extends ModifierType implements GeneratedPersistentModifierType { - public tempBattleStat: TempBattleStat; +export class TempStatStageBoosterModifierType extends ModifierType implements GeneratedPersistentModifierType { + private stat: TempBattleStat; + private nameKey: string; + private quantityKey: string; - constructor(tempBattleStat: TempBattleStat) { - super("", getTempBattleStatBoosterItemName(tempBattleStat).replace(/\./g, "").replace(/[ ]/g, "_").toLowerCase(), - (_type, _args) => new Modifiers.TempBattleStatBoosterModifier(this, this.tempBattleStat)); + constructor(stat: TempBattleStat) { + const nameKey = TempStatStageBoosterModifierTypeGenerator.items[stat]; + super("", nameKey, (_type, _args) => new Modifiers.TempStatStageBoosterModifier(this, this.stat, 5)); - this.tempBattleStat = tempBattleStat; + this.stat = stat; + this.nameKey = nameKey; + this.quantityKey = (stat !== Stat.ACC) ? "percentage" : "stage"; } get name(): string { - return i18next.t(`modifierType:TempBattleStatBoosterItem.${getTempBattleStatBoosterItemName(this.tempBattleStat).replace(/\./g, "").replace(/[ ]/g, "_").toLowerCase()}`); + return i18next.t(`modifierType:TempStatStageBoosterItem.${this.nameKey}`); } - getDescription(scene: BattleScene): string { - return i18next.t("modifierType:ModifierType.TempBattleStatBoosterModifierType.description", { tempBattleStatName: getTempBattleStatName(this.tempBattleStat) }); + getDescription(_scene: BattleScene): string { + return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { + stat: i18next.t(getStatKey(this.stat)), + amount: i18next.t(`modifierType:ModifierType.TempStatStageBoosterModifierType.extra.${this.quantityKey}`) + }); } getPregenArgs(): any[] { - return [ this.tempBattleStat ]; + return [ this.stat ]; } } @@ -611,40 +619,24 @@ export class AllPokemonLevelIncrementModifierType extends ModifierType { } } -function getBaseStatBoosterItemName(stat: Stat) { - switch (stat) { - case Stat.HP: - return "HP Up"; - case Stat.ATK: - return "Protein"; - case Stat.DEF: - return "Iron"; - case Stat.SPATK: - return "Calcium"; - case Stat.SPDEF: - return "Zinc"; - case Stat.SPD: - return "Carbos"; - } -} +export class BaseStatBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType { + private stat: PermanentStat; + private key: string; -export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType { - private localeName: string; - private stat: Stat; + constructor(stat: PermanentStat) { + const key = BaseStatBoosterModifierTypeGenerator.items[stat]; + super("", key, (_type, args) => new Modifiers.BaseStatModifier(this, (args[0] as Pokemon).id, this.stat)); - constructor(localeName: string, stat: Stat) { - super("", localeName.replace(/[ \-]/g, "_").toLowerCase(), (_type, args) => new Modifiers.PokemonBaseStatModifier(this, (args[0] as Pokemon).id, this.stat)); - - this.localeName = localeName; this.stat = stat; + this.key = key; } get name(): string { - return i18next.t(`modifierType:BaseStatBoosterItem.${this.localeName.replace(/[ \-]/g, "_").toLowerCase()}`); + return i18next.t(`modifierType:BaseStatBoosterItem.${this.key}`); } - getDescription(scene: BattleScene): string { - return i18next.t("modifierType:ModifierType.PokemonBaseStatBoosterModifierType.description", { statName: getStatName(this.stat) }); + getDescription(_scene: BattleScene): string { + return i18next.t("modifierType:ModifierType.BaseStatBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) }); } getPregenArgs(): any[] { @@ -922,6 +914,48 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { } } +class BaseStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { + public static readonly items: Record = { + [Stat.HP]: "hp_up", + [Stat.ATK]: "protein", + [Stat.DEF]: "iron", + [Stat.SPATK]: "calcium", + [Stat.SPDEF]: "zinc", + [Stat.SPD]: "carbos" + }; + + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs) { + return new BaseStatBoosterModifierType(pregenArgs[0]); + } + const randStat: PermanentStat = Utils.randSeedInt(Stat.SPD + 1); + return new BaseStatBoosterModifierType(randStat); + }); + } +} + +class TempStatStageBoosterModifierTypeGenerator extends ModifierTypeGenerator { + public static readonly items: Record = { + [Stat.ATK]: "x_attack", + [Stat.DEF]: "x_defense", + [Stat.SPATK]: "x_sp_atk", + [Stat.SPDEF]: "x_sp_def", + [Stat.SPD]: "x_speed", + [Stat.ACC]: "x_accuracy" + }; + + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && (pregenArgs.length === 1) && TEMP_BATTLE_STATS.includes(pregenArgs[0])) { + return new TempStatStageBoosterModifierType(pregenArgs[0]); + } + const randStat: TempBattleStat = Utils.randSeedInt(Stat.ACC, Stat.ATK); + return new TempStatStageBoosterModifierType(randStat); + }); + } +} + /** * Modifier type generator for {@linkcode SpeciesStatBoosterModifierType}, which * encapsulates the logic for weighting the most useful held item from @@ -930,7 +964,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { */ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { /** Object comprised of the currently available species-based stat boosting held items */ - public static items = { + public static readonly items = { LIGHT_BALL: { stats: [Stat.ATK, Stat.SPATK], multiplier: 2, species: [Species.PIKACHU] }, THICK_CLUB: { stats: [Stat.ATK], multiplier: 2, species: [Species.CUBONE, Species.MAROWAK, Species.ALOLA_MAROWAK] }, METAL_POWDER: { stats: [Stat.DEF], multiplier: 2, species: [Species.DITTO] }, @@ -1043,7 +1077,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { } class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { - constructor() { + constructor(rare: boolean) { super((party: Pokemon[], pregenArgs?: any[]) => { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in FormChangeItem)) { return new FormChangeItemModifierType(pregenArgs[0] as FormChangeItem); @@ -1083,7 +1117,8 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { } } return formChangeItemTriggers; - }).flat().flatMap(fc => fc.item))]; + }).flat()) + ].flat().flatMap(fc => fc.item).filter(i => (i && i < 100) === rare); // convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party. if (!formChangeItemPool.length) { @@ -1232,7 +1267,7 @@ export type GeneratorModifierOverride = { type?: SpeciesStatBoosterItem; } | { - name: keyof Pick; + name: keyof Pick; type?: TempBattleStat; } | { @@ -1282,7 +1317,8 @@ export const modifierTypes = { EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(false), RARE_EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(true), - FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(), + FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(false), + RARE_FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(true), MEGA_BRACELET: () => new ModifierType("modifierType:ModifierType.MEGA_BRACELET", "mega_bracelet", (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)), DYNAMAX_BAND: () => new ModifierType("modifierType:ModifierType.DYNAMAX_BAND", "dynamax_band", (type, _args) => new Modifiers.GigantamaxAccessModifier(type)), @@ -1304,7 +1340,7 @@ export const modifierTypes = { SACRED_ASH: () => new AllPokemonFullReviveModifierType("modifierType:ModifierType.SACRED_ASH", "sacred_ash"), REVIVER_SEED: () => new PokemonHeldItemModifierType("modifierType:ModifierType.REVIVER_SEED", "reviver_seed", (type, args) => new Modifiers.PokemonInstantReviveModifier(type, (args[0] as Pokemon).id)), - WHITE_HERB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.WHITE_HERB", "white_herb", (type, args) => new Modifiers.PokemonResetNegativeStatStageModifier(type, (args[0] as Pokemon).id)), + WHITE_HERB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.WHITE_HERB", "white_herb", (type, args) => new Modifiers.ResetNegativeStatStageModifier(type, (args[0] as Pokemon).id)), ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.ETHER", "ether", 10), MAX_ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.MAX_ETHER", "max_ether", -1), @@ -1319,29 +1355,24 @@ export const modifierTypes = { SUPER_REPEL: () => new DoubleBattleChanceBoosterModifierType('Super Repel', 10), MAX_REPEL: () => new DoubleBattleChanceBoosterModifierType('Max Repel', 25),*/ - LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.LURE", "lure", 5), - SUPER_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.SUPER_LURE", "super_lure", 10), - MAX_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.MAX_LURE", "max_lure", 25), + LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.LURE", "lure", 10), + SUPER_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.SUPER_LURE", "super_lure", 15), + MAX_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.MAX_LURE", "max_lure", 30), SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(), - TEMP_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in TempBattleStat)) { - return new TempBattleStatBoosterModifierType(pregenArgs[0] as TempBattleStat); - } - const randTempBattleStat = Utils.randSeedInt(6) as TempBattleStat; - return new TempBattleStatBoosterModifierType(randTempBattleStat); - }), - DIRE_HIT: () => new TempBattleStatBoosterModifierType(TempBattleStat.CRIT), + TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterModifierTypeGenerator(), - BASE_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Stat)) { - const stat = pregenArgs[0] as Stat; - return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(stat), stat); + DIRE_HIT: () => new class extends ModifierType { + getDescription(_scene: BattleScene): string { + return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { + stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"), + amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage") + }); } - const randStat = Utils.randSeedInt(6) as Stat; - return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(randStat), randStat); - }), + }("modifierType:ModifierType.DIRE_HIT", "dire_hit", (type, _args) => new Modifiers.TempCritBoosterModifier(type, 5)), + + BASE_STAT_BOOSTER: () => new BaseStatBoosterModifierTypeGenerator(), ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterModifierTypeGenerator(), @@ -1511,9 +1542,10 @@ const modifierPool: ModifierPool = { return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.LURE, skipInLastClassicWaveOrDefault(2)), - new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4), + new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4), new WeightedModifierType(modifierTypes.BERRY, 2), new WeightedModifierType(modifierTypes.TM_COMMON, 2), + new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1), ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), @@ -1584,7 +1616,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), new WeightedModifierType(modifierTypes.TERA_SHARD, 1), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0), - new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1), + new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 3, 0) : 0, 3), ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), @@ -1595,6 +1627,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.PP_MAX, 3), new WeightedModifierType(modifierTypes.MINT, 4), new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32), + new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { const { gameMode, gameData } = party[0].scene; @@ -1624,7 +1657,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.WHITE_HERB, (party: Pokemon[]) => { const checkedAbilities = [Abilities.WEAK_ARMOR, Abilities.CONTRARY, Abilities.MOODY, Abilities.ANGER_SHELL, Abilities.COMPETITIVE, Abilities.DEFIANT]; const weightMultiplier = party.filter( - p => !p.getHeldItems().some(i => i instanceof Modifiers.PokemonResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) && + p => !p.getHeldItems().some(i => i instanceof Modifiers.ResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && selfStatLowerMoves.includes(m.moveId)))).length; // If a party member has one of the above moves or abilities and doesn't have max herbs, the herb will appear more frequently return 0 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier * 0 : 0); @@ -1662,10 +1695,10 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), new WeightedModifierType(modifierTypes.LOCK_CAPSULE, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), - new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), + new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.DYNAMAX_BAND, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), - new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3), + new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(9 - rerollCount * 3, 0) : 0, 9), ].map(m => { m.setTier(ModifierTier.ROGUE); return m; }), @@ -1674,7 +1707,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.SHINY_CHARM, 14), new WeightedModifierType(modifierTypes.HEALING_CHARM, 18), new WeightedModifierType(modifierTypes.MULTI_LENS, 18), - new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5), + new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(15 - rerollCount * 5, 0) : 0, 15), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24), new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))) ? 1 : 0, 1), ].map(m => { @@ -2236,7 +2269,7 @@ export function getPartyLuckValue(party: Pokemon[]): integer { if (party[0].scene.gameMode.isDaily) { return 0; } - const luck = Phaser.Math.Clamp(party.map(p => p.isFainted() ? 0 : p.getLuck()) + const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) .reduce((total: integer, value: integer) => total += value, 0), 0, 14); return luck || 0; } diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 99f4540f493..14a5bac361f 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3,33 +3,33 @@ import BattleScene from "../battle-scene"; import { getLevelTotalExp } from "../data/exp"; import { MAX_PER_TYPE_POKEBALLS, PokeballType } from "../data/pokeball"; import Pokemon, { PlayerPokemon } from "../field/pokemon"; -import { Stat } from "../data/pokemon-stat"; import { addTextObject, TextStyle } from "../ui/text"; import { Type } from "../data/type"; import { EvolutionPhase } from "../phases/evolution-phase"; import { FusionSpeciesFormEvolution, pokemonEvolutions, pokemonPrevolutions } from "../data/pokemon-evolutions"; import { getPokemonNameWithAffix } from "../messages"; import * as Utils from "../utils"; -import { TempBattleStat } from "../data/temp-battle-stat"; import { getBerryEffectFunc, getBerryPredicate } from "../data/berry"; -import { BattlerTagType} from "#enums/battler-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { StatusEffect, getStatusEffectHealText } from "../data/status-effect"; import { achvs } from "../system/achv"; import { VoucherType } from "../system/voucher"; -import { FormChangeItem, SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; +import { FormChangeItem, SpeciesFormChangeItemTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeTeraTrigger } from "../data/pokemon-forms"; import { Nature } from "#app/data/nature"; import Overrides from "#app/overrides"; import { ModifierType, modifierTypes } from "./modifier-type"; import { Command } from "#app/ui/command-ui-handler"; import { Species } from "#enums/species"; +import { Stat, type PermanentStat, type TempBattleStat, BATTLE_STATS, TEMP_BATTLE_STATS } from "#app/enums/stat"; import i18next from "i18next"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; -import { LearnMovePhase } from "#app/phases/learn-move-phase.js"; -import { LevelUpPhase } from "#app/phases/level-up-phase.js"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { LevelUpPhase } from "#app/phases/level-up-phase"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import { SpeciesFormKey } from "#app/data/pokemon-species"; export type ModifierPredicate = (modifier: Modifier) => boolean; @@ -293,110 +293,237 @@ export class AddVoucherModifier extends ConsumableModifier { } } +/** + * Modifier used for party-wide or passive items that start an initial + * {@linkcode battleCount} equal to {@linkcode maxBattles} that, for every + * battle, decrements. Typically, when {@linkcode battleCount} reaches 0, the + * modifier will be removed. If a modifier of the same type is to be added, it + * will reset {@linkcode battleCount} back to {@linkcode maxBattles} of the + * existing modifier instead of adding that modifier directly. + * @extends PersistentModifier + * @abstract + * @see {@linkcode add} + */ export abstract class LapsingPersistentModifier extends PersistentModifier { - protected battlesLeft: integer; + /** The maximum amount of battles the modifier will exist for */ + private maxBattles: number; + /** The current amount of battles the modifier will exist for */ + private battleCount: number; - constructor(type: ModifierTypes.ModifierType, battlesLeft?: integer, stackCount?: integer) { + constructor(type: ModifierTypes.ModifierType, maxBattles: number, battleCount?: number, stackCount?: integer) { super(type, stackCount); - this.battlesLeft = battlesLeft!; // TODO: is this bang correct? + this.maxBattles = maxBattles; + this.battleCount = battleCount ?? this.maxBattles; } - lapse(args: any[]): boolean { - return !!--this.battlesLeft; + /** + * Goes through existing modifiers for any that match the selected modifier, + * which will then either add it to the existing modifiers if none were found + * or, if one was found, it will refresh {@linkcode battleCount}. + * @param modifiers {@linkcode PersistentModifier} array of the player's modifiers + * @param _virtual N/A + * @param _scene N/A + * @returns true if the modifier was successfully added or applied, false otherwise + */ + add(modifiers: PersistentModifier[], _virtual: boolean, scene: BattleScene): boolean { + for (const modifier of modifiers) { + if (this.match(modifier)) { + const modifierInstance = modifier as LapsingPersistentModifier; + if (modifierInstance.getBattleCount() < modifierInstance.getMaxBattles()) { + modifierInstance.resetBattleCount(); + scene.playSound("se/restore"); + return true; + } + // should never get here + return false; + } + } + + modifiers.push(this); + return true; + } + + lapse(_args: any[]): boolean { + this.battleCount--; + return this.battleCount > 0; } getIcon(scene: BattleScene): Phaser.GameObjects.Container { const container = super.getIcon(scene); - const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" }); + // Linear interpolation on hue + const hue = Math.floor(120 * (this.battleCount / this.maxBattles) + 5); + + // Generates the color hex code with a constant saturation and lightness but varying hue + const typeHex = Utils.hslToHex(hue, 0.50, 0.90); + const strokeHex = Utils.hslToHex(hue, 0.70, 0.30); + + const battleCountText = addTextObject(scene, 27, 0, this.battleCount.toString(), TextStyle.PARTY, { fontSize: "66px", color: typeHex }); battleCountText.setShadow(0, 0); - battleCountText.setStroke("#984038", 16); + battleCountText.setStroke(strokeHex, 16); battleCountText.setOrigin(1, 0); container.add(battleCountText); return container; } - getBattlesLeft(): integer { - return this.battlesLeft; + getIconStackText(_scene: BattleScene, _virtual?: boolean): Phaser.GameObjects.BitmapText | null { + return null; } - getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { - return 99; - } -} - -export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier { - constructor(type: ModifierTypes.DoubleBattleChanceBoosterModifierType, battlesLeft: integer, stackCount?: integer) { - super(type, battlesLeft, stackCount); + getBattleCount(): number { + return this.battleCount; } - match(modifier: Modifier): boolean { - if (modifier instanceof DoubleBattleChanceBoosterModifier) { - // Check type id to not match different tiers of lures - return modifier.type.id === this.type.id && modifier.battlesLeft === this.battlesLeft; - } - return false; + resetBattleCount(): void { + this.battleCount = this.maxBattles; } - clone(): DoubleBattleChanceBoosterModifier { - return new DoubleBattleChanceBoosterModifier(this.type as ModifierTypes.DoubleBattleChanceBoosterModifierType, this.battlesLeft, this.stackCount); + getMaxBattles(): number { + return this.maxBattles; } getArgs(): any[] { - return [ this.battlesLeft ]; + return [ this.maxBattles, this.battleCount ]; } + + getMaxStackCount(_scene: BattleScene, _forThreshold?: boolean): number { + // Must be an abitrary number greater than 1 + return 2; + } +} + +/** + * Modifier used for passive items, specifically lures, that + * temporarily increases the chance of a double battle. + * @extends LapsingPersistentModifier + * @see {@linkcode apply} + */ +export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier { + constructor(type: ModifierType, maxBattles:number, battleCount?: number, stackCount?: integer) { + super(type, maxBattles, battleCount, stackCount); + } + + match(modifier: Modifier): boolean { + return (modifier instanceof DoubleBattleChanceBoosterModifier) && (modifier.getMaxBattles() === this.getMaxBattles()); + } + + clone(): DoubleBattleChanceBoosterModifier { + return new DoubleBattleChanceBoosterModifier(this.type as ModifierTypes.DoubleBattleChanceBoosterModifierType, this.getMaxBattles(), this.getBattleCount(), this.stackCount); + } + /** * Modifies the chance of a double battle occurring - * @param args A single element array containing the double battle chance as a NumberHolder - * @returns {boolean} Returns true if the modifier was applied + * @param args [0] {@linkcode Utils.NumberHolder} for double battle chance + * @returns true if the modifier was applied */ apply(args: any[]): boolean { const doubleBattleChance = args[0] as Utils.NumberHolder; // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt // A double battle will initiate if the generated number is 0 - doubleBattleChance.value = Math.ceil(doubleBattleChance.value / 2); + doubleBattleChance.value = Math.ceil(doubleBattleChance.value / 4); return true; } } -export class TempBattleStatBoosterModifier extends LapsingPersistentModifier { - private tempBattleStat: TempBattleStat; +/** + * Modifier used for party-wide items, specifically the X items, that + * temporarily increases the stat stage multiplier of the corresponding + * {@linkcode TempBattleStat}. + * @extends LapsingPersistentModifier + * @see {@linkcode apply} + */ +export class TempStatStageBoosterModifier extends LapsingPersistentModifier { + /** The stat whose stat stage multiplier will be temporarily increased */ + private stat: TempBattleStat; + /** The amount by which the stat stage itself or its multiplier will be increased by */ + private boost: number; - constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: TempBattleStat, battlesLeft?: integer, stackCount?: integer) { - super(type, battlesLeft || 5, stackCount); + constructor(type: ModifierType, stat: TempBattleStat, maxBattles: number, battleCount?: number, stackCount?: number) { + super(type, maxBattles, battleCount, stackCount); - this.tempBattleStat = tempBattleStat; + this.stat = stat; + // Note that, because we want X Accuracy to maintain its original behavior, + // it will increment as it did previously, directly to the stat stage. + this.boost = (stat !== Stat.ACC) ? 0.3 : 1; } match(modifier: Modifier): boolean { - if (modifier instanceof TempBattleStatBoosterModifier) { - return (modifier as TempBattleStatBoosterModifier).tempBattleStat === this.tempBattleStat - && (modifier as TempBattleStatBoosterModifier).battlesLeft === this.battlesLeft; + if (modifier instanceof TempStatStageBoosterModifier) { + const modifierInstance = modifier as TempStatStageBoosterModifier; + return (modifierInstance.stat === this.stat); } return false; } - clone(): TempBattleStatBoosterModifier { - return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.tempBattleStat, this.battlesLeft, this.stackCount); + clone() { + return new TempStatStageBoosterModifier(this.type, this.stat, this.getMaxBattles(), this.getBattleCount(), this.stackCount); } getArgs(): any[] { - return [ this.tempBattleStat, this.battlesLeft ]; + return [ this.stat, ...super.getArgs() ]; } + /** + * Checks if {@linkcode args} contains the necessary elements and if the + * incoming stat is matches {@linkcode stat}. + * @param args [0] {@linkcode TempBattleStat} being checked at the time + * [1] {@linkcode Utils.NumberHolder} N/A + * @returns true if the modifier can be applied, false otherwise + */ + shouldApply(args: any[]): boolean { + return args && (args.length === 2) && TEMP_BATTLE_STATS.includes(args[0]) && (args[0] === this.stat) && (args[1] instanceof Utils.NumberHolder); + } + + /** + * Increases the incoming stat stage matching {@linkcode stat} by {@linkcode boost}. + * @param args [0] {@linkcode TempBattleStat} N/A + * [1] {@linkcode Utils.NumberHolder} that holds the resulting value of the stat stage multiplier + */ apply(args: any[]): boolean { - const tempBattleStat = args[0] as TempBattleStat; + (args[1] as Utils.NumberHolder).value += this.boost; + return true; + } +} - if (tempBattleStat === this.tempBattleStat) { - const statLevel = args[1] as Utils.IntegerHolder; - statLevel.value = Math.min(statLevel.value + 1, 6); - return true; - } +/** + * Modifier used for party-wide items, namely Dire Hit, that + * temporarily increments the critical-hit stage + * @extends LapsingPersistentModifier + * @see {@linkcode apply} + */ +export class TempCritBoosterModifier extends LapsingPersistentModifier { + constructor(type: ModifierType, maxBattles: number, battleCount?: number, stackCount?: number) { + super(type, maxBattles, battleCount, stackCount); + } - return false; + clone() { + return new TempCritBoosterModifier(this.type, this.getMaxBattles(), this.getBattleCount(), this.stackCount); + } + + match(modifier: Modifier): boolean { + return (modifier instanceof TempCritBoosterModifier); + } + + /** + * Checks if {@linkcode args} contains the necessary elements. + * @param args [1] {@linkcode Utils.NumberHolder} N/A + * @returns true if the critical-hit stage boost applies successfully + */ + shouldApply(args: any[]): boolean { + return args && (args.length === 1) && (args[0] instanceof Utils.NumberHolder); + } + + /** + * Increases the current critical-hit stage value by 1. + * @param args [0] {@linkcode Utils.IntegerHolder} that holds the resulting critical-hit level + * @returns true if the critical-hit stage boost applies successfully + */ + apply(args: any[]): boolean { + (args[0] as Utils.NumberHolder).value++; + return true; } } @@ -636,6 +763,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; if (pokemon.isPlayer()) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeTeraTrigger); pokemon.scene.validateAchv(achvs.TERASTALLIZE); if (this.teraType === Type.STELLAR) { pokemon.scene.validateAchv(achvs.STELLAR_TERASTALLIZE); @@ -649,6 +777,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier { const ret = super.lapse(args); if (!ret) { const pokemon = args[0] as Pokemon; + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeLapseTeraTrigger); pokemon.updateSpritePipelineData(); } return ret; @@ -663,24 +792,30 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier { } } -export class PokemonBaseStatModifier extends PokemonHeldItemModifier { - protected stat: Stat; +/** + * Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that + * increase the value of a given {@linkcode PermanentStat}. + * @extends PokemonHeldItemModifier + * @see {@linkcode apply} + */ +export class BaseStatModifier extends PokemonHeldItemModifier { + protected stat: PermanentStat; readonly isTransferrable: boolean = false; - constructor(type: ModifierTypes.PokemonBaseStatBoosterModifierType, pokemonId: integer, stat: Stat, stackCount?: integer) { + constructor(type: ModifierType, pokemonId: integer, stat: PermanentStat, stackCount?: integer) { super(type, pokemonId, stackCount); this.stat = stat; } matchType(modifier: Modifier): boolean { - if (modifier instanceof PokemonBaseStatModifier) { - return (modifier as PokemonBaseStatModifier).stat === this.stat; + if (modifier instanceof BaseStatModifier) { + return (modifier as BaseStatModifier).stat === this.stat; } return false; } clone(): PersistentModifier { - return new PokemonBaseStatModifier(this.type as ModifierTypes.PokemonBaseStatBoosterModifierType, this.pokemonId, this.stat, this.stackCount); + return new BaseStatModifier(this.type, this.pokemonId, this.stat, this.stackCount); } getArgs(): any[] { @@ -688,12 +823,12 @@ export class PokemonBaseStatModifier extends PokemonHeldItemModifier { } shouldApply(args: any[]): boolean { - return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array; + return super.shouldApply(args) && args.length === 2 && Array.isArray(args[1]); } apply(args: any[]): boolean { - args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.getStackCount() * 0.1)), 999999); - + const baseStats = args[1] as number[]; + baseStats[this.stat] = Math.floor(baseStats[this.stat] * (1 + this.getStackCount() * 0.1)); return true; } @@ -791,6 +926,18 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier { return modifier instanceof EvolutionStatBoosterModifier; } + /** + * Checks if the stat boosts can apply and if the holder is not currently + * Gigantamax'd. + * @param args [0] {@linkcode Pokemon} that holds the held item + * [1] {@linkcode Stat} N/A + * [2] {@linkcode Utils.NumberHolder} N/A + * @returns true if the stat boosts can be applied, false otherwise + */ + shouldApply(args: any[]): boolean { + return super.shouldApply(args) && ((args[0] as Pokemon).getFormKey() !== SpeciesFormKey.GIGANTAMAX); + } + /** * Boosts the incoming stat value by a {@linkcode multiplier} if the holder * can evolve. Note that, if the holder is a fusion, they will receive @@ -1398,42 +1545,48 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { } /** - * Modifier used for White Herb, which resets negative {@linkcode Stat} changes + * Modifier used for held items, namely White Herb, that restore adverse stat + * stages in battle. * @extends PokemonHeldItemModifier * @see {@linkcode apply} */ -export class PokemonResetNegativeStatStageModifier extends PokemonHeldItemModifier { +export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier { constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, pokemonId, stackCount); } matchType(modifier: Modifier) { - return modifier instanceof PokemonResetNegativeStatStageModifier; + return modifier instanceof ResetNegativeStatStageModifier; } clone() { - return new PokemonResetNegativeStatStageModifier(this.type, this.pokemonId, this.stackCount); + return new ResetNegativeStatStageModifier(this.type, this.pokemonId, this.stackCount); } /** - * Restores any negative stat stages of the mon to 0 - * @param args args[0] is the {@linkcode Pokemon} whose stat stages are being checked - * @returns true if any stat changes were applied (item was used), false otherwise + * Goes through the holder's stat stages and, if any are negative, resets that + * stat stage back to 0. + * @param args [0] {@linkcode Pokemon} that holds the held item + * @returns true if any stat stages were reset, false otherwise */ apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - const loweredStats = pokemon.summonData.battleStats.filter(s => s < 0); - if (loweredStats.length) { - for (let s = 0; s < pokemon.summonData.battleStats.length; s++) { - pokemon.summonData.battleStats[s] = Math.max(0, pokemon.summonData.battleStats[s]); + let statRestored = false; + + for (const s of BATTLE_STATS) { + if (pokemon.getStatStage(s) < 0) { + pokemon.setStatStage(s, 0); + statRestored = true; } - pokemon.scene.queueMessage(i18next.t("modifier:pokemonResetNegativeStatStageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name })); - return true; } - return false; + + if (statRestored) { + pokemon.scene.queueMessage(i18next.t("modifier:resetNegativeStatStageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name })); + } + return statRestored; } - getMaxHeldItemCount(pokemon: Pokemon): integer { + getMaxHeldItemCount(_pokemon: Pokemon): integer { return 2; } } @@ -1625,7 +1778,7 @@ export class TmModifier extends ConsumablePokemonModifier { apply(args: any[]): boolean { const pokemon = args[0] as PlayerPokemon; - pokemon.scene.unshiftPhase(new LearnMovePhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), (this.type as ModifierTypes.TmModifierType).moveId)); + pokemon.scene.unshiftPhase(new LearnMovePhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), (this.type as ModifierTypes.TmModifierType).moveId, true)); return true; } @@ -2193,7 +2346,7 @@ export class ShinyRateBoosterModifier extends PersistentModifier { } apply(args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value *= Math.pow(2, 2 + this.getStackCount()); + (args[0] as Utils.IntegerHolder).value *= Math.pow(2, 1 + this.getStackCount()); return true; } @@ -2358,7 +2511,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { } getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string { - return i18next.t("modifier:turnHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.name, typeName: this.type.name }); + return i18next.t("modifier:turnHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.getNameToRender(), typeName: this.type.name }); } getMaxHeldItemCount(pokemon: Pokemon): integer { @@ -2745,7 +2898,7 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier { * - The player * - The enemy * @param scene current {@linkcode BattleScene} - * @param isPlayer {@linkcode boolean} for whether the the player (`true`) or enemy (`false`) is being overridden + * @param isPlayer {@linkcode boolean} for whether the player (`true`) or enemy (`false`) is being overridden */ export function overrideModifiers(scene: BattleScene, isPlayer: boolean = true): void { const modifiersOverride: ModifierTypes.ModifierOverride[] = isPlayer ? Overrides.STARTING_MODIFIER_OVERRIDE : Overrides.OPP_MODIFIER_OVERRIDE; @@ -2760,13 +2913,22 @@ export function overrideModifiers(scene: BattleScene, isPlayer: boolean = true): modifiersOverride.forEach(item => { const modifierFunc = modifierTypes[item.name]; - const modifier = modifierFunc().withIdFromFunc(modifierFunc).newModifier() as PersistentModifier; - modifier.stackCount = item.count || 1; + let modifierType: ModifierType | null = modifierFunc(); - if (isPlayer) { - scene.addModifier(modifier, true, false, false, true); - } else { - scene.addEnemyModifier(modifier, true, true); + if (modifierType instanceof ModifierTypes.ModifierTypeGenerator) { + const pregenArgs = ("type" in item) && (item.type !== null) ? [item.type] : undefined; + modifierType = modifierType.generateType([], pregenArgs); + } + + const modifier = modifierType && modifierType.withIdFromFunc(modifierFunc).newModifier() as PersistentModifier; + if (modifier) { + modifier.stackCount = item.count || 1; + + if (isPlayer) { + scene.addModifier(modifier, true, false, false, true); + } else { + scene.addEnemyModifier(modifier, true, true); + } } }); } diff --git a/src/overrides.ts b/src/overrides.ts index a7dcd210a22..417c86271ab 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -101,7 +101,7 @@ class DefaultOverrides { readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; readonly GENDER_OVERRIDE: Gender | null = null; - readonly MOVESET_OVERRIDE: Array = []; + readonly MOVESET_OVERRIDE: Moves | Array = []; readonly SHINY_OVERRIDE: boolean = false; readonly VARIANT_OVERRIDE: Variant = 0; @@ -114,11 +114,19 @@ class DefaultOverrides { readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; readonly OPP_GENDER_OVERRIDE: Gender | null = null; - readonly OPP_MOVESET_OVERRIDE: Array = []; + readonly OPP_MOVESET_OVERRIDE: Moves | Array = []; readonly OPP_SHINY_OVERRIDE: boolean = false; readonly OPP_VARIANT_OVERRIDE: Variant = 0; readonly OPP_IVS_OVERRIDE: number | number[] = []; readonly OPP_FORM_OVERRIDES: Partial> = {}; + /** + * Override to give the enemy Pokemon a given amount of health segments + * + * 0 (default): the health segments will be handled normally based on wave, level and species + * 1: the Pokemon will have a single health segment and therefore will not be a boss + * 2+: the Pokemon will be a boss with the given number of health segments + */ + readonly OPP_HEALTH_SEGMENTS_OVERRIDE: number = 0; // ------------- // EGG OVERRIDES diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-buff-modifier-phase.ts index a9936eb765d..451e6e2662c 100644 --- a/src/phases/add-enemy-buff-modifier-phase.ts +++ b/src/phases/add-enemy-buff-modifier-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { ModifierTier } from "#app/modifier/modifier-tier.js"; -import { regenerateModifierPoolThresholds, ModifierPoolType, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type.js"; -import { EnemyPersistentModifier } from "#app/modifier/modifier.js"; -import { Phase } from "#app/phase.js"; +import BattleScene from "#app/battle-scene"; +import { ModifierTier } from "#app/modifier/modifier-tier"; +import { regenerateModifierPoolThresholds, ModifierPoolType, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type"; +import { EnemyPersistentModifier } from "#app/modifier/modifier"; +import { Phase } from "#app/phase"; export class AddEnemyBuffModifierPhase extends Phase { constructor(scene: BattleScene) { diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index 72995c0f006..cf9ce997bfd 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -1,17 +1,17 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, doPokeballBounceAnim } from "#app/data/pokeball.js"; -import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect.js"; -import { PokeballType } from "#app/enums/pokeball.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { addPokeballOpenParticles, addPokeballCaptureStars } from "#app/field/anims.js"; -import { EnemyPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { PokemonHeldItemModifier } from "#app/modifier/modifier.js"; -import { achvs } from "#app/system/achv.js"; -import { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; -import { SummaryUiMode } from "#app/ui/summary-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, doPokeballBounceAnim } from "#app/data/pokeball"; +import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; +import { PokeballType } from "#app/enums/pokeball"; +import { StatusEffect } from "#app/enums/status-effect"; +import { addPokeballOpenParticles, addPokeballCaptureStars } from "#app/field/anims"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PokemonHeldItemModifier } from "#app/modifier/modifier"; +import { achvs } from "#app/system/achv"; +import { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; +import { SummaryUiMode } from "#app/ui/summary-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; import { VictoryPhase } from "./victory-phase"; @@ -221,8 +221,8 @@ export class AttemptCapturePhase extends PokemonPhase { this.scene.clearEnemyHeldItemModifiers(); this.scene.field.remove(pokemon, true); }; - const addToParty = () => { - const newPokemon = pokemon.addToParty(this.pokeballType); + const addToParty = (slotIndex?: number) => { + const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex); const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { this.scene.validateAchv(achvs.SHINY_PARTY); @@ -253,7 +253,7 @@ export class AttemptCapturePhase extends PokemonPhase { this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (slotIndex < 6) { - addToParty(); + addToParty(slotIndex); } else { promptRelease(); } diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 817801985d2..46d68f6005a 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -1,31 +1,34 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability.js"; -import { Stat } from "#app/enums/stat.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import Pokemon from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability"; +import { Stat } from "#app/enums/stat"; +import { StatusEffect } from "#app/enums/status-effect"; +import Pokemon, { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { BattleEndPhase } from "./battle-end-phase"; import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; export class AttemptRunPhase extends PokemonPhase { - constructor(scene: BattleScene, fieldIndex: integer) { + constructor(scene: BattleScene, fieldIndex: number) { super(scene, fieldIndex); } start() { super.start(); - const playerPokemon = this.getPokemon(); + const playerField = this.scene.getPlayerField(); const enemyField = this.scene.getEnemyField(); - const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length; + const playerPokemon = this.getPokemon(); + + const escapeChance = new Utils.NumberHolder(0); + + this.attemptRunAway(playerField, enemyField, escapeChance); - const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256); applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); - if (playerPokemon.randSeedInt(256) < escapeChance.value) { + if (playerPokemon.randSeedInt(100) < escapeChance.value) { this.scene.playSound("se/flee"); this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); @@ -53,4 +56,48 @@ export class AttemptRunPhase extends PokemonPhase { this.end(); } + + attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: Utils.NumberHolder) { + /** Sum of the speed of all enemy pokemon on the field */ + const enemySpeed = enemyField.reduce((total: number, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0); + /** Sum of the speed of all player pokemon on the field */ + const playerSpeed = playerField.reduce((total: number, playerPokemon: Pokemon) => total + playerPokemon.getStat(Stat.SPD), 0); + + /* The way the escape chance works is by looking at the difference between your speed and the enemy field's average speed as a ratio. The higher this ratio, the higher your chance of success. + * However, there is a cap for the ratio of your speed vs enemy speed which beyond that point, you won't gain any advantage. It also looks at how many times you've tried to escape. + * Again, the more times you've tried to escape, the higher your odds of escaping. Bosses and non-bosses are calculated differently - bosses are harder to escape from vs non-bosses + * Finally, there's a minimum and maximum escape chance as well so that escapes aren't guaranteed, yet they are never 0 either. + * The percentage chance to escape from a pokemon for both bosses and non bosses is linear and based on the minimum and maximum chances, and the speed ratio cap. + * + * At the time of writing, these conditions should be met: + * - The minimum escape chance should be 5% for bosses and non bosses + * - Bosses should have a maximum escape chance of 25%, whereas non-bosses should be 95% + * - The bonus per previous escape attempt should be 2% for bosses and 10% for non-bosses + * - The speed ratio cap should be 6x for bosses and 4x for non-bosses + * - The "default" escape chance when your speed equals the enemy speed should be 8.33% for bosses and 27.5% for non-bosses + * + * From the above, we can calculate the below values + */ + + let isBoss = false; + for (let e = 0; e < enemyField.length; e++) { + isBoss = isBoss || enemyField[e].isBoss(); // this line checks if any of the enemy pokemon on the field are bosses; if so, the calculation for escaping is different + } + + /** The ratio between the speed of your active pokemon and the speed of the enemy field */ + const speedRatio = playerSpeed / enemySpeed; + /** The max ratio before escape chance stops increasing. Increased if there is a boss on the field */ + const speedCap = isBoss ? 6 : 4; + /** Minimum percent chance to escape */ + const minChance = 5; + /** Maximum percent chance to escape. Decreased if a boss is on the field */ + const maxChance = isBoss ? 25 : 95; + /** How much each escape attempt increases the chance of the next attempt. Decreased if a boss is on the field */ + const escapeBonus = isBoss ? 2 : 10; + /** Slope of the escape chance curve */ + const escapeSlope = (maxChance - minChance) / speedCap; + + // This will calculate the escape chance given all of the above and clamp it to the range of [`minChance`, `maxChance`] + escapeChance.value = Phaser.Math.Clamp(Math.round((escapeSlope * speedRatio) + minChance + (escapeBonus * this.scene.currentBattle.escapeAttempts++)), minChance, maxChance); + } } diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index a9999370cdd..f08e04b443a 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -1,5 +1,5 @@ -import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability.js"; -import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier.js"; +import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability"; +import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier"; import { BattlePhase } from "./battle-phase"; import { GameOverPhase } from "./game-over-phase"; @@ -23,12 +23,6 @@ export class BattleEndPhase extends BattlePhase { this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); } - for (const pokemon of this.scene.getField()) { - if (pokemon) { - pokemon.resetBattleSummonData(); - } - } - for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) { applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); } diff --git a/src/phases/battle-phase.ts b/src/phases/battle-phase.ts index 3e7e0e28596..b51e19bdf0e 100644 --- a/src/phases/battle-phase.ts +++ b/src/phases/battle-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { TrainerSlot } from "#app/data/trainer-config.js"; -import { Phase } from "#app/phase.js"; +import BattleScene from "#app/battle-scene"; +import { TrainerSlot } from "#app/data/trainer-config"; +import { Phase } from "#app/phase"; export class BattlePhase extends Phase { constructor(scene: BattleScene) { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 504fb6ec163..66ecaa6c7f8 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -1,10 +1,10 @@ -import { applyAbAttrs, PreventBerryUseAbAttr, HealFromBerryUseAbAttr } from "#app/data/ability.js"; -import { CommonAnim } from "#app/data/battle-anims.js"; -import { BerryUsedEvent } from "#app/events/battle-scene.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { BerryModifier } from "#app/modifier/modifier.js"; +import { applyAbAttrs, PreventBerryUseAbAttr, HealFromBerryUseAbAttr } from "#app/data/ability"; +import { CommonAnim } from "#app/data/battle-anims"; +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.js"; +import * as Utils from "#app/utils"; import { FieldPhase } from "./field-phase"; import { CommonAnimPhase } from "./common-anim-phase"; diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index cd8f2b00c46..a069ba224a2 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattleStyle } from "#app/enums/battle-style.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { BattleStyle } from "#app/enums/battle-style"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; import { PostSummonPhase } from "./post-summon-phase"; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 68ede826d95..47d212aa598 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -1,21 +1,18 @@ -import BattleScene from "#app/battle-scene.js"; -import { TurnCommand, BattleType } from "#app/battle.js"; -import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; -import { TrappedTag, EncoreTag } from "#app/data/battler-tags.js"; -import { MoveTargetSet, getMoveTargets } from "#app/data/move.js"; -import { speciesStarters } from "#app/data/pokemon-species.js"; -import { Type } from "#app/data/type.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Biome } from "#app/enums/biome.js"; -import { Moves } from "#app/enums/moves.js"; -import { PokeballType } from "#app/enums/pokeball.js"; -import { FieldPosition, PlayerPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { TurnCommand, BattleType } from "#app/battle"; +import { TrappedTag, EncoreTag } from "#app/data/battler-tags"; +import { MoveTargetSet, getMoveTargets } from "#app/data/move"; +import { speciesStarters } from "#app/data/pokemon-species"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Biome } from "#app/enums/biome"; +import { Moves } from "#app/enums/moves"; +import { PokeballType } from "#app/enums/pokeball"; +import { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { Command } from "#app/ui/command-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; import { FieldPhase } from "./field-phase"; import { SelectTargetPhase } from "./select-target-phase"; @@ -77,7 +74,6 @@ export class CommandPhase extends FieldPhase { handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; - const enemyField = this.scene.getEnemyField(); let success: boolean; switch (command) { @@ -111,8 +107,9 @@ export class CommandPhase extends FieldPhase { // Decides between a Disabled, Not Implemented, or No PP translation message const errorMessage = - playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" : - move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; + playerPokemon.isMoveRestricted(move.moveId) + ? playerPokemon.getRestrictingTag(move.moveId)!.selectionDeniedText(playerPokemon, move.moveId) + : move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { @@ -184,14 +181,9 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { - const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); const batonPass = isSwitch && args[0] as boolean; const trappedAbMessages: string[] = []; - if (!batonPass) { - enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true)); - } - if (batonPass || (!trapTag && !trapped.value)) { + if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; @@ -199,14 +191,27 @@ export class CommandPhase extends FieldPhase { if (!isSwitch && this.fieldIndex) { this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } - } else if (trapTag) { - if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { - success = true; + } else if (trappedAbMessages.length > 0) { + if (!isSwitch) { + this.scene.ui.setMode(Mode.MESSAGE); + } + this.scene.ui.showText(trappedAbMessages[0], null, () => { + this.scene.ui.showText("", 0); + if (!isSwitch) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + }, null, true); + } else { + const trapTag = playerPokemon.getTag(TrappedTag); + + // trapTag should be defined at this point, but just in case... + if (!trapTag) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; break; } + if (!isSwitch) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); @@ -224,16 +229,6 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); } }, null, true); - } else if (trapped.value && trappedAbMessages.length > 0) { - if (!isSwitch) { - this.scene.ui.setMode(Mode.MESSAGE); - } - this.scene.ui.showText(trappedAbMessages[0], null, () => { - this.scene.ui.showText("", 0); - if (!isSwitch) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - }, null, true); } } break; diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts index d3663abe3b6..a85cd7629d9 100644 --- a/src/phases/common-anim-phase.ts +++ b/src/phases/common-anim-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import { PokemonPhase } from "./pokemon-phase"; export class CommonAnimPhase extends PokemonPhase { diff --git a/src/phases/damage-phase.ts b/src/phases/damage-phase.ts index 029c1e717f1..5add0345358 100644 --- a/src/phases/damage-phase.ts +++ b/src/phases/damage-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { BattleSpec } from "#app/enums/battle-spec.js"; -import { DamageResult, HitResult } from "#app/field/pokemon.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { BattleSpec } from "#app/enums/battle-spec"; +import { DamageResult, HitResult } from "#app/field/pokemon"; +import * as Utils from "#app/utils"; import { PokemonPhase } from "./pokemon-phase"; export class DamagePhase extends PokemonPhase { diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index a5b0252d4de..90aceeb46bc 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -1,23 +1,29 @@ -import BattleScene, { AnySound } from "#app/battle-scene.js"; -import { Egg, EGG_SEED } from "#app/data/egg.js"; -import { EggCountChangedEvent } from "#app/events/egg.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { Phase } from "#app/phase.js"; -import { achvs } from "#app/system/achv.js"; -import EggCounterContainer from "#app/ui/egg-counter-container.js"; -import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler.js"; -import PokemonInfoContainer from "#app/ui/pokemon-info-container.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene, { AnySound } from "#app/battle-scene"; +import { Egg } from "#app/data/egg"; +import { EggCountChangedEvent } from "#app/events/egg"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { Phase } from "#app/phase"; +import { achvs } from "#app/system/achv"; +import EggCounterContainer from "#app/ui/egg-counter-container"; +import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; +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.js"; +import * as Utils from "#app/utils"; +import { EggLapsePhase } from "./egg-lapse-phase"; +import { EggHatchData } from "#app/data/egg-hatch-data"; + + /** * Class that represents egg hatching */ export class EggHatchPhase extends Phase { /** The egg that is hatching */ private egg: Egg; + /** The new EggHatchData for the egg/pokemon that hatches */ + private eggHatchData: EggHatchData; /** The number of eggs that are hatching */ private eggsToHatchCount: integer; @@ -58,10 +64,11 @@ export class EggHatchPhase extends Phase { private skipped: boolean; /** The sound effect being played when the egg is hatched */ private evolutionBgm: AnySound; + private eggLapsePhase: EggLapsePhase; - constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) { + constructor(scene: BattleScene, hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) { super(scene); - + this.eggLapsePhase = hatchScene; this.egg = egg; this.eggsToHatchCount = eggsToHatchCount; } @@ -307,6 +314,7 @@ export class EggHatchPhase extends Phase { * Function to do the logic and animation of completing a hatch and revealing the Pokemon */ doReveal(): void { + // set the previous dex data so info container can show new unlocks in egg summary const isShiny = this.pokemon.isShiny(); if (this.pokemon.species.subLegendary) { this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY); @@ -345,13 +353,13 @@ export class EggHatchPhase extends Phase { this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => { this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { - this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => { + this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => { + this.eggHatchData.setEggMoveUnlocked(value); this.scene.ui.showText("", 0); this.end(); }); }); }, null, true, 3000); - //this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm()); }); }); this.scene.tweens.add({ @@ -435,17 +443,12 @@ export class EggHatchPhase extends Phase { /** * Generates a Pokemon to be hatched by the egg + * Also stores the generated pokemon in this.eggHatchData * @returns the hatched PlayerPokemon */ generatePokemon(): PlayerPokemon { - let ret: PlayerPokemon; - - this.scene.executeWithSeedOffset(() => { - ret = this.egg.generatePlayerPokemon(this.scene); - this.eggMoveIndex = this.egg.eggMoveIndex; - - }, this.egg.id, EGG_SEED.toString()); - - return ret!; + this.eggHatchData = this.eggLapsePhase.generatePokemon(this.egg); + this.eggMoveIndex = this.eggHatchData.eggMoveIndex; + return this.eggHatchData.pokemon; } } diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 50d7106f229..65426846bb3 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -1,35 +1,140 @@ -import BattleScene from "#app/battle-scene.js"; -import { Egg } from "#app/data/egg.js"; -import { Phase } from "#app/phase.js"; +import BattleScene from "#app/battle-scene"; +import { Egg, EGG_SEED } from "#app/data/egg"; +import { Phase } from "#app/phase"; import i18next from "i18next"; import Overrides from "#app/overrides"; import { EggHatchPhase } from "./egg-hatch-phase"; +import { Mode } from "#app/ui/ui"; +import { achvs } from "#app/system/achv"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { EggSummaryPhase } from "./egg-summary-phase"; +import { EggHatchData } from "#app/data/egg-hatch-data"; +/** + * Phase that handles updating eggs, and hatching any ready eggs + * Also handles prompts for skipping animation, and calling the egg summary phase + */ export class EggLapsePhase extends Phase { + + private eggHatchData: EggHatchData[] = []; + private readonly minEggsToSkip: number = 2; constructor(scene: BattleScene) { super(scene); } start() { super.start(); - const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => { return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1; }); + const eggsToHatchCount: number = eggsToHatch.length; + this.eggHatchData= []; - let eggCount: integer = eggsToHatch.length; - - if (eggCount) { - this.scene.queueMessage(i18next.t("battle:eggHatching")); - - for (const egg of eggsToHatch) { - this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount)); - if (eggCount > 0) { - eggCount--; - } + if (eggsToHatchCount > 0) { + if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) { + this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => { + // show prompt for skip + this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0); + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { + this.hatchEggsSkipped(eggsToHatch); + this.showSummary(); + }, () => { + this.hatchEggsRegular(eggsToHatch); + this.showSummary(); + } + ); + }, 100, true); + } else if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 2) { + this.scene.queueMessage(i18next.t("battle:eggHatching")); + this.hatchEggsSkipped(eggsToHatch); + this.showSummary(); + } else { + // regular hatches, no summary + this.scene.queueMessage(i18next.t("battle:eggHatching")); + this.hatchEggsRegular(eggsToHatch); + this.end(); } - + } else { + this.end(); } + } + + /** + * Hatches eggs normally one by one, showing animations + * @param eggsToHatch list of eggs to hatch + */ + hatchEggsRegular(eggsToHatch: Egg[]) { + let eggsToHatchCount: number = eggsToHatch.length; + for (const egg of eggsToHatch) { + this.scene.unshiftPhase(new EggHatchPhase(this.scene, this, egg, eggsToHatchCount)); + eggsToHatchCount--; + } + } + + /** + * Hatches eggs with no animations + * @param eggsToHatch list of eggs to hatch + */ + hatchEggsSkipped(eggsToHatch: Egg[]) { + for (const egg of eggsToHatch) { + this.hatchEggSilently(egg); + } + } + + showSummary() { + this.scene.unshiftPhase(new EggSummaryPhase(this.scene, this.eggHatchData)); this.end(); } + + /** + * Hatches an egg and stores it in the local EggHatchData array without animations + * Also validates the achievements for the hatched pokemon and removes the egg + * @param egg egg to hatch + */ + hatchEggSilently(egg: Egg) { + const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === egg.id); + if (eggIndex === -1) { + return this.end(); + } + this.scene.gameData.eggs.splice(eggIndex, 1); + + const data = this.generatePokemon(egg); + const pokemon = data.pokemon; + if (pokemon.fusionSpecies) { + pokemon.clearFusionSpecies(); + } + + if (pokemon.species.subLegendary) { + this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY); + } + if (pokemon.species.legendary) { + this.scene.validateAchv(achvs.HATCH_LEGENDARY); + } + if (pokemon.species.mythical) { + this.scene.validateAchv(achvs.HATCH_MYTHICAL); + } + if (pokemon.isShiny()) { + this.scene.validateAchv(achvs.HATCH_SHINY); + } + + } + + /** + * Generates a Pokemon and creates a new EggHatchData instance for the given egg + * @param egg the egg to hatch + * @returns the hatched PlayerPokemon + */ + generatePokemon(egg: Egg): EggHatchData { + let ret: PlayerPokemon; + let newHatchData: EggHatchData; + this.scene.executeWithSeedOffset(() => { + ret = egg.generatePlayerPokemon(this.scene); + newHatchData = new EggHatchData(this.scene, ret, egg.eggMoveIndex); + newHatchData.setDex(); + this.eggHatchData.push(newHatchData); + + }, egg.id, EGG_SEED.toString()); + return newHatchData!; + } + } diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts new file mode 100644 index 00000000000..75c6939daf1 --- /dev/null +++ b/src/phases/egg-summary-phase.ts @@ -0,0 +1,51 @@ +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; +import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; +import { EggHatchData } from "#app/data/egg-hatch-data"; + +/** + * Class that represents the egg summary phase + * It does some of the function for updating egg data + * Phase is handled mostly by the egg-hatch-scene-handler UI + */ +export class EggSummaryPhase extends Phase { + private eggHatchData: EggHatchData[]; + private eggHatchHandler: EggHatchSceneHandler; + + constructor(scene: BattleScene, eggHatchData: EggHatchData[]) { + super(scene); + this.eggHatchData = eggHatchData; + } + + start() { + super.start(); + + // updates next pokemon once the current update has been completed + const updateNextPokemon = (i: number) => { + if (i >= this.eggHatchData.length) { + this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => { + this.scene.fadeOutBgm(undefined, false); + this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; + }); + + } else { + this.eggHatchData[i].setDex(); + this.eggHatchData[i].updatePokemon().then(() => { + if (i < this.eggHatchData.length) { + updateNextPokemon(i + 1); + } + }); + } + }; + updateNextPokemon(0); + + } + + end() { + this.scene.time.delayedCall(250, () => this.scene.setModifiersVisible(true)); + this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => { + super.end(); + }); + } +} diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 07bfd72a8bf..3f37095569a 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -26,6 +26,7 @@ import { ScanIvsPhase } from "./scan-ivs-phase"; import { ShinySparklePhase } from "./shiny-sparkle-phase"; import { SummonPhase } from "./summon-phase"; import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; +import Overrides from "#app/overrides"; export class EncounterPhase extends BattlePhase { private loaded: boolean; @@ -112,10 +113,11 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? } else { - // This block only applies for double battles to init the boss segments (idk why it's split up like this) - if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { + const overridedBossSegments = Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE > 1; + // for double battles, reduce the health segments for boss Pokemon unless there is an override + if (!overridedBossSegments && battle.enemyParty.filter(p => p.isBoss()).length > 1) { for (const enemyPokemon of battle.enemyParty) { - // If the enemy pokemon is a boss and wasn't populated from data source, then set it up + // If the enemy pokemon is a boss and wasn't populated from data source, then update the number of segments if (enemyPokemon.isBoss() && !enemyPokemon.isPopulatedFromDataSource) { enemyPokemon.setBoss(true, Math.ceil(enemyPokemon.bossSegments * (enemyPokemon.getSpeciesForm().baseTotal / totalBst))); enemyPokemon.initBattleInfo(); @@ -159,9 +161,11 @@ export class EncounterPhase extends BattlePhase { return this.scene.reset(true); } this.doEncounter(); + this.scene.resetSeed(); }); } else { this.doEncounter(); + this.scene.resetSeed(); } }); }); diff --git a/src/phases/end-card-phase.ts b/src/phases/end-card-phase.ts index 0b70664b993..274a745017a 100644 --- a/src/phases/end-card-phase.ts +++ b/src/phases/end-card-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { PlayerGender } from "#app/enums/player-gender.js"; -import { Phase } from "#app/phase.js"; -import { addTextObject, TextStyle } from "#app/ui/text.js"; +import BattleScene from "#app/battle-scene"; +import { PlayerGender } from "#app/enums/player-gender"; +import { Phase } from "#app/phase"; +import { addTextObject, TextStyle } from "#app/ui/text"; import i18next from "i18next"; export class EndCardPhase extends Phase { diff --git a/src/phases/end-evolution-phase.ts b/src/phases/end-evolution-phase.ts index 2a6d492a425..8a6ce52553d 100644 --- a/src/phases/end-evolution-phase.ts +++ b/src/phases/end-evolution-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; export class EndEvolutionPhase extends Phase { diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 5277b2666c7..91ee0456cd4 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -1,9 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; -import { TrappedTag } from "#app/data/battler-tags.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { Command } from "#app/ui/command-ui-handler"; import { FieldPhase } from "./field-phase"; /** @@ -45,10 +42,7 @@ export class EnemyCommandPhase extends FieldPhase { if (trainer && !enemyPokemon.getMoveQueue().length) { const opponents = enemyPokemon.getOpponents(); - const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); - opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, [""], true)); - if (!trapTag && !trapped.value) { + if (!enemyPokemon.isTrapped()) { const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); if (partyMemberScores.length) { @@ -83,4 +77,8 @@ export class EnemyCommandPhase extends FieldPhase { this.end(); } + + getFieldIndex(): number { + return this.fieldIndex; + } } diff --git a/src/phases/enemy-party-member-pokemon-phase.ts b/src/phases/enemy-party-member-pokemon-phase.ts index 10af0913f93..bb34f53b475 100644 --- a/src/phases/enemy-party-member-pokemon-phase.ts +++ b/src/phases/enemy-party-member-pokemon-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { EnemyPokemon } from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { EnemyPokemon } from "#app/field/pokemon"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPhase { diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 9c2ba95d550..81982980d2a 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { ExpBoosterModifier } from "#app/modifier/modifier.js"; +import BattleScene from "#app/battle-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { ExpBoosterModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; import { LevelUpPhase } from "./level-up-phase"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 66946d268cb..169d667113a 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -1,14 +1,14 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex, BattleType } from "#app/battle.js"; -import { applyPostFaintAbAttrs, PostFaintAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr } from "#app/data/ability.js"; -import { BattlerTagLapseType } from "#app/data/battler-tags.js"; -import { battleSpecDialogue } from "#app/data/dialogue.js"; -import { allMoves, PostVictoryStatChangeAttr } from "#app/data/move.js"; -import { BattleSpec } from "#app/enums/battle-spec.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { PokemonMove, EnemyPokemon, PlayerPokemon, HitResult } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { PokemonInstantReviveModifier } from "#app/modifier/modifier.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex, BattleType } from "#app/battle"; +import { applyPostFaintAbAttrs, PostFaintAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr } from "#app/data/ability"; +import { BattlerTagLapseType } from "#app/data/battler-tags"; +import { battleSpecDialogue } from "#app/data/dialogue"; +import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/move"; +import { BattleSpec } from "#app/enums/battle-spec"; +import { StatusEffect } from "#app/enums/status-effect"; +import { PokemonMove, EnemyPokemon, PlayerPokemon, HitResult } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import i18next from "i18next"; import { DamagePhase } from "./damage-phase"; import { PokemonPhase } from "./pokemon-phase"; @@ -17,6 +17,7 @@ import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; import { GameOverPhase } from "./game-over-phase"; import { SwitchPhase } from "./switch-phase"; import { VictoryPhase } from "./victory-phase"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; export class FaintPhase extends PokemonPhase { private preventEndure: boolean; @@ -54,15 +55,20 @@ export class FaintPhase extends PokemonPhase { // Track total times pokemon have been KO'd for supreme overlord/last respects if (pokemon.isPlayer()) { this.scene.currentBattle.playerFaints += 1; + this.scene.currentBattle.playerFaintsHistory.push({ pokemon: pokemon, turn: this.scene.currentBattle.turn }); } else { this.scene.currentBattle.enemyFaints += 1; + this.scene.currentBattle.enemyFaintsHistory.push({ pokemon: pokemon, turn: this.scene.currentBattle.turn }); } this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true); + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); if (pokemon.turnData?.attacksReceived?.length) { const lastAttack = pokemon.turnData.attacksReceived[0]; applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? + } else { //If killed by indirect damage, apply post-faint abilities without providing a last move + applyPostFaintAbAttrs(PostFaintAbAttr, pokemon); } const alivePlayField = this.scene.getField(true); @@ -72,7 +78,7 @@ export class FaintPhase extends PokemonPhase { if (defeatSource?.isOnField()) { applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource); const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; - const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr); + const pvattrs = pvmove.getAttrs(PostVictoryStatStageChangeAttr); if (pvattrs.length) { for (const pvattr of pvattrs) { pvattr.applyPostVictory(defeatSource, defeatSource, pvmove); diff --git a/src/phases/field-phase.ts b/src/phases/field-phase.ts index a9622271f14..46c3c4983b4 100644 --- a/src/phases/field-phase.ts +++ b/src/phases/field-phase.ts @@ -1,42 +1,9 @@ -import { BattlerIndex } from "#app/battle.js"; -import { TrickRoomTag } from "#app/data/arena-tag.js"; -import { Stat } from "#app/enums/stat.js"; -import Pokemon from "#app/field/pokemon.js"; +import Pokemon from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; -import * as Utils from "#app/utils.js"; type PokemonFunc = (pokemon: Pokemon) => void; export abstract class FieldPhase extends BattlePhase { - getOrder(): BattlerIndex[] { - const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; - const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - - // We shuffle the list before sorting so speed ties produce random results - let orderedTargets: Pokemon[] = playerField.concat(enemyField); - // We seed it with the current turn to prevent an inconsistency where it - // was varying based on how long since you last reloaded - this.scene.executeWithSeedOffset(() => { - orderedTargets = Utils.randSeedShuffle(orderedTargets); - }, this.scene.currentBattle.turn, this.scene.waveSeed); - - orderedTargets.sort((a: Pokemon, b: Pokemon) => { - const aSpeed = a?.getBattleStat(Stat.SPD) || 0; - const bSpeed = b?.getBattleStat(Stat.SPD) || 0; - - return bSpeed - aSpeed; - }); - - const speedReversed = new Utils.BooleanHolder(false); - this.scene.arena.applyTags(TrickRoomTag, speedReversed); - - if (speedReversed.value) { - orderedTargets = orderedTargets.reverse(); - } - - return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - } - executeForAll(func: PokemonFunc): void { const field = this.scene.getField(true).filter(p => p.summonData); field.forEach(pokemon => func(pokemon)); diff --git a/src/phases/game-over-modifier-reward-phase.ts b/src/phases/game-over-modifier-reward-phase.ts index c27659bf9d4..a698e62e2e1 100644 --- a/src/phases/game-over-modifier-reward-phase.ts +++ b/src/phases/game-over-modifier-reward-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { ModifierTypeFunc } from "#app/modifier/modifier-type.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { ModifierTypeFunc } from "#app/modifier/modifier-type"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index ebe58b20d3e..17805e90f0f 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -49,7 +49,9 @@ export class GameOverPhase extends BattlePhase { } if (this.victory && this.scene.gameMode.isEndless) { - this.scene.ui.showDialogue(i18next.t("PGMmiscDialogue:ending_endless"), i18next.t("PGMmiscDialogue:ending_name"), 0, () => this.handleGameOver()); + const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET; + const genderStr = PlayerGender[genderIndex].toLowerCase(); + this.scene.ui.showDialogue(i18next.t("miscDialogue:ending_endless", { context: genderStr }), i18next.t("miscDialogue:ending_name"), 0, () => this.handleGameOver()); } else if (this.victory || !this.scene.enableRetries) { this.handleGameOver(); } else { diff --git a/src/phases/hide-party-exp-bar-phase.ts b/src/phases/hide-party-exp-bar-phase.ts index c2c9d96462e..303650ea1ad 100644 --- a/src/phases/hide-party-exp-bar-phase.ts +++ b/src/phases/hide-party-exp-bar-phase.ts @@ -1,4 +1,4 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { BattlePhase } from "./battle-phase"; export class HidePartyExpBarPhase extends BattlePhase { diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index 5a9a16b6f5e..5b4b16f3785 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -1,22 +1,25 @@ -import BattleScene from "#app/battle-scene.js"; -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims.js"; -import { allMoves } from "#app/data/move.js"; -import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms.js"; -import { Moves } from "#app/enums/moves.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import EvolutionSceneHandler from "#app/ui/evolution-scene-handler.js"; -import { SummaryUiMode } from "#app/ui/summary-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +import Move, { allMoves } from "#app/data/move"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms"; +import { Moves } from "#app/enums/moves"; +import { getPokemonNameWithAffix } from "#app/messages"; +import EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; +import { SummaryUiMode } from "#app/ui/summary-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; +import Pokemon from "#app/field/pokemon"; export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { private moveId: Moves; + private messageMode: Mode; + private fromTM: boolean; - constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) { + constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves, fromTM?: boolean) { super(scene, partyMemberIndex); - this.moveId = moveId; + this.fromTM = fromTM ?? false; } start() { @@ -24,81 +27,131 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { const pokemon = this.getPokemon(); const move = allMoves[this.moveId]; + const currentMoveset = pokemon.getMoveset(); - const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id); - - if (existingMoveIndex > -1) { + // The game first checks if the Pokemon already has the move and ends the phase if it does. + const hasMoveAlready = currentMoveset.some(m => m?.moveId === move.id) && this.moveId !== Moves.SKETCH; + if (hasMoveAlready) { return this.end(); } - const emptyMoveIndex = pokemon.getMoveset().length < 4 - ? pokemon.getMoveset().length - : pokemon.getMoveset().findIndex(m => m === null); - - const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler - ? Mode.EVOLUTION_SCENE - : Mode.MESSAGE; - - if (emptyMoveIndex > -1) { - pokemon.setMove(emptyMoveIndex, this.moveId); - initMoveAnim(this.scene, this.moveId).then(() => { - loadMoveAnimAssets(this.scene, [this.moveId], true) - .then(() => { - this.scene.ui.setMode(messageMode).then(() => { - // Sound loaded into game as is - this.scene.playSound("level_up_fanfare"); - this.scene.ui.showText(i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { - this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); - this.end(); - }, messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true); - }); - }); - }); + this.messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler ? Mode.EVOLUTION_SCENE : Mode.MESSAGE; + this.scene.ui.setMode(this.messageMode); + // If the Pokemon has less than 4 moves, the new move is added to the largest empty moveset index + // If it has 4 moves, the phase then checks if the player wants to replace the move itself. + if (currentMoveset.length < 4) { + this.learnMove(currentMoveset.length, move, pokemon); } else { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => { - const noHandler = () => { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => { - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); - }, () => { - this.scene.ui.setMode(messageMode); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }); - }); - }); - }; - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => { - this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { - if (moveIndex === 4) { - noHandler(); - return; - } - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? - this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { - pokemon.setMove(moveIndex, Moves.NONE); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }, null, true); - }, null, true); - }, null, true); - }); - }); - }, null, true); - }, noHandler); - }); - }, null, true); - }, null, true); - }); + this.replaceMoveCheck(move, pokemon); } } + + /** + * This displays a chain of messages (listed below) and asks if the user wishes to forget a move. + * + * > [Pokemon] wants to learn the move [MoveName] + * > However, [Pokemon] already knows four moves. + * > Should a move be forgotten and replaced with [MoveName]? --> `Mode.CONFIRM` -> Yes: Go to `this.forgetMoveProcess()`, No: Go to `this.rejectMoveAndEnd()` + * @param move The Move to be learned + * @param Pokemon The Pokemon learning the move + */ + async replaceMoveCheck(move: Move, pokemon: Pokemon) { + const learnMovePrompt = i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }); + const moveLimitReached = i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }); + const shouldReplaceQ = i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }); + const preQText = [learnMovePrompt, moveLimitReached].join("$"); + await this.scene.ui.showTextPromise(preQText); + await this.scene.ui.showTextPromise(shouldReplaceQ, undefined, false); + await this.scene.ui.setModeWithoutClear(Mode.CONFIRM, + () => this.forgetMoveProcess(move, pokemon), // Yes + () => { // No + this.scene.ui.setMode(this.messageMode); + this.rejectMoveAndEnd(move, pokemon); + } + ); + } + + /** + * This facilitates the process in which an old move is chosen to be forgotten. + * + * > Which move should be forgotten? + * + * The game then goes `Mode.SUMMARY` to select a move to be forgotten. + * If a player does not select a move or chooses the new move (`moveIndex === 4`), the game goes to `this.rejectMoveAndEnd()`. + * If an old move is selected, the function then passes the `moveIndex` to `this.learnMove()` + * @param move The Move to be learned + * @param Pokemon The Pokemon learning the move + */ + async forgetMoveProcess(move: Move, pokemon: Pokemon) { + this.scene.ui.setMode(this.messageMode); + await this.scene.ui.showTextPromise(i18next.t("battle:learnMoveForgetQuestion"), undefined, true); + await this.scene.ui.setModeWithoutClear(Mode.SUMMARY, pokemon, SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { + if (moveIndex === 4) { + this.scene.ui.setMode(this.messageMode).then(() => this.rejectMoveAndEnd(move, pokemon)); + return; + } + const forgetSuccessText = i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }); + const fullText = [i18next.t("battle:countdownPoof"), forgetSuccessText, i18next.t("battle:learnMoveAnd")].join("$"); + this.scene.ui.setMode(this.messageMode).then(() => this.learnMove(moveIndex, move, pokemon, fullText)); + }); + } + + /** + * This asks the player if they wish to end the current move learning process. + * + * > Stop trying to teach [MoveName]? --> `Mode.CONFIRM` --> Yes: > [Pokemon] did not learn the move [MoveName], No: `this.replaceMoveCheck()` + * + * If the player wishes to not teach the Pokemon the move, it displays a message and ends the phase. + * If the player reconsiders, it repeats the process for a Pokemon with a full moveset once again. + * @param move The Move to be learned + * @param Pokemon The Pokemon learning the move + */ + async rejectMoveAndEnd(move: Move, pokemon: Pokemon) { + await this.scene.ui.showTextPromise(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), undefined, false); + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, + () => { + this.scene.ui.setMode(this.messageMode); + this.scene.ui.showTextPromise(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), undefined, true).then(() => this.end()); + }, + () => { + this.scene.ui.setMode(this.messageMode); + this.replaceMoveCheck(move, pokemon); + } + ); + } + + /** + * This teaches the Pokemon the new move and ends the phase. + * When a Pokemon forgets a move and learns a new one, its 'Learn Move' message is significantly longer. + * + * Pokemon with a `moveset.length < 4` + * > [Pokemon] learned [MoveName] + * + * Pokemon with a `moveset.length > 4` + * > 1... 2... and 3... and Poof! + * > [Pokemon] forgot how to use [MoveName] + * > And... + * > [Pokemon] learned [MoveName]! + * @param move The Move to be learned + * @param Pokemon The Pokemon learning the move + */ + async learnMove(index: number, move: Move, pokemon: Pokemon, textMessage?: string) { + if (this.fromTM) { + if (!pokemon.usedTMs) { + pokemon.usedTMs = []; + } + pokemon.usedTMs.push(this.moveId); + } + pokemon.setMove(index, this.moveId); + initMoveAnim(this.scene, this.moveId).then(() => { + loadMoveAnimAssets(this.scene, [this.moveId], true); + this.scene.playSound("level_up_fanfare"); // Sound loaded into game as is + }); + this.scene.ui.setMode(this.messageMode); + const learnMoveText = i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }); + textMessage = textMessage ? textMessage+"$"+learnMoveText : learnMoveText; + await this.scene.ui.showTextPromise(textMessage, this.messageMode === Mode.EVOLUTION_SCENE ? 1000 : undefined, true); + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); + this.end(); + } } diff --git a/src/phases/level-cap-phase.ts b/src/phases/level-cap-phase.ts index db59fbd6473..d1404e45010 100644 --- a/src/phases/level-cap-phase.ts +++ b/src/phases/level-cap-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index a8a6b8f3d80..a99e038acba 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -1,11 +1,11 @@ -import BattleScene from "#app/battle-scene.js"; -import { ExpNotification } from "#app/enums/exp-notification.js"; -import { EvolutionPhase } from "#app/phases/evolution-phase.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { LevelAchv } from "#app/system/achv.js"; +import BattleScene from "#app/battle-scene"; +import { ExpNotification } from "#app/enums/exp-notification"; +import { EvolutionPhase } from "#app/phases/evolution-phase"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { LevelAchv } from "#app/system/achv"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; import { LearnMovePhase } from "./learn-move-phase"; diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index b58cc9bca1f..89ced201e9e 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -1,10 +1,10 @@ -import { updateUserInfo } from "#app/account.js"; -import BattleScene, { bypassLogin } from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { handleTutorial, Tutorial } from "#app/tutorial.js"; -import { Mode } from "#app/ui/ui.js"; +import { updateUserInfo } from "#app/account"; +import BattleScene, { bypassLogin } from "#app/battle-scene"; +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.js"; +import * as Utils from "#app/utils"; import { SelectGenderPhase } from "./select-gender-phase"; import { UnavailablePhase } from "./unavailable-phase"; diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index 46e907ae2ba..2244980c899 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; export class MessagePhase extends Phase { private text: string; diff --git a/src/phases/modifier-reward-phase.ts b/src/phases/modifier-reward-phase.ts index 4d083a367a6..20a8366d9c6 100644 --- a/src/phases/modifier-reward-phase.ts +++ b/src/phases/modifier-reward-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { ModifierType, ModifierTypeFunc, getModifierType } from "#app/modifier/modifier-type.js"; +import BattleScene from "#app/battle-scene"; +import { ModifierType, ModifierTypeFunc, getModifierType } from "#app/modifier/modifier-type"; import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts index e0bff608972..2f0a4f7b990 100644 --- a/src/phases/money-reward-phase.ts +++ b/src/phases/money-reward-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { MoneyMultiplierModifier } from "#app/modifier/modifier.js"; +import BattleScene from "#app/battle-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.js"; +import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; export class MoneyRewardPhase extends BattlePhase { diff --git a/src/phases/move-anim-test-phase.ts b/src/phases/move-anim-test-phase.ts index 7fb3c1d61e7..2d3b54bfd9a 100644 --- a/src/phases/move-anim-test-phase.ts +++ b/src/phases/move-anim-test-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { initMoveAnim, loadMoveAnimAssets, MoveAnim } from "#app/data/battle-anims.js"; -import { allMoves, SelfStatusMove } from "#app/data/move.js"; -import { Moves } from "#app/enums/moves.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { initMoveAnim, loadMoveAnimAssets, MoveAnim } from "#app/data/battle-anims"; +import { allMoves, SelfStatusMove } from "#app/data/move"; +import { Moves } from "#app/enums/moves"; +import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; export class MoveAnimTestPhase extends BattlePhase { diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index f100a763219..41fb03c4f4f 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -1,18 +1,18 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability.js"; -import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag.js"; -import { MoveAnim } from "#app/data/battle-anims.js"; -import { BattlerTagLapseType, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags.js"; -import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move.js"; -import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability"; +import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; +import { MoveAnim } from "#app/data/battle-anims"; +import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags"; +import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move"; +import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { PokemonPhase } from "./pokemon-phase"; export class MoveEffectPhase extends PokemonPhase { @@ -152,7 +152,8 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) - && (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))); + && (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)))); /** Does this phase represent the invoked move's first strike? */ const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); @@ -377,16 +378,16 @@ export class MoveEffectPhase extends PokemonPhase { return false; } - const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here? + const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target); if (moveAccuracy === -1) { return true; } const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); - const rand = user.randSeedInt(100, 1); + const rand = user.randSeedInt(100); - return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? + return rand < (moveAccuracy * accuracyMultiplier); } /** Returns the {@linkcode Pokemon} using this phase's invoked move */ diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 4bce2185aea..e03f2ec14b0 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { BattlerTagLapseType } from "#app/data/battler-tags.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { BattlerTagLapseType } from "#app/data/battler-tags"; import { PokemonPhase } from "./pokemon-phase"; export class MoveEndPhase extends PokemonPhase { diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts index 5166f287731..c307ff0be6e 100644 --- a/src/phases/move-header-phase.ts +++ b/src/phases/move-header-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/move.js"; -import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/move"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; export class MoveHeaderPhase extends BattlePhase { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index c446660b16f..6089e7d3202 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -1,22 +1,22 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyAbAttrs, RedirectMoveAbAttr, BlockRedirectAbAttr, IncreasePpAbAttr, applyPreAttackAbAttrs, PokemonTypeChangeAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "#app/data/ability.js"; -import { CommonAnim } from "#app/data/battle-anims.js"; -import { CenterOfAttentionTag, BattlerTagLapseType } from "#app/data/battler-tags.js"; -import { MoveFlags, BypassRedirectAttr, allMoves, CopyMoveAttr, applyMoveAttrs, BypassSleepAttr, HealStatusEffectAttr, ChargeAttr, PreMoveMessageAttr } from "#app/data/move.js"; -import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms.js"; -import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect.js"; -import { Type } from "#app/data/type.js"; -import { getTerrainBlockMessage } from "#app/data/weather.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { MoveUsedEvent } from "#app/events/battle-scene.js"; -import Pokemon, { PokemonMove, MoveResult, TurnMove } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs, BlockRedirectAbAttr, IncreasePpAbAttr, PokemonTypeChangeAbAttr, PostMoveUsedAbAttr, RedirectMoveAbAttr } from "#app/data/ability"; +import { CommonAnim } from "#app/data/battle-anims"; +import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; +import { allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, ChargeAttr, CopyMoveAttr, HealStatusEffectAttr, MoveFlags, PreMoveMessageAttr } from "#app/data/move"; +import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; +import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; +import { Type } from "#app/data/type"; +import { getTerrainBlockMessage } from "#app/data/weather"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { StatusEffect } from "#app/enums/status-effect"; +import { MoveUsedEvent } from "#app/events/battle-scene"; +import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import * as Utils from "#app/utils"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; import { BattlePhase } from "./battle-phase"; import { CommonAnimPhase } from "./common-anim-phase"; import { MoveEffectPhase } from "./move-effect-phase"; @@ -38,14 +38,14 @@ export class MovePhase extends BattlePhase { this.pokemon = pokemon; this.targets = targets; this.move = move; - this.followUp = !!followUp; - this.ignorePp = !!ignorePp; + this.followUp = followUp ?? false; + this.ignorePp = ignorePp ?? false; this.failed = false; this.cancelled = false; } - canMove(): boolean { - return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp) && !!this.targets.length; + canMove(ignoreDisableTags?: boolean): boolean { + return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp, ignoreDisableTags) && !!this.targets.length; } /**Signifies the current move should fail but still use PP */ @@ -63,10 +63,7 @@ export class MovePhase extends BattlePhase { console.log(Moves[this.move.moveId]); - if (!this.canMove()) { - if (this.move.moveId && this.pokemon.summonData?.disabledMove === this.move.moveId) { - this.scene.queueMessage(i18next.t("battle:moveDisabled", { moveName: this.move.getName() })); - } + if (!this.canMove(true)) { if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails this.fail(); this.showMoveText(); @@ -194,7 +191,7 @@ export class MovePhase extends BattlePhase { return this.end(); } - if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used + if ((!moveQueue.length || !moveQueue.shift()?.ignorePP) && !this.ignorePp) { // using .shift here clears out two turn moves once they've been used this.move.usePp(ppUsed); this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } @@ -207,7 +204,7 @@ export class MovePhase extends BattlePhase { let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove()); const cancelled = new Utils.BooleanHolder(false); let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled); - if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove())) { + if (success && this.scene.arena.isMoveWeatherCancelled(this.pokemon, this.move.getMove())) { success = false; } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { success = false; diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index c447e78f7b1..9f1209fb7ee 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/ability.js"; -import { getRandomWeatherType } from "#app/data/weather.js"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/ability"; +import { getRandomWeatherType } from "#app/data/weather"; import { NextEncounterPhase } from "./next-encounter-phase"; export class NewBiomeEncounterPhase extends NextEncounterPhase { diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts index 89987534fc0..d51aa374b6e 100644 --- a/src/phases/next-encounter-phase.ts +++ b/src/phases/next-encounter-phase.ts @@ -1,4 +1,4 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { EncounterPhase } from "./encounter-phase"; export class NextEncounterPhase extends EncounterPhase { diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index ac6e66a2e9f..bb06fafb1c9 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -1,10 +1,10 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; -import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import Pokemon from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims"; +import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect"; +import { StatusEffect } from "#app/enums/status-effect"; +import Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; diff --git a/src/phases/outdated-phase.ts b/src/phases/outdated-phase.ts index 72d1bb3671d..4baf16d2f56 100644 --- a/src/phases/outdated-phase.ts +++ b/src/phases/outdated-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; export class OutdatedPhase extends Phase { constructor(scene: BattleScene) { diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index d9179826a19..e6ee11202df 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; export class PartyHealPhase extends BattlePhase { diff --git a/src/phases/party-member-pokemon-phase.ts b/src/phases/party-member-pokemon-phase.ts index 1f27826884e..2b6ca01261d 100644 --- a/src/phases/party-member-pokemon-phase.ts +++ b/src/phases/party-member-pokemon-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import Pokemon from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import Pokemon from "#app/field/pokemon"; import { FieldPhase } from "./field-phase"; export abstract class PartyMemberPokemonPhase extends FieldPhase { diff --git a/src/phases/party-status-cure-phase.ts b/src/phases/party-status-cure-phase.ts index a11aa01b63a..e4903c7fc1f 100644 --- a/src/phases/party-status-cure-phase.ts +++ b/src/phases/party-status-cure-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Abilities } from "#app/enums/abilities.js"; -import Pokemon from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { Abilities } from "#app/enums/abilities"; +import Pokemon from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; import { ShowAbilityPhase } from "./show-ability-phase"; diff --git a/src/phases/player-party-member-pokemon-phase.ts b/src/phases/player-party-member-pokemon-phase.ts index 4b1600b33d2..87855b9334c 100644 --- a/src/phases/player-party-member-pokemon-phase.ts +++ b/src/phases/player-party-member-pokemon-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { PlayerPokemon } from "#app/field/pokemon"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase { diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 6db8aeb4fca..49db2641e98 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -1,14 +1,14 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { CommonAnim } from "#app/data/battle-anims.js"; -import { getStatusEffectHealText } from "#app/data/status-effect.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { HitResult, DamageResult } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { HealingBoosterModifier } from "#app/modifier/modifier.js"; -import { HealAchv } from "#app/system/achv.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { CommonAnim } from "#app/data/battle-anims"; +import { getStatusEffectHealText } from "#app/data/status-effect"; +import { StatusEffect } from "#app/enums/status-effect"; +import { HitResult, DamageResult } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { HealingBoosterModifier } from "#app/modifier/modifier"; +import { HealAchv } from "#app/system/achv"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; export class PokemonHealPhase extends CommonAnimPhase { diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts index 871ee57d7a5..b980c1d1719 100644 --- a/src/phases/pokemon-phase.ts +++ b/src/phases/pokemon-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import Pokemon from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import Pokemon from "#app/field/pokemon"; import { FieldPhase } from "./field-phase"; export abstract class PokemonPhase extends FieldPhase { diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index 02413b41a23..beeb30c7260 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; import { EndCardPhase } from "./end-card-phase"; import { TitlePhase } from "./title-phase"; diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index e671bf30ed1..47a5513f0eb 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyPostSummonAbAttrs, PostSummonAbAttr } from "#app/data/ability.js"; -import { ArenaTrapTag } from "#app/data/arena-tag.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { applyPostSummonAbAttrs, PostSummonAbAttr } from "#app/data/ability"; +import { ArenaTrapTag } from "#app/data/arena-tag"; +import { StatusEffect } from "#app/enums/status-effect"; import { PokemonPhase } from "./pokemon-phase"; export class PostSummonPhase extends PokemonPhase { diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index 47db32303a5..413f9eae65e 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -1,12 +1,12 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyAbAttrs, BlockNonDirectDamageAbAttr, BlockStatusDamageAbAttr, ReduceBurnDamageAbAttr } from "#app/data/ability.js"; -import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; -import { getStatusEffectActivationText } from "#app/data/status-effect.js"; -import { BattleSpec } from "#app/enums/battle-spec.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { applyAbAttrs, BlockNonDirectDamageAbAttr, BlockStatusDamageAbAttr, ReduceBurnDamageAbAttr } from "#app/data/ability"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims"; +import { getStatusEffectActivationText } from "#app/data/status-effect"; +import { BattleSpec } from "#app/enums/battle-spec"; +import { StatusEffect } from "#app/enums/status-effect"; +import { getPokemonNameWithAffix } from "#app/messages"; +import * as Utils from "#app/utils"; import { PokemonPhase } from "./pokemon-phase"; export class PostTurnStatusEffectPhase extends PokemonPhase { diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 3d30d36907e..dde500e156a 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -1,10 +1,10 @@ -import BattleScene from "#app/battle-scene.js"; -import { SemiInvulnerableTag } from "#app/data/battler-tags.js"; -import { SpeciesFormChange, getSpeciesFormChangeMessage } from "#app/data/pokemon-forms.js"; -import { getTypeRgb } from "#app/data/type.js"; -import { BattleSpec } from "#app/enums/battle-spec.js"; -import Pokemon, { EnemyPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleScene from "#app/battle-scene"; +import { SemiInvulnerableTag } from "#app/data/battler-tags"; +import { SpeciesFormChange, getSpeciesFormChangeMessage } from "#app/data/pokemon-forms"; +import { getTypeRgb } from "#app/data/type"; +import { BattleSpec } from "#app/enums/battle-spec"; +import Pokemon, { EnemyPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; import { BattlePhase } from "./battle-phase"; import { MovePhase } from "./move-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase"; @@ -65,7 +65,7 @@ export class QuietFormChangePhase extends BattlePhase { pokemonFormTintSprite.setVisible(false); pokemonFormTintSprite.setTintFill(0xFFFFFF); - this.scene.playSound("PRSFX- Transform"); + this.scene.playSound("battle_anims/PRSFX- Transform"); this.scene.tweens.add({ targets: pokemonTintSprite, diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts index a61c52323bf..f8a38105869 100644 --- a/src/phases/reload-session-phase.ts +++ b/src/phases/reload-session-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Mode } from "#app/ui/ui.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; +import * as Utils from "#app/utils"; export class ReloadSessionPhase extends Phase { private systemDataStr: string | null; diff --git a/src/phases/return-phase.ts b/src/phases/return-phase.ts index e1753670ad4..dfc458eb817 100644 --- a/src/phases/return-phase.ts +++ b/src/phases/return-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; +import BattleScene from "#app/battle-scene"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; import { SwitchSummonPhase } from "./switch-summon-phase"; export class ReturnPhase extends SwitchSummonPhase { diff --git a/src/phases/ribbon-modifier-reward-phase.ts b/src/phases/ribbon-modifier-reward-phase.ts index 4a80325b7e7..fabb3bfa1b1 100644 --- a/src/phases/ribbon-modifier-reward-phase.ts +++ b/src/phases/ribbon-modifier-reward-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import PokemonSpecies from "#app/data/pokemon-species.js"; -import { ModifierTypeFunc } from "#app/modifier/modifier-type.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import PokemonSpecies from "#app/data/pokemon-species"; +import { ModifierTypeFunc } from "#app/modifier/modifier-type"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index f5e1a814612..ba27e4f1943 100644 --- a/src/phases/scan-ivs-phase.ts +++ b/src/phases/scan-ivs-phase.ts @@ -1,10 +1,10 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; -import { Stat } from "#app/enums/stat.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { getTextColor, TextStyle } from "#app/ui/text.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims"; +import { Stat } from "#app/enums/stat"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { getTextColor, TextStyle } from "#app/ui/text"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 68c2cd29f26..fe9b5b3996b 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -1,11 +1,11 @@ -import BattleScene from "#app/battle-scene.js"; -import { biomeLinks, getBiomeName } from "#app/data/biomes.js"; -import { Biome } from "#app/enums/biome.js"; -import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier.js"; -import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { biomeLinks, getBiomeName } from "#app/data/biomes"; +import { Biome } from "#app/enums/biome"; +import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { BattlePhase } from "./battle-phase"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { PartyHealPhase } from "./party-heal-phase"; import { SwitchBiomePhase } from "./switch-biome-phase"; diff --git a/src/phases/select-challenge-phase.ts b/src/phases/select-challenge-phase.ts index eaf830e0059..9450c60fec5 100644 --- a/src/phases/select-challenge-phase.ts +++ b/src/phases/select-challenge-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; export class SelectChallengePhase extends Phase { constructor(scene: BattleScene) { diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts index 3fc6916e233..7f2c965f1d1 100644 --- a/src/phases/select-gender-phase.ts +++ b/src/phases/select-gender-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { PlayerGender } from "#app/enums/player-gender.js"; -import { Phase } from "#app/phase.js"; -import { SettingKeys } from "#app/system/settings/settings.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { PlayerGender } from "#app/enums/player-gender"; +import { Phase } from "#app/phase"; +import { SettingKeys } from "#app/system/settings/settings"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; export class SelectGenderPhase extends Phase { diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 1c96d278d69..e14638c5dd2 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -1,12 +1,12 @@ -import BattleScene from "#app/battle-scene.js"; -import { ModifierTier } from "#app/modifier/modifier-tier.js"; -import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type.js"; -import { ExtraModifierModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier.js"; -import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler.js"; -import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { ModifierTier } from "#app/modifier/modifier-tier"; +import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type"; +import { ExtraModifierModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; +import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; +import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; import Overrides from "#app/overrides"; diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index ad972a49225..cd3c112549c 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -1,15 +1,15 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; -import { Gender } from "#app/data/gender.js"; -import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms.js"; -import { getPokemonSpecies } from "#app/data/pokemon-species.js"; -import { Species } from "#app/enums/species.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { overrideModifiers, overrideHeldItems } from "#app/modifier/modifier.js"; -import { Phase } from "#app/phase.js"; -import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler.js"; -import { Starter } from "#app/ui/starter-select-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { applyChallenges, ChallengeType } from "#app/data/challenge"; +import { Gender } from "#app/data/gender"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { Species } from "#app/enums/species"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { overrideModifiers, overrideHeldItems } from "#app/modifier/modifier"; +import { Phase } from "#app/phase"; +import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; +import { Starter } from "#app/ui/starter-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { TitlePhase } from "./title-phase"; import Overrides from "#app/overrides"; diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index fe72335e312..716d2737a6c 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { Command } from "#app/ui/command-ui-handler"; +import { Mode } from "#app/ui/ui"; import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; diff --git a/src/phases/shiny-sparkle-phase.ts b/src/phases/shiny-sparkle-phase.ts index 4cd2b68f881..49c60a82dd5 100644 --- a/src/phases/shiny-sparkle-phase.ts +++ b/src/phases/shiny-sparkle-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; import { PokemonPhase } from "./pokemon-phase"; export class ShinySparklePhase extends PokemonPhase { diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index ee0b98f7886..cf34e327b4f 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; import { PokemonPhase } from "./pokemon-phase"; export class ShowAbilityPhase extends PokemonPhase { diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 9920472e801..9e019b202a5 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { ExpNotification } from "#app/enums/exp-notification.js"; -import { ExpBoosterModifier } from "#app/modifier/modifier.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { ExpNotification } from "#app/enums/exp-notification"; +import { ExpBoosterModifier } from "#app/modifier/modifier"; +import * as Utils from "#app/utils"; import { HidePartyExpBarPhase } from "./hide-party-exp-bar-phase"; import { LevelUpPhase } from "./level-up-phase"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; diff --git a/src/phases/show-trainer-phase.ts b/src/phases/show-trainer-phase.ts index 8a869f582d8..26ccddd53fc 100644 --- a/src/phases/show-trainer-phase.ts +++ b/src/phases/show-trainer-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { PlayerGender } from "#app/enums/player-gender.js"; +import BattleScene from "#app/battle-scene"; +import { PlayerGender } from "#app/enums/player-gender"; import { BattlePhase } from "./battle-phase"; export class ShowTrainerPhase extends BattlePhase { diff --git a/src/phases/stat-change-phase.ts b/src/phases/stat-change-phase.ts deleted file mode 100644 index 856d0a33eea..00000000000 --- a/src/phases/stat-change-phase.ts +++ /dev/null @@ -1,234 +0,0 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyPreStatChangeAbAttrs, ProtectStatAbAttr, applyAbAttrs, StatChangeMultiplierAbAttr, StatChangeCopyAbAttr, applyPostStatChangeAbAttrs, PostStatChangeAbAttr } from "#app/data/ability.js"; -import { MistTag, ArenaTagSide } from "#app/data/arena-tag.js"; -import { BattleStat, getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; -import Pokemon from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier.js"; -import { handleTutorial, Tutorial } from "#app/tutorial.js"; -import i18next from "i18next"; -import * as Utils from "#app/utils.js"; -import { PokemonPhase } from "./pokemon-phase"; - -export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; - -export class StatChangePhase extends PokemonPhase { - private stats: BattleStat[]; - private selfTarget: boolean; - private levels: integer; - private showMessage: boolean; - private ignoreAbilities: boolean; - private canBeCopied: boolean; - private onChange: StatChangeCallback | null; - - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) { - super(scene, battlerIndex); - - this.selfTarget = selfTarget; - this.stats = stats; - this.levels = levels; - this.showMessage = showMessage; - this.ignoreAbilities = ignoreAbilities; - this.canBeCopied = canBeCopied; - this.onChange = onChange; - } - - start() { - const pokemon = this.getPokemon(); - - let random = false; - - if (this.stats.length === 1 && this.stats[0] === BattleStat.RAND) { - this.stats[0] = this.getRandomStat(); - random = true; - } - - this.aggregateStatChanges(random); - - if (!pokemon.isActive(true)) { - return this.end(); - } - - const filteredStats = this.stats.map(s => s !== BattleStat.RAND ? s : this.getRandomStat()).filter(stat => { - const cancelled = new Utils.BooleanHolder(false); - - if (!this.selfTarget && this.levels < 0) { - this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); - } - - if (!cancelled.value && !this.selfTarget && this.levels < 0) { - applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled); - } - - return !cancelled.value; - }); - - const levels = new Utils.IntegerHolder(this.levels); - - if (!this.ignoreAbilities) { - applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, false, levels); - } - - const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); - - this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); - - const end = () => { - if (this.showMessage) { - const messages = this.getStatChangeMessages(filteredStats, levels.value, relLevels); - for (const message of messages) { - this.scene.queueMessage(message); - } - } - - for (const stat of filteredStats) { - pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); - } - - if (levels.value > 0 && this.canBeCopied) { - for (const opponent of pokemon.getOpponents()) { - applyAbAttrs(StatChangeCopyAbAttr, opponent, null, false, this.stats, levels.value); - } - } - - applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget); - - // Look for any other stat change phases; if this is the last one, do White Herb check - const existingPhase = this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex); - if (!(existingPhase instanceof StatChangePhase)) { - // Apply White Herb if needed - const whiteHerb = this.scene.applyModifier(PokemonResetNegativeStatStageModifier, this.player, pokemon) as PokemonResetNegativeStatStageModifier; - // If the White Herb was applied, consume it - if (whiteHerb) { - --whiteHerb.stackCount; - if (whiteHerb.stackCount <= 0) { - this.scene.removeModifier(whiteHerb); - } - this.scene.updateModifiers(this.player); - } - } - - pokemon.updateInfo(); - - handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end()); - }; - - if (relLevels.filter(l => l).length && this.scene.moveAnimations) { - pokemon.enableMask(); - const pokemonMaskSprite = pokemon.maskSprite; - - const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale; - const tileY = ((this.player ? 148 : 84) + (levels.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale; - const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale(); - const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale(); - - // On increase, show the red sprite located at ATK - // On decrease, show the blue sprite located at SPD - const spriteColor = levels.value >= 1 ? BattleStat[BattleStat.ATK].toLowerCase() : BattleStat[BattleStat.SPD].toLowerCase(); - const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor); - statSprite.setPipeline(this.scene.fieldSpritePipeline); - statSprite.setAlpha(0); - statSprite.setScale(6); - statSprite.setOrigin(0.5, 1); - - this.scene.playSound(`se/stat_${levels.value >= 1 ? "up" : "down"}`); - - statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); - - this.scene.tweens.add({ - targets: statSprite, - duration: 250, - alpha: 0.8375, - onComplete: () => { - this.scene.tweens.add({ - targets: statSprite, - delay: 1000, - duration: 250, - alpha: 0 - }); - } - }); - - this.scene.tweens.add({ - targets: statSprite, - duration: 1500, - y: `${levels.value >= 1 ? "-" : "+"}=${160 * 6}` - }); - - this.scene.time.delayedCall(1750, () => { - pokemon.disableMask(); - end(); - }); - } else { - end(); - } - } - - getRandomStat(): BattleStat { - const allStats = Utils.getEnumValues(BattleStat); - return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... - } - - aggregateStatChanges(random: boolean = false): void { - const isAccEva = [BattleStat.ACC, BattleStat.EVA].some(s => this.stats.includes(s)); - let existingPhase: StatChangePhase; - if (this.stats.length === 1) { - while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.stats.length === 1 - && (p.stats[0] === this.stats[0] || (random && p.stats[0] === BattleStat.RAND)) - && p.selfTarget === this.selfTarget && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { - if (existingPhase.stats[0] === BattleStat.RAND) { - existingPhase.stats[0] = this.getRandomStat(); - if (existingPhase.stats[0] !== this.stats[0]) { - continue; - } - } - this.levels += existingPhase.levels; - - if (!this.scene.tryRemovePhase(p => p === existingPhase)) { - break; - } - } - } - while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget - && ([BattleStat.ACC, BattleStat.EVA].some(s => p.stats.includes(s)) === isAccEva) - && p.levels === this.levels && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { - this.stats.push(...existingPhase.stats); - if (!this.scene.tryRemovePhase(p => p === existingPhase)) { - break; - } - } - } - - getStatChangeMessages(stats: BattleStat[], levels: integer, relLevels: integer[]): string[] { - const messages: string[] = []; - - const relLevelStatIndexes = {}; - for (let rl = 0; rl < relLevels.length; rl++) { - const relLevel = relLevels[rl]; - if (!relLevelStatIndexes[relLevel]) { - relLevelStatIndexes[relLevel] = []; - } - relLevelStatIndexes[relLevel].push(rl); - } - - Object.keys(relLevelStatIndexes).forEach(rl => { - const relLevelStats = stats.filter((_, i) => relLevelStatIndexes[rl].includes(i)); - let statsFragment = ""; - - if (relLevelStats.length > 1) { - statsFragment = relLevelStats.length >= 5 - ? i18next.t("battle:stats") - : `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(", ")}${relLevelStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`; - messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1, relLevelStats.length)); - } else { - statsFragment = getBattleStatName(relLevelStats[0]); - messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1, relLevelStats.length)); - } - }); - - return messages; - } -} diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts new file mode 100644 index 00000000000..55faaa29903 --- /dev/null +++ b/src/phases/stat-stage-change-phase.ts @@ -0,0 +1,244 @@ +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, applyPostStatStageChangeAbAttrs, applyPreStatStageChangeAbAttrs, PostStatStageChangeAbAttr, ProtectStatAbAttr, StatStageChangeCopyAbAttr, StatStageChangeMultiplierAbAttr } from "#app/data/ability"; +import { ArenaTagSide, MistTag } from "#app/data/arena-tag"; +import Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { ResetNegativeStatStageModifier } from "#app/modifier/modifier"; +import { handleTutorial, Tutorial } from "#app/tutorial"; +import * as Utils from "#app/utils"; +import i18next from "i18next"; +import { PokemonPhase } from "./pokemon-phase"; +import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; + +export type StatStageChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; + +export class StatStageChangePhase extends PokemonPhase { + private stats: BattleStat[]; + private selfTarget: boolean; + private stages: integer; + private showMessage: boolean; + private ignoreAbilities: boolean; + private canBeCopied: boolean; + private onChange: StatStageChangeCallback | null; + + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], stages: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatStageChangeCallback | null = null) { + super(scene, battlerIndex); + + this.selfTarget = selfTarget; + this.stats = stats; + this.stages = stages; + this.showMessage = showMessage; + this.ignoreAbilities = ignoreAbilities; + this.canBeCopied = canBeCopied; + this.onChange = onChange; + } + + start() { + const pokemon = this.getPokemon(); + + if (!pokemon.isActive(true)) { + return this.end(); + } + + let simulate = false; + + const filteredStats = this.stats.filter(stat => { + const cancelled = new Utils.BooleanHolder(false); + + if (!this.selfTarget && this.stages < 0) { + // TODO: Include simulate boolean when tag applications can be simulated + this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); + } + + if (!cancelled.value && !this.selfTarget && this.stages < 0) { + applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate); + } + + // If one stat stage decrease is cancelled, simulate the rest of the applications + if (cancelled.value) { + simulate = true; + } + + return !cancelled.value; + }); + + const stages = new Utils.IntegerHolder(this.stages); + + if (!this.ignoreAbilities) { + applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages); + } + + const relLevels = filteredStats.map(s => (stages.value >= 1 ? Math.min(pokemon.getStatStage(s) + stages.value, 6) : Math.max(pokemon.getStatStage(s) + stages.value, -6)) - pokemon.getStatStage(s)); + + this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); + + const end = () => { + if (this.showMessage) { + const messages = this.getStatStageChangeMessages(filteredStats, stages.value, relLevels); + for (const message of messages) { + this.scene.queueMessage(message); + } + } + + for (const s of filteredStats) { + if (stages.value > 0 && pokemon.getStatStage(s) < 6) { + if (!pokemon.turnData) { + // Temporary fix for missing turn data struct on turn 1 + pokemon.resetTurnData(); + } + pokemon.turnData.statStagesIncreased = true; + } else if (stages.value < 0 && pokemon.getStatStage(s) > -6) { + if (!pokemon.turnData) { + // Temporary fix for missing turn data struct on turn 1 + pokemon.resetTurnData(); + } + pokemon.turnData.statStagesDecreased = true; + } + + pokemon.setStatStage(s, pokemon.getStatStage(s) + stages.value); + } + + if (stages.value > 0 && this.canBeCopied) { + for (const opponent of pokemon.getOpponents()) { + applyAbAttrs(StatStageChangeCopyAbAttr, opponent, null, false, this.stats, stages.value); + } + } + + applyPostStatStageChangeAbAttrs(PostStatStageChangeAbAttr, pokemon, filteredStats, this.stages, this.selfTarget); + + // Look for any other stat change phases; if this is the last one, do White Herb check + const existingPhase = this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex); + if (!(existingPhase instanceof StatStageChangePhase)) { + // Apply White Herb if needed + const whiteHerb = this.scene.applyModifier(ResetNegativeStatStageModifier, this.player, pokemon) as ResetNegativeStatStageModifier; + // If the White Herb was applied, consume it + if (whiteHerb) { + whiteHerb.stackCount--; + if (whiteHerb.stackCount <= 0) { + this.scene.removeModifier(whiteHerb); + } + this.scene.updateModifiers(this.player); + } + } + + pokemon.updateInfo(); + + handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end()); + }; + + if (relLevels.filter(l => l).length && this.scene.moveAnimations) { + pokemon.enableMask(); + const pokemonMaskSprite = pokemon.maskSprite; + + const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileY = ((this.player ? 148 : 84) + (stages.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale(); + const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale(); + + // On increase, show the red sprite located at ATK + // On decrease, show the blue sprite located at SPD + const spriteColor = stages.value >= 1 ? Stat[Stat.ATK].toLowerCase() : Stat[Stat.SPD].toLowerCase(); + const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor); + statSprite.setPipeline(this.scene.fieldSpritePipeline); + statSprite.setAlpha(0); + statSprite.setScale(6); + statSprite.setOrigin(0.5, 1); + + this.scene.playSound(`se/stat_${stages.value >= 1 ? "up" : "down"}`); + + statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); + + this.scene.tweens.add({ + targets: statSprite, + duration: 250, + alpha: 0.8375, + onComplete: () => { + this.scene.tweens.add({ + targets: statSprite, + delay: 1000, + duration: 250, + alpha: 0 + }); + } + }); + + this.scene.tweens.add({ + targets: statSprite, + duration: 1500, + y: `${stages.value >= 1 ? "-" : "+"}=${160 * 6}` + }); + + this.scene.time.delayedCall(1750, () => { + pokemon.disableMask(); + end(); + }); + } else { + end(); + } + } + + aggregateStatStageChanges(): void { + const accEva: BattleStat[] = [ Stat.ACC, Stat.EVA ]; + const isAccEva = accEva.some(s => this.stats.includes(s)); + let existingPhase: StatStageChangePhase; + if (this.stats.length === 1) { + while ((existingPhase = (this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex && p.stats.length === 1 + && (p.stats[0] === this.stats[0]) + && p.selfTarget === this.selfTarget && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatStageChangePhase))) { + this.stages += existingPhase.stages; + + if (!this.scene.tryRemovePhase(p => p === existingPhase)) { + break; + } + } + } + while ((existingPhase = (this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget + && (accEva.some(s => p.stats.includes(s)) === isAccEva) + && p.stages === this.stages && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatStageChangePhase))) { + this.stats.push(...existingPhase.stats); + if (!this.scene.tryRemovePhase(p => p === existingPhase)) { + break; + } + } + } + + getStatStageChangeMessages(stats: BattleStat[], stages: integer, relStages: integer[]): string[] { + const messages: string[] = []; + + const relStageStatIndexes = {}; + for (let rl = 0; rl < relStages.length; rl++) { + const relStage = relStages[rl]; + if (!relStageStatIndexes[relStage]) { + relStageStatIndexes[relStage] = []; + } + relStageStatIndexes[relStage].push(rl); + } + + Object.keys(relStageStatIndexes).forEach(rl => { + const relStageStats = stats.filter((_, i) => relStageStatIndexes[rl].includes(i)); + let statsFragment = ""; + + if (relStageStats.length > 1) { + statsFragment = relStageStats.length >= 5 + ? i18next.t("battle:stats") + : `${relStageStats.slice(0, -1).map(s => i18next.t(getStatKey(s))).join(", ")}${relStageStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${i18next.t(getStatKey(relStageStats[relStageStats.length - 1]))}`; + messages.push(i18next.t(getStatStageChangeDescriptionKey(Math.abs(parseInt(rl)), stages >= 1), { + pokemonNameWithAffix: getPokemonNameWithAffix(this.getPokemon()), + stats: statsFragment, + count: relStageStats.length + })); + } else { + statsFragment = i18next.t(getStatKey(relStageStats[0])); + messages.push(i18next.t(getStatStageChangeDescriptionKey(Math.abs(parseInt(rl)), stages >= 1), { + pokemonNameWithAffix: getPokemonNameWithAffix(this.getPokemon()), + stats: statsFragment, + count: relStageStats.length + })); + } + }); + + return messages; + } +} diff --git a/src/phases/summon-missing-phase.ts b/src/phases/summon-missing-phase.ts index bb9607285ad..83ac8779dd8 100644 --- a/src/phases/summon-missing-phase.ts +++ b/src/phases/summon-missing-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleScene from "#app/battle-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { SummonPhase } from "./summon-phase"; diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index f65a2063d4c..2645060c547 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -1,12 +1,12 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattleType } from "#app/battle.js"; -import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball.js"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; -import { TrainerSlot } from "#app/data/trainer-config.js"; -import { PlayerGender } from "#app/enums/player-gender.js"; -import { addPokeballOpenParticles } from "#app/field/anims.js"; -import Pokemon, { FieldPosition } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleScene from "#app/battle-scene"; +import { BattleType } from "#app/battle"; +import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; +import { TrainerSlot } from "#app/data/trainer-config"; +import { PlayerGender } from "#app/enums/player-gender"; +import { addPokeballOpenParticles } from "#app/field/anims"; +import Pokemon, { FieldPosition } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; import { PostSummonPhase } from "./post-summon-phase"; diff --git a/src/phases/switch-biome-phase.ts b/src/phases/switch-biome-phase.ts index f20cd59b240..9cf5635a39f 100644 --- a/src/phases/switch-biome-phase.ts +++ b/src/phases/switch-biome-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Biome } from "#app/enums/biome.js"; -import { getBiomeKey } from "#app/field/arena.js"; +import BattleScene from "#app/battle-scene"; +import { Biome } from "#app/enums/biome"; +import { getBiomeKey } from "#app/field/arena"; import { BattlePhase } from "./battle-phase"; export class SwitchBiomePhase extends BattlePhase { diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 93b0943febf..b1a2e991ed8 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; +import { Mode } from "#app/ui/ui"; import { BattlePhase } from "./battle-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 68dc8f8bf65..2a5fd0cc3ac 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -1,13 +1,13 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr } from "#app/data/ability.js"; -import { allMoves, ForceSwitchOutAttr } from "#app/data/move.js"; -import { getPokeballTintColor } from "#app/data/pokeball.js"; -import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; -import { TrainerSlot } from "#app/data/trainer-config.js"; -import Pokemon from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { SwitchEffectTransferModifier } from "#app/modifier/modifier.js"; -import { Command } from "#app/ui/command-ui-handler.js"; +import BattleScene from "#app/battle-scene"; +import { applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr } from "#app/data/ability"; +import { allMoves, ForceSwitchOutAttr } from "#app/data/move"; +import { getPokeballTintColor } from "#app/data/pokeball"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; +import { TrainerSlot } from "#app/data/trainer-config"; +import Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { SwitchEffectTransferModifier } from "#app/modifier/modifier"; +import { Command } from "#app/ui/command-ui-handler"; import i18next from "i18next"; import { PostSummonPhase } from "./post-summon-phase"; import { SummonPhase } from "./summon-phase"; @@ -71,7 +71,7 @@ export class SwitchSummonPhase extends SummonPhase { i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) : i18next.t("battle:trainerComeBack", { trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), - pokemonName: getPokemonNameWithAffix(pokemon) + pokemonName: pokemon.getNameToRender() }) ); this.scene.playSound("se/pb_rel"); diff --git a/src/phases/test-message-phase.ts b/src/phases/test-message-phase.ts index 14fed24ef4b..464a5ed1f94 100644 --- a/src/phases/test-message-phase.ts +++ b/src/phases/test-message-phase.ts @@ -1,4 +1,4 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { MessagePhase } from "./message-phase"; export class TestMessagePhase extends MessagePhase { diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 5c9f68fef5d..2c7084e808f 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -1,21 +1,21 @@ -import { loggedInUser } from "#app/account.js"; -import BattleScene from "#app/battle-scene.js"; -import { BattleType } from "#app/battle.js"; -import { getDailyRunStarters, fetchDailyRunSeed } from "#app/data/daily-run.js"; -import { Gender } from "#app/data/gender.js"; -import { getBiomeKey } from "#app/field/arena.js"; -import { GameModes, GameMode, getGameMode } from "#app/game-mode.js"; -import { regenerateModifierPoolThresholds, ModifierPoolType, modifierTypes, getDailyRunStarterModifiers } from "#app/modifier/modifier-type.js"; -import { Phase } from "#app/phase.js"; -import { SessionSaveData } from "#app/system/game-data.js"; -import { Unlockables } from "#app/system/unlockables.js"; -import { vouchers } from "#app/system/voucher.js"; -import { OptionSelectItem, OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler.js"; -import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { loggedInUser } from "#app/account"; +import BattleScene from "#app/battle-scene"; +import { BattleType } from "#app/battle"; +import { getDailyRunStarters, fetchDailyRunSeed } from "#app/data/daily-run"; +import { Gender } from "#app/data/gender"; +import { getBiomeKey } from "#app/field/arena"; +import { GameModes, GameMode, getGameMode } from "#app/game-mode"; +import { regenerateModifierPoolThresholds, ModifierPoolType, modifierTypes, getDailyRunStarterModifiers } from "#app/modifier/modifier-type"; +import { Phase } from "#app/phase"; +import { SessionSaveData } from "#app/system/game-data"; +import { Unlockables } from "#app/system/unlockables"; +import { vouchers } from "#app/system/voucher"; +import { OptionSelectItem, OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; +import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; -import { Modifier } from "#app/modifier/modifier.js"; +import * as Utils from "#app/utils"; +import { Modifier } from "#app/modifier/modifier"; import { CheckSwitchPhase } from "./check-switch-phase"; import { EncounterPhase } from "./encounter-phase"; import { SelectChallengePhase } from "./select-challenge-phase"; diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts index fe3d0482483..563af8575d7 100644 --- a/src/phases/toggle-double-position-phase.ts +++ b/src/phases/toggle-double-position-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { FieldPosition } from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { FieldPosition } from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; export class ToggleDoublePositionPhase extends BattlePhase { diff --git a/src/phases/trainer-message-test-phase.ts b/src/phases/trainer-message-test-phase.ts index 4ea451660c3..8075dd761e2 100644 --- a/src/phases/trainer-message-test-phase.ts +++ b/src/phases/trainer-message-test-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { trainerConfigs } from "#app/data/trainer-config.js"; -import { TrainerType } from "#app/enums/trainer-type.js"; +import BattleScene from "#app/battle-scene"; +import { trainerConfigs } from "#app/data/trainer-config"; +import { TrainerType } from "#app/enums/trainer-type"; import { BattlePhase } from "./battle-phase"; import { TestMessagePhase } from "./test-message-phase"; diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index a38874c9acd..55b2a1608c0 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -1,10 +1,10 @@ -import BattleScene from "#app/battle-scene.js"; -import { getCharVariantFromDialogue } from "#app/data/dialogue.js"; -import { TrainerType } from "#app/enums/trainer-type.js"; -import { modifierTypes } from "#app/modifier/modifier-type.js"; -import { vouchers } from "#app/system/voucher.js"; +import BattleScene from "#app/battle-scene"; +import { getCharVariantFromDialogue } from "#app/data/dialogue"; +import { TrainerType } from "#app/enums/trainer-type"; +import { modifierTypes } from "#app/modifier/modifier-type"; +import { vouchers } from "#app/system/voucher"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; +import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; import { ModifierRewardPhase } from "./modifier-reward-phase"; import { MoneyRewardPhase } from "./money-reward-phase"; @@ -30,7 +30,7 @@ export class TrainerVictoryPhase extends BattlePhase { const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? if (vouchers.hasOwnProperty(TrainerType[trainerType])) { if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { - this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); } } diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 9f4de46b0fa..724a5206d74 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -1,17 +1,14 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability.js"; -import { BattlerTagLapseType } from "#app/data/battler-tags.js"; -import { allMoves } from "#app/data/move.js"; -import { TerrainType } from "#app/data/terrain.js"; -import { Moves } from "#app/enums/moves.js"; -import { WeatherType } from "#app/enums/weather-type.js"; -import { TurnEndEvent } from "#app/events/battle-scene.js"; -import Pokemon from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; +import BattleScene from "#app/battle-scene"; +import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability"; +import { BattlerTagLapseType } from "#app/data/battler-tags"; +import { TerrainType } from "#app/data/terrain"; +import { WeatherType } from "#app/enums/weather-type"; +import { TurnEndEvent } from "#app/events/battle-scene"; +import Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; -import { MessagePhase } from "./message-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase"; export class TurnEndPhase extends FieldPhase { @@ -28,11 +25,6 @@ export class TurnEndPhase extends FieldPhase { const handlePokemon = (pokemon: Pokemon) => { pokemon.lapseTags(BattlerTagLapseType.TURN_END); - if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) { - this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: allMoves[pokemon.summonData.disabledMove].name }))); - pokemon.summonData.disabledMove = Moves.NONE; - } - this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index a999d57ca0f..568cfdc5714 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { TurnInitEvent } from "#app/events/battle-scene.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { TurnInitEvent } from "#app/events/battle-scene"; +import { PlayerPokemon } from "#app/field/pokemon"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index e4064fc784a..5c1af4228c6 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,12 +1,12 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, ChangeMovePriorityAbAttr } from "#app/data/ability.js"; -import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Stat } from "#app/enums/stat.js"; -import { PokemonMove } from "#app/field/pokemon.js"; -import { BypassSpeedChanceModifier } from "#app/modifier/modifier.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, ChangeMovePriorityAbAttr } from "#app/data/ability"; +import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; +import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; +import { Command } from "#app/ui/command-ui-handler"; +import * as Utils from "#app/utils"; import { AttemptCapturePhase } from "./attempt-capture-phase"; import { AttemptRunPhase } from "./attempt-run-phase"; import { BerryPhase } from "./berry-phase"; @@ -17,18 +17,59 @@ import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; +import { BattlerIndex } from "#app/battle"; +import { TrickRoomTag } from "#app/data/arena-tag"; export class TurnStartPhase extends FieldPhase { constructor(scene: BattleScene) { super(scene); } - start() { - super.start(); + /** + * This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array. + * It also checks for Trick Room and reverses the array if it is present. + * @returns {@linkcode BattlerIndex[]} the battle indices of all pokemon on the field ordered by speed + */ + getSpeedOrder(): BattlerIndex[] { + const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; + const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - const field = this.scene.getField(); - const order = this.getOrder(); + // We shuffle the list before sorting so speed ties produce random results + let orderedTargets: Pokemon[] = playerField.concat(enemyField); + // We seed it with the current turn to prevent an inconsistency where it + // was varying based on how long since you last reloaded + this.scene.executeWithSeedOffset(() => { + orderedTargets = Utils.randSeedShuffle(orderedTargets); + }, this.scene.currentBattle.turn, this.scene.waveSeed); + orderedTargets.sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getEffectiveStat(Stat.SPD) || 0; + const bSpeed = b?.getEffectiveStat(Stat.SPD) || 0; + + return bSpeed - aSpeed; + }); + + // Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. + const speedReversed = new Utils.BooleanHolder(false); + this.scene.arena.applyTags(TrickRoomTag, speedReversed); + + if (speedReversed.value) { + orderedTargets = orderedTargets.reverse(); + } + + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER)); + } + + /** + * This takes the result of getSpeedOrder and applies priority / bypass speed attributes to it. + * This also considers the priority levels of various commands and changes the result of getSpeedOrder based on such. + * @returns {@linkcode BattlerIndex[]} the final sequence of commands for this turn + */ + getCommandOrder(): BattlerIndex[] { + let moveOrder = this.getSpeedOrder(); + // The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw + // The ability Mycelium Might disables Quick Claw's activation when using a status move + // This occurs before the main loop because of battles with more than two Pokemon const battlerBypassSpeed = {}; this.scene.getField(true).filter(p => p.summonData).map(p => { @@ -42,8 +83,9 @@ export class TurnStartPhase extends FieldPhase { battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; }); - const moveOrder = order.slice(0); - + // The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses. + // Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands. + moveOrder = moveOrder.slice(0); moveOrder.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; @@ -55,37 +97,50 @@ export class TurnStartPhase extends FieldPhase { return -1; } } else if (aCommand?.command === Command.FIGHT) { - const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? - const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? + const aMove = allMoves[aCommand.move!.move]; + const bMove = allMoves[bCommand!.move!.move]; + // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); //TODO: is the bang correct here? - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); //TODO: is the bang correct here? + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); + // The game now checks for differences in priority levels. + // If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result. + // This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only) + // Otherwise, the game returns the user of the move with the highest priority. + const isSameBracket = Math.ceil(aPriority.value) - Math.ceil(bPriority.value) === 0; if (aPriority.value !== bPriority.value) { - const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); - const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; - if (bracketDifference === 0 && hasSpeedDifference) { + if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } return aPriority.value < bPriority.value ? 1 : -1; } } + // If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result. if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } - const aIndex = order.indexOf(a); - const bIndex = order.indexOf(b); + const aIndex = moveOrder.indexOf(a); + const bIndex = moveOrder.indexOf(b); return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; }); + return moveOrder; + } + + start() { + super.start(); + + const field = this.scene.getField(); + const moveOrder = this.getCommandOrder(); let orderIndex = 0; @@ -150,10 +205,9 @@ export class TurnStartPhase extends FieldPhase { } } - this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - for (const o of order) { + for (const o of moveOrder) { if (field[o].status && field[o].status.isPostTurn()) { this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); } diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index 4757af5e15d..59bfca7875e 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -1,6 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Mode } from "#app/ui/ui"; import { LoginPhase } from "./login-phase"; export class UnavailablePhase extends Phase { diff --git a/src/phases/unlock-phase.ts b/src/phases/unlock-phase.ts index 1662afaa758..65060309a6c 100644 --- a/src/phases/unlock-phase.ts +++ b/src/phases/unlock-phase.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { Phase } from "#app/phase.js"; -import { Unlockables, getUnlockableName } from "#app/system/unlockables.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { Phase } from "#app/phase"; +import { Unlockables, getUnlockableName } from "#app/system/unlockables"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; export class UnlockPhase extends Phase { diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index b7587de4dbb..9679a79a37d 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,8 +1,8 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex, BattleType } from "#app/battle.js"; -import { modifierTypes } from "#app/modifier/modifier-type.js"; -import { ExpShareModifier, ExpBalanceModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex, BattleType } from "#app/battle"; +import { modifierTypes } from "#app/modifier/modifier-type"; +import { ExpShareModifier, ExpBalanceModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; +import * as Utils from "#app/utils"; import Overrides from "#app/overrides"; import { BattleEndPhase } from "./battle-end-phase"; import { NewBattlePhase } from "./new-battle-phase"; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index 71ca7f9b505..73de44389d0 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -1,10 +1,11 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyPreWeatherEffectAbAttrs, SuppressWeatherEffectAbAttr, PreWeatherDamageAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr } from "#app/data/ability.js"; -import { CommonAnim } from "#app/data/battle-anims.js"; -import { Weather, getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather.js"; -import { WeatherType } from "#app/enums/weather-type.js"; -import Pokemon, { HitResult } from "#app/field/pokemon.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { applyPreWeatherEffectAbAttrs, SuppressWeatherEffectAbAttr, PreWeatherDamageAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr } from "#app/data/ability"; +import { CommonAnim } from "#app/data/battle-anims"; +import { Weather, getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { WeatherType } from "#app/enums/weather-type"; +import Pokemon, { HitResult } from "#app/field/pokemon"; +import * as Utils from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; export class WeatherEffectPhase extends CommonAnimPhase { @@ -39,7 +40,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - if (cancelled.value) { + if (cancelled.value || pokemon.getTag(BattlerTagType.UNDERGROUND) || pokemon.getTag(BattlerTagType.UNDERWATER)) { return; } diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 3d24458a06c..705fd5143a4 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -2,17 +2,17 @@ import i18next from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; -import { caEsConfig} from "#app/locales/ca_ES/config.js"; -import { deConfig } from "#app/locales/de/config.js"; -import { enConfig } from "#app/locales/en/config.js"; -import { esConfig } from "#app/locales/es/config.js"; -import { frConfig } from "#app/locales/fr/config.js"; -import { itConfig } from "#app/locales/it/config.js"; -import { koConfig } from "#app/locales/ko/config.js"; -import { jaConfig } from "#app/locales/ja/config.js"; -import { ptBrConfig } from "#app/locales/pt_BR/config.js"; -import { zhCnConfig } from "#app/locales/zh_CN/config.js"; -import { zhTwConfig } from "#app/locales/zh_TW/config.js"; +import { caEsConfig} from "#app/locales/ca_ES/config"; +import { deConfig } from "#app/locales/de/config"; +import { enConfig } from "#app/locales/en/config"; +import { esConfig } from "#app/locales/es/config"; +import { frConfig } from "#app/locales/fr/config"; +import { itConfig } from "#app/locales/it/config"; +import { koConfig } from "#app/locales/ko/config"; +import { jaConfig } from "#app/locales/ja/config"; +import { ptBrConfig } from "#app/locales/pt_BR/config"; +import { zhCnConfig } from "#app/locales/zh_CN/config"; +import { zhTwConfig } from "#app/locales/zh_TW/config"; interface LoadingFontFaceProperty { face: FontFace, diff --git a/src/system/achv.ts b/src/system/achv.ts index de2862c2813..6170fe23e1d 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -5,9 +5,10 @@ import { pokemonEvolutions } from "#app/data/pokemon-evolutions"; import i18next from "i18next"; import * as Utils from "../utils"; import { PlayerGender } from "#enums/player-gender"; -import { Challenge, FreshStartChallenge, InverseBattleChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge"; -import { Challenges } from "#app/enums/challenges"; +import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge, InverseBattleChallenge } from "#app/data/challenge"; import { ConditionFn } from "#app/@types/common"; +import { Stat, getShortenedStatKey } from "#app/enums/stat"; +import { Challenges } from "#app/enums/challenges"; export enum AchvTier { COMMON, @@ -172,13 +173,13 @@ export function getAchievementDescription(localizationKey: string): string { case "10000_DMG": return i18next.t("achv:DamageAchv.description", {context: genderStr, "damageAmount": achvs._10000_DMG.damageAmount.toLocaleString("en-US")}); case "250_HEAL": - return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._250_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")}); + return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._250_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP))}); case "1000_HEAL": - return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._1000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")}); + return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._1000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP))}); case "2500_HEAL": - return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._2500_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")}); + return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._2500_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP))}); case "10000_HEAL": - return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._10000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")}); + return i18next.t("achv:HealAchv.description", {context: genderStr, "healAmount": achvs._10000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP))}); case "LV_100": return i18next.t("achv:LevelAchv.description", {context: genderStr, "level": achvs.LV_100.level}); case "LV_250": @@ -195,8 +196,8 @@ export function getAchievementDescription(localizationKey: string): string { return i18next.t("achv:RibbonAchv.description", {context: genderStr, "ribbonAmount": achvs._75_RIBBONS.ribbonAmount.toLocaleString("en-US")}); case "100_RIBBONS": return i18next.t("achv:RibbonAchv.description", {context: genderStr, "ribbonAmount": achvs._100_RIBBONS.ribbonAmount.toLocaleString("en-US")}); - case "TRANSFER_MAX_BATTLE_STAT": - return i18next.t("achv:TRANSFER_MAX_BATTLE_STAT.description", { context: genderStr }); + case "TRANSFER_MAX_STAT_STAGE": + return i18next.t("achv:TRANSFER_MAX_STAT_STAGE.description", { context: genderStr }); case "MAX_FRIENDSHIP": return i18next.t("achv:MAX_FRIENDSHIP.description", { context: genderStr }); case "MEGA_EVOLVE": @@ -305,7 +306,7 @@ export const achvs = { _50_RIBBONS: new RibbonAchv("50_RIBBONS", "", 50, "ultra_ribbon", 50).setSecret(true), _75_RIBBONS: new RibbonAchv("75_RIBBONS", "", 75, "rogue_ribbon", 75).setSecret(true), _100_RIBBONS: new RibbonAchv("100_RIBBONS", "", 100, "master_ribbon", 100).setSecret(true), - TRANSFER_MAX_BATTLE_STAT: new Achv("TRANSFER_MAX_BATTLE_STAT", "", "TRANSFER_MAX_BATTLE_STAT.description", "baton", 20), + TRANSFER_MAX_STAT_STAGE: new Achv("TRANSFER_MAX_STAT_STAGE", "", "TRANSFER_MAX_STAT_STAGE.description", "baton", 20), MAX_FRIENDSHIP: new Achv("MAX_FRIENDSHIP", "", "MAX_FRIENDSHIP.description", "soothe_bell", 25), MEGA_EVOLVE: new Achv("MEGA_EVOLVE", "", "MEGA_EVOLVE.description", "mega_bracelet", 50), GIGANTAMAX: new Achv("GIGANTAMAX", "", "GIGANTAMAX.description", "dynamax_band", 50), diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index 886129edcf6..5b907805372 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -2,7 +2,7 @@ import { Arena } from "../field/arena"; import { ArenaTag } from "../data/arena-tag"; import { Biome } from "#enums/biome"; import { Weather } from "../data/weather"; -import { Terrain } from "#app/data/terrain.js"; +import { Terrain } from "#app/data/terrain"; export default class ArenaData { public biome: Biome; diff --git a/src/system/challenge-data.ts b/src/system/challenge-data.ts index 69df11dd395..394d63867be 100644 --- a/src/system/challenge-data.ts +++ b/src/system/challenge-data.ts @@ -1,4 +1,4 @@ -import { Challenge, copyChallenge } from "#app/data/challenge.js"; +import { Challenge, copyChallenge } from "#app/data/challenge"; export default class ChallengeData { public id: integer; diff --git a/src/system/egg-data.ts b/src/system/egg-data.ts index b4bd4368bd9..785ae364efe 100644 --- a/src/system/egg-data.ts +++ b/src/system/egg-data.ts @@ -2,7 +2,7 @@ import { EggTier } from "#enums/egg-type"; import { Species } from "#enums/species"; import { VariantTier } from "#enums/variant-tiers"; import { EGG_SEED, Egg } from "../data/egg"; -import { EggSourceType } from "#app/enums/egg-source-types.js"; +import { EggSourceType } from "#app/enums/egg-source-types"; export default class EggData { public id: integer; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 43982b46705..84c314374b7 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,7 +1,7 @@ import i18next from "i18next"; import BattleScene, { PokeballCounts, bypassLogin } from "../battle-scene"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "../field/pokemon"; -import { pokemonEvolutions, pokemonPrevolutions } from "../data/pokemon-evolutions"; +import { pokemonPrevolutions } from "../data/pokemon-evolutions"; import PokemonSpecies, { allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species"; import * as Utils from "../utils"; import Overrides from "#app/overrides"; @@ -27,24 +27,25 @@ import { Tutorial } from "../tutorial"; import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; -import { Variant, variantData } from "#app/data/variant"; +import { Variant } from "#app/data/variant"; import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad"; import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard"; -import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena.js"; -import { EnemyAttackStatusEffectChanceModifier } from "../modifier/modifier"; -import { StatusEffect } from "#app/data/status-effect.js"; +import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; +import * as Modifier from "../modifier/modifier"; +import { StatusEffect } from "#app/data/status-effect"; import ChallengeData from "./challenge-data"; import { Device } from "#enums/devices"; import { GameDataType } from "#enums/game-data-type"; import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; -import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; -import { WeatherType } from "#app/enums/weather-type.js"; -import { TerrainType } from "#app/data/terrain.js"; -import { OutdatedPhase } from "#app/phases/outdated-phase.js"; -import { ReloadSessionPhase } from "#app/phases/reload-session-phase.js"; +import { applyChallenges, ChallengeType } from "#app/data/challenge"; +import { WeatherType } from "#app/enums/weather-type"; +import { TerrainType } from "#app/data/terrain"; +import { OutdatedPhase } from "#app/phases/outdated-phase"; +import { ReloadSessionPhase } from "#app/phases/reload-session-phase"; import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler"; +import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatches } from "./version-converter"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -93,7 +94,7 @@ export function decrypt(data: string, bypassLogin: boolean): string { : (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8))(data); } -interface SystemSaveData { +export interface SystemSaveData { trainerId: integer; secretId: integer; gender: PlayerGender; @@ -462,17 +463,14 @@ export class GameData { localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemDataStr, bypassLogin)); - /*const versions = [ this.scene.game.config.gameVersion, data.gameVersion || '0.0.0' ]; - - if (versions[0] !== versions[1]) { - const [ versionNumbers, oldVersionNumbers ] = versions.map(ver => ver.split('.').map(v => parseInt(v))); - }*/ const lsItemKey = `runHistoryData_${loggedInUser?.username}`; const lsItem = localStorage.getItem(lsItemKey); if (!lsItem) { localStorage.setItem(lsItemKey, ""); } + applySystemDataPatches(systemData); + this.trainerId = systemData.trainerId; this.secretId = systemData.secretId; @@ -480,9 +478,7 @@ export class GameData { this.saveSetting(SettingKeys.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0); - const initStarterData = !systemData.starterData; - - if (initStarterData) { + if (!systemData.starterData) { this.initStarterData(); if (systemData["starterMoveData"]) { @@ -500,25 +496,20 @@ export class GameData { } this.migrateStarterAbilities(systemData, this.starterData); - } else { - if ([ "1.0.0", "1.0.1" ].includes(systemData.gameVersion)) { - this.migrateStarterAbilities(systemData); - } - //this.fixVariantData(systemData); - this.fixStarterData(systemData); - // Migrate ability starter data if empty for caught species - Object.keys(systemData.starterData).forEach(sd => { - if (systemData.dexData[sd].caughtAttr && !systemData.starterData[sd].abilityAttr) { - systemData.starterData[sd].abilityAttr = 1; + + const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); + for (const s of starterIds) { + this.starterData[s].candyCount += this.dexData[s].caughtCount; + this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2; + if (this.dexData[s].caughtAttr & DexAttr.SHINY) { + this.starterData[s].candyCount += 4; } - }); + } + } else { this.starterData = systemData.starterData; } if (systemData.gameStats) { - if (systemData.gameStats.legendaryPokemonCaught !== undefined && systemData.gameStats.subLegendaryPokemonCaught === undefined) { - this.fixLegendaryStats(systemData); - } this.gameStats = systemData.gameStats; } @@ -564,17 +555,6 @@ export class GameData { this.consolidateDexData(this.dexData); this.defaultDexData = null; - if (initStarterData) { - const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); - for (const s of starterIds) { - this.starterData[s].candyCount += this.dexData[s].caughtCount; - this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2; - if (this.dexData[s].caughtAttr & DexAttr.SHINY) { - this.starterData[s].candyCount += 4; - } - } - } - resolve(true); } catch (err) { console.error(err); @@ -753,6 +733,7 @@ export class GameData { setSetting(this.scene, setting, valueIndex); settings[setting] = valueIndex; + settings["gameVersion"] = this.scene.game.config.gameVersion; localStorage.setItem("settings", JSON.stringify(settings)); @@ -863,6 +844,8 @@ export class GameData { const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct? + applySettingsDataPatches(settings); + for (const setting of Object.keys(settings)) { setSetting(this.scene, setting, settings[setting]); } @@ -952,7 +935,7 @@ export class GameData { return ret; } - private getSessionSaveData(scene: BattleScene): SessionSaveData { + public getSessionSaveData(scene: BattleScene): SessionSaveData { return { seed: scene.seed, playTime: scene.sessionPlayTime, @@ -1088,10 +1071,8 @@ export class GameData { // TODO //scene.arena.tags = sessionData.arena.tags; - const modifiersModule = await import("../modifier/modifier"); - for (const modifierData of sessionData.modifiers) { - const modifier = modifierData.toModifier(scene, modifiersModule[modifierData.className]); + const modifier = modifierData.toModifier(scene, Modifier[modifierData.className]); if (modifier) { scene.addModifier(modifier, true); } @@ -1100,7 +1081,7 @@ export class GameData { scene.updateModifiers(true); for (const enemyModifierData of sessionData.enemyModifiers) { - const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]); + const modifier = enemyModifierData.toModifier(scene, Modifier[enemyModifierData.className]); if (modifier) { scene.addEnemyModifier(modifier, true); } @@ -1224,7 +1205,7 @@ export class GameData { } parseSessionData(dataStr: string): SessionSaveData { - return JSON.parse(dataStr, (k: string, v: any) => { + const sessionData = JSON.parse(dataStr, (k: string, v: any) => { /*const versions = [ scene.game.config.gameVersion, sessionData.gameVersion || '0.0.0' ]; if (versions[0] !== versions[1]) { @@ -1256,7 +1237,7 @@ export class GameData { if (md?.className === "ExpBalanceModifier") { // Temporarily limit EXP Balance until it gets reworked md.stackCount = Math.min(md.stackCount, 4); } - if (md instanceof EnemyAttackStatusEffectChanceModifier && md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP) { + if (md instanceof Modifier.EnemyAttackStatusEffectChanceModifier && md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP) { continue; } ret.push(new PersistentModifierData(md, player)); @@ -1281,6 +1262,10 @@ export class GameData { return v; }) as SessionSaveData; + + applySessionDataPatches(sessionData); + + return sessionData; } saveAll(scene: BattleScene, skipVerification: boolean = false, sync: boolean = false, useCachedSession: boolean = false, useCachedSystem: boolean = false): Promise { @@ -1494,7 +1479,7 @@ export class GameData { }; } - const defaultStarterAttr = DexAttr.NON_SHINY | DexAttr.MALE | DexAttr.DEFAULT_VARIANT | DexAttr.DEFAULT_FORM; + const defaultStarterAttr = DexAttr.NON_SHINY | DexAttr.MALE | DexAttr.FEMALE | DexAttr.DEFAULT_VARIANT | DexAttr.DEFAULT_FORM; const defaultStarterNatures: Nature[] = []; @@ -1559,11 +1544,11 @@ export class GameData { } } - setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false): Promise { - return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg); + setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise { + return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage); } - setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false): Promise { + setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise { return new Promise(resolve => { const dexEntry = this.dexData[species.speciesId]; const caughtAttr = dexEntry.caughtAttr; @@ -1622,13 +1607,17 @@ export class GameData { const checkPrevolution = () => { if (hasPrevolution) { const prevolutionSpecies = pokemonPrevolutions[species.speciesId]; - return this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg).then(() => resolve()); + this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg, showMessage).then(() => resolve()); } else { resolve(); } }; if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) { + if (!showMessage) { + resolve(); + return; + } this.scene.playSound("level_up_fanfare"); this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true); } else { @@ -1674,7 +1663,7 @@ export class GameData { this.starterData[species.speciesId].candyCount += count; } - setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise { + setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer, showMessage: boolean = true): Promise { return new Promise(resolve => { const speciesId = species.speciesId; if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) { @@ -1694,11 +1683,15 @@ export class GameData { } this.starterData[speciesId].eggMoves |= value; - + if (!showMessage) { + resolve(true); + return; + } this.scene.playSound("level_up_fanfare"); - const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name; - this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, () => resolve(true), null, true); + this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, (() => { + resolve(true); + }), null, true); }); } @@ -1875,74 +1868,4 @@ export class GameData { } } } - - fixVariantData(systemData: SystemSaveData): void { - const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); - const starterData = systemData.starterData; - const dexData = systemData.dexData; - if (starterIds.find(id => (dexData[id].caughtAttr & DexAttr.VARIANT_2 || dexData[id].caughtAttr & DexAttr.VARIANT_3) && !variantData[id])) { - for (const s of starterIds) { - const species = getPokemonSpecies(s); - if (variantData[s]) { - const tempCaughtAttr = dexData[s].caughtAttr; - let seenVariant2 = false; - let seenVariant3 = false; - const checkEvoSpecies = (es: Species) => { - seenVariant2 ||= !!(dexData[es].seenAttr & DexAttr.VARIANT_2); - seenVariant3 ||= !!(dexData[es].seenAttr & DexAttr.VARIANT_3); - if (pokemonEvolutions.hasOwnProperty(es)) { - for (const pe of pokemonEvolutions[es]) { - checkEvoSpecies(pe.speciesId); - } - } - }; - checkEvoSpecies(s); - if (dexData[s].caughtAttr & DexAttr.VARIANT_2 && !seenVariant2) { - dexData[s].caughtAttr ^= DexAttr.VARIANT_2; - } - if (dexData[s].caughtAttr & DexAttr.VARIANT_3 && !seenVariant3) { - dexData[s].caughtAttr ^= DexAttr.VARIANT_3; - } - starterData[s].abilityAttr = (tempCaughtAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0) - | (tempCaughtAttr & DexAttr.VARIANT_2 && species.ability2 ? AbilityAttr.ABILITY_2 : 0) - | (tempCaughtAttr & DexAttr.VARIANT_3 && species.abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0); - } else { - const tempCaughtAttr = dexData[s].caughtAttr; - if (dexData[s].caughtAttr & DexAttr.VARIANT_2) { - dexData[s].caughtAttr ^= DexAttr.VARIANT_2; - } - if (dexData[s].caughtAttr & DexAttr.VARIANT_3) { - dexData[s].caughtAttr ^= DexAttr.VARIANT_3; - } - starterData[s].abilityAttr = (tempCaughtAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0) - | (tempCaughtAttr & DexAttr.VARIANT_2 && species.ability2 ? AbilityAttr.ABILITY_2 : 0) - | (tempCaughtAttr & DexAttr.VARIANT_3 && species.abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0); - } - } - } - } - - fixStarterData(systemData: SystemSaveData): void { - for (const starterId of defaultStarterSpecies) { - systemData.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; - } - } - - fixLegendaryStats(systemData: SystemSaveData): void { - systemData.gameStats.subLegendaryPokemonSeen = 0; - systemData.gameStats.subLegendaryPokemonCaught = 0; - systemData.gameStats.subLegendaryPokemonHatched = 0; - allSpecies.filter(s => s.subLegendary).forEach(s => { - const dexEntry = systemData.dexData[s.speciesId]; - systemData.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount; - systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0); - systemData.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount; - systemData.gameStats.legendaryPokemonCaught = Math.max(systemData.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0); - systemData.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount; - systemData.gameStats.legendaryPokemonHatched = Math.max(systemData.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0); - }); - systemData.gameStats.subLegendaryPokemonSeen = Math.max(systemData.gameStats.subLegendaryPokemonSeen, systemData.gameStats.subLegendaryPokemonCaught); - systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen, systemData.gameStats.legendaryPokemonCaught); - systemData.gameStats.mythicalPokemonSeen = Math.max(systemData.gameStats.mythicalPokemonSeen, systemData.gameStats.mythicalPokemonCaught); - } } diff --git a/src/system/modifier-data.ts b/src/system/modifier-data.ts index 0f3e28fe11c..1514f7e3fb3 100644 --- a/src/system/modifier-data.ts +++ b/src/system/modifier-data.ts @@ -3,11 +3,11 @@ import { PersistentModifier } from "../modifier/modifier"; import { GeneratedPersistentModifierType, ModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type"; export default class ModifierData { - private player: boolean; - private typeId: string; - private typePregenArgs: any[]; - private args: any[]; - private stackCount: integer; + public player: boolean; + public typeId: string; + public typePregenArgs: any[]; + public args: any[]; + public stackCount: integer; public className: string; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 8f094379434..1fafcbf8acc 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -42,6 +42,7 @@ export default class PokemonData { public luck: integer; public pauseEvolutions: boolean; public pokerus: boolean; + public usedTMs: Moves[]; public fusionSpecies: Species; public fusionFormIndex: integer; @@ -98,6 +99,7 @@ export default class PokemonData { this.fusionVariant = source.fusionVariant; this.fusionGender = source.fusionGender; this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0); + this.usedTMs = source.usedTMs ?? []; if (!forHistory) { this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss); @@ -122,10 +124,9 @@ export default class PokemonData { this.summonData = new PokemonSummonData(); if (!forHistory && source.summonData) { - this.summonData.battleStats = source.summonData.battleStats; + this.summonData.stats = source.summonData.stats; + this.summonData.statStages = source.summonData.statStages; this.summonData.moveQueue = source.summonData.moveQueue; - this.summonData.disabledMove = source.summonData.disabledMove; - this.summonData.disabledTurns = source.summonData.disabledTurns; this.summonData.abilitySuppressed = source.summonData.abilitySuppressed; this.summonData.abilitiesApplied = source.summonData.abilitiesApplied; diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index 7b0fea95a98..66021845c29 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -8,7 +8,7 @@ import SettingsUiHandler from "#app/ui/settings/settings-ui-handler"; import { EaseType } from "#enums/ease-type"; import { MoneyFormat } from "#enums/money-format"; import { PlayerGender } from "#enums/player-gender"; -import { getIsInitialized, initI18n } from "#app/plugins/i18n.js"; +import { getIsInitialized, initI18n } from "#app/plugins/i18n"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; function getTranslation(key: string): string { @@ -25,6 +25,7 @@ const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => i value: "Mute", label: getTranslation("settings:mute") }); + const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => { const value = ((i + 1) * 10).toString(); return { @@ -32,6 +33,7 @@ const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).ma label: value, }; }); + const OFF_ON: SettingOption[] = [ { value: "Off", @@ -53,6 +55,40 @@ const AUTO_DISABLED: SettingOption[] = [ } ]; +const SHOP_CURSOR_TARGET_OPTIONS: SettingOption[] = [ + { + value: "Rewards", + label: i18next.t("settings:rewards") + }, + { + value: "Shop", + label: i18next.t("settings:shop") + }, + { + value: "Reroll", + label: i18next.t("settings:reroll") + }, + { + value: "Check Team", + label: i18next.t("settings:checkTeam") + } +]; + +const shopCursorTargetIndexMap = SHOP_CURSOR_TARGET_OPTIONS.map(option => { + switch (option.value) { + case "Rewards": + return ShopCursorTarget.REWARDS; + case "Shop": + return ShopCursorTarget.SHOP; + case "Reroll": + return ShopCursorTarget.REROLL; + case "Check Team": + return ShopCursorTarget.CHECK_TEAM; + default: + throw new Error(`Unknown value: ${option.value}`); + } +}); + /** * Types for helping separate settings to different menus */ @@ -90,6 +126,7 @@ export const SettingKeys = { EXP_Gains_Speed: "EXP_GAINS_SPEED", EXP_Party_Display: "EXP_PARTY_DISPLAY", Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", + Egg_Skip: "EGG_SKIP", Battle_Style: "BATTLE_STYLE", Enable_Retries: "ENABLE_RETRIES", Hide_IVs: "HIDE_IVS", @@ -103,7 +140,7 @@ export const SettingKeys = { Damage_Numbers: "DAMAGE_NUMBERS", Move_Animations: "MOVE_ANIMATIONS", Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS", - Reroll_Target: "REROLL_TARGET", + Shop_Cursor_Target: "SHOP_CURSOR_TARGET", Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION", Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY", Move_Info: "MOVE_INFO", @@ -245,6 +282,26 @@ export const Setting: Array = [ default: 0, type: SettingType.GENERAL }, + { + key: SettingKeys.Egg_Skip, + label: i18next.t("settings:eggSkip"), + options: [ + { + value: "Never", + label: i18next.t("settings:never") + }, + { + value: "Ask", + label: i18next.t("settings:ask") + }, + { + value: "Always", + label: i18next.t("settings:always") + } + ], + default: 1, + type: SettingType.GENERAL + }, { key: SettingKeys.Battle_Style, label: i18next.t("settings:battleStyle"), @@ -596,27 +653,10 @@ export const Setting: Array = [ isHidden: () => !hasTouchscreen() }, { - key: SettingKeys.Reroll_Target, + key: SettingKeys.Shop_Cursor_Target, label: i18next.t("settings:shopCursorTarget"), - options: [ - { - value:"Reroll", - label: i18next.t("settings:reroll") - }, - { - value:"Items", - label: i18next.t("settings:items") - }, - { - value:"Shop", - label: i18next.t("settings:shop") - }, - { - value:"Check Team", - label: i18next.t("settings:checkTeam") - } - ], - default: ShopCursorTarget.CHECK_TEAM, + options: SHOP_CURSOR_TARGET_OPTIONS, + default: 0, type: SettingType.DISPLAY }, { @@ -708,6 +748,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Skip_Seen_Dialogues: scene.skipSeenDialogues = Setting[index].options[value].value === "On"; break; + case SettingKeys.Egg_Skip: + scene.eggSkipPreference = value; + break; case SettingKeys.Battle_Style: scene.battleStyle = value; break; @@ -758,8 +801,10 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Show_Stats_on_Level_Up: scene.showLevelUpStats = Setting[index].options[value].value === "On"; break; - case SettingKeys.Reroll_Target: - scene.shopCursorTarget = value; + case SettingKeys.Shop_Cursor_Target: + const selectedValue = shopCursorTargetIndexMap[value]; + scene.shopCursorTarget = selectedValue; + break; case SettingKeys.EXP_Gains_Speed: scene.expGainsSpeed = value; break; diff --git a/src/system/version-converter.ts b/src/system/version-converter.ts new file mode 100644 index 00000000000..c297782ba66 --- /dev/null +++ b/src/system/version-converter.ts @@ -0,0 +1,141 @@ +import { allSpecies } from "#app/data/pokemon-species"; +import { AbilityAttr, defaultStarterSpecies, DexAttr, SessionSaveData, SystemSaveData } from "./game-data"; +import { SettingKeys } from "./settings/settings"; + +const LATEST_VERSION = "1.0.5"; + +export function applySessionDataPatches(data: SessionSaveData) { + const curVersion = data.gameVersion; + if (curVersion !== LATEST_VERSION) { + switch (curVersion) { + case "1.0.0": + case "1.0.1": + case "1.0.2": + case "1.0.3": + case "1.0.4": + // --- PATCHES --- + + // Fix Battle Items, Vitamins, and Lures + data.modifiers.forEach((m) => { + if (m.className === "PokemonBaseStatModifier") { + m.className = "BaseStatModifier"; + } else if (m.className === "PokemonResetNegativeStatStageModifier") { + m.className = "ResetNegativeStatStageModifier"; + } else if (m.className === "TempBattleStatBoosterModifier") { + m.className = "TempStatStageBoosterModifier"; + m.typeId = "TEMP_STAT_STAGE_BOOSTER"; + + // Migration from TempBattleStat to Stat + const newStat = m.typePregenArgs[0] + 1; + m.typePregenArgs[0] = newStat; + + // From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ] + m.args = [ newStat, 5, m.args[1] ]; + } else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) { + let maxBattles: number; + switch (m.typeId) { + case "MAX_LURE": + maxBattles = 30; + break; + case "SUPER_LURE": + maxBattles = 15; + break; + default: + maxBattles = 10; + break; + } + + // From [ battlesLeft ] to [ maxBattles, battleCount ] + m.args = [ maxBattles, m.args[0] ]; + } + }); + + data.enemyModifiers.forEach((m) => { + if (m.className === "PokemonBaseStatModifier") { + m.className = "BaseStatModifier"; + } else if (m.className === "PokemonResetNegativeStatStageModifier") { + m.className = "ResetNegativeStatStageModifier"; + } + }); + } + + data.gameVersion = LATEST_VERSION; + } +} + +export function applySystemDataPatches(data: SystemSaveData) { + const curVersion = data.gameVersion; + if (curVersion !== LATEST_VERSION) { + switch (curVersion) { + case "1.0.0": + case "1.0.1": + case "1.0.2": + case "1.0.3": + case "1.0.4": + // --- LEGACY PATCHES --- + if (data.starterData) { + // Migrate ability starter data if empty for caught species + Object.keys(data.starterData).forEach(sd => { + if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !data.starterData[sd].abilityAttr)) { + data.starterData[sd].abilityAttr = 1; + } + }); + } + + // Fix Legendary Stats + if (data.gameStats && (data.gameStats.legendaryPokemonCaught !== undefined && data.gameStats.subLegendaryPokemonCaught === undefined)) { + data.gameStats.subLegendaryPokemonSeen = 0; + data.gameStats.subLegendaryPokemonCaught = 0; + data.gameStats.subLegendaryPokemonHatched = 0; + allSpecies.filter(s => s.subLegendary).forEach(s => { + const dexEntry = data.dexData[s.speciesId]; + data.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount; + data.gameStats.legendaryPokemonSeen = Math.max(data.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0); + data.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount; + data.gameStats.legendaryPokemonCaught = Math.max(data.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0); + data.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount; + data.gameStats.legendaryPokemonHatched = Math.max(data.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0); + }); + data.gameStats.subLegendaryPokemonSeen = Math.max(data.gameStats.subLegendaryPokemonSeen, data.gameStats.subLegendaryPokemonCaught); + data.gameStats.legendaryPokemonSeen = Math.max(data.gameStats.legendaryPokemonSeen, data.gameStats.legendaryPokemonCaught); + data.gameStats.mythicalPokemonSeen = Math.max(data.gameStats.mythicalPokemonSeen, data.gameStats.mythicalPokemonCaught); + } + + // --- PATCHES --- + + // Fix Starter Data + for (const starterId of defaultStarterSpecies) { + if (data.starterData[starterId]?.abilityAttr) { + data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; + } + if (data.dexData[starterId]?.caughtAttr) { + data.dexData[starterId].caughtAttr |= DexAttr.FEMALE; + } + } + } + + data.gameVersion = LATEST_VERSION; + } +} + +export function applySettingsDataPatches(settings: Object) { + const curVersion = settings.hasOwnProperty("gameVersion") ? settings["gameVersion"] : "1.0.0"; + if (curVersion !== LATEST_VERSION) { + switch (curVersion) { + case "1.0.0": + case "1.0.1": + case "1.0.2": + case "1.0.3": + case "1.0.4": + // --- PATCHES --- + + // Fix Reward Cursor Target + if (settings.hasOwnProperty("REROLL_TARGET") && !settings.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) { + settings[SettingKeys.Shop_Cursor_Target] = settings["REROLL_TARGET"]; + delete settings["REROLL_TARGET"]; + localStorage.setItem("settings", JSON.stringify(settings)); + } + } + // Note that the current game version will be written at `saveSettings` + } +} diff --git a/src/system/voucher.ts b/src/system/voucher.ts index 2f94308d9c8..06edfe5c6a6 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -3,8 +3,8 @@ import i18next from "i18next"; import { AchvTier, achvs, getAchievementDescription } from "./achv"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; -import { ConditionFn } from "#app/@types/common.js"; -import { trainerConfigs } from "#app/data/trainer-config.js"; +import { ConditionFn } from "#app/@types/common"; +import { trainerConfigs } from "#app/data/trainer-config"; export enum VoucherType { REGULAR, diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 3238f880992..fb3d3af1a6b 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,11 @@ +import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; import i18next, { initI18n } from "#app/plugins/i18n"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -28,19 +26,18 @@ describe("Ability Timing", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.PIDGEY); - game.override.enemyAbility(Abilities.INTIMIDATE); - game.override.enemyMoveset(SPLASH_ONLY); - - game.override.ability(Abilities.BALL_FETCH); - game.override.moveset([Moves.SPLASH, Moves.ICE_BEAM]); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.INTIMIDATE) + .ability(Abilities.BALL_FETCH); }); - it("should trigger after switch check", async() => { + it("should trigger after switch check", async () => { initI18n(); i18next.changeLanguage("en"); + game.settings.battleStyle = BattleStyle.SWITCH; await game.classicMode.runToSummon([Species.EEVEE, Species.FEEBAS]); game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { @@ -48,7 +45,7 @@ describe("Ability Timing", () => { game.endPhase(); }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); - await game.phaseInterceptor.to(MessagePhase); + await game.phaseInterceptor.to("MessagePhase"); const message = game.textInterceptor.getLatestMessage(); expect(message).toContain("Attack fell"); }, 5000); diff --git a/src/test/abilities/aura_break.test.ts b/src/test/abilities/aura_break.test.ts index 7de300c157a..422ac5178c1 100644 --- a/src/test/abilities/aura_break.test.ts +++ b/src/test/abilities/aura_break.test.ts @@ -1,10 +1,8 @@ import { allMoves } from "#app/data/move"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -28,36 +26,50 @@ describe("Abilities - Aura Break", () => { game = new GameManager(phaserGame); game.override.battleType("single"); game.override.moveset([Moves.MOONBLAST, Moves.DARK_PULSE, Moves.MOONBLAST, Moves.DARK_PULSE]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.AURA_BREAK); game.override.enemySpecies(Species.SHUCKLE); }); - it("reverses the effect of fairy aura", async () => { + it("reverses the effect of Fairy Aura", async () => { const moveToCheck = allMoves[Moves.MOONBLAST]; const basePower = moveToCheck.power; game.override.ability(Abilities.FAIRY_AURA); vi.spyOn(moveToCheck, "calculateBattlePower"); - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); game.move.select(Moves.MOONBLAST); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); }); - it("reverses the effect of dark aura", async () => { + it("reverses the effect of Dark Aura", async () => { const moveToCheck = allMoves[Moves.DARK_PULSE]; const basePower = moveToCheck.power; game.override.ability(Abilities.DARK_AURA); vi.spyOn(moveToCheck, "calculateBattlePower"); - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); game.move.select(Moves.DARK_PULSE); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); }); + + it("has no effect if neither Fairy Aura nor Dark Aura are present", async () => { + const moveToCheck = allMoves[Moves.MOONBLAST]; + const basePower = moveToCheck.power; + + game.override.ability(Abilities.BALL_FETCH); + vi.spyOn(moveToCheck, "calculateBattlePower"); + + await game.classicMode.startBattle([Species.PIKACHU]); + game.move.select(Moves.MOONBLAST); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); + }); }); diff --git a/src/test/abilities/battery.test.ts b/src/test/abilities/battery.test.ts index 020866509d6..cd02ed0c4eb 100644 --- a/src/test/abilities/battery.test.ts +++ b/src/test/abilities/battery.test.ts @@ -5,7 +5,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -31,7 +30,7 @@ describe("Abilities - Battery", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); it("raises the power of allies' special moves by 30%", async () => { diff --git a/src/test/abilities/beast_boost.test.ts b/src/test/abilities/beast_boost.test.ts new file mode 100644 index 00000000000..26bae7b8838 --- /dev/null +++ b/src/test/abilities/beast_boost.test.ts @@ -0,0 +1,88 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Abilities - Beast Boost", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.BULBASAUR) + .enemyAbility(Abilities.BEAST_BOOST) + .ability(Abilities.BEAST_BOOST) + .startingLevel(2000) + .moveset([ Moves.FLAMETHROWER ]) + .enemyMoveset(Moves.SPLASH); + }); + + it("should prefer highest stat to boost its corresponding stat stage by 1 when winning a battle", async() => { + await game.classicMode.startBattle([Species.SLOWBRO]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + // Set the pokemon's highest stat to DEF, so it should be picked by Beast Boost + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([ 10000, 100, 1000, 200, 100, 100 ]); + console.log(playerPokemon.stats); + + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0); + + game.move.select(Moves.FLAMETHROWER); + await game.phaseInterceptor.to("VictoryPhase"); + + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(1); + }, 20000); + + it("should use in-battle overriden stats when determining the stat stage to raise by 1", async() => { + game.override.enemyMoveset([Moves.GUARD_SPLIT]); + + await game.classicMode.startBattle([Species.SLOWBRO]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + // If the opponent uses Guard Split, the pokemon's second highest stat (SPATK) should be chosen + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([ 10000, 100, 201, 200, 100, 100 ]); + + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); + + game.move.select(Moves.FLAMETHROWER); + + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.phaseInterceptor.to("VictoryPhase"); + + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); + }, 20000); + + it("should have order preference in case of stat ties", async() => { + // Order preference follows the order of EFFECTIVE_STAT + await game.classicMode.startBattle([Species.SLOWBRO]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + // Set up tie between SPATK, SPDEF, and SPD, where SPATK should win + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([ 10000, 1, 1, 100, 100, 100 ]); + + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); + + game.move.select(Moves.FLAMETHROWER); + + await game.phaseInterceptor.to("VictoryPhase"); + + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); + }, 20000); +}); diff --git a/src/test/abilities/contrary.test.ts b/src/test/abilities/contrary.test.ts new file mode 100644 index 00000000000..95a209395dc --- /dev/null +++ b/src/test/abilities/contrary.test.ts @@ -0,0 +1,42 @@ +import { Moves } from "#app/enums/moves"; +import { Abilities } from "#enums/abilities"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Contrary", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.BULBASAUR) + .enemyAbility(Abilities.CONTRARY) + .ability(Abilities.INTIMIDATE) + .enemyMoveset(Moves.SPLASH); + }); + + it("should invert stat changes when applied", async() => { + await game.startBattle([ + Species.SLOWBRO + ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); + }, 20000); +}); diff --git a/src/test/abilities/costar.test.ts b/src/test/abilities/costar.test.ts index 9a4baeef1fb..794bed0d3cf 100644 --- a/src/test/abilities/costar.test.ts +++ b/src/test/abilities/costar.test.ts @@ -1,11 +1,10 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { CommandPhase } from "#app/phases/command-phase"; import { MessagePhase } from "#app/phases/message-phase"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; @@ -30,12 +29,12 @@ describe("Abilities - COSTAR", () => { game.override.battleType("double"); game.override.ability(Abilities.COSTAR); game.override.moveset([Moves.SPLASH, Moves.NASTY_PLOT]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); test( - "ability copies positive stat changes", + "ability copies positive stat stages", async () => { game.override.enemyAbility(Abilities.BALL_FETCH); @@ -48,8 +47,8 @@ describe("Abilities - COSTAR", () => { game.move.select(Moves.SPLASH, 1); await game.toNextTurn(); - expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); - expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0); + expect(leftPokemon.getStatStage(Stat.SPATK)).toBe(2); + expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(0); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); @@ -57,14 +56,14 @@ describe("Abilities - COSTAR", () => { await game.phaseInterceptor.to(MessagePhase); [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); - expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); + expect(leftPokemon.getStatStage(Stat.SPATK)).toBe(2); + expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(2); }, TIMEOUT, ); test( - "ability copies negative stat changes", + "ability copies negative stat stages", async () => { game.override.enemyAbility(Abilities.INTIMIDATE); @@ -72,8 +71,8 @@ describe("Abilities - COSTAR", () => { let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); - expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); + expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2); + expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); @@ -81,8 +80,8 @@ describe("Abilities - COSTAR", () => { await game.phaseInterceptor.to(MessagePhase); [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); - expect(rightPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); + expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2); + expect(rightPokemon.getStatStage(Stat.ATK)).toBe(-2); }, TIMEOUT, ); diff --git a/src/test/abilities/dancer.test.ts b/src/test/abilities/dancer.test.ts new file mode 100644 index 00000000000..ec5ce53f4c3 --- /dev/null +++ b/src/test/abilities/dancer.test.ts @@ -0,0 +1,64 @@ +import { BattlerIndex } from "#app/battle"; +import { MovePhase } from "#app/phases/move-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Abilities - Dancer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("double") + .moveset([Moves.SWORDS_DANCE, Moves.SPLASH]) + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.DANCER) + .enemyMoveset([Moves.VICTORY_DANCE]); + }); + + // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability) + + it("triggers when dance moves are used, doesn't consume extra PP", async () => { + await game.classicMode.startBattle([Species.ORICORIO, Species.FEEBAS]); + + const [oricorio] = game.scene.getPlayerField(); + + game.move.select(Moves.SPLASH); + game.move.select(Moves.SWORDS_DANCE, 1); + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("MovePhase"); + // immediately copies ally move + await game.phaseInterceptor.to("MovePhase", false); + let currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(oricorio); + expect(currentPhase.move.moveId).toBe(Moves.SWORDS_DANCE); + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to("MovePhase"); + // immediately copies enemy move + await game.phaseInterceptor.to("MovePhase", false); + currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(oricorio); + expect(currentPhase.move.moveId).toBe(Moves.VICTORY_DANCE); + await game.phaseInterceptor.to("BerryPhase"); + + // doesn't use PP if copied move is also in moveset + expect(oricorio.moveset[0]?.ppUsed).toBe(0); + }, TIMEOUT); +}); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 85141fdb491..fa7f26d2716 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,17 +1,11 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { StatusEffect } from "#app/data/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { Mode } from "#app/ui/ui"; import { toDmgValue } from "#app/utils"; +import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { StatusEffect } from "#app/data/status-effect"; +import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -33,17 +27,16 @@ describe("Abilities - Disguise", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - - game.override.enemySpecies(Species.MIMIKYU); - game.override.enemyMoveset(SPLASH_ONLY); - - game.override.starterSpecies(Species.REGIELEKI); - game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); + game.override + .battleType("single") + .enemySpecies(Species.MIMIKYU) + .enemyMoveset(Moves.SPLASH) + .starterSpecies(Species.REGIELEKI) + .moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); }, TIMEOUT); it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); @@ -53,14 +46,14 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(mimikyu.hp).equals(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); it("doesn't break disguise when attacked with ineffective move", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; @@ -68,15 +61,15 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.VACUUM_WAVE); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(mimikyu.formIndex).toBe(disguisedForm); }, TIMEOUT); it("takes no damage from the first hit of a multihit move and transforms to Busted form, then takes damage from the second hit", async () => { - game.override.moveset([Moves.SURGING_STRIKES]); + game.override.moveset([ Moves.SURGING_STRIKES ]); game.override.enemyLevel(5); - await game.startBattle(); + await game.classicMode.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); @@ -87,37 +80,37 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SURGING_STRIKES); // First hit - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(mimikyu.hp).equals(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(disguisedForm); // Second hit - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(mimikyu.hp).lessThan(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); it("takes effects from status moves and damage from status effects", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; expect(mimikyu.hp).toBe(mimikyu.getMaxHp()); game.move.select(Moves.TOXIC_THREAD); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(disguisedForm); expect(mimikyu.status?.effect).toBe(StatusEffect.POISON); - expect(mimikyu.summonData.battleStats[BattleStat.SPD]).toBe(-1); + expect(mimikyu.getStatStage(Stat.SPD)).toBe(-1); expect(mimikyu.hp).toBeLessThan(mimikyu.getMaxHp()); }, TIMEOUT); it("persists form change when switched out", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); + game.override.enemyMoveset([Moves.SHADOW_SNEAK]); game.override.starterSpecies(0); - await game.startBattle([Species.MIMIKYU, Species.FURRET]); + await game.classicMode.startBattle([ Species.MIMIKYU, Species.FURRET ]); const mimikyu = game.scene.getPlayerPokemon()!; const maxHp = mimikyu.getMaxHp(); @@ -125,7 +118,7 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.hp).equals(maxHp - disguiseDamage); @@ -133,7 +126,7 @@ describe("Abilities - Disguise", () => { await game.toNextTurn(); game.doSwitchPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); @@ -143,7 +136,7 @@ describe("Abilities - Disguise", () => { game.override.starterForms({ [Species.MIMIKYU]: bustedForm }); - await game.startBattle([Species.FURRET, Species.MIMIKYU]); + await game.classicMode.startBattle([ Species.FURRET, Species.MIMIKYU ]); const mimikyu = game.scene.getParty()[1]!; expect(mimikyu.formIndex).toBe(bustedForm); @@ -162,7 +155,7 @@ describe("Abilities - Disguise", () => { [Species.MIMIKYU]: bustedForm }); - await game.startBattle(); + await game.classicMode.startBattle(); const mimikyu = game.scene.getPlayerPokemon()!; @@ -182,7 +175,7 @@ describe("Abilities - Disguise", () => { [Species.MIMIKYU]: bustedForm }); - await game.startBattle([Species.MIMIKYU, Species.FURRET]); + await game.classicMode.startBattle([ Species.MIMIKYU, Species.FURRET ]); const mimikyu1 = game.scene.getPlayerPokemon()!; @@ -194,23 +187,14 @@ describe("Abilities - Disguise", () => { await game.toNextTurn(); game.move.select(Moves.SPLASH); await game.doKillOpponents(); - game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { // TODO: Make tests run in set mode instead of switch mode - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); - - game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); await game.phaseInterceptor.to("PartyHealPhase"); expect(mimikyu1.formIndex).toBe(disguisedForm); }, TIMEOUT); it("doesn't faint twice when fainting due to Disguise break damage, nor prevent faint from Disguise break damage if using Endure", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.ENDURE)); - await game.startBattle(); + game.override.enemyMoveset([Moves.ENDURE]); + await game.classicMode.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; mimikyu.hp = 1; @@ -221,4 +205,22 @@ describe("Abilities - Disguise", () => { expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); }, TIMEOUT); + + it("activates when Aerilate circumvents immunity to the move's base type", async () => { + game.override.ability(Abilities.AERILATE); + game.override.moveset([Moves.TACKLE]); + + await game.classicMode.startBattle(); + + const mimikyu = game.scene.getEnemyPokemon()!; + const maxHp = mimikyu.getMaxHp(); + const disguiseDamage = toDmgValue(maxHp / 8); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(mimikyu.formIndex).toBe(bustedForm); + expect(mimikyu.hp).toBe(maxHp - disguiseDamage); + }, TIMEOUT); }); diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index b337e4d96f7..1af8831f25b 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -1,9 +1,7 @@ import { Species } from "#app/enums/species"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -23,63 +21,56 @@ describe("Abilities - Dry Skin", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.disableCrits(); - game.override.enemyAbility(Abilities.DRY_SKIN); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.enemySpecies(Species.CHARMANDER); - game.override.ability(Abilities.UNNERVE); - game.override.starterSpecies(Species.CHANDELURE); + game.override + .battleType("single") + .disableCrits() + .enemyAbility(Abilities.DRY_SKIN) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.CHARMANDER) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.SUNNY_DAY, Moves.RAIN_DANCE, Moves.SPLASH, Moves.WATER_GUN]) + .starterSpecies(Species.CHANDELURE); }); it("during sunlight, lose 1/8 of maximum health at the end of each turn", async () => { - game.override.moveset([Moves.SUNNY_DAY, Moves.SPLASH]); - - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - expect(enemy).not.toBe(undefined); // first turn - let previousEnemyHp = enemy.hp; game.move.select(Moves.SUNNY_DAY); - await game.phaseInterceptor.to(TurnEndPhase); - expect(enemy.hp).toBeLessThan(previousEnemyHp); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); // second turn - previousEnemyHp = enemy.hp; + enemy.hp = enemy.getMaxHp(); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - expect(enemy.hp).toBeLessThan(previousEnemyHp); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }); it("during rain, gain 1/8 of maximum health at the end of each turn", async () => { - game.override.moveset([Moves.RAIN_DANCE, Moves.SPLASH]); - - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - expect(enemy).not.toBe(undefined); enemy.hp = 1; // first turn - let previousEnemyHp = enemy.hp; game.move.select(Moves.RAIN_DANCE); - await game.phaseInterceptor.to(TurnEndPhase); - expect(enemy.hp).toBeGreaterThan(previousEnemyHp); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.hp).toBeGreaterThan(1); // second turn - previousEnemyHp = enemy.hp; + enemy.hp = 1; game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); - expect(enemy.hp).toBeGreaterThan(previousEnemyHp); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.hp).toBeGreaterThan(1); }); it("opposing fire attacks do 25% more damage", async () => { game.override.moveset([Moves.FLAMETHROWER]); - - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; const initialHP = 1000; @@ -87,72 +78,65 @@ describe("Abilities - Dry Skin", () => { // first turn game.move.select(Moves.FLAMETHROWER); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const fireDamageTakenWithDrySkin = initialHP - enemy.hp; - expect(enemy.hp > 0); enemy.hp = initialHP; game.override.enemyAbility(Abilities.NONE); // second turn game.move.select(Moves.FLAMETHROWER); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const fireDamageTakenWithoutDrySkin = initialHP - enemy.hp; expect(fireDamageTakenWithDrySkin).toBeGreaterThan(fireDamageTakenWithoutDrySkin); }); it("opposing water attacks heal 1/4 of maximum health and deal no damage", async () => { - game.override.moveset([Moves.WATER_GUN]); - - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - expect(enemy).not.toBe(undefined); enemy.hp = 1; game.move.select(Moves.WATER_GUN); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.hp).toBeGreaterThan(1); }); it("opposing water attacks do not heal if they were protected from", async () => { - game.override.moveset([Moves.WATER_GUN]); + game.override.enemyMoveset([Moves.PROTECT]); - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - expect(enemy).not.toBe(undefined); enemy.hp = 1; - game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]); game.move.select(Moves.WATER_GUN); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.hp).toBe(1); }); it("multi-strike water attacks only heal once", async () => { game.override.moveset([Moves.WATER_GUN, Moves.WATER_SHURIKEN]); - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - expect(enemy).not.toBe(undefined); enemy.hp = 1; // first turn game.move.select(Moves.WATER_SHURIKEN); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const healthGainedFromWaterShuriken = enemy.hp - 1; enemy.hp = 1; // second turn game.move.select(Moves.WATER_GUN); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const healthGainedFromWaterGun = enemy.hp - 1; expect(healthGainedFromWaterShuriken).toBe(healthGainedFromWaterGun); diff --git a/src/test/abilities/flash_fire.test.ts b/src/test/abilities/flash_fire.test.ts index de40873998f..c3cf31496ea 100644 --- a/src/test/abilities/flash_fire.test.ts +++ b/src/test/abilities/flash_fire.test.ts @@ -7,7 +7,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -38,7 +37,7 @@ describe("Abilities - Flash Fire", () => { it("immune to Fire-type moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); + game.override.enemyMoveset([Moves.EMBER]).moveset(Moves.SPLASH); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; @@ -49,7 +48,7 @@ describe("Abilities - Flash Fire", () => { }, 20000); it("not activate if the Pokémon is protected from the Fire-type move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.PROTECT]); + game.override.enemyMoveset([Moves.EMBER]).moveset([Moves.PROTECT]); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; @@ -60,7 +59,7 @@ describe("Abilities - Flash Fire", () => { }, 20000); it("activated by Will-O-Wisp", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.WILL_O_WISP)).moveset(SPLASH_ONLY); + game.override.enemyMoveset([Moves.WILL_O_WISP]).moveset(Moves.SPLASH); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; @@ -75,7 +74,7 @@ describe("Abilities - Flash Fire", () => { }, 20000); it("activated after being frozen", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); + game.override.enemyMoveset([Moves.EMBER]).moveset(Moves.SPLASH); game.override.statusEffect(StatusEffect.FREEZE); await game.startBattle([Species.BLISSEY]); @@ -88,7 +87,7 @@ describe("Abilities - Flash Fire", () => { }, 20000); it("not passing with baton pass", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.BATON_PASS]); + game.override.enemyMoveset([Moves.EMBER]).moveset([Moves.BATON_PASS]); await game.startBattle([Species.BLISSEY, Species.CHANSEY]); // ensure use baton pass after enemy moved @@ -104,7 +103,7 @@ describe("Abilities - Flash Fire", () => { }, 20000); it("boosts Fire-type move when the ability is activated", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.FIRE_PLEDGE)).moveset([Moves.EMBER, Moves.SPLASH]); + game.override.enemyMoveset([Moves.FIRE_PLEDGE]).moveset([Moves.EMBER, Moves.SPLASH]); game.override.enemyAbility(Abilities.FLASH_FIRE).ability(Abilities.NONE); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; diff --git a/src/test/abilities/flower_gift.test.ts b/src/test/abilities/flower_gift.test.ts new file mode 100644 index 00000000000..256b61c6fea --- /dev/null +++ b/src/test/abilities/flower_gift.test.ts @@ -0,0 +1,153 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { WeatherType } from "#app/enums/weather-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Flower Gift", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const OVERCAST_FORM = 0; + const SUNSHINE_FORM = 1; + + /** + * Tests reverting to normal form when Cloud Nine/Air Lock is active on the field + * @param {GameManager} game The game manager instance + * @param {Abilities} ability The ability that is active on the field + */ + const testRevertFormAgainstAbility = async (game: GameManager, ability: Abilities) => { + game.override.starterForms({ [Species.CASTFORM]: SUNSHINE_FORM }).enemyAbility(ability); + await game.classicMode.startBattle([Species.CASTFORM]); + + game.move.select(Moves.SPLASH); + + expect(game.scene.getPlayerPokemon()?.formIndex).toBe(OVERCAST_FORM); + }; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.SKILL_SWAP]) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.SPLASH) + .enemyAbility(Abilities.BALL_FETCH); + }); + + // TODO: Uncomment expect statements when the ability is implemented - currently does not increase stats of allies + it("increases the ATK and SPDEF stat stages of the Pokémon with this Ability and its allies by 1.5× during Harsh Sunlight", async () => { + game.override.battleType("double"); + await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]); + + const [ cherrim ] = game.scene.getPlayerField(); + const cherrimAtkStat = cherrim.getEffectiveStat(Stat.ATK); + const cherrimSpDefStat = cherrim.getEffectiveStat(Stat.SPDEF); + + // const magikarpAtkStat = magikarp.getEffectiveStat(Stat.ATK);; + // const magikarpSpDefStat = magikarp.getEffectiveStat(Stat.SPDEF); + + game.move.select(Moves.SUNNY_DAY, 0); + game.move.select(Moves.SPLASH, 1); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + expect(cherrim.getEffectiveStat(Stat.ATK)).toBe(Math.floor(cherrimAtkStat * 1.5)); + expect(cherrim.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(cherrimSpDefStat * 1.5)); + // expect(magikarp.getEffectiveStat(Stat.ATK)).toBe(Math.floor(magikarpAtkStat * 1.5)); + // expect(magikarp.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(magikarpSpDefStat * 1.5)); + }); + + it("changes the Pokemon's form during Harsh Sunlight", async () => { + game.override.weather(WeatherType.HARSH_SUN); + await game.classicMode.startBattle([Species.CHERRIM]); + + const cherrim = game.scene.getPlayerPokemon()!; + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.move.select(Moves.SPLASH); + }); + + it("reverts to Overcast Form if a Pokémon on the field has Air Lock", async () => { + await testRevertFormAgainstAbility(game, Abilities.AIR_LOCK); + }); + + it("reverts to Overcast Form if a Pokémon on the field has Cloud Nine", async () => { + await testRevertFormAgainstAbility(game, Abilities.CLOUD_NINE); + }); + + it("reverts to Overcast Form when the Pokémon loses Flower Gift, changes form under Harsh Sunlight/Sunny when it regains it", async () => { + game.override.enemyMoveset([Moves.SKILL_SWAP]).weather(WeatherType.HARSH_SUN); + + await game.classicMode.startBattle([Species.CHERRIM]); + + const cherrim = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.SKILL_SWAP); + + await game.phaseInterceptor.to("TurnStartPhase"); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + }); + + it("reverts to Overcast Form when the Flower Gift is suppressed, changes form under Harsh Sunlight/Sunny when it regains it", async () => { + game.override.enemyMoveset([Moves.GASTRO_ACID]).weather(WeatherType.HARSH_SUN); + + await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]); + + const cherrim = game.scene.getPlayerPokemon()!; + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(cherrim.summonData.abilitySuppressed).toBe(true); + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("MovePhase"); + + expect(cherrim.summonData.abilitySuppressed).toBe(false); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + }); + + it("should be in Overcast Form after the user is switched out", async () => { + game.override.weather(WeatherType.SUNNY); + + await game.classicMode.startBattle([Species.CASTFORM, Species.MAGIKARP]); + const cherrim = game.scene.getPlayerPokemon()!; + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + }); +}); diff --git a/src/test/abilities/forecast.test.ts b/src/test/abilities/forecast.test.ts index 78453c5f4d2..c1eb3600b8b 100644 --- a/src/test/abilities/forecast.test.ts +++ b/src/test/abilities/forecast.test.ts @@ -10,7 +10,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -67,7 +66,7 @@ describe("Abilities - Forecast", () => { game.override .moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.TACKLE]) .enemySpecies(Species.MAGIKARP) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH); }); @@ -229,7 +228,7 @@ describe("Abilities - Forecast", () => { }); it("reverts to Normal Form when Forecast is suppressed, changes form to match the weather when it regains it", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)).weather(WeatherType.RAIN); + game.override.enemyMoveset([Moves.GASTRO_ACID]).weather(WeatherType.RAIN); await game.startBattle([Species.CASTFORM, Species.PIKACHU]); const castform = game.scene.getPlayerPokemon()!; @@ -260,7 +259,7 @@ describe("Abilities - Forecast", () => { }); it("does not change Castform's form until after Stealth Rock deals damage", async () => { - game.override.weather(WeatherType.RAIN).enemyMoveset(Array(4).fill(Moves.STEALTH_ROCK)); + game.override.weather(WeatherType.RAIN).enemyMoveset([Moves.STEALTH_ROCK]); await game.startBattle([Species.PIKACHU, Species.CASTFORM]); // First turn - set up stealth rock diff --git a/src/test/abilities/galvanize.test.ts b/src/test/abilities/galvanize.test.ts index 4b0ddc14d7c..f81b854180a 100644 --- a/src/test/abilities/galvanize.test.ts +++ b/src/test/abilities/galvanize.test.ts @@ -6,7 +6,6 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { HitResult } from "#app/field/pokemon"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -36,7 +35,7 @@ describe("Abilities - Galvanize", () => { .moveset([Moves.TACKLE, Moves.REVELATION_DANCE, Moves.FURY_SWIPES]) .enemySpecies(Species.DUSCLOPS) .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyLevel(100); }); diff --git a/src/test/abilities/gorilla_tactics.test.ts b/src/test/abilities/gorilla_tactics.test.ts new file mode 100644 index 00000000000..df698194323 --- /dev/null +++ b/src/test/abilities/gorilla_tactics.test.ts @@ -0,0 +1,83 @@ +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Gorilla Tactics", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([Moves.SPLASH, Moves.DISABLE]) + .enemySpecies(Species.MAGIKARP) + .enemyLevel(30) + .moveset([Moves.SPLASH, Moves.TACKLE, Moves.GROWL]) + .ability(Abilities.GORILLA_TACTICS); + }); + + it("boosts the Pokémon's Attack by 50%, but limits the Pokémon to using only one move", async () => { + await game.classicMode.startBattle([Species.GALAR_DARMANITAN]); + + const darmanitan = game.scene.getPlayerPokemon()!; + const initialAtkStat = darmanitan.getStat(Stat.ATK); + + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.SPLASH); + + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5); + // Other moves should be restricted + expect(darmanitan.isMoveRestricted(Moves.TACKLE)).toBe(true); + expect(darmanitan.isMoveRestricted(Moves.SPLASH)).toBe(false); + }, TIMEOUT); + + it("should struggle if the only usable move is disabled", async () => { + await game.classicMode.startBattle([Species.GALAR_DARMANITAN]); + + const darmanitan = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + // First turn, lock move to Growl + game.move.select(Moves.GROWL); + await game.forceEnemyMove(Moves.SPLASH); + + // Second turn, Growl is interrupted by Disable + await game.toNextTurn(); + + game.move.select(Moves.GROWL); + await game.forceEnemyMove(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.ATK)).toBe(-1); // Only the effect of the first Growl should be applied + + // Third turn, Struggle is used + await game.toNextTurn(); + + game.move.select(Moves.TACKLE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp()); + }, TIMEOUT); +}); diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index a451d290906..ac0efd8e8d1 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,4 +1,3 @@ -import { BattleStat } from "#app/data/battle-stat"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { StatusEffect } from "#app/enums/status-effect"; import Pokemon from "#app/field/pokemon"; @@ -12,7 +11,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { Stat } from "#enums/stat"; describe("Abilities - Gulp Missile", () => { let phaserGame: Phaser.Game; @@ -49,7 +48,7 @@ describe("Abilities - Gulp Missile", () => { .moveset([Moves.SURF, Moves.DIVE, Moves.SPLASH]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyLevel(5); }); @@ -107,8 +106,8 @@ describe("Abilities - Gulp Missile", () => { expect(cramorant.formIndex).toBe(GULPING_FORM); }); - it("deals ¼ of the attacker's maximum HP when hit by a damaging attack", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => { + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CRAMORANT]); const enemy = game.scene.getEnemyPokemon()!; @@ -121,7 +120,7 @@ describe("Abilities - Gulp Missile", () => { }); it("does not have any effect when hit by non-damaging attack", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TAIL_WHIP)); + game.override.enemyMoveset([Moves.TAIL_WHIP]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -139,8 +138,8 @@ describe("Abilities - Gulp Missile", () => { expect(cramorant.formIndex).toBe(GULPING_FORM); }); - it("lowers the attacker's Defense by 1 stage when hit in Gulping form", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => { + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -158,13 +157,13 @@ describe("Abilities - Gulp Missile", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); - expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1); + expect(enemy.getStatStage(Stat.DEF)).toBe(-1); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); expect(cramorant.formIndex).toBe(NORMAL_FORM); }); it("paralyzes the enemy when hit in Gorging form", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -188,7 +187,7 @@ describe("Abilities - Gulp Missile", () => { }); it("does not activate the ability when underwater", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SURF)); + game.override.enemyMoveset([Moves.SURF]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -201,7 +200,7 @@ describe("Abilities - Gulp Missile", () => { }); it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)).enemyAbility(Abilities.MAGIC_GUARD); + game.override.enemyMoveset([Moves.TACKLE]).enemyAbility(Abilities.MAGIC_GUARD); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -219,13 +218,13 @@ describe("Abilities - Gulp Missile", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBe(enemyHpPreEffect); - expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1); + expect(enemy.getStatStage(Stat.DEF)).toBe(-1); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); expect(cramorant.formIndex).toBe(NORMAL_FORM); }); it("cannot be suppressed", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)); + game.override.enemyMoveset([Moves.GASTRO_ACID]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; @@ -245,7 +244,7 @@ describe("Abilities - Gulp Missile", () => { }); it("cannot be swapped with another ability", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)); + game.override.enemyMoveset([Moves.SKILL_SWAP]); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; diff --git a/src/test/abilities/heatproof.test.ts b/src/test/abilities/heatproof.test.ts index e2a558e6d99..61c406201bd 100644 --- a/src/test/abilities/heatproof.test.ts +++ b/src/test/abilities/heatproof.test.ts @@ -5,7 +5,6 @@ import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -30,7 +29,7 @@ describe("Abilities - Heatproof", () => { .disableCrits() .enemySpecies(Species.CHARMANDER) .enemyAbility(Abilities.HEATPROOF) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyLevel(100) .starterSpecies(Species.CHANDELURE) .ability(Abilities.BALL_FETCH) diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index b7c3b723c4b..29cbfdc1a5d 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -1,12 +1,9 @@ import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; -import { DamagePhase } from "#app/phases/damage-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -26,54 +23,54 @@ describe("Abilities - Hustle", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.ability(Abilities.HUSTLE); - game.override.moveset([Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE]); - game.override.startingLevel(5); - game.override.disableCrits(); - game.override.enemyLevel(5); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.enemySpecies(Species.SHUCKLE); - game.override.enemyAbility(Abilities.BALL_FETCH); + game.override + .ability(Abilities.HUSTLE) + .moveset([ Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE ]) + .disableCrits() + .battleType("single") + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.BALL_FETCH); }); it("increases the user's Attack stat by 50%", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const atk = pikachu.stats[Stat.ATK]; - vi.spyOn(pikachu, "getBattleStat"); + vi.spyOn(pikachu, "getEffectiveStat"); game.move.select(Moves.TACKLE); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); - expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); + expect(pikachu.getEffectiveStat).toHaveReturnedWith(Math.floor(atk * 1.5)); }); it("lowers the accuracy of the user's physical moves by 20%", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(0.8); }); it("does not affect non-physical moves", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const spatk = pikachu.stats[Stat.SPATK]; - vi.spyOn(pikachu, "getBattleStat"); + vi.spyOn(pikachu, "getEffectiveStat"); vi.spyOn(pikachu, "getAccuracyMultiplier"); game.move.select(Moves.GIGA_DRAIN); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); - expect(pikachu.getBattleStat).toHaveReturnedWith(spatk); + expect(pikachu.getEffectiveStat).toHaveReturnedWith(spatk); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); }); @@ -81,7 +78,7 @@ describe("Abilities - Hustle", () => { game.override.startingLevel(100); game.override.enemyLevel(30); - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -89,7 +86,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); game.move.select(Moves.FISSURE); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp()); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); diff --git a/src/test/abilities/hyper_cutter.test.ts b/src/test/abilities/hyper_cutter.test.ts index 28fcc2f6085..ec947add939 100644 --- a/src/test/abilities/hyper_cutter.test.ts +++ b/src/test/abilities/hyper_cutter.test.ts @@ -1,9 +1,8 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -29,7 +28,7 @@ describe("Abilities - Hyper Cutter", () => { .ability(Abilities.BALL_FETCH) .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.HYPER_CUTTER) - .enemyMoveset(SPLASH_ONLY); + .enemyMoveset(Moves.SPLASH); }); // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Hyper_Cutter_(Ability) @@ -51,7 +50,7 @@ describe("Abilities - Hyper Cutter", () => { game.move.select(Moves.STRING_SHOT); await game.toNextTurn(); - expect(enemy.summonData.battleStats[BattleStat.ATK]).toEqual(0); - [BattleStat.ACC, BattleStat.DEF, BattleStat.EVA, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD].forEach((stat: number) => expect(enemy.summonData.battleStats[stat]).toBeLessThan(0)); + expect(enemy.getStatStage(Stat.ATK)).toEqual(0); + [Stat.ACC, Stat.DEF, Stat.EVA, Stat.SPATK, Stat.SPDEF, Stat.SPD].forEach((stat: number) => expect(enemy.getStatStage(stat)).toBeLessThan(0)); }); }); diff --git a/src/test/abilities/imposter.test.ts b/src/test/abilities/imposter.test.ts new file mode 100644 index 00000000000..27673564aaa --- /dev/null +++ b/src/test/abilities/imposter.test.ts @@ -0,0 +1,100 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; + +// TODO: Add more tests once Imposter is fully implemented +describe("Abilities - Imposter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.MEW) + .enemyLevel(200) + .enemyAbility(Abilities.BEAST_BOOST) + .enemyPassiveAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .ability(Abilities.IMPOSTER) + .moveset(Moves.SPLASH); + }); + + it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { + await game.startBattle([ + Species.DITTO + ]); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to(TurnEndPhase); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(player.getAbility()).toBe(enemy.getAbility()); + expect(player.getGender()).toBe(enemy.getGender()); + + expect(player.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); + for (const s of EFFECTIVE_STATS) { + expect(player.getStat(s, false)).toBe(enemy.getStat(s, false)); + } + + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(enemy.getStatStage(s)); + } + + const playerMoveset = player.getMoveset(); + const enemyMoveset = player.getMoveset(); + + for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { + // TODO: Checks for 5 PP should be done here when that gets addressed + expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId); + } + + const playerTypes = player.getTypes(); + const enemyTypes = enemy.getTypes(); + + for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) { + expect(playerTypes[i]).toBe(enemyTypes[i]); + } + }, 20000); + + it("should copy in-battle overridden stats", async () => { + game.override.enemyMoveset([Moves.POWER_SPLIT]); + + await game.startBattle([ + Species.DITTO + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); + + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); + + expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + }); +}); diff --git a/src/test/abilities/intimidate.test.ts b/src/test/abilities/intimidate.test.ts index 93b663d06da..d4c097022df 100644 --- a/src/test/abilities/intimidate.test.ts +++ b/src/test/abilities/intimidate.test.ts @@ -1,21 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { Status, StatusEffect } from "#app/data/status-effect"; -import { GameModes, getGameMode } from "#app/game-mode"; -import { CommandPhase } from "#app/phases/command-phase"; -import { DamagePhase } from "#app/phases/damage-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; +import { Stat } from "#enums/stat"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import { generateStarter } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Intimidate", () => { let phaserGame: Phaser.Game; @@ -33,16 +24,16 @@ describe("Abilities - Intimidate", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyAbility(Abilities.INTIMIDATE); - game.override.enemyPassiveAbility(Abilities.HYDRATION); - game.override.ability(Abilities.INTIMIDATE); - game.override.startingWave(3); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.battleType("single") + .enemySpecies(Species.RATTATA) + .enemyAbility(Abilities.INTIMIDATE) + .enemyPassiveAbility(Abilities.HYDRATION) + .ability(Abilities.INTIMIDATE) + .startingWave(3) + .enemyMoveset(Moves.SPLASH); }); - it("single - wild with switch", async () => { + it("should lower ATK stat stage by 1 of enemy Pokemon on entry and player switch", async () => { await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); game.onNextPrompt( "CheckSwitchPhase", @@ -51,28 +42,30 @@ describe("Abilities - Intimidate", () => { game.setMode(Mode.MESSAGE); game.endPhase(); }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) + () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase") ); - await game.phaseInterceptor.to(CommandPhase, false); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.MIGHTYENA); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + await game.phaseInterceptor.to("CommandPhase", false); + + let playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(playerPokemon.species.speciesId).toBe(Species.MIGHTYENA); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); + await game.phaseInterceptor.run("CommandPhase"); + await game.phaseInterceptor.to("CommandPhase"); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); - - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); + playerPokemon = game.scene.getPlayerPokemon()!; + expect(playerPokemon.species.speciesId).toBe(Species.POOCHYENA); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); }, 20000); - it("single - boss should only trigger once then switch", async () => { - game.override.startingWave(10); + it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => { + game.override.battleType("double") + .startingWave(3); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); game.onNextPrompt( "CheckSwitchPhase", @@ -81,262 +74,63 @@ describe("Abilities - Intimidate", () => { game.setMode(Mode.MESSAGE); game.endPhase(); }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) + () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase") ); - await game.phaseInterceptor.to(CommandPhase, false); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); + await game.phaseInterceptor.to("CommandPhase", false); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); + const playerField = game.scene.getPlayerField()!; + const enemyField = game.scene.getEnemyField()!; - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); + expect(enemyField[0].getStatStage(Stat.ATK)).toBe(-2); + expect(enemyField[1].getStatStage(Stat.ATK)).toBe(-2); + expect(playerField[0].getStatStage(Stat.ATK)).toBe(-2); + expect(playerField[1].getStatStage(Stat.ATK)).toBe(-2); }, 20000); - it("single - trainer should only trigger once with switch", async () => { - game.override.startingWave(5); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); - - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); - - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); - }, 200000); - - it("double - trainer should only trigger once per pokemon", async () => { - game.override.battleType("double"); - game.override.startingWave(5); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); - const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; - expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats; - expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2); - }, 20000); - - it("double - wild: should only trigger once per pokemon", async () => { - game.override.battleType("double"); - game.override.startingWave(3); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); - const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; - expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats; - expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2); - }, 20000); - - it("double - boss: should only trigger once per pokemon", async () => { - game.override.battleType("double"); - game.override.startingWave(10); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); - const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; - expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - - const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats; - expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2); - }, 20000); - - it("single - wild next wave opp triger once, us: none", async () => { - game.override.startingWave(2); - game.override.moveset([Moves.AERIAL_ACE]); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - - game.move.select(Moves.AERIAL_ACE); - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase); - await game.killPokemon(game.scene.currentBattle.enemyParty[0]); - expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); - await game.toNextWave(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - }, 20000); - - it("single - wild next turn - no retrigger on next turn", async () => { + it("should not activate again if there is no switch or new entry", async () => { game.override.startingWave(2); game.override.moveset([Moves.SPLASH]); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + await game.classicMode.startBattle([ Species.MIGHTYENA, Species.POOCHYENA ]); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + + game.move.select(Moves.SPLASH); await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); }, 20000); - it("single - trainer should only trigger once and each time he switch", async () => { - game.override.moveset([Moves.SPLASH]); - game.override.enemyMoveset([Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH]); - game.override.startingWave(5); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + it("should lower ATK stat stage by 1 for every switch", async () => { + game.override.moveset([Moves.SPLASH]) + .enemyMoveset([Moves.VOLT_SWITCH]) + .startingWave(5); + await game.classicMode.startBattle([ Species.MIGHTYENA, Species.POOCHYENA ]); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); - await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); + const playerPokemon = game.scene.getPlayerPokemon()!; + let enemyPokemon = game.scene.getEnemyPokemon()!; - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + + game.move.select(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-3); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); + + enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-2); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-3); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); }, 200000); - - it("single - trainer should only trigger once whatever turn we are", async () => { - game.override.moveset([Moves.SPLASH]); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.startingWave(5); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); - await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); - await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - }, 20000); - - it("double - wild vs only 1 on player side", async () => { - game.override.battleType("double"); - game.override.startingWave(3); - await game.classicMode.runToSummon([Species.MIGHTYENA]); - await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; - expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1); - - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - }, 20000); - - it("double - wild vs only 1 alive on player side", async () => { - game.override.battleType("double"); - game.override.startingWave(3); - await game.runToTitle(); - - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { - game.scene.gameMode = getGameMode(GameModes.CLASSIC); - const starters = generateStarter(game.scene, [Species.MIGHTYENA, Species.POOCHYENA]); - const selectStarterPhase = new SelectStarterPhase(game.scene); - game.scene.pushPhase(new EncounterPhase(game.scene, false)); - selectStarterPhase.initBattle(starters); - game.scene.getParty()[1].hp = 0; - game.scene.getParty()[1].status = new Status(StatusEffect.FAINT); - }); - - await game.phaseInterceptor.run(EncounterPhase); - - await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; - expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1); - - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); - }, 20000); }); diff --git a/src/test/abilities/intrepid_sword.test.ts b/src/test/abilities/intrepid_sword.test.ts index 18d6c04adbc..7bf0654276c 100644 --- a/src/test/abilities/intrepid_sword.test.ts +++ b/src/test/abilities/intrepid_sword.test.ts @@ -1,8 +1,8 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -29,14 +29,17 @@ describe("Abilities - Intrepid Sword", () => { game.override.ability(Abilities.INTREPID_SWORD); }); - it("INTREPID SWORD on player", async() => { + it("should raise ATK stat stage by 1 on entry", async() => { await game.classicMode.runToSummon([ Species.ZACIAN, ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + await game.phaseInterceptor.to(CommandPhase, false); - const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(1); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(1); + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }, 20000); }); diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index 7895e7de6bf..51f182d5401 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -9,7 +9,6 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; @@ -183,7 +182,7 @@ describe("Abilities - Libero", () => { "ability applies correctly even if the pokemon's move misses", async () => { game.override.moveset([Moves.TACKLE]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.MAGIKARP]); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index 64c1746c7d9..4b3fb0ba985 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -8,7 +8,6 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -39,7 +38,7 @@ describe("Abilities - Magic Guard", () => { /** Enemy Pokemon overrides */ game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.INSOMNIA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyLevel(100); }); diff --git a/src/test/abilities/moody.test.ts b/src/test/abilities/moody.test.ts index 9e936e8100a..166f69b0fe3 100644 --- a/src/test/abilities/moody.test.ts +++ b/src/test/abilities/moody.test.ts @@ -1,18 +1,15 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Moody", () => { let phaserGame: Phaser.Game; let game: GameManager; - const battleStatsArray = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -30,63 +27,61 @@ describe("Abilities - Moody", () => { .battleType("single") .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.BALL_FETCH) - .enemyPassiveAbility(Abilities.HYDRATION) .ability(Abilities.MOODY) - .enemyMoveset(SPLASH_ONLY) - .moveset(SPLASH_ONLY); + .enemyMoveset(Moves.SPLASH) + .moveset(Moves.SPLASH); }); - it( - "should increase one BattleStat by 2 stages and decrease a different BattleStat by 1 stage", + it("should increase one stat stage by 2 and decrease a different stat stage by 1", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.SPLASH); await game.toNextTurn(); // Find the increased and decreased stats, make sure they are different. - const statChanges = playerPokemon.summonData.battleStats; - const changedStats = battleStatsArray.filter(bs => statChanges[bs] === 2 || statChanges[bs] === -1); + const changedStats = EFFECTIVE_STATS.filter(s => playerPokemon.getStatStage(s) === 2 || playerPokemon.getStatStage(s) === -1); expect(changedStats).toBeTruthy(); expect(changedStats.length).toBe(2); expect(changedStats[0] !== changedStats[1]).toBeTruthy(); }); - it( - "should only increase one BattleStat by 2 stages if all BattleStats are at -6", + it("should only increase one stat stage by 2 if all stat stages are at -6", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; - // Set all BattleStats to -6 - battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = -6); + + // Set all stat stages to -6 + vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(-6)); game.move.select(Moves.SPLASH); await game.toNextTurn(); - // Should increase one BattleStat by 2 (from -6, meaning it will be -4) - const increasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === -4); + // Should increase one stat stage by 2 (from -6, meaning it will be -4) + const increasedStat = EFFECTIVE_STATS.filter(s => playerPokemon.getStatStage(s) === -4); expect(increasedStat).toBeTruthy(); expect(increasedStat.length).toBe(1); }); - it( - "should only decrease one BattleStat by 1 stage if all BattleStats are at 6", + it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; - // Set all BattleStats to 6 - battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = 6); + + // Set all stat stages to 6 + vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6)); game.move.select(Moves.SPLASH); await game.toNextTurn(); - // Should decrease one BattleStat by 1 (from 6, meaning it will be 5) - const decreasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === 5); + // Should decrease one stat stage by 1 (from 6, meaning it will be 5) + const decreasedStat = EFFECTIVE_STATS.filter(s => playerPokemon.getStatStage(s) === 5); + expect(decreasedStat).toBeTruthy(); expect(decreasedStat.length).toBe(1); }); diff --git a/src/test/abilities/moxie.test.ts b/src/test/abilities/moxie.test.ts index 6a1838c9a98..5f337fedabb 100644 --- a/src/test/abilities/moxie.test.ts +++ b/src/test/abilities/moxie.test.ts @@ -1,14 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { Stat } from "#app/data/pokemon-stat"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { BattlerIndex } from "#app/battle"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; describe("Abilities - Moxie", () => { let phaserGame: Phaser.Game; @@ -32,23 +32,47 @@ describe("Abilities - Moxie", () => { game.override.enemyAbility(Abilities.MOXIE); game.override.ability(Abilities.MOXIE); game.override.startingLevel(2000); - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); }); - it("MOXIE", async () => { + it("should raise ATK stat stage by 1 when winning a battle", async() => { const moveToUse = Moves.AERIAL_ACE; await game.startBattle([ Species.MIGHTYENA, Species.MIGHTYENA, ]); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[Stat.ATK]).toBe(0); + const playerPokemon = game.scene.getPlayerPokemon()!; + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(1); + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); + }, 20000); + + // TODO: Activate this test when MOXIE is corrected to work on faint and not on battle victory + it.todo("should raise ATK stat stage by 1 when defeating an ally Pokemon", async() => { + game.override.battleType("double"); + const moveToUse = Moves.AERIAL_ACE; + await game.startBattle([ + Species.MIGHTYENA, + Species.MIGHTYENA, + ]); + + const [ firstPokemon, secondPokemon ] = game.scene.getPlayerField(); + + expect(firstPokemon.getStatStage(Stat.ATK)).toBe(0); + + secondPokemon.hp = 1; + + game.move.select(moveToUse); + game.selectTarget(BattlerIndex.PLAYER_2); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1); }, 20000); }); diff --git a/src/test/abilities/mycelium_might.test.ts b/src/test/abilities/mycelium_might.test.ts index 83396f7950f..d8947935880 100644 --- a/src/test/abilities/mycelium_might.test.ts +++ b/src/test/abilities/mycelium_might.test.ts @@ -1,14 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { MovePhase } from "#app/phases/move-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; +import { Stat } from "#enums/stat"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - describe("Abilities - Mycelium Might", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -35,7 +34,7 @@ describe("Abilities - Mycelium Might", () => { }); /** - * Bulbapedia References: + * References: * https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 @@ -44,62 +43,65 @@ describe("Abilities - Mycelium Might", () => { it("will move last in its priority bracket and ignore protective abilities", async () => { await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); + const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); const enemyIndex = enemyPokemon?.getBattlerIndex(); game.move.select(Moves.BABY_DOLL_EYES); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); - // The player Pokemon (with Mycelium Might) goes last despite having higher speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); await game.phaseInterceptor.to(TurnEndPhase); - expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); + + // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. + expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); }, 20000); it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); + const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); const enemyIndex = enemyPokemon?.getBattlerIndex(); game.move.select(Moves.BABY_DOLL_EYES); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); await game.phaseInterceptor.to(TurnEndPhase); - expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); + // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. + expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); }, 20000); it("will not affect non-status moves", async () => { await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.QUICK_ATTACK); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon (without M.M.) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); }, 20000); }); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 1404f597ccf..2ad3f9e3f5c 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -1,21 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { StatusEffect } from "#app/data/status-effect"; import { Type } from "#app/data/type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { DamagePhase } from "#app/phases/damage-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -39,36 +32,31 @@ describe("Abilities - Parental Bond", () => { game.override.disableCrits(); game.override.ability(Abilities.PARENTAL_BOND); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyAbility(Abilities.INSOMNIA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyAbility(Abilities.FUR_COAT); + game.override.enemyMoveset(Moves.SPLASH); game.override.startingLevel(100); game.override.enemyLevel(100); }); - test( - "ability should add second strike to attack move", + it( + "should add second strike to attack move", async () => { game.override.moveset([Moves.TACKLE]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); let enemyStartingHp = enemyPokemon.hp; game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MoveEffectPhase, false); - - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp; enemyStartingHp = enemyPokemon.hp; - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp; @@ -77,556 +65,460 @@ describe("Abilities - Parental Bond", () => { }, TIMEOUT ); - test( - "ability should apply secondary effects to both strikes", + it( + "should apply secondary effects to both strikes", async () => { game.override.moveset([Moves.POWER_UP_PUNCH]); game.override.enemySpecies(Species.AMOONGUSS); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.POWER_UP_PUNCH); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(2); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); }, TIMEOUT ); - test( - "ability should not apply to Status moves", + it( + "should not apply to Status moves", async () => { game.override.moveset([Moves.BABY_DOLL_EYES]); - await game.startBattle([Species.CHARIZARD]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); + await game.classicMode.startBattle([Species.MAGIKARP]); const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.BABY_DOLL_EYES); - await game.phaseInterceptor.to(BerryPhase, false); - expect(enemyPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); + await game.phaseInterceptor.to("BerryPhase", false); + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); }, TIMEOUT ); - test( - "ability should not apply to multi-hit moves", + it( + "should not apply to multi-hit moves", async () => { game.override.moveset([Moves.DOUBLE_HIT]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.DOUBLE_HIT); await game.move.forceHit(); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); }, TIMEOUT ); - test( - "ability should not apply to self-sacrifice moves", + it( + "should not apply to self-sacrifice moves", async () => { game.override.moveset([Moves.SELF_DESTRUCT]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.SELF_DESTRUCT); - await game.phaseInterceptor.to(DamagePhase, false); + await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(1); }, TIMEOUT ); - test( - "ability should not apply to Rollout", + it( + "should not apply to Rollout", async () => { game.override.moveset([Moves.ROLLOUT]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.ROLLOUT); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase, false); + await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(1); }, TIMEOUT ); - test( - "ability should not apply multiplier to fixed-damage moves", + it( + "should not apply multiplier to fixed-damage moves", async () => { game.override.moveset([Moves.DRAGON_RAGE]); - await game.startBattle([Species.CHARIZARD]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); + await game.classicMode.startBattle([Species.MAGIKARP]); const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); - - const enemyStartingHp = enemyPokemon.hp; game.move.select(Moves.DRAGON_RAGE); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.hp).toBe(enemyStartingHp - 80); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 80); }, TIMEOUT ); - test( - "ability should not apply multiplier to counter moves", + it( + "should not apply multiplier to counter moves", async () => { game.override.moveset([Moves.COUNTER]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.SHUCKLE]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); - - const playerStartingHp = leadPokemon.hp; - const enemyStartingHp = enemyPokemon.hp; game.move.select(Moves.COUNTER); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); - const playerDamage = playerStartingHp - leadPokemon.hp; + const playerDamage = leadPokemon.getMaxHp() - leadPokemon.hp; - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.hp).toBe(enemyStartingHp - 4 * playerDamage); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 4 * playerDamage); }, TIMEOUT ); - test( - "ability should not apply to multi-target moves", + it( + "should not apply to multi-target moves", async () => { game.override.battleType("double"); game.override.moveset([Moves.EARTHQUAKE]); + game.override.passiveAbility(Abilities.LEVITATE); - await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); + await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); game.move.select(Moves.EARTHQUAKE); - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.EARTHQUAKE, 1); - await game.phaseInterceptor.to(BerryPhase, false); + + await game.phaseInterceptor.to("BerryPhase", false); playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); }, TIMEOUT ); - test( - "ability should apply to multi-target moves when hitting only one target", + it( + "should apply to multi-target moves when hitting only one target", async () => { game.override.moveset([Moves.EARTHQUAKE]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.EARTHQUAKE); - await game.phaseInterceptor.to(DamagePhase, false); + await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); }, TIMEOUT ); - test( - "ability should only trigger post-target move effects once", + it( + "should only trigger post-target move effects once", async () => { game.override.moveset([Moves.MIND_BLOWN]); - await game.startBattle([Species.PIDGEOT]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.MIND_BLOWN); - await game.phaseInterceptor.to(DamagePhase, false); + await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); // This test will time out if the user faints - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp() / 2)); + expect(leadPokemon.hp).toBe(Math.ceil(leadPokemon.getMaxHp() / 2)); }, TIMEOUT ); - test( - "Burn Up only removes type after second strike with this ability", + it( + "Burn Up only removes type after the second strike", async () => { game.override.moveset([Moves.BURN_UP]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.CHARIZARD]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.BURN_UP); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(leadPokemon.turnData.hitCount).toBe(2); expect(enemyPokemon.hp).toBeGreaterThan(0); expect(leadPokemon.isOfType(Type.FIRE)).toBe(true); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.isOfType(Type.FIRE)).toBe(false); }, TIMEOUT ); - test( + it( "Moves boosted by this ability and Multi-Lens should strike 4 times", async () => { game.override.moveset([Moves.TACKLE]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(4); }, TIMEOUT ); - test( + it( "Super Fang boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SUPER_FANG]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); - - const enemyStartingHp = enemyPokemon.hp; game.move.select(Moves.SUPER_FANG); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); - await game.phaseInterceptor.to(MoveEndPhase, false); + await game.phaseInterceptor.to("MoveEndPhase", false); - expect(enemyPokemon.hp).toBe(Math.ceil(enemyStartingHp * 0.25)); + expect(enemyPokemon.hp).toBe(Math.ceil(enemyPokemon.getMaxHp() * 0.25)); }, TIMEOUT ); - test( + it( "Seismic Toss boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SEISMIC_TOSS]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; game.move.select(Moves.SEISMIC_TOSS); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); - await game.phaseInterceptor.to(MoveEndPhase, false); + await game.phaseInterceptor.to("MoveEndPhase", false); expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); }, TIMEOUT ); - test( + it( "Hyper Beam boosted by this ability should strike twice, then recharge", async () => { game.override.moveset([Moves.HYPER_BEAM]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.HYPER_BEAM); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined(); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined(); }, TIMEOUT ); - /** TODO: Fix TRAPPED tag lapsing incorrectly, then run this test */ - test( + it( "Anchor Shot boosted by this ability should only trap the target after the second hit", async () => { game.override.moveset([Moves.ANCHOR_SHOT]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.ANCHOR_SHOT); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); - expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); // Passes + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); - await game.phaseInterceptor.to(MoveEndPhase); - expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Passes + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Fails :( + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); }, TIMEOUT ); - test( + it( "Smack Down boosted by this ability should only ground the target after the second hit", async () => { game.override.moveset([Moves.SMACK_DOWN]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.SMACK_DOWN); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined(); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); }, TIMEOUT ); - test( + it( "U-turn boosted by this ability should strike twice before forcing a switch", async () => { game.override.moveset([Moves.U_TURN]); - await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + await game.classicMode.startBattle([Species.MAGIKARP, Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.U_TURN); await game.move.forceHit(); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(leadPokemon.turnData.hitCount).toBe(2); // This will cause this test to time out if the switch was forced on the first hit. - await game.phaseInterceptor.to(MoveEffectPhase, false); + await game.phaseInterceptor.to("MoveEffectPhase", false); }, TIMEOUT ); - test( + it( "Wake-Up Slap boosted by this ability should only wake up the target after the second hit", async () => { game.override.moveset([Moves.WAKE_UP_SLAP]).enemyStatusEffect(StatusEffect.SLEEP); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.WAKE_UP_SLAP); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(2); expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status?.effect).toBeUndefined(); }, TIMEOUT ); - test( - "ability should not cause user to hit into King's Shield more than once", + it( + "should not cause user to hit into King's Shield more than once", async () => { game.override.moveset([Moves.TACKLE]); - game.override.enemyMoveset([Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD]); + game.override.enemyMoveset([Moves.KINGS_SHIELD]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1); }, TIMEOUT ); - test( - "ability should not cause user to hit into Storm Drain more than once", + it( + "should not cause user to hit into Storm Drain more than once", async () => { game.override.moveset([Moves.WATER_GUN]); game.override.enemyAbility(Abilities.STORM_DRAIN); - await game.startBattle([Species.CHARIZARD]); - - const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).not.toBe(undefined); + await game.classicMode.startBattle([Species.MAGIKARP]); const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).not.toBe(undefined); game.move.select(Moves.WATER_GUN); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1); }, TIMEOUT ); - test( - "ability should not apply to multi-target moves with Multi-Lens", + it( + "should not apply to multi-target moves with Multi-Lens", async () => { game.override.battleType("double"); game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]); + game.override.passiveAbility(Abilities.LEVITATE); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); game.move.select(Moves.EARTHQUAKE); - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(MoveEffectPhase, false); - - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); const enemyFirstHitDamage = enemyStartingHp.map((hp, i) => hp - enemyPokemon[i].hp); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i])); - }, TIMEOUT ); }); diff --git a/src/test/abilities/pastel_veil.test.ts b/src/test/abilities/pastel_veil.test.ts index ba90c7e3b3f..31490aab143 100644 --- a/src/test/abilities/pastel_veil.test.ts +++ b/src/test/abilities/pastel_veil.test.ts @@ -8,7 +8,6 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Abilities - Pastel Veil", () => { let phaserGame: Phaser.Game; @@ -31,7 +30,7 @@ describe("Abilities - Pastel Veil", () => { .moveset([Moves.TOXIC_THREAD, Moves.SPLASH]) .enemyAbility(Abilities.BALL_FETCH) .enemySpecies(Species.SUNKERN) - .enemyMoveset(SPLASH_ONLY); + .enemyMoveset(Moves.SPLASH); }); it("prevents the user and its allies from being afflicted by poison", async () => { diff --git a/src/test/abilities/power_spot.test.ts b/src/test/abilities/power_spot.test.ts index b83284c0bac..6d349a1a3f9 100644 --- a/src/test/abilities/power_spot.test.ts +++ b/src/test/abilities/power_spot.test.ts @@ -5,7 +5,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -29,7 +28,7 @@ describe("Abilities - Power Spot", () => { game = new GameManager(phaserGame); game.override.battleType("double"); game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); }); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 6ecabbfade0..4be58a677a6 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -9,7 +9,6 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; @@ -183,7 +182,7 @@ describe("Abilities - Protean", () => { "ability applies correctly even if the pokemon's move misses", async () => { game.override.moveset([Moves.TACKLE]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.MAGIKARP]); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index 00d344ed333..a02ee5cf56a 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Quick Draw", () => { game.override.enemyLevel(100); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); vi.spyOn(allAbilities[Abilities.QUICK_DRAW].getAttrs(BypassSpeedChanceAbAttr)[0], "chance", "get").mockReturnValue(100); }); @@ -76,7 +76,7 @@ describe("Abilities - Quick Draw", () => { ); test("does not increase priority", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.EXTREME_SPEED)); + game.override.enemyMoveset([Moves.EXTREME_SPEED]); await game.startBattle(); diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts index 041e20faf7f..add13ede296 100644 --- a/src/test/abilities/sand_spit.test.ts +++ b/src/test/abilities/sand_spit.test.ts @@ -35,7 +35,7 @@ describe("Abilities - Sand Spit", () => { }); it("should trigger when hit with damaging move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle(); game.move.select(Moves.SPLASH); @@ -45,7 +45,7 @@ describe("Abilities - Sand Spit", () => { }, 20000); it("should not trigger when targetted with status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset([Moves.GROWL]); await game.startBattle(); game.move.select(Moves.COIL); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index 2336e2b50de..da9fdcc01ab 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -1,5 +1,5 @@ -import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability"; -import { BattleStat } from "#app/data/battle-stat"; +import { StatMultiplierAbAttr, allAbilities } from "#app/data/ability"; +import { Stat } from "#enums/stat"; import { WeatherType } from "#app/data/weather"; import { CommandPhase } from "#app/phases/command-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; @@ -49,10 +49,10 @@ describe("Abilities - Sand Veil", () => { vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[Abilities.SAND_VEIL]); - const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0]; - vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation( - (pokemon, passive, simulated, battleStat, statValue, args) => { - if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { + const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(StatMultiplierAbAttr)[0]; + vi.spyOn(sandVeilAttr, "applyStatStage").mockImplementation( + (_pokemon, _passive, _simulated, stat, statValue, _args) => { + if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { statValue.value *= -1; // will make all attacks miss return true; } diff --git a/src/test/abilities/sap_sipper.test.ts b/src/test/abilities/sap_sipper.test.ts index f9c20e85eab..5e8cac74c95 100644 --- a/src/test/abilities/sap_sipper.test.ts +++ b/src/test/abilities/sap_sipper.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { TerrainType } from "#app/data/terrain"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; @@ -31,52 +31,55 @@ describe("Abilities - Sap Sipper", () => { game.override.disableCrits(); }); - it("raise attack 1 level and block effects when activated against a grass attack", async () => { + it("raises ATK stat stage by 1 and block effects when activated against a grass attack", async() => { const moveToUse = Moves.LEAFAGE; const enemyAbility = Abilities.SAP_SIPPER; - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.DUSKULL); game.override.enemyAbility(enemyAbility); await game.startBattle(); - const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; + const enemyPokemon = game.scene.getEnemyPokemon()!; + const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); - expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0); - expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(initialEnemyHp - enemyPokemon.hp).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }); - it("raise attack 1 level and block effects when activated against a grass status move", async () => { + it("raises ATK stat stage by 1 and block effects when activated against a grass status move", async() => { const moveToUse = Moves.SPORE; const enemyAbility = Abilities.SAP_SIPPER; - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(enemyAbility); await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getEnemyParty()[0].status).toBeUndefined(); - expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(enemyPokemon.status).toBeUndefined(); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }); it("do not activate against status moves that target the field", async () => { const moveToUse = Moves.GRASSY_TERRAIN; const enemyAbility = Abilities.SAP_SIPPER; - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(enemyAbility); @@ -88,51 +91,54 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.arena.terrain).toBeDefined(); expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY); - expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(0); }); it("activate once against multi-hit grass attacks", async () => { const moveToUse = Moves.BULLET_SEED; const enemyAbility = Abilities.SAP_SIPPER; - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(enemyAbility); await game.startBattle(); - const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; + const enemyPokemon = game.scene.getEnemyPokemon()!; + const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); - expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0); - expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(initialEnemyHp - enemyPokemon.hp).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }); it("do not activate against status moves that target the user", async () => { const moveToUse = Moves.SPIKY_SHIELD; const ability = Abilities.SAP_SIPPER; - game.override.moveset([moveToUse]); + game.override.moveset([ moveToUse ]); game.override.ability(ability); - game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.NONE); await game.startBattle(); + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(moveToUse); await game.phaseInterceptor.to(MoveEndPhase); - expect(game.scene.getParty()[0].getTag(BattlerTagType.SPIKY_SHIELD)).toBeDefined(); + expect(playerPokemon.getTag(BattlerTagType.SPIKY_SHIELD)).toBeDefined(); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); }); @@ -149,13 +155,14 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; + const enemyPokemon = game.scene.getEnemyPokemon()!; + const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); - expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0); - expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(initialEnemyHp - enemyPokemon.hp).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }); }); diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index 7316b2ea920..e06288b9de9 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index f73b749dac2..69b47e1eaae 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability"; -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index 14770c49427..8a0b769827d 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/abilities/simple.test.ts b/src/test/abilities/simple.test.ts new file mode 100644 index 00000000000..e5ca474d7c3 --- /dev/null +++ b/src/test/abilities/simple.test.ts @@ -0,0 +1,42 @@ +import { Moves } from "#app/enums/moves"; +import { Abilities } from "#enums/abilities"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Simple", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.BULBASAUR) + .enemyAbility(Abilities.SIMPLE) + .ability(Abilities.INTIMIDATE) + .enemyMoveset(Moves.SPLASH); + }); + + it("should double stat changes when applied", async() => { + await game.startBattle([ + Species.SLOWBRO + ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); + }, 20000); +}); diff --git a/src/test/abilities/stall.test.ts b/src/test/abilities/stall.test.ts index d8dbe9d0e06..7baf7c846f0 100644 --- a/src/test/abilities/stall.test.ts +++ b/src/test/abilities/stall.test.ts @@ -1,11 +1,10 @@ -import { MovePhase } from "#app/phases/move-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { TurnStartPhase } from "#app/phases/turn-start-phase"; describe("Abilities - Stall", () => { let phaserGame: Phaser.Game; @@ -32,7 +31,7 @@ describe("Abilities - Stall", () => { }); /** - * Bulbapedia References: + * References: * https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority **/ @@ -40,55 +39,56 @@ describe("Abilities - Stall", () => { it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.QUICK_ATTACK); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); }, 20000); it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); }, 20000); it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => { game.override.ability(Abilities.STALL); await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MovePhase, false); - // The opponent Pokemon (with Stall) goes first because it has a higher speed. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); + // The opponent Pokemon (with Stall) goes first because it has a higher speed. // The player Pokemon (with Stall) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); }, 20000); }); diff --git a/src/test/abilities/steely_spirit.test.ts b/src/test/abilities/steely_spirit.test.ts index c632d0be777..7b5879555be 100644 --- a/src/test/abilities/steely_spirit.test.ts +++ b/src/test/abilities/steely_spirit.test.ts @@ -1,11 +1,9 @@ import { allAbilities } from "#app/data/ability"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -32,12 +30,12 @@ describe("Abilities - Steely Spirit", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([Moves.IRON_HEAD, Moves.SPLASH]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); vi.spyOn(allMoves[moveToCheck], "calculateBattlePower"); }); it("increases Steel-type moves' power used by the user and its allies by 50%", async () => { - await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); + await game.classicMode.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; const enemyToCheck = game.scene.getEnemyPokemon()!; @@ -47,13 +45,13 @@ describe("Abilities - Steely Spirit", () => { game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * steelySpiritMultiplier); }); it("stacks if multiple users with this ability are on the field.", async () => { - await game.startBattle([Species.PIKACHU, Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU, Species.PIKACHU]); const enemyToCheck = game.scene.getEnemyPokemon()!; game.scene.getPlayerField().forEach(p => { @@ -64,13 +62,13 @@ describe("Abilities - Steely Spirit", () => { game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); game.move.select(moveToCheck, 1, enemyToCheck.getBattlerIndex()); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * Math.pow(steelySpiritMultiplier, 2)); }); it("does not take effect when suppressed", async () => { - await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); + await game.classicMode.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; const enemyToCheck = game.scene.getEnemyPokemon()!; @@ -84,8 +82,25 @@ describe("Abilities - Steely Spirit", () => { game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower); }); + + it("affects variable-type moves if their resolved type is Steel", async () => { + game.override + .ability(Abilities.STEELY_SPIRIT) + .moveset([Moves.REVELATION_DANCE]); + + const revelationDance = allMoves[Moves.REVELATION_DANCE]; + vi.spyOn(revelationDance, "calculateBattlePower"); + + await game.classicMode.startBattle([Species.KLINKLANG]); + + game.move.select(Moves.REVELATION_DANCE); + + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(revelationDance.calculateBattlePower).toHaveReturnedWith(revelationDance.power * 1.5); + }); }); diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index 5de3c7285a9..c2946443245 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -6,7 +6,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -45,7 +44,7 @@ describe("Abilities - Sweet Veil", () => { }); it("causes Rest to fail when used by the user or its allies", async () => { - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); game.move.select(Moves.SPLASH); @@ -72,7 +71,7 @@ describe("Abilities - Sweet Veil", () => { game.override.enemySpecies(Species.PIKACHU); game.override.enemyLevel(5); game.override.startingLevel(5); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.SHUCKLE, Species.SHUCKLE, Species.SWIRLIX]); diff --git a/src/test/abilities/tera_shell.test.ts b/src/test/abilities/tera_shell.test.ts new file mode 100644 index 00000000000..2826469f3bf --- /dev/null +++ b/src/test/abilities/tera_shell.test.ts @@ -0,0 +1,111 @@ +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { HitResult } from "#app/field/pokemon"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 10 * 1000; // 10 second timeout + +describe("Abilities - Tera Shell", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .ability(Abilities.TERA_SHELL) + .moveset([Moves.SPLASH]) + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset([Moves.MACH_PUNCH]) + .startingLevel(100) + .enemyLevel(100); + }); + + it( + "should change the effectiveness of non-resisted attacks when the source is at full HP", + async () => { + await game.classicMode.startBattle([Species.SNORLAX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0.5); + + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(2); + }, TIMEOUT + ); + + it( + "should not override type immunities", + async () => { + game.override.enemyMoveset([Moves.SHADOW_SNEAK]); + + await game.classicMode.startBattle([Species.SNORLAX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0); + }, TIMEOUT + ); + + it( + "should not override type multipliers less than 0.5x", + async () => { + game.override.enemyMoveset([Moves.QUICK_ATTACK]); + + await game.classicMode.startBattle([Species.AGGRON]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0.25); + }, TIMEOUT + ); + + it( + "should not affect the effectiveness of fixed-damage moves", + async () => { + game.override.enemyMoveset([Moves.DRAGON_RAGE]); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "apply"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("BerryPhase", false); + expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.EFFECTIVE); + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40); + }, TIMEOUT + ); +}); diff --git a/src/test/abilities/volt_absorb.test.ts b/src/test/abilities/volt_absorb.test.ts index d9c3fe34c24..7f3e160c7d0 100644 --- a/src/test/abilities/volt_absorb.test.ts +++ b/src/test/abilities/volt_absorb.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -41,12 +41,14 @@ describe("Abilities - Volt Absorb", () => { await game.startBattle(); + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getParty()[0].summonData.battleStats[BattleStat.SPDEF]).toBe(1); - expect(game.scene.getParty()[0].getTag(BattlerTagType.CHARGED)).toBeDefined(); + expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(1); + expect(playerPokemon.getTag(BattlerTagType.CHARGED)).toBeDefined(); expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); }); }); diff --git a/src/test/abilities/wind_power.test.ts b/src/test/abilities/wind_power.test.ts index c944e01b43a..12b8d2f2299 100644 --- a/src/test/abilities/wind_power.test.ts +++ b/src/test/abilities/wind_power.test.ts @@ -4,7 +4,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -28,7 +27,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(Species.SHIFTRY); game.override.enemyAbility(Abilities.WIND_POWER); game.override.moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); it("it becomes charged when hit by wind moves", async () => { diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index 97e2e6456dc..c917f56e101 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -1,10 +1,8 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -24,94 +22,99 @@ describe("Abilities - Wind Rider", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.SHIFTRY); - game.override.enemyAbility(Abilities.WIND_RIDER); - game.override.moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override + .battleType("single") + .enemySpecies(Species.SHIFTRY) + .enemyAbility(Abilities.WIND_RIDER) + .moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]) + .enemyMoveset(Moves.SPLASH); }); - it("takes no damage from wind moves and its Attack is increased by one stage when hit by one", async () => { - await game.startBattle([Species.MAGIKARP]); + it("takes no damage from wind moves and its ATK stat stage is raised by 1 when hit by one", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); const shiftry = game.scene.getEnemyPokemon()!; - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); game.move.select(Moves.PETAL_BLIZZARD); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.isFullHp()).toBe(true); - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(shiftry.getStatStage(Stat.ATK)).toBe(1); }); - it("Attack is increased by one stage when Tailwind is present on its side", async () => { - game.override.ability(Abilities.WIND_RIDER); - game.override.enemySpecies(Species.MAGIKARP); + it("ATK stat stage is raised by 1 when Tailwind is present on its side", async () => { + game.override + .enemySpecies(Species.MAGIKARP) + .ability(Abilities.WIND_RIDER); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const shiftry = game.scene.getPlayerPokemon()!; - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); + expect(shiftry.getStatStage(Stat.ATK)).toBe(1); }); - it("does not increase Attack when Tailwind is present on opposing side", async () => { - game.override.ability(Abilities.WIND_RIDER); - game.override.enemySpecies(Species.MAGIKARP); + it("does not raise ATK stat stage when Tailwind is present on opposing side", async () => { + game.override + .enemySpecies(Species.MAGIKARP) + .ability(Abilities.WIND_RIDER); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const magikarp = game.scene.getEnemyPokemon()!; const shiftry = game.scene.getPlayerPokemon()!; - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); + expect(magikarp.getStatStage(Stat.ATK)).toBe(0); game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); - expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(1); + expect(magikarp.getStatStage(Stat.ATK)).toBe(0); }); - it("does not increase Attack when Tailwind is present on opposing side", async () => { - game.override.enemySpecies(Species.MAGIKARP); + it("does not raise ATK stat stage when Tailwind is present on opposing side", async () => { + game.override + .enemySpecies(Species.MAGIKARP) + .ability(Abilities.WIND_RIDER); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const magikarp = game.scene.getEnemyPokemon()!; const shiftry = game.scene.getPlayerPokemon()!; - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); + expect(magikarp.getStatStage(Stat.ATK)).toBe(0); game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); - expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(1); + expect(magikarp.getStatStage(Stat.ATK)).toBe(0); }); it("does not interact with Sandstorm", async () => { game.override.enemySpecies(Species.MAGIKARP); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const shiftry = game.scene.getPlayerPokemon()!; - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); expect(shiftry.isFullHp()).toBe(true); game.move.select(Moves.SANDSTORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); - expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(shiftry.getStatStage(Stat.ATK)).toBe(0); expect(shiftry.hp).lessThan(shiftry.getMaxHp()); }); }); diff --git a/src/test/abilities/wonder_skin.test.ts b/src/test/abilities/wonder_skin.test.ts index 0c2aedc8ce8..6ef985fbd42 100644 --- a/src/test/abilities/wonder_skin.test.ts +++ b/src/test/abilities/wonder_skin.test.ts @@ -5,7 +5,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -30,7 +29,7 @@ describe("Abilities - Wonder Skin", () => { game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.WONDER_SKIN); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); it("lowers accuracy of status moves to 50%", async () => { diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index 677d998e876..fd378647184 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -1,6 +1,5 @@ +import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; -import { Stat } from "#app/data/pokemon-stat"; -import { Status, StatusEffect } from "#app/data/status-effect"; import { DamagePhase } from "#app/phases/damage-phase"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { MessagePhase } from "#app/phases/message-phase"; @@ -18,6 +17,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { Status, StatusEffect } from "#app/data/status-effect"; const TIMEOUT = 20 * 1000; diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index 1a9697f974e..eafc32b4c79 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -6,7 +6,6 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -30,8 +29,8 @@ describe("Abilities - ZERO TO HERO", () => { game = new GameManager(phaserGame); game.override .battleType("single") - .moveset(SPLASH_ONLY) - .enemyMoveset(SPLASH_ONLY) + .moveset(Moves.SPLASH) + .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH); }); diff --git a/src/test/account.spec.ts b/src/test/account.test.ts similarity index 100% rename from src/test/account.spec.ts rename to src/test/account.test.ts diff --git a/src/test/achievements/achievement.test.ts b/src/test/achievements/achievement.test.ts index 36c20ae2248..24d00a3e77b 100644 --- a/src/test/achievements/achievement.test.ts +++ b/src/test/achievements/achievement.test.ts @@ -224,7 +224,7 @@ describe("achvs", () => { expect(achvs._50_RIBBONS).toBeInstanceOf(RibbonAchv); expect(achvs._75_RIBBONS).toBeInstanceOf(RibbonAchv); expect(achvs._100_RIBBONS).toBeInstanceOf(RibbonAchv); - expect(achvs.TRANSFER_MAX_BATTLE_STAT).toBeInstanceOf(Achv); + expect(achvs.TRANSFER_MAX_STAT_STAGE).toBeInstanceOf(Achv); expect(achvs.MAX_FRIENDSHIP).toBeInstanceOf(Achv); expect(achvs.MEGA_EVOLVE).toBeInstanceOf(Achv); expect(achvs.GIGANTAMAX).toBeInstanceOf(Achv); diff --git a/src/test/arena/arena_gravity.test.ts b/src/test/arena/arena_gravity.test.ts index eda8c687ba1..47b8bf4cf70 100644 --- a/src/test/arena/arena_gravity.test.ts +++ b/src/test/arena/arena_gravity.test.ts @@ -8,7 +8,6 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Arena - Gravity", () => { let phaserGame: Phaser.Game; @@ -32,7 +31,7 @@ describe("Arena - Gravity", () => { .ability(Abilities.UNNERVE) .enemyAbility(Abilities.BALL_FETCH) .enemySpecies(Species.SHUCKLE) - .enemyMoveset(SPLASH_ONLY); + .enemyMoveset(Moves.SPLASH); }); // Reference: https://bulbapedia.bulbagarden.net/wiki/Gravity_(move) diff --git a/src/test/arena/weather_fog.test.ts b/src/test/arena/weather_fog.test.ts index b36b0de2e06..b47145e8dd0 100644 --- a/src/test/arena/weather_fog.test.ts +++ b/src/test/arena/weather_fog.test.ts @@ -31,7 +31,7 @@ describe("Weather - Fog", () => { game.override.ability(Abilities.BALL_FETCH); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH)); + game.override.enemyMoveset([Moves.SPLASH]); }); it("move accuracy is multiplied by 90%", async () => { diff --git a/src/test/arena/weather_hail.test.ts b/src/test/arena/weather_hail.test.ts new file mode 100644 index 00000000000..31d20be2ded --- /dev/null +++ b/src/test/arena/weather_hail.test.ts @@ -0,0 +1,61 @@ +import { WeatherType } from "#app/data/weather"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { BattlerIndex } from "#app/battle"; + +describe("Weather - Hail", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .weather(WeatherType.HAIL) + .battleType("single") + .moveset(Moves.SPLASH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.MAGIKARP); + }); + + it("inflicts damage equal to 1/16 of Pokemon's max HP at turn end", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("TurnEndPhase"); + + game.scene.getField(true).forEach(pokemon => { + expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16)); + }); + }); + + it("does not inflict damage to a Pokemon that is underwater (Dive) or underground (Dig)", async () => { + game.override.moveset([Moves.DIG]); + await game.classicMode.startBattle([Species.MAGIKARP]); + + game.move.select(Moves.DIG); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("TurnEndPhase"); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16)); + }); +}); diff --git a/src/test/arena/weather_sandstorm.test.ts b/src/test/arena/weather_sandstorm.test.ts new file mode 100644 index 00000000000..91188de6985 --- /dev/null +++ b/src/test/arena/weather_sandstorm.test.ts @@ -0,0 +1,58 @@ +import { WeatherType } from "#app/data/weather"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Weather - Sandstorm", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .weather(WeatherType.SANDSTORM) + .battleType("single") + .moveset(Moves.SPLASH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.MAGIKARP); + }); + + it("inflicts damage equal to 1/16 of Pokemon's max HP at turn end", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("TurnEndPhase"); + + game.scene.getField(true).forEach(pokemon => { + expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16)); + }); + }); + + it("does not inflict damage to a Pokemon that is underwater (Dive) or underground (Dig)", async () => { + game.override.moveset([Moves.DIVE]); + await game.classicMode.startBattle([Species.MAGIKARP]); + + game.move.select(Moves.DIVE); + + await game.phaseInterceptor.to("TurnEndPhase"); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16)); + }); +}); diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts index 8b2d3e2547e..5ce0e61e647 100644 --- a/src/test/arena/weather_strong_winds.test.ts +++ b/src/test/arena/weather_strong_winds.test.ts @@ -1,4 +1,5 @@ import { allMoves } from "#app/data/move"; +import { StatusEffect } from "#app/enums/status-effect"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -33,7 +34,7 @@ describe("Weather - Strong Winds", () => { it("electric type move is not very effective on Rayquaza", async () => { game.override.enemySpecies(Species.RAYQUAZA); - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -44,7 +45,7 @@ describe("Weather - Strong Winds", () => { }); it("electric type move is neutral for flying type pokemon", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -55,7 +56,7 @@ describe("Weather - Strong Winds", () => { }); it("ice type move is neutral for flying type pokemon", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -66,7 +67,7 @@ describe("Weather - Strong Winds", () => { }); it("rock type move is neutral for flying type pokemon", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -75,4 +76,18 @@ describe("Weather - Strong Winds", () => { await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ROCK_SLIDE].type, pikachu)).toBe(1); }); + + it("weather goes away when last trainer pokemon dies to indirect damage", async () => { + game.override.enemyStatusEffect(StatusEffect.POISON); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 1; + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(game.scene.arena.weather?.weatherType).toBeUndefined(); + }); }); diff --git a/src/test/battle-scene.test.ts b/src/test/battle-scene.test.ts index 9e28ec99791..4da75cea197 100644 --- a/src/test/battle-scene.test.ts +++ b/src/test/battle-scene.test.ts @@ -1,5 +1,5 @@ import { LoadingScene } from "#app/loading-scene"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import GameManager from "./utils/gameManager"; describe("BattleScene", () => { @@ -24,4 +24,12 @@ describe("BattleScene", () => { // `BattleScene.create()` is called during the `new GameManager()` call expect(game.scene.scene.remove).toHaveBeenCalledWith(LoadingScene.KEY); }); + + it("should also reset RNG on reset", () => { + vi.spyOn(game.scene, "resetSeed"); + + game.scene.reset(); + + expect(game.scene.resetSeed).toHaveBeenCalled(); + }); }); diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts deleted file mode 100644 index 16fce962838..00000000000 --- a/src/test/battle-stat.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; -import { describe, expect, it } from "vitest"; -import { arrayOfRange, mockI18next } from "./utils/testUtils"; - -const TEST_BATTLE_STAT = -99 as unknown as BattleStat; -const TEST_POKEMON = "Testmon"; -const TEST_STAT = "Teststat"; - -describe("battle-stat", () => { - describe("getBattleStatName", () => { - it("should return the correct name for each BattleStat", () => { - mockI18next(); - - expect(getBattleStatName(BattleStat.ATK)).toBe("pokemonInfo:Stat.ATK"); - expect(getBattleStatName(BattleStat.DEF)).toBe("pokemonInfo:Stat.DEF"); - expect(getBattleStatName(BattleStat.SPATK)).toBe( - "pokemonInfo:Stat.SPATK" - ); - expect(getBattleStatName(BattleStat.SPDEF)).toBe( - "pokemonInfo:Stat.SPDEF" - ); - expect(getBattleStatName(BattleStat.SPD)).toBe("pokemonInfo:Stat.SPD"); - expect(getBattleStatName(BattleStat.ACC)).toBe("pokemonInfo:Stat.ACC"); - expect(getBattleStatName(BattleStat.EVA)).toBe("pokemonInfo:Stat.EVA"); - }); - - it("should fall back to ??? for an unknown BattleStat", () => { - expect(getBattleStatName(TEST_BATTLE_STAT)).toBe("???"); - }); - }); - - describe("getBattleStatLevelChangeDescription", () => { - it("should return battle:statRose for +1", () => { - mockI18next(); - - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - 1, - true - ); - - expect(message).toBe("battle:statRose"); - }); - - it("should return battle:statSharplyRose for +2", () => { - mockI18next(); - - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - 2, - true - ); - - expect(message).toBe("battle:statSharplyRose"); - }); - - it("should return battle:statRoseDrastically for +3 to +6", () => { - mockI18next(); - - arrayOfRange(3, 6).forEach((n) => { - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - n, - true - ); - - expect(message).toBe("battle:statRoseDrastically"); - }); - }); - - it("should return battle:statWontGoAnyHigher for 7 or higher", () => { - mockI18next(); - - arrayOfRange(7, 10).forEach((n) => { - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - n, - true - ); - - expect(message).toBe("battle:statWontGoAnyHigher"); - }); - }); - - it("should return battle:statFell for -1", () => { - mockI18next(); - - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - 1, - false - ); - - expect(message).toBe("battle:statFell"); - }); - - it("should return battle:statHarshlyFell for -2", () => { - mockI18next(); - - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - 2, - false - ); - - expect(message).toBe("battle:statHarshlyFell"); - }); - - it("should return battle:statSeverelyFell for -3 to -6", () => { - mockI18next(); - - arrayOfRange(3, 6).forEach((n) => { - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - n, - false - ); - - expect(message).toBe("battle:statSeverelyFell"); - }); - }); - - it("should return battle:statWontGoAnyLower for -7 or lower", () => { - mockI18next(); - - arrayOfRange(7, 10).forEach((n) => { - const message = getBattleStatLevelChangeDescription( - TEST_POKEMON, - TEST_STAT, - n, - false - ); - - expect(message).toBe("battle:statWontGoAnyLower"); - }); - }); - }); -}); diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 0129ecad254..e19168962dc 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -1,4 +1,3 @@ -import { Stat } from "#app/data/pokemon-stat"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { SelectTargetPhase } from "#app/phases/select-target-phase"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; @@ -7,8 +6,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Battle order", () => { let phaserGame: Phaser.Game; @@ -37,30 +35,42 @@ describe("Battle order", () => { await game.startBattle([ Species.BULBASAUR, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 50; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); + + const playerPokemonIndex = playerPokemon.getBattlerIndex(); + const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order[0]).toBe(2); - expect(order[1]).toBe(0); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(enemyPokemonIndex); + expect(order[1]).toBe(playerPokemonIndex); }, 20000); it("Player faster than opponent 150 vs 50", async () => { await game.startBattle([ Species.BULBASAUR, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 50; + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50 game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); + + const playerPokemonIndex = playerPokemon.getBattlerIndex(); + const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order[0]).toBe(0); - expect(order[1]).toBe(2); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(playerPokemonIndex); + expect(order[1]).toBe(enemyPokemonIndex); }, 20000); it("double - both opponents faster than player 50/50 vs 150/150", async () => { @@ -69,20 +79,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 50; - game.scene.getParty()[1].stats[Stat.SPD] = 50; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50])); // set both playerPokemons' speed to 50 + enemyPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150])); // set both enemyPokemons' speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(2)); - expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(3)); - expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(2)); - expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(3)); + const order = phase.getCommandOrder(); + expect(order.slice(0, 2).includes(enemyIndices[0])).toBe(true); + expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[1])).toBe(true); }, 20000); it("double - speed tie except 1 - 100/100 vs 100/150", async () => { @@ -91,19 +106,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 100; - game.scene.getParty()[1].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100])); //set both playerPokemons' speed to 100 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(1)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(enemyIndices[1]); + expect(order.slice(1, 4).includes(enemyIndices[0])).toBe(true); + expect(order.slice(1, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(1, 4).includes(playerIndices[1])).toBe(true); }, 20000); it("double - speed tie 100/150 vs 100/150", async () => { @@ -112,19 +133,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 100; - game.scene.getParty()[1].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one playerPokemon's speed to 100 + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other playerPokemon's speed to 150 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other enemyPokemon's speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(1)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(1)).toBeLessThan(order.indexOf(2)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); + const order = phase.getCommandOrder(); + expect(order.slice(0, 2).includes(playerIndices[1])).toBe(true); + expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(2, 4).includes(enemyIndices[0])).toBe(true); }, 20000); }); diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index be89fdeb2af..6e15bbd99d9 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -1,5 +1,5 @@ import { allSpecies } from "#app/data/pokemon-species"; -import { TempBattleStat } from "#app/data/temp-battle-stat"; +import { Stat } from "#enums/stat"; import { GameModes, getGameMode } from "#app/game-mode"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { CommandPhase } from "#app/phases/command-phase"; @@ -25,7 +25,6 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Test Battle Phase", () => { let phaserGame: Phaser.Game; @@ -319,8 +318,8 @@ describe("Test Battle Phase", () => { .startingWave(1) .startingLevel(100) .moveset([moveToUse]) - .enemyMoveset(SPLASH_ONLY) - .startingHeldItems([{ name: "TEMP_STAT_BOOSTER", type: TempBattleStat.ACC }]); + .enemyMoveset(Moves.SPLASH) + .startingHeldItems([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]); await game.startBattle(); game.scene.getPlayerPokemon()!.hp = 1; diff --git a/src/test/battle/damage_calculation.test.ts b/src/test/battle/damage_calculation.test.ts index 665000450be..89f2bb4c269 100644 --- a/src/test/battle/damage_calculation.test.ts +++ b/src/test/battle/damage_calculation.test.ts @@ -1,11 +1,10 @@ -import { DamagePhase } from "#app/phases/damage-phase.js"; +import { DamagePhase } from "#app/phases/damage-phase"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -31,7 +30,7 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => { it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => { game.override.enemySpecies(Species.GASTLY); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.starterSpecies(Species.SHEDINJA); game.override.moveset([Moves.JUMP_KICK]); game.override.ability(Abilities.WONDER_GUARD); diff --git a/src/test/battle/double_battle.test.ts b/src/test/battle/double_battle.test.ts index d264a29ef9b..b7a5616d642 100644 --- a/src/test/battle/double_battle.test.ts +++ b/src/test/battle/double_battle.test.ts @@ -4,7 +4,6 @@ import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -29,7 +28,7 @@ describe("Double Battles", () => { // double-battle player's pokemon both fainted in same round, then revive one, and next double battle summons two player's pokemon successfully. // (There were bugs that either only summon one when can summon two, player stuck in switchPhase etc) it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async () => { - game.override.battleType("double").enemyMoveset(SPLASH_ONLY).moveset(SPLASH_ONLY); + game.override.battleType("double").enemyMoveset(Moves.SPLASH).moveset(Moves.SPLASH); await game.startBattle([ Species.BULBASAUR, Species.CHARIZARD, diff --git a/src/test/battle/inverse_battle.test.ts b/src/test/battle/inverse_battle.test.ts index be8b04155eb..d808f71addb 100644 --- a/src/test/battle/inverse_battle.test.ts +++ b/src/test/battle/inverse_battle.test.ts @@ -1,8 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/move"; import { Type } from "#app/data/type"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Challenges } from "#enums/challenges"; @@ -11,7 +8,7 @@ import { Species } from "#enums/species"; import { StatusEffect } from "#enums/status-effect"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const TIMEOUT = 20 * 1000; @@ -39,43 +36,63 @@ describe("Inverse Battle", () => { .starterSpecies(Species.FEEBAS) .ability(Abilities.BALL_FETCH) .enemySpecies(Species.MAGIKARP) - .enemyAbility(Abilities.BALL_FETCH); + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); }); - it("1. immune types are 2x effective - Thunderbolt against Ground Type", async () => { - game.override.enemySpecies(Species.SANDSHREW); + it("Immune types are 2x effective - Thunderbolt against Ground Type", async () => { + game.override + .moveset([Moves.THUNDERBOLT]) + .enemySpecies(Species.SANDSHREW); + await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); - expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2); + game.move.select(Moves.THUNDERBOLT); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); }, TIMEOUT); - it("2. 2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => { - game.override.enemySpecies(Species.PIDGEY); + it("2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => { + game.override + .moveset([Moves.THUNDERBOLT]) + .enemySpecies(Species.PIDGEY); await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); - expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(0.5); + game.move.select(Moves.THUNDERBOLT); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(0.5); }, TIMEOUT); - it("3. 0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => { - game.override.enemySpecies(Species.CHIKORITA); + it("0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => { + game.override + .moveset([Moves.THUNDERBOLT]) + .enemySpecies(Species.CHIKORITA); await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); - expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2); + game.move.select(Moves.THUNDERBOLT); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); }, TIMEOUT); - it("4. Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => { + it("Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => { game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0); game.override .enemySpecies(Species.CHARIZARD) @@ -95,18 +112,24 @@ describe("Inverse Battle", () => { expect(currentHp).toBeGreaterThan(maxHp * 31 / 32 - 1); }, TIMEOUT); - it("5. Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => { - game.override.enemySpecies(Species.SQUIRTLE); + it("Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => { + game.override + .moveset([Moves.FREEZE_DRY]) + .enemySpecies(Species.SQUIRTLE); await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); - expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FREEZE_DRY])).toBe(2); + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); }, TIMEOUT); - it("6. Water Absorb should heal against water moves - Water Absorb against Water gun", async () => { + it("Water Absorb should heal against water moves - Water Absorb against Water gun", async () => { game.override .moveset([Moves.WATER_GUN]) .enemyAbility(Abilities.WATER_ABSORB); @@ -117,13 +140,12 @@ describe("Inverse Battle", () => { enemy.hp = enemy.getMaxHp() - 1; game.move.select(Moves.WATER_GUN); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.hp).toBe(enemy.getMaxHp()); }, TIMEOUT); - it("7. Fire type does not get burned - Will-O-Wisp against Charmander", async () => { + it("Fire type does not get burned - Will-O-Wisp against Charmander", async () => { game.override .moveset([Moves.WILL_O_WISP]) .enemySpecies(Species.CHARMANDER); @@ -135,13 +157,12 @@ describe("Inverse Battle", () => { game.move.select(Moves.WILL_O_WISP); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.move.forceHit(); - - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.status?.effect).not.toBe(StatusEffect.BURN); }, TIMEOUT); - it("8. Electric type does not get paralyzed - Nuzzle against Pikachu", async () => { + it("Electric type does not get paralyzed - Nuzzle against Pikachu", async () => { game.override .moveset([Moves.NUZZLE]) .enemySpecies(Species.PIKACHU) @@ -153,14 +174,30 @@ describe("Inverse Battle", () => { game.move.select(Moves.NUZZLE); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.status?.effect).not.toBe(StatusEffect.PARALYSIS); }, TIMEOUT); + it("Ground type is not immune to Thunder Wave - Thunder Wave against Sandshrew", async () => { + game.override + .moveset([Moves.THUNDER_WAVE]) + .enemySpecies(Species.SANDSHREW); - it("10. Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => { + await game.challengeMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.THUNDER_WAVE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.move.forceHit(); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS); + }, TIMEOUT); + + + it("Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => { game.override .moveset([Moves.THUNDERBOLT]) .enemySpecies(Species.SANDSHREW) @@ -171,7 +208,7 @@ describe("Inverse Battle", () => { expect(game.scene.getEnemyPokemon()?.summonData.abilitiesApplied[0]).toBe(Abilities.ANTICIPATION); }, TIMEOUT); - it("11. Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => { + it("Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => { game.override .moveset([Moves.CONVERSION_2]) .enemyMoveset([Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW]); @@ -183,21 +220,64 @@ describe("Inverse Battle", () => { game.move.select(Moves.CONVERSION_2); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.getTypes()[0]).toBe(Type.DRAGON); }, TIMEOUT); - it("12. Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => { + it("Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => { game.override .moveset([Moves.FLYING_PRESS]) .enemySpecies(Species.MEOWSCARADA); await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); - expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FLYING_PRESS])).toBe(0.25); + game.move.select(Moves.FLYING_PRESS); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(0.25); + }, TIMEOUT); + + it("Scrappy ability has no effect - Tackle against Ghost Type still 2x effective with Scrappy", async () => { + game.override + .moveset([Moves.TACKLE]) + .ability(Abilities.SCRAPPY) + .enemySpecies(Species.GASTLY); + + await game.challengeMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.TACKLE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); + }, TIMEOUT); + + it("FORESIGHT has no effect - Tackle against Ghost Type still 2x effective with Foresight", async () => { + game.override + .moveset([Moves.FORESIGHT, Moves.TACKLE]) + .enemySpecies(Species.GASTLY); + + await game.challengeMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FORESIGHT); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + game.move.select(Moves.TACKLE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); }, TIMEOUT); }); diff --git a/src/test/battlerTags/octolock.test.ts b/src/test/battlerTags/octolock.test.ts index fa491589f09..7b1f9264370 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -1,16 +1,16 @@ import BattleScene from "#app/battle-scene"; -import { BattleStat } from "#app/data/battle-stat"; -import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import Pokemon from "#app/field/pokemon"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; import { describe, expect, it, vi } from "vitest"; +import Pokemon from "#app/field/pokemon"; +import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Stat } from "#enums/stat"; vi.mock("#app/battle-scene.js"); describe("BattlerTag - OctolockTag", () => { describe("lapse behavior", () => { - it("unshifts a StatChangePhase with expected stat changes", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat stage changes", { timeout: 10000 }, async () => { const mockPokemon = { scene: new BattleScene(), getBattlerIndex: () => 0, @@ -19,9 +19,9 @@ describe("BattlerTag - OctolockTag", () => { const subject = new OctolockTag(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(-1); - expect((phase as StatChangePhase)["stats"]).toEqual([BattleStat.DEF, BattleStat.SPDEF]); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(-1); + expect((phase as StatStageChangePhase)["stats"]).toEqual([ Stat.DEF, Stat.SPDEF ]); }); subject.lapse(mockPokemon, BattlerTagLapseType.TURN_END); diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index fef1e938c09..e568016dfef 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -1,10 +1,10 @@ import BattleScene from "#app/battle-scene"; -import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags"; -import Pokemon, { PokemonSummonData } from "#app/field/pokemon"; -import * as messages from "#app/messages"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import Pokemon, { PokemonSummonData } from "#app/field/pokemon"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { Stat } from "#enums/stat"; +import * as messages from "#app/messages"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; beforeEach(() => { vi.spyOn(messages, "getPokemonNameWithAffix").mockImplementation(() => ""); @@ -12,7 +12,7 @@ beforeEach(() => { describe("BattlerTag - StockpilingTag", () => { describe("onAdd", () => { - it("unshifts a StatChangePhase with expected stat changes on add", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat stage changes on add", { timeout: 10000 }, async () => { const mockPokemon = { scene: vi.mocked(new BattleScene()) as BattleScene, getBattlerIndex: () => 0, @@ -23,11 +23,11 @@ describe("BattlerTag - StockpilingTag", () => { const subject = new StockpilingTag(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ])); - (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatStageChangePhase)["onChange"]!(mockPokemon, [Stat.DEF, Stat.SPDEF], [1, 1]); }); subject.onAdd(mockPokemon); @@ -35,7 +35,7 @@ describe("BattlerTag - StockpilingTag", () => { expect(mockPokemon.scene.unshiftPhase).toBeCalledTimes(1); }); - it("unshifts a StatChangePhase with expected stat changes on add (one stat maxed)", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat changes on add (one stat maxed)", { timeout: 10000 }, async () => { const mockPokemon = { scene: new BattleScene(), summonData: new PokemonSummonData(), @@ -44,17 +44,17 @@ describe("BattlerTag - StockpilingTag", () => { vi.spyOn(mockPokemon.scene, "queueMessage").mockImplementation(() => {}); - mockPokemon.summonData.battleStats[BattleStat.DEF] = 6; - mockPokemon.summonData.battleStats[BattleStat.SPDEF] = 5; + mockPokemon.summonData.statStages[Stat.DEF - 1] = 6; + mockPokemon.summonData.statStages[Stat.SPD - 1] = 5; const subject = new StockpilingTag(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); - (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [1, 1]); }); subject.onAdd(mockPokemon); @@ -64,7 +64,7 @@ describe("BattlerTag - StockpilingTag", () => { }); describe("onOverlap", () => { - it("unshifts a StatChangePhase with expected stat changes on overlap", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat changes on overlap", { timeout: 10000 }, async () => { const mockPokemon = { scene: new BattleScene(), getBattlerIndex: () => 0, @@ -75,11 +75,11 @@ describe("BattlerTag - StockpilingTag", () => { const subject = new StockpilingTag(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ])); - (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [1, 1]); }); subject.onOverlap(mockPokemon); @@ -98,39 +98,39 @@ describe("BattlerTag - StockpilingTag", () => { vi.spyOn(mockPokemon.scene, "queueMessage").mockImplementation(() => {}); - mockPokemon.summonData.battleStats[BattleStat.DEF] = 5; - mockPokemon.summonData.battleStats[BattleStat.SPDEF] = 4; + mockPokemon.summonData.statStages[Stat.DEF - 1] = 5; + mockPokemon.summonData.statStages[Stat.SPD - 1] = 4; const subject = new StockpilingTag(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ])); // def doesn't change - (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [1]); }); subject.onAdd(mockPokemon); expect(subject.stockpiledCount).toBe(1); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ])); // def doesn't change - (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [1]); }); subject.onOverlap(mockPokemon); expect(subject.stockpiledCount).toBe(2); vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(1); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(1); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ])); // neither stat changes, stack count should still increase }); @@ -138,20 +138,20 @@ describe("BattlerTag - StockpilingTag", () => { subject.onOverlap(mockPokemon); expect(subject.stockpiledCount).toBe(3); - vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => { + vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(_phase => { throw new Error("Should not be called a fourth time"); }); // fourth stack should not be applied subject.onOverlap(mockPokemon); expect(subject.stockpiledCount).toBe(3); - expect(subject.statChangeCounts).toMatchObject({ [BattleStat.DEF]: 0, [BattleStat.SPDEF]: 2 }); + expect(subject.statChangeCounts).toMatchObject({ [ Stat.DEF ]: 0, [Stat.SPDEF]: 2 }); // removing tag should reverse stat changes vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => { - expect(phase).toBeInstanceOf(StatChangePhase); - expect((phase as StatChangePhase)["levels"]).toEqual(-2); - expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.SPDEF])); + expect(phase).toBeInstanceOf(StatStageChangePhase); + expect((phase as StatStageChangePhase)["stages"]).toEqual(-2); + expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.SPDEF])); }); subject.onRemove(mockPokemon); diff --git a/src/test/boss-pokemon.test.ts b/src/test/boss-pokemon.test.ts new file mode 100644 index 00000000000..8a0a0e01617 --- /dev/null +++ b/src/test/boss-pokemon.test.ts @@ -0,0 +1,214 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "./utils/gameManager"; +import { Species } from "#app/enums/species"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { EFFECTIVE_STATS } from "#app/enums/stat"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { toDmgValue } from "#app/utils"; + +describe("Boss Pokemon / Shields", () => { + const TIMEOUT = 20 * 1000; + + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .disableTrainerWaves() + .disableCrits() + .enemySpecies(Species.RATTATA) + .enemyMoveset(Moves.SPLASH) + .enemyHeldItems([]) + .startingLevel(1000) + .moveset([Moves.FALSE_SWIPE, Moves.SUPER_FANG, Moves.SPLASH]) + .ability(Abilities.NO_GUARD); + }); + + it("Pokemon should get shields based on their Species and level and the current wave", async () => { + let level = 50; + let wave = 5; + + // On normal waves, no shields... + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.RATTATA))).toBe(0); + // ... expect (sub)-legendary and mythical Pokemon who always get shields + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.MEW))).toBe(2); + // Pokemon with 670+ BST get an extra shield + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.MEWTWO))).toBe(3); + + // Every 10 waves will always be a boss Pokemon with shield(s) + wave = 50; + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.RATTATA))).toBe(2); + // Every extra 250 waves adds a shield + wave += 250; + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.RATTATA))).toBe(3); + wave += 750; + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.RATTATA))).toBe(6); + + // Pokemon above level 100 get an extra shield + level = 100; + expect(game.scene.getEncounterBossSegments(wave, level, getPokemonSpecies(Species.RATTATA))).toBe(7); + }, TIMEOUT); + + it("should reduce the number of shields if we are in a double battle", async () => { + game.override + .battleType("double") + .startingWave(150); // Floor 150 > 2 shields / 3 health segments + + await game.classicMode.startBattle([ Species.MEWTWO ]); + + const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; + const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!; + expect(boss1.isBoss()).toBe(true); + expect(boss1.bossSegments).toBe(2); + expect(boss2.isBoss()).toBe(true); + expect(boss2.bossSegments).toBe(2); + }, TIMEOUT); + + it("shields should stop overflow damage and give stat stage boosts when broken", async () => { + game.override.startingWave(150); // Floor 150 > 2 shields / 3 health segments + + await game.classicMode.startBattle([ Species.MEWTWO ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + const segmentHp = enemyPokemon.getMaxHp() / enemyPokemon.bossSegments; + expect(enemyPokemon.isBoss()).toBe(true); + expect(enemyPokemon.bossSegments).toBe(3); + expect(getTotalStatStageBoosts(enemyPokemon)).toBe(0); + + game.move.select(Moves.SUPER_FANG); // Enough to break the first shield + await game.toNextTurn(); + + // Broke 1st of 2 shields, health at 2/3rd + expect(enemyPokemon.bossSegmentIndex).toBe(1); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - toDmgValue(segmentHp)); + // Breaking the shield gives a +1 boost to ATK, DEF, SP ATK, SP DEF or SPD + expect(getTotalStatStageBoosts(enemyPokemon)).toBe(1); + + game.move.select(Moves.FALSE_SWIPE); // Enough to break last shield but not kill + await game.toNextTurn(); + + expect(enemyPokemon.bossSegmentIndex).toBe(0); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - toDmgValue(2 * segmentHp)); + // Breaking the last shield gives a +2 boost to ATK, DEF, SP ATK, SP DEF or SPD + expect(getTotalStatStageBoosts(enemyPokemon)).toBe(3); + + }, TIMEOUT); + + it("breaking multiple shields at once requires extra damage", async () => { + game.override + .battleType("double") + .enemyHealthSegments(5); + + await game.classicMode.startBattle([ Species.MEWTWO ]); + + // In this test we want to break through 3 shields at once + const brokenShields = 3; + + const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; + const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments; + const requiredDamageBoss1 = boss1SegmentHp * (1 + Math.pow(2, brokenShields)); + expect(boss1.isBoss()).toBe(true); + expect(boss1.bossSegments).toBe(5); + expect(boss1.bossSegmentIndex).toBe(4); + + // Not enough damage to break through all shields + boss1.damageAndUpdate(Math.floor(requiredDamageBoss1 - 5)); + expect(boss1.bossSegmentIndex).toBe(1); + expect(boss1.hp).toBe(boss1.getMaxHp() - toDmgValue(boss1SegmentHp * 3)); + + const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!; + const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments; + const requiredDamageBoss2 = boss2SegmentHp * (1 + Math.pow(2, brokenShields)); + + expect(boss2.isBoss()).toBe(true); + expect(boss2.bossSegments).toBe(5); + + // Enough damage to break through all shields + boss2.damageAndUpdate(Math.ceil(requiredDamageBoss2)); + expect(boss2.bossSegmentIndex).toBe(0); + expect(boss2.hp).toBe(boss2.getMaxHp() - toDmgValue(boss2SegmentHp * 4)); + + }, TIMEOUT); + + it("the number of stat stage boosts is consistent when several shields are broken at once", async () => { + const shieldsToBreak = 4; + + game.override + .battleType("double") + .enemyHealthSegments(shieldsToBreak + 1); + + await game.classicMode.startBattle([ Species.MEWTWO ]); + + const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; + const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments; + const singleShieldDamage = Math.ceil(boss1SegmentHp); + expect(boss1.isBoss()).toBe(true); + expect(boss1.bossSegments).toBe(shieldsToBreak + 1); + expect(boss1.bossSegmentIndex).toBe(shieldsToBreak); + expect(getTotalStatStageBoosts(boss1)).toBe(0); + + + let totalStatStages = 0; + + // Break the shields one by one + for (let i = 1; i <= shieldsToBreak; i++) { + boss1.damageAndUpdate(singleShieldDamage); + expect(boss1.bossSegmentIndex).toBe(shieldsToBreak - i); + expect(boss1.hp).toBe(boss1.getMaxHp() - toDmgValue(boss1SegmentHp * i)); + // Do nothing and go to next turn so that the StatStageChangePhase gets applied + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + // All broken shields give +1 stat boost, except the last two that gives +2 + totalStatStages += i >= shieldsToBreak -1? 2 : 1; + expect(getTotalStatStageBoosts(boss1)).toBe(totalStatStages); + } + + const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!; + const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments; + const requiredDamage = boss2SegmentHp * (1 + Math.pow(2, shieldsToBreak - 1)); + + expect(boss2.isBoss()).toBe(true); + expect(boss2.bossSegments).toBe(shieldsToBreak + 1); + expect(boss2.bossSegmentIndex).toBe(shieldsToBreak); + expect(getTotalStatStageBoosts(boss2)).toBe(0); + + // Enough damage to break all shields at once + boss2.damageAndUpdate(Math.ceil(requiredDamage)); + expect(boss2.bossSegmentIndex).toBe(0); + expect(boss2.hp).toBe(boss2.getMaxHp() - toDmgValue(boss2SegmentHp * shieldsToBreak)); + // Do nothing and go to next turn so that the StatStageChangePhase gets applied + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + expect(getTotalStatStageBoosts(boss2)).toBe(totalStatStages); + + }, TIMEOUT); + + /** + * Gets the sum of the effective stat stage boosts for the given Pokemon + * @param enemyPokemon the pokemon to get stats from + * @returns the total stats boosts + */ + function getTotalStatStageBoosts(enemyPokemon: EnemyPokemon): number { + let boosts = 0; + for (const s of EFFECTIVE_STATS) { + boosts += enemyPokemon.getStatStage(s); + } + return boosts; + } +}); + diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index 28f1b7f0a6c..4f00e843b47 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -12,6 +12,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Egg Generation Tests", () => { let phaserGame: Phaser.Game; let game: GameManager; + const EGG_HATCH_COUNT: integer = 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -47,14 +48,21 @@ describe("Egg Generation Tests", () => { expect(result).toBe(expectedSpecies); }); - it("should hatch an Arceus. Set from legendary gacha", async () => { + it("should hatch an Arceus around half the time. Set from legendary gacha", async () => { const scene = game.scene; const timestamp = new Date(2024, 6, 10, 15, 0, 0, 0).getTime(); const expectedSpecies = Species.ARCEUS; + let gachaSpeciesCount = 0; - const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.MASTER }).generatePlayerPokemon(scene).species.speciesId; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.MASTER }).generatePlayerPokemon(scene).species.speciesId; + if (result === expectedSpecies) { + gachaSpeciesCount++; + } + } - expect(result).toBe(expectedSpecies); + expect(gachaSpeciesCount).toBeGreaterThan(0.4 * EGG_HATCH_COUNT); + expect(gachaSpeciesCount).toBeLessThan(0.6 * EGG_HATCH_COUNT); }); it("should hatch an Arceus. Set from species", () => { const scene = game.scene; @@ -156,7 +164,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const eggMoveIndex = new Egg({ scene }).eggMoveIndex; - const result = eggMoveIndex && eggMoveIndex >= 0 && eggMoveIndex <= 3; + const result = !Utils.isNullOrUndefined(eggMoveIndex) && eggMoveIndex >= 0 && eggMoveIndex <= 3; expect(result).toBe(true); }); @@ -309,4 +317,63 @@ describe("Egg Generation Tests", () => { expect(egg.variantTier).toBe(VariantTier.EPIC); }); + + it("should generate egg moves, species, shinyness, and ability unpredictably for the egg gacha", () => { + const scene = game.scene; + scene.setSeed("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + scene.resetSeed(); + + const firstEgg = new Egg({scene, sourceType: EggSourceType.GACHA_SHINY, tier: EggTier.COMMON}); + const firstHatch = firstEgg.generatePlayerPokemon(scene); + let diffEggMove = false; + let diffSpecies = false; + let diffShiny = false; + let diffAbility = false; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + scene.gameData.unlockPity[EggTier.COMMON] = 0; + scene.setSeed("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + scene.resetSeed(); // Make sure that eggs are unpredictable even if using same seed + + const newEgg = new Egg({scene, sourceType: EggSourceType.GACHA_SHINY, tier: EggTier.COMMON}); + const newHatch = newEgg.generatePlayerPokemon(scene); + diffEggMove = diffEggMove || (newEgg.eggMoveIndex !== firstEgg.eggMoveIndex); + diffSpecies = diffSpecies || (newHatch.species.speciesId !== firstHatch.species.speciesId); + diffShiny = diffShiny || (newHatch.shiny !== firstHatch.shiny); + diffAbility = diffAbility || (newHatch.abilityIndex !== firstHatch.abilityIndex); + } + + expect(diffEggMove).toBe(true); + expect(diffSpecies).toBe(true); + expect(diffShiny).toBe(true); + expect(diffAbility).toBe(true); + }); + + it("should generate egg moves, shinyness, and ability unpredictably for species eggs", () => { + const scene = game.scene; + scene.setSeed("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + scene.resetSeed(); + + const firstEgg = new Egg({scene, species: Species.BULBASAUR}); + const firstHatch = firstEgg.generatePlayerPokemon(scene); + let diffEggMove = false; + let diffSpecies = false; + let diffShiny = false; + let diffAbility = false; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + scene.setSeed("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + scene.resetSeed(); // Make sure that eggs are unpredictable even if using same seed + + const newEgg = new Egg({scene, species: Species.BULBASAUR}); + const newHatch = newEgg.generatePlayerPokemon(scene); + diffEggMove = diffEggMove || (newEgg.eggMoveIndex !== firstEgg.eggMoveIndex); + diffSpecies = diffSpecies || (newHatch.species.speciesId !== firstHatch.species.speciesId); + diffShiny = diffShiny || (newHatch.shiny !== firstHatch.shiny); + diffAbility = diffAbility || (newHatch.abilityIndex !== firstHatch.abilityIndex); + } + + expect(diffEggMove).toBe(true); + expect(diffSpecies).toBe(false); + expect(diffShiny).toBe(true); + expect(diffAbility).toBe(true); + }); }); diff --git a/src/test/eggs/manaphy-egg.test.ts b/src/test/eggs/manaphy-egg.test.ts new file mode 100644 index 00000000000..257bf330bb8 --- /dev/null +++ b/src/test/eggs/manaphy-egg.test.ts @@ -0,0 +1,118 @@ +import { Egg } from "#app/data/egg"; +import { EggSourceType } from "#app/enums/egg-source-types"; +import { EggTier } from "#app/enums/egg-type"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Manaphy Eggs", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const EGG_HATCH_COUNT: integer = 48; + let rngSweepProgress: number = 0; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + game = new GameManager(phaserGame); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + vi.restoreAllMocks(); + }); + + beforeEach(async () => { + await game.importData("src/test/utils/saves/everything.prsv"); + + /** + * In our tests, we will perform an "RNG sweep" by letting rngSweepProgress + * increase uniformly from 0 to 1 in order to get a uniform sample of the + * possible RNG outcomes. This will let us quickly and consistently find + * the probability of each RNG outcome. + */ + vi.spyOn(Phaser.Math.RND, "realInRange").mockImplementation((min: number, max: number) => { + return rngSweepProgress * (max - min) + min; + }); + }); + + it("should have correct Manaphy rates and Rare Egg Move rates, from the egg gacha", () => { + const scene = game.scene; + + let manaphyCount = 0; + let phioneCount = 0; + let rareEggMoveCount = 0; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + rngSweepProgress = (2 * i + 1) / (2 * EGG_HATCH_COUNT); + + const newEgg = new Egg({ scene, tier: EggTier.COMMON, sourceType: EggSourceType.GACHA_SHINY, id: 204 }); + const newHatch = newEgg.generatePlayerPokemon(scene); + if (newHatch.species.speciesId === Species.MANAPHY) { + manaphyCount++; + } else if (newHatch.species.speciesId === Species.PHIONE) { + phioneCount++; + } + if (newEgg.eggMoveIndex === 3) { + rareEggMoveCount++; + } + } + + expect(manaphyCount + phioneCount).toBe(EGG_HATCH_COUNT); + expect(manaphyCount).toBe(1/8 * EGG_HATCH_COUNT); + expect(rareEggMoveCount).toBe(1/12 * EGG_HATCH_COUNT); + }); + + it("should have correct Manaphy rates and Rare Egg Move rates, from Phione species eggs", () => { + const scene = game.scene; + + let manaphyCount = 0; + let phioneCount = 0; + let rareEggMoveCount = 0; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + rngSweepProgress = (2 * i + 1) / (2 * EGG_HATCH_COUNT); + + const newEgg = new Egg({ scene, species: Species.PHIONE, sourceType: EggSourceType.SAME_SPECIES_EGG }); + const newHatch = newEgg.generatePlayerPokemon(scene); + if (newHatch.species.speciesId === Species.MANAPHY) { + manaphyCount++; + } else if (newHatch.species.speciesId === Species.PHIONE) { + phioneCount++; + } + if (newEgg.eggMoveIndex === 3) { + rareEggMoveCount++; + } + } + + expect(manaphyCount + phioneCount).toBe(EGG_HATCH_COUNT); + expect(manaphyCount).toBe(1/8 * EGG_HATCH_COUNT); + expect(rareEggMoveCount).toBe(1/6 * EGG_HATCH_COUNT); + }); + + it("should have correct Manaphy rates and Rare Egg Move rates, from Manaphy species eggs", () => { + const scene = game.scene; + + let manaphyCount = 0; + let phioneCount = 0; + let rareEggMoveCount = 0; + for (let i = 0; i < EGG_HATCH_COUNT; i++) { + rngSweepProgress = (2 * i + 1) / (2 * EGG_HATCH_COUNT); + + const newEgg = new Egg({ scene, species: Species.MANAPHY, sourceType: EggSourceType.SAME_SPECIES_EGG }); + const newHatch = newEgg.generatePlayerPokemon(scene); + if (newHatch.species.speciesId === Species.MANAPHY) { + manaphyCount++; + } else if (newHatch.species.speciesId === Species.PHIONE) { + phioneCount++; + } + if (newEgg.eggMoveIndex === 3) { + rareEggMoveCount++; + } + } + + expect(phioneCount).toBe(0); + expect(manaphyCount).toBe(EGG_HATCH_COUNT); + expect(rareEggMoveCount).toBe(1/6 * EGG_HATCH_COUNT); + }); +}); diff --git a/src/test/endless_boss.test.ts b/src/test/endless_boss.test.ts index e983be245b6..8a564695e42 100644 --- a/src/test/endless_boss.test.ts +++ b/src/test/endless_boss.test.ts @@ -32,7 +32,7 @@ describe("Endless Boss", () => { it(`should spawn a minor boss every ${EndlessBossWave.Minor} waves in END biome in Endless`, async () => { game.override.startingWave(EndlessBossWave.Minor); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -44,7 +44,7 @@ describe("Endless Boss", () => { it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Endless`, async () => { game.override.startingWave(EndlessBossWave.Major); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -56,7 +56,7 @@ describe("Endless Boss", () => { it(`should spawn a minor boss every ${EndlessBossWave.Minor} waves in END biome in Spliced Endless`, async () => { game.override.startingWave(EndlessBossWave.Minor); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.SPLICED_ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.SPLICED_ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -68,7 +68,7 @@ describe("Endless Boss", () => { it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Spliced Endless`, async () => { game.override.startingWave(EndlessBossWave.Major); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.SPLICED_ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.SPLICED_ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -80,7 +80,7 @@ describe("Endless Boss", () => { it(`should NOT spawn major or minor boss outside wave ${EndlessBossWave.Minor}s in END biome`, async () => { game.override.startingWave(EndlessBossWave.Minor - 1); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).not.toBe(EndlessBossWave.Minor); expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); diff --git a/src/test/escape-calculations.test.ts b/src/test/escape-calculations.test.ts new file mode 100644 index 00000000000..ecf22fc74aa --- /dev/null +++ b/src/test/escape-calculations.test.ts @@ -0,0 +1,303 @@ +import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Command } from "#app/ui/command-ui-handler"; +import * as Utils from "#app/utils"; +import { Abilities } from "#enums/abilities"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Escape chance calculations", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.BULBASAUR) + .enemyAbility(Abilities.INSOMNIA) + .ability(Abilities.INSOMNIA); + }); + + it("single non-boss opponent", async () => { + await game.classicMode.startBattle([Species.BULBASAUR]); + + const playerPokemon = game.scene.getPlayerField(); + const enemyField = game.scene.getEnemyField(); + const enemySpeed = 100; + // set enemyPokemon's speed to 100 + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); + + const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + commandPhase.handleCommand(Command.RUN, 0); + + await game.phaseInterceptor.to(AttemptRunPhase, false); + const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const escapePercentage = new Utils.NumberHolder(0); + + // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping + const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [ + { pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 }, + { pokemonSpeedRatio: 0.1, escapeAttempts: 0, expectedEscapeChance: 7 }, + { pokemonSpeedRatio: 0.25, escapeAttempts: 0, expectedEscapeChance: 11 }, + { pokemonSpeedRatio: 0.5, escapeAttempts: 0, expectedEscapeChance: 16 }, + { pokemonSpeedRatio: 0.8, escapeAttempts: 0, expectedEscapeChance: 23 }, + { pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 28 }, + { pokemonSpeedRatio: 1.2, escapeAttempts: 0, expectedEscapeChance: 32 }, + { pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 39 }, + { pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 73 }, + { pokemonSpeedRatio: 3.8, escapeAttempts: 0, expectedEscapeChance: 91 }, + { pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 4.2, escapeAttempts: 0, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 95 }, + + // retries section + { pokemonSpeedRatio: 0.4, escapeAttempts: 1, expectedEscapeChance: 24 }, + { pokemonSpeedRatio: 1.6, escapeAttempts: 2, expectedEscapeChance: 61 }, + { pokemonSpeedRatio: 3.7, escapeAttempts: 5, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 0.2, escapeAttempts: 2, expectedEscapeChance: 30 }, + { pokemonSpeedRatio: 1, escapeAttempts: 3, expectedEscapeChance: 58 }, + { pokemonSpeedRatio: 2.9, escapeAttempts: 0, expectedEscapeChance: 70 }, + { pokemonSpeedRatio: 0.01, escapeAttempts: 7, expectedEscapeChance: 75 }, + { pokemonSpeedRatio: 16.2, escapeAttempts: 4, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 80 }, + ]; + + for (let i = 0; i < escapeChances.length; i++) { + // sets the number of escape attempts to the required amount + game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts; + // set playerPokemon's speed to a multiple of the enemySpeed + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * enemySpeed]); + phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + } + }, 20000); + + it("double non-boss opponent", async () => { + game.override.battleType("double"); + await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]); + + const playerPokemon = game.scene.getPlayerField(); + const enemyField = game.scene.getEnemyField(); + const enemyASpeed = 70; + const enemyBSpeed = 30; + // gets the sum of the speed of the two pokemon + const totalEnemySpeed = enemyASpeed + enemyBSpeed; + // this is used to find the ratio of the player's first pokemon + const playerASpeedPercentage = 0.4; + // set enemyAPokemon's speed to 70 + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]); + // set enemyBPokemon's speed to 30 + vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); + + const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + commandPhase.handleCommand(Command.RUN, 0); + + await game.phaseInterceptor.to(AttemptRunPhase, false); + const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const escapePercentage = new Utils.NumberHolder(0); + + // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping + const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [ + { pokemonSpeedRatio: 0.3, escapeAttempts: 0, expectedEscapeChance: 12 }, + { pokemonSpeedRatio: 0.7, escapeAttempts: 0, expectedEscapeChance: 21 }, + { pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 39 }, + { pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 73 }, + { pokemonSpeedRatio: 9, escapeAttempts: 0, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 }, + { pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 28 }, + { pokemonSpeedRatio: 4.3, escapeAttempts: 0, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 2.7, escapeAttempts: 0, expectedEscapeChance: 66 }, + { pokemonSpeedRatio: 2.1, escapeAttempts: 0, expectedEscapeChance: 52 }, + { pokemonSpeedRatio: 1.8, escapeAttempts: 0, expectedEscapeChance: 46 }, + { pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 95 }, + + // retries section + { pokemonSpeedRatio: 0.9, escapeAttempts: 1, expectedEscapeChance: 35 }, + { pokemonSpeedRatio: 3.6, escapeAttempts: 2, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 0.03, escapeAttempts: 7, expectedEscapeChance: 76 }, + { pokemonSpeedRatio: 0.02, escapeAttempts: 7, expectedEscapeChance: 75 }, + { pokemonSpeedRatio: 1, escapeAttempts: 5, expectedEscapeChance: 78 }, + { pokemonSpeedRatio: 0.7, escapeAttempts: 3, expectedEscapeChance: 51 }, + { pokemonSpeedRatio: 2.4, escapeAttempts: 9, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 1.8, escapeAttempts: 7, expectedEscapeChance: 95 }, + { pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 95 }, + + ]; + + for (let i = 0; i < escapeChances.length; i++) { + // sets the number of escape attempts to the required amount + game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts; + // set the first playerPokemon's speed to a multiple of the enemySpeed + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage)]); + // set the second playerPokemon's speed to the remaining value of speed + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]]); + phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + // checks to make sure the escape values are the same + expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + // checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed + expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed); + } + }, 20000); + + it("single boss opponent", async () => { + game.override.startingWave(10); + await game.classicMode.startBattle([Species.BULBASAUR]); + + const playerPokemon = game.scene.getPlayerField()!; + const enemyField = game.scene.getEnemyField()!; + const enemySpeed = 100; + // set enemyPokemon's speed to 100 + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); + + const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + commandPhase.handleCommand(Command.RUN, 0); + + await game.phaseInterceptor.to(AttemptRunPhase, false); + const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const escapePercentage = new Utils.NumberHolder(0); + + // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping + const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [ + { pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 }, + { pokemonSpeedRatio: 0.1, escapeAttempts: 0, expectedEscapeChance: 5 }, + { pokemonSpeedRatio: 0.25, escapeAttempts: 0, expectedEscapeChance: 6 }, + { pokemonSpeedRatio: 0.5, escapeAttempts: 0, expectedEscapeChance: 7 }, + { pokemonSpeedRatio: 0.8, escapeAttempts: 0, expectedEscapeChance: 8 }, + { pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 8 }, + { pokemonSpeedRatio: 1.2, escapeAttempts: 0, expectedEscapeChance: 9 }, + { pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 10 }, + { pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 15 }, + { pokemonSpeedRatio: 3.8, escapeAttempts: 0, expectedEscapeChance: 18 }, + { pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 18 }, + { pokemonSpeedRatio: 4.2, escapeAttempts: 0, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 4.7, escapeAttempts: 0, expectedEscapeChance: 21 }, + { pokemonSpeedRatio: 5, escapeAttempts: 0, expectedEscapeChance: 22 }, + { pokemonSpeedRatio: 5.9, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 6.7, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 25 }, + + // retries section + { pokemonSpeedRatio: 0.4, escapeAttempts: 1, expectedEscapeChance: 8 }, + { pokemonSpeedRatio: 1.6, escapeAttempts: 2, expectedEscapeChance: 14 }, + { pokemonSpeedRatio: 3.7, escapeAttempts: 5, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 0.2, escapeAttempts: 2, expectedEscapeChance: 10 }, + { pokemonSpeedRatio: 1, escapeAttempts: 3, expectedEscapeChance: 14 }, + { pokemonSpeedRatio: 2.9, escapeAttempts: 0, expectedEscapeChance: 15 }, + { pokemonSpeedRatio: 0.01, escapeAttempts: 7, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 16.2, escapeAttempts: 4, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 18 }, + { pokemonSpeedRatio: 4.5, escapeAttempts: 1, expectedEscapeChance: 22 }, + { pokemonSpeedRatio: 6.8, escapeAttempts: 6, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 5.2, escapeAttempts: 8, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 4.7, escapeAttempts: 10, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 5.1, escapeAttempts: 1, expectedEscapeChance: 24 }, + { pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 5.9, escapeAttempts: 2, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 6.1, escapeAttempts: 3, expectedEscapeChance: 25 }, + + ]; + + for (let i = 0; i < escapeChances.length; i++) { + // sets the number of escape attempts to the required amount + game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts; + // set playerPokemon's speed to a multiple of the enemySpeed + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * enemySpeed]); + phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + } + }, 20000); + + it("double boss opponent", async () => { + game.override.battleType("double"); + game.override.startingWave(10); + await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]); + + const playerPokemon = game.scene.getPlayerField(); + const enemyField = game.scene.getEnemyField(); + const enemyASpeed = 70; + const enemyBSpeed = 30; + // gets the sum of the speed of the two pokemon + const totalEnemySpeed = enemyASpeed + enemyBSpeed; + // this is used to find the ratio of the player's first pokemon + const playerASpeedPercentage = 0.8; + // set enemyAPokemon's speed to 70 + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]); + // set enemyBPokemon's speed to 30 + vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); + + const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + commandPhase.handleCommand(Command.RUN, 0); + + await game.phaseInterceptor.to(AttemptRunPhase, false); + const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const escapePercentage = new Utils.NumberHolder(0); + + // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping + const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [ + { pokemonSpeedRatio: 0.3, escapeAttempts: 0, expectedEscapeChance: 6 }, + { pokemonSpeedRatio: 0.7, escapeAttempts: 0, expectedEscapeChance: 7 }, + { pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 10 }, + { pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 15 }, + { pokemonSpeedRatio: 9, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 }, + { pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 8 }, + { pokemonSpeedRatio: 4.3, escapeAttempts: 0, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 2.7, escapeAttempts: 0, expectedEscapeChance: 14 }, + { pokemonSpeedRatio: 2.1, escapeAttempts: 0, expectedEscapeChance: 12 }, + { pokemonSpeedRatio: 1.8, escapeAttempts: 0, expectedEscapeChance: 11 }, + { pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 18 }, + { pokemonSpeedRatio: 5.7, escapeAttempts: 0, expectedEscapeChance: 24 }, + { pokemonSpeedRatio: 5, escapeAttempts: 0, expectedEscapeChance: 22 }, + { pokemonSpeedRatio: 6.1, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 6.8, escapeAttempts: 0, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 25 }, + + // retries section + { pokemonSpeedRatio: 0.9, escapeAttempts: 1, expectedEscapeChance: 10 }, + { pokemonSpeedRatio: 3.6, escapeAttempts: 2, expectedEscapeChance: 21 }, + { pokemonSpeedRatio: 0.03, escapeAttempts: 7, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 0.02, escapeAttempts: 7, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 1, escapeAttempts: 5, expectedEscapeChance: 18 }, + { pokemonSpeedRatio: 0.7, escapeAttempts: 3, expectedEscapeChance: 13 }, + { pokemonSpeedRatio: 2.4, escapeAttempts: 9, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 1.8, escapeAttempts: 7, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 3, escapeAttempts: 1, expectedEscapeChance: 17 }, + { pokemonSpeedRatio: 4.5, escapeAttempts: 3, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 3.7, escapeAttempts: 1, expectedEscapeChance: 19 }, + { pokemonSpeedRatio: 6.5, escapeAttempts: 1, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 12, escapeAttempts: 4, expectedEscapeChance: 25 }, + { pokemonSpeedRatio: 5.2, escapeAttempts: 2, expectedEscapeChance: 25 }, + + ]; + + for (let i = 0; i < escapeChances.length; i++) { + // sets the number of escape attempts to the required amount + game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts; + // set the first playerPokemon's speed to a multiple of the enemySpeed + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage)]); + // set the second playerPokemon's speed to the remaining value of speed + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]]); + phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); + // checks to make sure the escape values are the same + expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); + // checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed + expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed); + } + }, 20000); +}); diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index f9123cf6d9a..16922babd7c 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -2,10 +2,10 @@ import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } fr import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; +import * as Utils from "#app/utils"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "./utils/testUtils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Evolution", () => { let phaserGame: Phaser.Game; @@ -78,12 +78,15 @@ describe("Evolution", () => { const nincada = game.scene.getPlayerPokemon()!; nincada.abilityIndex = 2; + nincada.metBiome = -1; nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm()); const ninjask = game.scene.getParty()[0]; const shedinja = game.scene.getParty()[1]; expect(ninjask.abilityIndex).toBe(2); expect(shedinja.abilityIndex).toBe(1); + // Regression test for https://github.com/pagefaultgames/pokerogue/issues/3842 + expect(shedinja.metBiome).toBe(-1); }, TIMEOUT); it("should set wild delay to NONE by default", () => { @@ -95,7 +98,7 @@ describe("Evolution", () => { it("should increase both HP and max HP when evolving", async () => { game.override.moveset([Moves.SURF]) .enemySpecies(Species.GOLEM) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .startingWave(21) .startingLevel(16) .enemyLevel(50); @@ -122,7 +125,7 @@ describe("Evolution", () => { it("should not fully heal HP when evolving", async () => { game.override.moveset([Moves.SURF]) .enemySpecies(Species.GOLEM) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .startingWave(21) .startingLevel(13) .enemyLevel(30); @@ -148,4 +151,28 @@ describe("Evolution", () => { expect(cyndaquil.hp).toBeGreaterThan(hpBefore); expect(cyndaquil.hp).toBeLessThan(cyndaquil.getMaxHp()); }, TIMEOUT); + + it("should handle rng-based split evolution", async () => { + /* this test checks to make sure that tandemaus will + * evolve into a 3 family maushold 25% of the time + * and a 4 family maushold the other 75% of the time + * This is done by using the getEvolution method in pokemon.ts + * getEvolution will give back the form that the pokemon can evolve into + * It does this by checking the pokemon conditions in pokemon-forms.ts + * For tandemaus, the conditions are random due to a randSeedInt(4) + * If the value is 0, it's a 3 family maushold, whereas if the value is + * 1, 2 or 3, it's a 4 family maushold + */ + await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus + const playerPokemon = game.scene.getPlayerPokemon()!; + playerPokemon.level = 25; // tandemaus evolves at level 25 + vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold + const threeForm = playerPokemon.getEvolution()!; + expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" + for (let f = 1; f < 4; f++) { + vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds + const fourForm = playerPokemon.getEvolution()!; + expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null + } + }, TIMEOUT); }); diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts deleted file mode 100644 index 2028764115c..00000000000 --- a/src/test/evolutions/evolutions.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as Utils from "#app/utils"; -import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; - -describe("Evolution tests", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - }); - - it("tandemaus evolution form test", async () => { - /* this test checks to make sure that tandemaus will - * evolve into a 3 family maushold 25% of the time - * and a 4 family maushold the other 75% of the time - * This is done by using the getEvolution method in pokemon.ts - * getEvolution will give back the form that the pokemon can evolve into - * It does this by checking the pokemon conditions in pokemon-forms.ts - * For tandemaus, the conditions are random due to a randSeedInt(4) - * If the value is 0, it's a 3 family maushold, whereas if the value is - * 1, 2 or 3, it's a 4 family maushold - */ - await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus - const playerPokemon = game.scene.getPlayerPokemon()!; - playerPokemon.level = 25; // tandemaus evolves at level 25 - vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold - const threeForm = playerPokemon.getEvolution()!; - expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" - for (let f = 1; f < 4; f++) { - vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds - const fourForm = playerPokemon.getEvolution()!; - expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null - } - }, 5000); -}); diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts index d597cd5219c..f7c1cf8bc3d 100644 --- a/src/test/field/pokemon.test.ts +++ b/src/test/field/pokemon.test.ts @@ -1,6 +1,8 @@ import { Species } from "#app/enums/species"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; +import { PokeballType } from "#app/enums/pokeball"; +import BattleScene from "#app/battle-scene"; describe("Spec - Pokemon", () => { let phaserGame: Phaser.Game; @@ -28,4 +30,37 @@ describe("Spec - Pokemon", () => { expect(pkm.trySetStatus(undefined)).toBe(true); }); + + describe("Add To Party", () => { + let scene: BattleScene; + + beforeEach(async () => { + game.override.enemySpecies(Species.ZUBAT); + await game.classicMode.runToSummon([Species.ABRA, Species.ABRA, Species.ABRA, Species.ABRA, Species.ABRA]); // 5 Abra, only 1 slot left + scene = game.scene; + }); + + it("should append a new pokemon by default", async () => { + const zubat = scene.getEnemyPokemon()!; + zubat.addToParty(PokeballType.LUXURY_BALL); + + const party = scene.getParty(); + expect(party).toHaveLength(6); + party.forEach((pkm, index) =>{ + expect(pkm.species.speciesId).toBe(index === 5 ? Species.ZUBAT : Species.ABRA); + }); + }); + + it("should put a new pokemon into the passed slotIndex", async () => { + const slotIndex = 1; + const zubat = scene.getEnemyPokemon()!; + zubat.addToParty(PokeballType.LUXURY_BALL, slotIndex); + + const party = scene.getParty(); + expect(party).toHaveLength(6); + party.forEach((pkm, index) =>{ + expect(pkm.species.speciesId).toBe(index === slotIndex ? Species.ZUBAT : Species.ABRA); + }); + }); + }); }); diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index 0f59572619b..fee4dc6c8f6 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -1,6 +1,10 @@ +import { StatusEffect } from "#app/data/status-effect"; +import { Abilities } from "#app/enums/abilities"; import { Biome } from "#app/enums/biome"; +import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { GameModes } from "#app/game-mode"; +import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; @@ -20,7 +24,13 @@ describe("Final Boss", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.startingWave(FinalWave.Classic).startingBiome(Biome.END).disableCrits(); + game.override + .startingWave(FinalWave.Classic) + .startingBiome(Biome.END) + .disableCrits() + .enemyMoveset(Moves.SPLASH) + .moveset([ Moves.SPLASH, Moves.WILL_O_WISP, Moves.DRAGON_PULSE ]) + .startingLevel(10000); }); afterEach(() => { @@ -28,7 +38,7 @@ describe("Final Boss", () => { }); it("should spawn Eternatus on wave 200 in END biome", async () => { - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -37,7 +47,7 @@ describe("Final Boss", () => { it("should NOT spawn Eternatus before wave 200 in END biome", async () => { game.override.startingWave(FinalWave.Classic - 1); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).not.toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -46,7 +56,7 @@ describe("Final Boss", () => { it("should NOT spawn Eternatus outside of END biome", async () => { game.override.startingBiome(Biome.FOREST); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).not.toBe(Biome.END); @@ -54,12 +64,81 @@ describe("Final Boss", () => { }); it("should not have passive enabled on Eternatus", async () => { - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(Species.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); + const eternatus = game.scene.getEnemyPokemon()!; + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + }); + + it("should change form on direct hit down to last boss fragment", async () => { + await game.runToFinalBossEncounter([Species.KYUREM], GameModes.CLASSIC); + await game.phaseInterceptor.to("CommandPhase"); + + // Eternatus phase 1 + const eternatus = game.scene.getEnemyPokemon()!; + const phase1Hp = eternatus.getMaxHp(); + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.formIndex).toBe(0); + expect(eternatus.bossSegments).toBe(4); + expect(eternatus.bossSegmentIndex).toBe(3); + + game.move.select(Moves.DRAGON_PULSE); + await game.toNextTurn(); + + // Eternatus phase 2: changed form, healed and restored its shields + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.hp).toBeGreaterThan(phase1Hp); + expect(eternatus.hp).toBe(eternatus.getMaxHp()); + expect(eternatus.formIndex).toBe(1); + expect(eternatus.bossSegments).toBe(5); + expect(eternatus.bossSegmentIndex).toBe(4); + const miniBlackHole = eternatus.getHeldItems().find(m => m instanceof TurnHeldItemTransferModifier); + expect(miniBlackHole).toBeDefined(); + expect(miniBlackHole?.stackCount).toBe(1); + }); + + it("should change form on status damage down to last boss fragment", async () => { + game.override.ability(Abilities.NO_GUARD); + + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); + await game.phaseInterceptor.to("CommandPhase"); + + // Eternatus phase 1 + const eternatus = game.scene.getEnemyPokemon()!; + const phase1Hp = eternatus.getMaxHp(); + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.formIndex).toBe(0); + expect(eternatus.bossSegments).toBe(4); + expect(eternatus.bossSegmentIndex).toBe(3); + + game.move.select(Moves.WILL_O_WISP); + await game.toNextTurn(); + expect(eternatus.status?.effect).toBe(StatusEffect.BURN); + + const tickDamage = phase1Hp - eternatus.hp; + const lastShieldHp = Math.ceil(phase1Hp / eternatus.bossSegments); + // Stall until the burn is one hit away from breaking the last shield + while (eternatus.hp - tickDamage > lastShieldHp) { + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + } + + expect(eternatus.bossSegmentIndex).toBe(1); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Eternatus phase 2: changed form, healed and restored its shields + expect(eternatus.hp).toBeGreaterThan(phase1Hp); + expect(eternatus.hp).toBe(eternatus.getMaxHp()); + expect(eternatus.status).toBeFalsy(); + expect(eternatus.formIndex).toBe(1); + expect(eternatus.bossSegments).toBe(5); + expect(eternatus.bossSegmentIndex).toBe(4); + const miniBlackHole = eternatus.getHeldItems().find(m => m instanceof TurnHeldItemTransferModifier); + expect(miniBlackHole).toBeDefined(); + expect(miniBlackHole?.stackCount).toBe(1); }); - it.todo("should change form on direct hit down to last boss fragment", () => {}); }); diff --git a/src/test/items/dire_hit.test.ts b/src/test/items/dire_hit.test.ts new file mode 100644 index 00000000000..601552de7f1 --- /dev/null +++ b/src/test/items/dire_hit.test.ts @@ -0,0 +1,97 @@ +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phase from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { TempCritBoosterModifier } from "#app/modifier/modifier"; +import { Mode } from "#app/ui/ui"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Button } from "#app/enums/buttons"; +import { CommandPhase } from "#app/phases/command-phase"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; + +describe("Items - Dire Hit", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phase.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.SPLASH) + .moveset([ Moves.POUND ]) + .startingHeldItems([{ name: "DIRE_HIT" }]) + .battleType("single") + .disableCrits(); + + }, 20000); + + it("should raise CRIT stage by 1", async () => { + await game.startBattle([ + Species.GASTLY + ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemyPokemon, "getCritStage"); + + game.move.select(Moves.POUND); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemyPokemon.getCritStage).toHaveReturnedWith(1); + }, 20000); + + it("should renew how many battles are left of existing DIRE_HIT when picking up new DIRE_HIT", async() => { + game.override.itemRewards([{ name: "DIRE_HIT" }]); + + await game.startBattle([ + Species.PIKACHU + ]); + + game.move.select(Moves.SPLASH); + + await game.doKillOpponents(); + + await game.phaseInterceptor.to(BattleEndPhase); + + const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier) as TempCritBoosterModifier; + expect(modifier.getBattleCount()).toBe(4); + + // Forced DIRE_HIT to spawn in the first slot with override + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to first modifier slot + handler.setCursor(0); + handler.setRowCursor(ShopCursorTarget.REWARDS); + handler.processInput(Button.ACTION); + }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true); + + await game.phaseInterceptor.to(TurnInitPhase); + + // Making sure only one booster is in the modifier list even after picking up another + let count = 0; + for (const m of game.scene.modifiers) { + if (m instanceof TempCritBoosterModifier) { + count++; + expect((m as TempCritBoosterModifier).getBattleCount()).toBe(5); + } + } + expect(count).toBe(1); + }, 20000); +}); diff --git a/src/test/items/double_battle_chance_booster.test.ts b/src/test/items/double_battle_chance_booster.test.ts new file mode 100644 index 00000000000..1d5051fa9e9 --- /dev/null +++ b/src/test/items/double_battle_chance_booster.test.ts @@ -0,0 +1,104 @@ +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { DoubleBattleChanceBoosterModifier } from "#app/modifier/modifier"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; +import { Mode } from "#app/ui/ui"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Button } from "#app/enums/buttons"; + +describe("Items - Double Battle Chance Boosters", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + it("should guarantee double battle with 2 unique tiers", async () => { + game.override + .startingModifier([ + { name: "LURE" }, + { name: "SUPER_LURE" } + ]) + .startingWave(2); + + await game.classicMode.startBattle(); + + expect(game.scene.getEnemyField().length).toBe(2); + }, TIMEOUT); + + it("should guarantee double boss battle with 3 unique tiers", async () => { + game.override + .startingModifier([ + { name: "LURE" }, + { name: "SUPER_LURE" }, + { name: "MAX_LURE" } + ]) + .startingWave(10); + + await game.classicMode.startBattle(); + + const enemyField = game.scene.getEnemyField(); + + expect(enemyField.length).toBe(2); + expect(enemyField[0].isBoss()).toBe(true); + expect(enemyField[1].isBoss()).toBe(true); + }, TIMEOUT); + + it("should renew how many battles are left of existing booster when picking up new booster of same tier", async() => { + game.override + .startingModifier([{ name: "LURE" }]) + .itemRewards([{ name: "LURE" }]) + .moveset(Moves.SPLASH) + .startingLevel(200); + + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + game.move.select(Moves.SPLASH); + + await game.doKillOpponents(); + + await game.phaseInterceptor.to("BattleEndPhase"); + + const modifier = game.scene.findModifier(m => m instanceof DoubleBattleChanceBoosterModifier) as DoubleBattleChanceBoosterModifier; + expect(modifier.getBattleCount()).toBe(9); + + // Forced LURE to spawn in the first slot with override + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to first modifier slot + handler.setCursor(0); + handler.setRowCursor(ShopCursorTarget.REWARDS); + handler.processInput(Button.ACTION); + }, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), true); + + await game.phaseInterceptor.to("TurnInitPhase"); + + // Making sure only one booster is in the modifier list even after picking up another + let count = 0; + for (const m of game.scene.modifiers) { + if (m instanceof DoubleBattleChanceBoosterModifier) { + count++; + const modifierInstance = m as DoubleBattleChanceBoosterModifier; + expect(modifierInstance.getBattleCount()).toBe(modifierInstance.getMaxBattles()); + } + } + expect(count).toBe(1); + }, TIMEOUT); +}); diff --git a/src/test/items/eviolite.test.ts b/src/test/items/eviolite.test.ts index e491784acec..d9991d47a89 100644 --- a/src/test/items/eviolite.test.ts +++ b/src/test/items/eviolite.test.ts @@ -1,16 +1,15 @@ -import { Stat } from "#app/data/pokemon-stat"; -import { EvolutionStatBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/modifier/modifier-type"; -import i18next from "#app/plugins/i18n"; -import * as Utils from "#app/utils"; +import { Stat } from "#enums/stat"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; +import * as Utils from "#app/utils"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { StatBoosterModifier } from "#app/modifier/modifier"; describe("Items - Eviolite", () => { let phaserGame: Phaser.Game; let game: GameManager; + const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phase.Game({ @@ -25,108 +24,65 @@ describe("Items - Eviolite", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override + .battleType("single") + .startingHeldItems([{ name: "EVIOLITE" }]); }); - it("EVIOLITE activates in battle correctly", async() => { - game.override.startingHeldItems([{ name: "EVIOLITE" }]); - const consoleSpy = vi.spyOn(console, "log"); - await game.startBattle([ + it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async() => { + await game.classicMode.startBattle([ Species.PICHU ]); - const partyMember = game.scene.getParty()[0]; + const partyMember = game.scene.getPlayerPokemon()!; - // Checking consoe log to make sure Eviolite is applied when getBattleStat (with the appropriate stat) is called - partyMember.getBattleStat(Stat.DEF); - expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), ""); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Printing dummy console messages along the way so subsequent checks don't pass because of the first - console.log(""); + // Ignore other calculations for simplicity - partyMember.getBattleStat(Stat.SPDEF); - expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), ""); + return Math.floor(statValue.value); + }); - console.log(""); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - partyMember.getBattleStat(Stat.ATK); - expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), ""); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5)); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5)); + }, TIMEOUT); - console.log(""); - - partyMember.getBattleStat(Stat.SPATK); - expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), ""); - - console.log(""); - - partyMember.getBattleStat(Stat.SPD); - expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), ""); - }); - - it("EVIOLITE held by unevolved, unfused pokemon", async() => { - await game.startBattle([ - Species.PICHU - ]); - - const partyMember = game.scene.getParty()[0]; - - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); - - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); - - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); - - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); - - expect(defValue.value / defStat).toBe(1.5); - expect(spDefValue.value / spDefStat).toBe(1.5); - }, 20000); - - it("EVIOLITE held by fully evolved, unfused pokemon", async() => { - await game.startBattle([ + it("should not provide a boost for fully evolved, unfused pokemon", async() => { + await game.classicMode.startBattle([ Species.RAICHU, ]); const partyMember = game.scene.getParty()[0]; - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + // Ignore other calculations for simplicity - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); + return Math.floor(statValue.value); + }); - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); - }, 20000); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat); - it("EVIOLITE held by completely unevolved, fused pokemon", async() => { - await game.startBattle([ + }, TIMEOUT); + + it("should provide 50% boost to DEF and SPDEF for completely unevolved, fused pokemon", async() => { + await game.classicMode.startBattle([ Species.PICHU, Species.CLEFFA ]); - const partyMember = game.scene.getParty()[0]; - const ally = game.scene.getParty()[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -137,35 +93,29 @@ describe("Items - Eviolite", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + // Ignore other calculations for simplicity - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); + return Math.floor(statValue.value); + }); - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - expect(defValue.value / defStat).toBe(1.5); - expect(spDefValue.value / spDefStat).toBe(1.5); - }, 20000); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5)); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5)); + }, TIMEOUT); - it("EVIOLITE held by partially unevolved (base), fused pokemon", async() => { - await game.startBattle([ + it("should provide 25% boost to DEF and SPDEF for partially unevolved (base), fused pokemon", async() => { + await game.classicMode.startBattle([ Species.PICHU, Species.CLEFABLE ]); - const partyMember = game.scene.getParty()[0]; - const ally = game.scene.getParty()[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -176,35 +126,29 @@ describe("Items - Eviolite", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + // Ignore other calculations for simplicity - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); + return Math.floor(statValue.value); + }); - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - expect(defValue.value / defStat).toBe(1.25); - expect(spDefValue.value / spDefStat).toBe(1.25); - }, 20000); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25)); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25)); + }, TIMEOUT); - it("EVIOLITE held by partially unevolved (fusion), fused pokemon", async() => { - await game.startBattle([ + it("should provide 25% boost to DEF and SPDEF for partially unevolved (fusion), fused pokemon", async() => { + await game.classicMode.startBattle([ Species.RAICHU, Species.CLEFFA ]); - const partyMember = game.scene.getParty()[0]; - const ally = game.scene.getParty()[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -215,35 +159,29 @@ describe("Items - Eviolite", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + // Ignore other calculations for simplicity - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); + return Math.floor(statValue.value); + }); - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - expect(defValue.value / defStat).toBe(1.25); - expect(spDefValue.value / spDefStat).toBe(1.25); - }, 20000); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25)); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25)); + }, TIMEOUT); - it("EVIOLITE held by completely evolved, fused pokemon", async() => { - await game.startBattle([ + it("should not provide a boost for fully evolved, fused pokemon", async() => { + await game.classicMode.startBattle([ Species.RAICHU, Species.CLEFABLE ]); - const partyMember = game.scene.getParty()[0]; - const ally = game.scene.getParty()[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -254,24 +192,51 @@ describe("Items - Eviolite", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const defStat = partyMember.getStat(Stat.DEF); - const spDefStat = partyMember.getStat(Stat.SPDEF); + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); - // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - const spDefValue = new Utils.NumberHolder(spDefStat); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + // Ignore other calculations for simplicity - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); + return Math.floor(statValue.value); + }); - // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue); - partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue); + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); - expect(defValue.value / defStat).toBe(1); - expect(spDefValue.value / spDefStat).toBe(1); - }, 20000); + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat); + }, TIMEOUT); + + it("should not provide a boost for Gigantamax Pokémon", async() => { + game.override.starterForms({ + [Species.PIKACHU]: 8, + [Species.EEVEE]: 2, + [Species.DURALUDON]: 1, + [Species.MEOWTH]: 1 + }); + + const gMaxablePokemon = [ Species.PIKACHU, Species.EEVEE, Species.DURALUDON, Species.MEOWTH ]; + + await game.classicMode.startBattle([ + Utils.randItem(gMaxablePokemon) + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { + const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false)); + game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue); + + // Ignore other calculations for simplicity + + return Math.floor(statValue.value); + }); + + const defStat = partyMember.getStat(Stat.DEF, false); + const spDefStat = partyMember.getStat(Stat.SPDEF, false); + + expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat); + expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat); + }, TIMEOUT); }); diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index 09afa9aea0b..d9871616449 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -8,7 +8,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 seconds @@ -38,7 +37,7 @@ describe("Items - Grip Claw", () => { ]) .enemySpecies(Species.SNORLAX) .ability(Abilities.KLUTZ) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyHeldItems([ { name: "BERRY", type: BerryType.SITRUS, count: 2 }, { name: "BERRY", type: BerryType.LUM, count: 2 }, diff --git a/src/test/items/leek.test.ts b/src/test/items/leek.test.ts index 7505b6374a0..af20516ef83 100644 --- a/src/test/items/leek.test.ts +++ b/src/test/items/leek.test.ts @@ -1,7 +1,4 @@ -import { BattlerIndex } from "#app/battle"; -import { CritBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -26,91 +23,64 @@ describe("Items - Leek", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - game.override.disableCrits(); - - game.override.battleType("single"); + game.override + .enemySpecies(Species.MAGIKARP) + .enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]) + .startingHeldItems([{ name: "LEEK" }]) + .moveset([ Moves.TACKLE ]) + .disableCrits() + .battleType("single"); }); - it("LEEK activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "LEEK" }]); - game.override.moveset([Moves.POUND]); - const consoleSpy = vi.spyOn(console, "log"); + it("should raise CRIT stage by 2 when held by FARFETCHD", async () => { await game.startBattle([ Species.FARFETCHD ]); - game.move.select(Moves.POUND); + const enemyMember = game.scene.getEnemyPokemon()!; - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + vi.spyOn(enemyMember, "getCritStage"); - await game.phaseInterceptor.to(MoveEffectPhase); + game.move.select(Moves.TACKLE); - expect(consoleSpy).toHaveBeenCalledWith("Applied", "Leek", ""); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemyMember.getCritStage).toHaveReturnedWith(2); }, 20000); - it("LEEK held by FARFETCHD", async () => { - await game.startBattle([ - Species.FARFETCHD - ]); - - const partyMember = game.scene.getPlayerPokemon()!; - - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); - - expect(critLevel.value).toBe(0); - - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); - - expect(critLevel.value).toBe(2); - }, 20000); - - it("LEEK held by GALAR_FARFETCHD", async () => { + it("should raise CRIT stage by 2 when held by GALAR_FARFETCHD", async () => { await game.startBattle([ Species.GALAR_FARFETCHD ]); - const partyMember = game.scene.getPlayerPokemon()!; + const enemyMember = game.scene.getEnemyPokemon()!; - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + vi.spyOn(enemyMember, "getCritStage"); - expect(critLevel.value).toBe(0); + game.move.select(Moves.TACKLE); - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + await game.phaseInterceptor.to(TurnEndPhase); - expect(critLevel.value).toBe(2); + expect(enemyMember.getCritStage).toHaveReturnedWith(2); }, 20000); - it("LEEK held by SIRFETCHD", async () => { + it("should raise CRIT stage by 2 when held by SIRFETCHD", async () => { await game.startBattle([ Species.SIRFETCHD ]); - const partyMember = game.scene.getPlayerPokemon()!; + const enemyMember = game.scene.getEnemyPokemon()!; - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + vi.spyOn(enemyMember, "getCritStage"); - expect(critLevel.value).toBe(0); + game.move.select(Moves.TACKLE); - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + await game.phaseInterceptor.to(TurnEndPhase); - expect(critLevel.value).toBe(2); + expect(enemyMember.getCritStage).toHaveReturnedWith(2); }, 20000); - it("LEEK held by fused FARFETCHD line (base)", async () => { + it("should raise CRIT stage by 2 when held by FARFETCHD line fused with Pokemon", async () => { // Randomly choose from the Farfetch'd line const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; @@ -119,9 +89,7 @@ describe("Items - Leek", () => { Species.PIKACHU, ]); - const party = game.scene.getParty(); - const partyMember = party[0]; - const ally = party[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -132,20 +100,18 @@ describe("Items - Leek", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + const enemyMember = game.scene.getEnemyPokemon()!; - expect(critLevel.value).toBe(0); + vi.spyOn(enemyMember, "getCritStage"); - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + game.move.select(Moves.TACKLE); - expect(critLevel.value).toBe(2); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemyMember.getCritStage).toHaveReturnedWith(2); }, 20000); - it("LEEK held by fused FARFETCHD line (part)", async () => { + it("should raise CRIT stage by 2 when held by Pokemon fused with FARFETCHD line", async () => { // Randomly choose from the Farfetch'd line const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; @@ -154,9 +120,7 @@ describe("Items - Leek", () => { species[Utils.randInt(species.length)] ]); - const party = game.scene.getParty(); - const partyMember = party[0]; - const ally = party[1]; + const [ partyMember, ally ] = game.scene.getParty(); // Fuse party members (taken from PlayerPokemon.fuse(...) function) partyMember.fusionSpecies = ally.species; @@ -167,36 +131,31 @@ describe("Items - Leek", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); - expect(critLevel.value).toBe(0); + const enemyMember = game.scene.getEnemyPokemon()!; - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + vi.spyOn(enemyMember, "getCritStage"); - expect(critLevel.value).toBe(2); + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemyMember.getCritStage).toHaveReturnedWith(2); }, 20000); - it("LEEK not held by FARFETCHD line", async () => { + it("should not raise CRIT stage when held by a Pokemon outside of FARFETCHD line", async () => { await game.startBattle([ Species.PIKACHU ]); - const partyMember = game.scene.getPlayerPokemon()!; + const enemyMember = game.scene.getEnemyPokemon()!; - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + vi.spyOn(enemyMember, "getCritStage"); - expect(critLevel.value).toBe(0); + game.move.select(Moves.TACKLE); - // Giving Leek to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); + await game.phaseInterceptor.to(TurnEndPhase); - expect(critLevel.value).toBe(0); + expect(enemyMember.getCritStage).toHaveReturnedWith(0); }, 20000); }); diff --git a/src/test/items/light_ball.test.ts b/src/test/items/light_ball.test.ts index cf4f5c9e22f..673348e7b7a 100644 --- a/src/test/items/light_ball.test.ts +++ b/src/test/items/light_ball.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; @@ -37,29 +37,29 @@ describe("Items - Light Ball", () => { const partyMember = game.scene.getParty()[0]; - // Checking consoe log to make sure Light Ball is applied when getBattleStat (with the appropriate stat) is called - partyMember.getBattleStat(Stat.DEF); + // Checking console log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called + partyMember.getEffectiveStat(Stat.DEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), ""); // Printing dummy console messages along the way so subsequent checks don't pass because of the first console.log(""); - partyMember.getBattleStat(Stat.SPDEF); + partyMember.getEffectiveStat(Stat.SPDEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.ATK); + partyMember.getEffectiveStat(Stat.ATK); expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPATK); + partyMember.getEffectiveStat(Stat.SPATK); expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPD); + partyMember.getEffectiveStat(Stat.SPD); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), ""); }); diff --git a/src/test/items/metal_powder.test.ts b/src/test/items/metal_powder.test.ts index a3a4936532f..0206fd1f471 100644 --- a/src/test/items/metal_powder.test.ts +++ b/src/test/items/metal_powder.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; @@ -37,29 +37,29 @@ describe("Items - Metal Powder", () => { const partyMember = game.scene.getParty()[0]; - // Checking consoe log to make sure Metal Powder is applied when getBattleStat (with the appropriate stat) is called - partyMember.getBattleStat(Stat.DEF); + // Checking console log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called + partyMember.getEffectiveStat(Stat.DEF); expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), ""); // Printing dummy console messages along the way so subsequent checks don't pass because of the first console.log(""); - partyMember.getBattleStat(Stat.SPDEF); + partyMember.getEffectiveStat(Stat.SPDEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.ATK); + partyMember.getEffectiveStat(Stat.ATK); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPATK); + partyMember.getEffectiveStat(Stat.SPATK); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPD); + partyMember.getEffectiveStat(Stat.SPD); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), ""); }); diff --git a/src/test/items/quick_powder.test.ts b/src/test/items/quick_powder.test.ts index 53521ba78f1..344b772feb4 100644 --- a/src/test/items/quick_powder.test.ts +++ b/src/test/items/quick_powder.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; @@ -37,29 +37,29 @@ describe("Items - Quick Powder", () => { const partyMember = game.scene.getParty()[0]; - // Checking consoe log to make sure Quick Powder is applied when getBattleStat (with the appropriate stat) is called - partyMember.getBattleStat(Stat.DEF); + // Checking console log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called + partyMember.getEffectiveStat(Stat.DEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), ""); // Printing dummy console messages along the way so subsequent checks don't pass because of the first console.log(""); - partyMember.getBattleStat(Stat.SPDEF); + partyMember.getEffectiveStat(Stat.SPDEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.ATK); + partyMember.getEffectiveStat(Stat.ATK); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPATK); + partyMember.getEffectiveStat(Stat.SPATK); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPD); + partyMember.getEffectiveStat(Stat.SPD); expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), ""); }); diff --git a/src/test/items/scope_lens.test.ts b/src/test/items/scope_lens.test.ts index 85673218762..e39517ceae9 100644 --- a/src/test/items/scope_lens.test.ts +++ b/src/test/items/scope_lens.test.ts @@ -1,8 +1,4 @@ -import { BattlerIndex } from "#app/battle"; -import { CritBoosterModifier } from "#app/modifier/modifier"; -import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import * as Utils from "#app/utils"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; @@ -26,47 +22,29 @@ describe("Items - Scope Lens", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - game.override.disableCrits(); + game.override + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.SPLASH) + .moveset([ Moves.POUND ]) + .startingHeldItems([{ name: "SCOPE_LENS" }]) + .battleType("single") + .disableCrits(); - game.override.battleType("single"); }, 20000); - it("SCOPE_LENS activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "SCOPE_LENS" }]); - game.override.moveset([Moves.POUND]); - const consoleSpy = vi.spyOn(console, "log"); + it("should raise CRIT stage by 1", async () => { await game.startBattle([ Species.GASTLY ]); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemyPokemon, "getCritStage"); + game.move.select(Moves.POUND); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to(TurnEndPhase); - await game.phaseInterceptor.to(MoveEffectPhase); - - expect(consoleSpy).toHaveBeenCalledWith("Applied", "Scope Lens", ""); - }, 20000); - - it("SCOPE_LENS held by random pokemon", async () => { - await game.startBattle([ - Species.GASTLY - ]); - - const partyMember = game.scene.getPlayerPokemon()!; - - // Making sure modifier is not applied without holding item - const critLevel = new Utils.IntegerHolder(0); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); - - expect(critLevel.value).toBe(0); - - // Giving Scope Lens to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SCOPE_LENS().newModifier(partyMember), true); - partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critLevel); - - expect(critLevel.value).toBe(1); + expect(enemyPokemon.getCritStage).toHaveReturnedWith(1); }, 20000); }); diff --git a/src/test/items/temp_stat_stage_booster.test.ts b/src/test/items/temp_stat_stage_booster.test.ts new file mode 100644 index 00000000000..6186799623a --- /dev/null +++ b/src/test/items/temp_stat_stage_booster.test.ts @@ -0,0 +1,170 @@ +import { BATTLE_STATS, Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#enums/species"; +import Phase from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { Moves } from "#app/enums/moves"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#app/enums/abilities"; +import { TempStatStageBoosterModifier } from "#app/modifier/modifier"; +import { Mode } from "#app/ui/ui"; +import { Button } from "#app/enums/buttons"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; + + +describe("Items - Temporary Stat Stage Boosters", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phase.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .enemySpecies(Species.SHUCKLE) + .enemyMoveset(Moves.SPLASH) + .enemyAbility(Abilities.BALL_FETCH) + .moveset([ Moves.TACKLE, Moves.SPLASH, Moves.HONE_CLAWS, Moves.BELLY_DRUM ]) + .startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); + }); + + it("should provide a x1.3 stat stage multiplier", async() => { + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getStatStageMultiplier"); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase); + + expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3); + }, 20000); + + it("should increase existing ACC stat stage by 1 for X_ACCURACY only", async() => { + game.override + .startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]) + .ability(Abilities.SIMPLE); + + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getAccuracyMultiplier"); + + // Raise ACC by +2 stat stages + game.move.select(Moves.HONE_CLAWS); + + await game.phaseInterceptor.to(TurnEndPhase); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + // ACC at +3 stat stages yields a x2 multiplier + expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2); + }, 20000); + + + it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async() => { + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getStatStageMultiplier"); + + // Raise ATK by +1 stat stage + game.move.select(Moves.HONE_CLAWS); + + await game.phaseInterceptor.to(TurnEndPhase); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + // ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK + expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8); + }, 20000); + + it("should not increase past maximum stat stage multiplier", async() => { + game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }, { name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); + + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getStatStageMultiplier"); + vi.spyOn(partyMember, "getAccuracyMultiplier"); + + // Set all stat stages to 6 + vi.spyOn(partyMember.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6)); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3); + expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4); + }, 20000); + + it("should renew how many battles are left of existing booster when picking up new booster of same type", async() => { + game.override + .startingLevel(200) + .itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); + + await game.classicMode.startBattle([ + Species.PIKACHU + ]); + + game.move.select(Moves.SPLASH); + + await game.doKillOpponents(); + + await game.phaseInterceptor.to("BattleEndPhase"); + + const modifier = game.scene.findModifier(m => m instanceof TempStatStageBoosterModifier) as TempStatStageBoosterModifier; + expect(modifier.getBattleCount()).toBe(4); + + // Forced X_ATTACK to spawn in the first slot with override + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to first modifier slot + handler.setCursor(0); + handler.setRowCursor(ShopCursorTarget.REWARDS); + handler.processInput(Button.ACTION); + }, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), true); + + await game.phaseInterceptor.to("TurnInitPhase"); + + // Making sure only one booster is in the modifier list even after picking up another + let count = 0; + for (const m of game.scene.modifiers) { + if (m instanceof TempStatStageBoosterModifier) { + count++; + const modifierInstance = m as TempStatStageBoosterModifier; + expect(modifierInstance.getBattleCount()).toBe(modifierInstance.getMaxBattles()); + } + } + expect(count).toBe(1); + }, 20000); +}); diff --git a/src/test/items/thick_club.test.ts b/src/test/items/thick_club.test.ts index 347921446e6..bcb6b371264 100644 --- a/src/test/items/thick_club.test.ts +++ b/src/test/items/thick_club.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; @@ -37,29 +37,29 @@ describe("Items - Thick Club", () => { const partyMember = game.scene.getParty()[0]; - // Checking consoe log to make sure Thick Club is applied when getBattleStat (with the appropriate stat) is called - partyMember.getBattleStat(Stat.DEF); + // Checking console log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called + partyMember.getEffectiveStat(Stat.DEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), ""); // Printing dummy console messages along the way so subsequent checks don't pass because of the first console.log(""); - partyMember.getBattleStat(Stat.SPDEF); + partyMember.getEffectiveStat(Stat.SPDEF); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.ATK); + partyMember.getEffectiveStat(Stat.ATK); expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPATK); + partyMember.getEffectiveStat(Stat.SPATK); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), ""); console.log(""); - partyMember.getBattleStat(Stat.SPD); + partyMember.getEffectiveStat(Stat.SPD); expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), ""); }); diff --git a/src/test/localization/battle-stat.test.ts b/src/test/localization/battle-stat.test.ts deleted file mode 100644 index b5ba698c4b6..00000000000 --- a/src/test/localization/battle-stat.test.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; -import deBattleStat from "#app/locales/de/battle.json"; -import dePokemonInfo from "#app/locales/de/pokemon-info.json"; -import enBattleStat from "#app/locales/en/battle.json"; -import enPokemonInfo from "#app/locales/en/pokemon-info.json"; -import esBattleStat from "#app/locales/es/battle.json"; -import esPokemonInfo from "#app/locales/es/pokemon-info.json"; -import frBattleStat from "#app/locales/fr/battle.json"; -import frPokemonInfo from "#app/locales/fr/pokemon-info.json"; -import itBattleStat from "#app/locales/it/battle.json"; -import itPokemonInfo from "#app/locales/it/pokemon-info.json"; -import koBattleStat from "#app/locales/ko/battle.json"; -import koPokemonInfo from "#app/locales/ko/pokemon-info.json"; -import ptBrBattleStat from "#app/locales/pt_BR/battle.json"; -import ptBrPokemonInfo from "#app/locales/pt_BR/pokemon-info.json"; -import zhCnBattleStat from "#app/locales/zh_CN/battle.json"; -import zhCnPokemonInfo from "#app/locales/zh_CN/pokemon-info.json"; -import zhTwBattleStat from "#app/locales/zh_TW/battle.json"; -import zhTwPokemonInfo from "#app/locales/zh_TW/pokemon-info.json"; -import i18next, { initI18n } from "#app/plugins/i18n"; -import { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; -import { beforeAll, describe, expect, it } from "vitest"; - -interface BattleStatTestUnit { - stat: BattleStat, - key: string -} - -interface BattleStatLevelTestUnit { - levels: integer, - up: boolean, - key: string - changedStats: integer -} - -function testBattleStatName(stat: BattleStat, expectMessage: string) { - if (!expectMessage) { - return; - } // not translated yet! - const message = getBattleStatName(stat); - console.log(`message ${message}, expected ${expectMessage}`); - expect(message).toBe(expectMessage); -} - -function testBattleStatLevelChangeDescription(levels: integer, up: boolean, expectMessage: string, changedStats: integer) { - if (!expectMessage) { - return; - } // not translated yet! - const message = getBattleStatLevelChangeDescription("{{pokemonNameWithAffix}}", "{{stats}}", levels, up, changedStats); - console.log(`message ${message}, expected ${expectMessage}`); - expect(message).toBe(expectMessage); -} - -describe("Test for BattleStat Localization", () => { - const battleStatUnits: BattleStatTestUnit[] = []; - const battleStatLevelUnits: BattleStatLevelTestUnit[] = []; - - beforeAll(() => { - initI18n(); - - battleStatUnits.push({stat: BattleStat.ATK, key: "Stat.ATK"}); - battleStatUnits.push({stat: BattleStat.DEF, key: "Stat.DEF"}); - battleStatUnits.push({stat: BattleStat.SPATK, key: "Stat.SPATK"}); - battleStatUnits.push({stat: BattleStat.SPDEF, key: "Stat.SPDEF"}); - battleStatUnits.push({stat: BattleStat.SPD, key: "Stat.SPD"}); - battleStatUnits.push({stat: BattleStat.ACC, key: "Stat.ACC"}); - battleStatUnits.push({stat: BattleStat.EVA, key: "Stat.EVA"}); - - battleStatLevelUnits.push({levels: 1, up: true, key: "statRose_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 2, up: true, key: "statSharplyRose_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 3, up: true, key: "statRoseDrastically_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 4, up: true, key: "statRoseDrastically_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 5, up: true, key: "statRoseDrastically_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 6, up: true, key: "statRoseDrastically_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 7, up: true, key: "statWontGoAnyHigher_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 1, up: false, key: "statFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 2, up: false, key: "statHarshlyFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 3, up: false, key: "statSeverelyFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 4, up: false, key: "statSeverelyFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 5, up: false, key: "statSeverelyFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 6, up: false, key: "statSeverelyFell_one", changedStats: 1}); - battleStatLevelUnits.push({levels: 7, up: false, key: "statWontGoAnyLower_one", changedStats: 1}); - }); - - it("Test getBattleStatName() in English", async () => { - i18next.changeLanguage("en"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, enPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in English", async () => { - i18next.changeLanguage("en"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, enBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in Español", async () => { - i18next.changeLanguage("es"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, esPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in Español", async () => { - i18next.changeLanguage("es"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, esBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in Italiano", async () => { - i18next.changeLanguage("it"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, itPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in Italiano", async () => { - i18next.changeLanguage("it"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, itBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in Français", async () => { - i18next.changeLanguage("fr"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, frPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in Français", async () => { - i18next.changeLanguage("fr"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, frBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in Deutsch", async () => { - i18next.changeLanguage("de"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, dePokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in Deutsch", async () => { - i18next.changeLanguage("de"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, deBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in Português (BR)", async () => { - i18next.changeLanguage("pt-BR"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, ptBrPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in Português (BR)", async () => { - i18next.changeLanguage("pt-BR"); - battleStatLevelUnits.forEach(unit => { - testBattleStatLevelChangeDescription(unit.levels, unit.up, ptBrBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in 简体中文", async () => { - i18next.changeLanguage("zh-CN"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, zhCnPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in 简体中文", async () => { - i18next.changeLanguage("zh-CN"); - battleStatLevelUnits.forEach(unit => { - // In i18next, the pluralization rules are language-specific, and Chinese only supports the _other suffix. - unit.key = unit.key.replace("one", "other"); - testBattleStatLevelChangeDescription(unit.levels, unit.up, zhCnBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in 繁體中文", async () => { - i18next.changeLanguage("zh-TW"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, zhTwPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in 繁體中文", async () => { - i18next.changeLanguage("zh-TW"); - battleStatLevelUnits.forEach(unit => { - // In i18next, the pluralization rules are language-specific, and Chinese only supports the _other suffix. - unit.key = unit.key.replace("one", "other"); - testBattleStatLevelChangeDescription(unit.levels, unit.up, zhTwBattleStat[unit.key], unit.changedStats); - }); - }); - - it("Test getBattleStatName() in 한국어", async () => { - await i18next.changeLanguage("ko"); - battleStatUnits.forEach(unit => { - testBattleStatName(unit.stat, koPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]); - }); - }); - - it("Test getBattleStatLevelChangeDescription() in 한국어", async () => { - i18next.changeLanguage("ko", () => { - battleStatLevelUnits.forEach(unit => { - const processor = new KoreanPostpositionProcessor(); - const message = processor.process(koBattleStat[unit.key]); - testBattleStatLevelChangeDescription(unit.levels, unit.up, message, unit.changedStats); - }); - }); - }); -}); diff --git a/src/test/utils/misc.test.ts b/src/test/misc.test.ts similarity index 90% rename from src/test/utils/misc.test.ts rename to src/test/misc.test.ts index d7c10144ead..1052a282a64 100644 --- a/src/test/utils/misc.test.ts +++ b/src/test/misc.test.ts @@ -30,7 +30,7 @@ describe("Test misc", () => { return response.json(); }).then(data => { spy(); // Call the spy function - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); expect(spy).toHaveBeenCalled(); }); @@ -43,7 +43,7 @@ describe("Test misc", () => { return response.json(); }).then(data => { spy(); // Call the spy function - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); expect(spy).toHaveBeenCalled(); }); @@ -54,7 +54,7 @@ describe("Test misc", () => { expect(response.ok).toBe(true); expect(response.status).toBe(200); - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); it("test apifetch mock sync", async () => { diff --git a/src/test/moves/after_you.test.ts b/src/test/moves/after_you.test.ts new file mode 100644 index 00000000000..efce1b28a17 --- /dev/null +++ b/src/test/moves/after_you.test.ts @@ -0,0 +1,65 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { MoveResult } from "#app/field/pokemon"; +import { MovePhase } from "#app/phases/move-phase"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - After You", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("double") + .enemyLevel(5) + .enemySpecies(Species.PIKACHU) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.AFTER_YOU, Moves.SPLASH]); + }); + + it("makes the target move immediately after the user", async () => { + await game.classicMode.startBattle([Species.REGIELEKI, Species.SHUCKLE]); + + game.move.select(Moves.AFTER_YOU, 0, BattlerIndex.PLAYER_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("MoveEffectPhase"); + await game.phaseInterceptor.to(MovePhase, false); + const phase = game.scene.getCurrentPhase() as MovePhase; + expect(phase.pokemon).toBe(game.scene.getPlayerField()[1]); + await game.phaseInterceptor.to("MoveEndPhase"); + }, TIMEOUT); + + it("fails if target already moved", async () => { + game.override.enemySpecies(Species.SHUCKLE); + await game.classicMode.startBattle([Species.REGIELEKI, Species.PIKACHU]); + + game.move.select(Moves.SPLASH); + game.move.select(Moves.AFTER_YOU, 1, BattlerIndex.PLAYER); + + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to(MovePhase); + + expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); + }, TIMEOUT); +}); diff --git a/src/test/moves/alluring_voice.test.ts b/src/test/moves/alluring_voice.test.ts new file mode 100644 index 00000000000..b438d0f736a --- /dev/null +++ b/src/test/moves/alluring_voice.test.ts @@ -0,0 +1,54 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Alluring Voice", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.ICE_SCALES) + .enemyMoveset([Moves.HOWL]) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.ALLURING_VOICE]); + + }); + + it("should confuse the opponent if their stat stages were raised", async () => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.ALLURING_VOICE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(BerryPhase); + + expect(enemy.getTag(BattlerTagType.CONFUSED)?.tagType).toBe("CONFUSED"); + }, TIMEOUT); +}); diff --git a/src/test/moves/baddy_bad.test.ts b/src/test/moves/baddy_bad.test.ts new file mode 100644 index 00000000000..d1a221453a6 --- /dev/null +++ b/src/test/moves/baddy_bad.test.ts @@ -0,0 +1,43 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Baddy Bad", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH]) + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .ability(Abilities.BALL_FETCH); + }); + + it("should not activate Reflect if the move fails due to Protect", async () => { + game.override.enemyMoveset(Moves.PROTECT); + await game.classicMode.startBattle([Species.FEEBAS]); + + game.move.select(Moves.BADDY_BAD); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.arena.tags.length).toBe(0); + }, TIMEOUT); +}); diff --git a/src/test/moves/baton_pass.test.ts b/src/test/moves/baton_pass.test.ts index 602da9e37f8..5643efceae2 100644 --- a/src/test/moves/baton_pass.test.ts +++ b/src/test/moves/baton_pass.test.ts @@ -1,13 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BattlerIndex } from "#app/battle"; import GameManager from "#app/test/utils/gameManager"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; - describe("Moves - Baton Pass", () => { let phaserGame: Phaser.Game; @@ -27,35 +26,35 @@ describe("Moves - Baton Pass", () => { game = new GameManager(phaserGame); game.override .battleType("single") - .enemySpecies(Species.DUGTRIO) - .startingLevel(1) - .startingWave(97) + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) .moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH]) - .enemyMoveset(SPLASH_ONLY) + .ability(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) .disableCrits(); }); - it("passes stat stage buffs when player uses it", async () => { + it("transfers all stat stages when player uses it", async() => { // arrange - await game.startBattle([ - Species.RAICHU, - Species.SHUCKLE - ]); + await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]); // round 1 - buff game.move.select(Moves.NASTY_PLOT); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + + let playerPokemon = game.scene.getPlayerPokemon()!; + + expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2); // round 2 - baton pass game.move.select(Moves.BATON_PASS); game.doSelectPartyPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); // assert - const playerPkm = game.scene.getPlayerPokemon()!; - expect(playerPkm.species.speciesId).toEqual(Species.SHUCKLE); - expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + playerPokemon = game.scene.getPlayerPokemon()!; + expect(playerPokemon.species.speciesId).toEqual(Species.SHUCKLE); + expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2); }, 20000); it("passes stat stage buffs when AI uses it", async () => { @@ -63,10 +62,7 @@ describe("Moves - Baton Pass", () => { game.override .startingWave(5) .enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT])); - await game.startBattle([ - Species.RAICHU, - Species.SHUCKLE - ]); + await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]); // round 1 - ai buffs game.move.select(Moves.SPLASH); @@ -74,13 +70,16 @@ describe("Moves - Baton Pass", () => { // round 2 - baton pass game.scene.getEnemyPokemon()!.hp = 100; - game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); + game.override.enemyMoveset([Moves.BATON_PASS]); + // Force moveset to update mid-battle + // TODO: replace with enemy ai control function when it's added + game.scene.getEnemyParty()[0].getMoveset(); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(PostSummonPhase, false); + await game.phaseInterceptor.to("PostSummonPhase", false); // assert // check buffs are still there - expect(game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPATK)).toEqual(2); // confirm that a switch actually happened. can't use species because I // can't find a way to override trainer parties with more than 1 pokemon species expect(game.scene.getEnemyPokemon()!.hp).not.toEqual(100); @@ -91,4 +90,20 @@ describe("Moves - Baton Pass", () => { "PostSummonPhase" ]); }, 20000); + + it("doesn't transfer effects that aren't transferrable", async() => { + game.override.enemyMoveset([Moves.SALT_CURE]); + await game.classicMode.startBattle([Species.PIKACHU, Species.FEEBAS]); + + const [player1, player2] = game.scene.getParty(); + + game.move.select(Moves.BATON_PASS); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("MoveEndPhase"); + expect(player1.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeTruthy(); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + + expect(player2.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined(); + }, 20000); }); diff --git a/src/test/moves/beak_blast.test.ts b/src/test/moves/beak_blast.test.ts index 2a93dc00a54..fe748c87826 100644 --- a/src/test/moves/beak_blast.test.ts +++ b/src/test/moves/beak_blast.test.ts @@ -34,7 +34,7 @@ describe("Moves - Beak Blast", () => { .moveset([Moves.BEAK_BLAST]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) - .enemyMoveset(Array(4).fill(Moves.TACKLE)) + .enemyMoveset([Moves.TACKLE]) .startingLevel(100) .enemyLevel(100); }); @@ -80,7 +80,7 @@ describe("Moves - Beak Blast", () => { it( "should not burn attackers that don't make contact", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN)); + game.override.enemyMoveset([Moves.WATER_GUN]); await game.startBattle([Species.BLASTOISE]); @@ -116,7 +116,7 @@ describe("Moves - Beak Blast", () => { it( "should be blocked by Protect", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.PROTECT)); + game.override.enemyMoveset([Moves.PROTECT]); await game.startBattle([Species.BLASTOISE]); diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index ce1598a49b4..70b33f56583 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -29,7 +29,7 @@ describe("Moves - Beat Up", () => { game.override.enemySpecies(Species.SNORLAX); game.override.enemyLevel(100); - game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); + game.override.enemyMoveset([Moves.SPLASH]); game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index e4956c6e83a..3d85c59a2a5 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -1,12 +1,11 @@ -import { BattleStat } from "#app/data/battle-stat"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; import { Abilities } from "#app/enums/abilities"; const TIMEOUT = 20 * 1000; @@ -37,14 +36,14 @@ describe("Moves - BELLY DRUM", () => { .startingLevel(100) .enemyLevel(100) .moveset([Moves.BELLY_DRUM]) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH); }); // Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Belly_Drum_(move) - test("Belly Drum raises the user's Attack to its max, at the cost of 1/2 of its maximum HP", - async () => { + test("raises the user's ATK stat stage to its max, at the cost of 1/2 of its maximum HP", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -54,48 +53,48 @@ describe("Moves - BELLY DRUM", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); }, TIMEOUT ); - test("Belly Drum will still take effect if an uninvolved stat is at max", - async () => { + test("will still take effect if an uninvolved stat stage is at max", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - // Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6 - leadPokemon.summonData.battleStats[BattleStat.ATK] = -3; - leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; + // Here - Stat.ATK -> -3 and Stat.SPATK -> 6 + leadPokemon.setStatStage(Stat.ATK, -3); + leadPokemon.setStatStage(Stat.SPATK, 6); game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); }, TIMEOUT ); - test("Belly Drum fails if the pokemon's attack stat is at its maximum", - async () => { + test("fails if the pokemon's ATK stat stage is at its maximum", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; + leadPokemon.setStatStage(Stat.ATK, 6); game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); }, TIMEOUT ); - test("Belly Drum fails if the user's health is less than 1/2", - async () => { + test("fails if the user's health is less than 1/2", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -106,7 +105,7 @@ describe("Moves - BELLY DRUM", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); }, TIMEOUT ); }); diff --git a/src/test/moves/burning_jealousy.test.ts b/src/test/moves/burning_jealousy.test.ts new file mode 100644 index 00000000000..3f2bf453684 --- /dev/null +++ b/src/test/moves/burning_jealousy.test.ts @@ -0,0 +1,102 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Burning Jealousy", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.ICE_SCALES) + .enemyMoveset([Moves.HOWL]) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.BURNING_JEALOUSY, Moves.GROWL]); + + }); + + it("should burn the opponent if their stat stages were raised", async () => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBe(StatusEffect.BURN); + }, TIMEOUT); + + it("should still burn the opponent if their stat stages were both raised and lowered in the same turn", async () => { + game.override + .starterSpecies(0) + .battleType("double"); + await game.classicMode.startBattle([Species.FEEBAS, Species.ABRA]); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + game.move.select(Moves.GROWL, 1); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBe(StatusEffect.BURN); + }, TIMEOUT); + + it("should ignore stat stages raised by IMPOSTER", async () => { + game.override + .enemySpecies(Species.DITTO) + .enemyAbility(Abilities.IMPOSTER) + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBeUndefined(); + }, TIMEOUT); + + it.skip("should ignore weakness policy", async () => { // TODO: Make this test if WP is implemented + await game.classicMode.startBattle(); + }, TIMEOUT); + + it("should be boosted by Sheer Force even if opponent didn't raise stat stages", async () => { + game.override + .ability(Abilities.SHEER_FORCE) + .enemyMoveset(Moves.SPLASH); + vi.spyOn(allMoves[Moves.BURNING_JEALOUSY], "calculateBattlePower"); + await game.classicMode.startBattle(); + + game.move.select(Moves.BURNING_JEALOUSY); + await game.phaseInterceptor.to("BerryPhase"); + + expect(allMoves[Moves.BURNING_JEALOUSY].calculateBattlePower).toHaveReturnedWith(allMoves[Moves.BURNING_JEALOUSY].power * 5461 / 4096); + }, TIMEOUT); +}); diff --git a/src/test/moves/ceaseless_edge.test.ts b/src/test/moves/ceaseless_edge.test.ts index 34ecf8f39f6..8511b3179c6 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -110,7 +110,7 @@ describe("Moves - Ceaseless Edge", () => { const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp; // Check HP of pokemon that WILL BE switched in (index 1) - game.forceOpponentToSwitch(); + game.forceEnemyToSwitch(); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase, false); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes); diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 9ea6da91595..015b73b4dab 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -1,12 +1,10 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/utils/gameManager"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { Stat } from "#enums/stat"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -14,7 +12,7 @@ const RATIO = 3; /** Amount of extra HP lost */ const PREDAMAGE = 15; -describe("Moves - CLANGOROUS_SOUL", () => { +describe("Moves - Clangorous Soul", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -35,96 +33,96 @@ describe("Moves - CLANGOROUS_SOUL", () => { game.override.startingLevel(100); game.override.enemyLevel(100); game.override.moveset([Moves.CLANGOROUS_SOUL]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move) - test("Clangorous Soul raises the user's Attack, Defense, Special Attack, Special Defense and Speed by one stage each, at the cost of 1/3 of its maximum HP", - async () => { + it("raises the user's ATK, DEF, SPATK, SPDEF, and SPD stat stages by 1 each at the cost of 1/3 of its maximum HP", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); + const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(1); - expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1); - expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(1); + expect(leadPokemon.getStatStage(Stat.DEF)).toBe(1); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(1); + expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(1); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1); }, TIMEOUT ); - test("Clangorous Soul will still take effect if one or more of the involved stats are not at max", - async () => { + it("will still take effect if one or more of the involved stat stages are not at max", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); + const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); - //Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4 - leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.DEF] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 4; + //Here - Stat.SPD -> 0 and Stat.SPDEF -> 4 + leadPokemon.setStatStage(Stat.ATK, 6); + leadPokemon.setStatStage(Stat.DEF, 6); + leadPokemon.setStatStage(Stat.SPATK, 6); + leadPokemon.setStatStage(Stat.SPDEF, 4); game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(5); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.DEF)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(5); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1); }, TIMEOUT ); - test("Clangorous Soul fails if all stats involved are at max", - async () => { + it("fails if all stat stages involved are at max", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.DEF] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; + leadPokemon.setStatStage(Stat.ATK, 6); + leadPokemon.setStatStage(Stat.DEF, 6); + leadPokemon.setStatStage(Stat.SPATK, 6); + leadPokemon.setStatStage(Stat.SPDEF, 6); + leadPokemon.setStatStage(Stat.SPD, 6); game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(6); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.DEF)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6); }, TIMEOUT ); - test("Clangorous Soul fails if the user's health is less than 1/3", - async () => { + it("fails if the user's health is less than 1/3", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); + const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(0); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(leadPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(0); + expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(0); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(0); }, TIMEOUT ); }); diff --git a/src/test/moves/crafty_shield.test.ts b/src/test/moves/crafty_shield.test.ts index a341a50b0b9..7b962518944 100644 --- a/src/test/moves/crafty_shield.test.ts +++ b/src/test/moves/crafty_shield.test.ts @@ -1,13 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; const TIMEOUT = 20 * 1000; @@ -33,7 +33,7 @@ describe("Moves - Crafty Shield", () => { game.override.moveset([Moves.CRAFTY_SHIELD, Moves.SPLASH, Moves.SWORDS_DANCE]); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset([Moves.GROWL]); game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); @@ -55,14 +55,14 @@ describe("Moves - Crafty Shield", () => { await game.phaseInterceptor.to(BerryPhase, false); - leadPokemon.forEach(p => expect(p.summonData.battleStats[BattleStat.ATK]).toBe(0)); + leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); }, TIMEOUT ); test( "should not protect the user and allies from attack moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -84,7 +84,7 @@ describe("Moves - Crafty Shield", () => { "should protect the user and allies from moves that ignore other protection", async () => { game.override.enemySpecies(Species.DUSCLOPS); - game.override.enemyMoveset(Array(4).fill(Moves.CURSE)); + game.override.enemyMoveset([Moves.CURSE]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -117,8 +117,8 @@ describe("Moves - Crafty Shield", () => { await game.phaseInterceptor.to(BerryPhase, false); - expect(leadPokemon[0].summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(leadPokemon[1].summonData.battleStats[BattleStat.ATK]).toBe(2); + expect(leadPokemon[0].getStatStage(Stat.ATK)).toBe(0); + expect(leadPokemon[1].getStatStage(Stat.ATK)).toBe(2); } ); }); diff --git a/src/test/moves/disable.test.ts b/src/test/moves/disable.test.ts new file mode 100644 index 00000000000..a35d294e91f --- /dev/null +++ b/src/test/moves/disable.test.ts @@ -0,0 +1,128 @@ +import { BattlerIndex } from "#app/battle"; +import { MoveResult } from "#app/field/pokemon"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Disable", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH) + .moveset([Moves.DISABLE, Moves.SPLASH]) + .enemyMoveset(Moves.SPLASH) + .starterSpecies(Species.PIKACHU) + .enemySpecies(Species.SHUCKLE); + }); + + it("restricts moves", async () => { + await game.classicMode.startBattle(); + + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyMon.getMoveHistory()).toHaveLength(1); + expect(enemyMon.isMoveRestricted(Moves.SPLASH)).toBe(true); + }); + + it("fails if enemy has no move history", async() => { + await game.classicMode.startBattle(); + + const playerMon = game.scene.getPlayerPokemon()!; + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + + expect(playerMon.getMoveHistory()[0]).toMatchObject({ move: Moves.DISABLE, result: MoveResult.FAIL }); + expect(enemyMon.isMoveRestricted(Moves.SPLASH)).toBe(false); + }, 20000); + + it("causes STRUGGLE if all usable moves are disabled", async() => { + await game.classicMode.startBattle(); + + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + const enemyHistory = enemyMon.getMoveHistory(); + expect(enemyHistory).toHaveLength(2); + expect(enemyHistory[0].move).toBe(Moves.SPLASH); + expect(enemyHistory[1].move).toBe(Moves.STRUGGLE); + }, 20000); + + it("cannot disable STRUGGLE", async() => { + game.override.enemyMoveset([Moves.STRUGGLE]); + await game.classicMode.startBattle(); + + const playerMon = game.scene.getPlayerPokemon()!; + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(playerMon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(enemyMon.getLastXMoves()[0].move).toBe(Moves.STRUGGLE); + expect(enemyMon.isMoveRestricted(Moves.STRUGGLE)).toBe(false); + }, 20000); + + it("interrupts target's move when target moves after", async() => { + await game.classicMode.startBattle(); + + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Both mons just used Splash last turn; now have player use Disable. + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + + const enemyHistory = enemyMon.getMoveHistory(); + expect(enemyHistory).toHaveLength(2); + expect(enemyHistory[0]).toMatchObject({ move: Moves.SPLASH, result: MoveResult.SUCCESS }); + expect(enemyHistory[1].result).toBe(MoveResult.FAIL); + }, 20000); + + it("disables NATURE POWER, not the move invoked by it", async() => { + game.override.enemyMoveset([Moves.NATURE_POWER]); + await game.classicMode.startBattle(); + + const enemyMon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.DISABLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyMon.isMoveRestricted(Moves.NATURE_POWER)).toBe(true); + expect(enemyMon.isMoveRestricted(enemyMon.getLastXMoves(2)[1].move)).toBe(false); + }, 20000); +}); diff --git a/src/test/moves/double_team.test.ts b/src/test/moves/double_team.test.ts index c45c8bd8516..fa224c8df9e 100644 --- a/src/test/moves/double_team.test.ts +++ b/src/test/moves/double_team.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { Abilities } from "#app/enums/abilities"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; @@ -32,20 +32,20 @@ describe("Moves - Double Team", () => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("increases the user's evasion by one stage.", async () => { + it("raises the user's EVA stat stage by 1", async () => { await game.startBattle([Species.MAGIKARP]); const ally = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getAccuracyMultiplier"); - expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0); + expect(ally.getStatStage(Stat.EVA)).toBe(0); game.move.select(Moves.DOUBLE_TEAM); await game.phaseInterceptor.to(TurnEndPhase); await game.toNextTurn(); - expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(1); + expect(ally.getStatStage(Stat.EVA)).toBe(1); expect(enemy.getAccuracyMultiplier).toHaveReturnedWith(.75); }); }); diff --git a/src/test/moves/dragon_cheer.test.ts b/src/test/moves/dragon_cheer.test.ts new file mode 100644 index 00000000000..0fc389ccfb6 --- /dev/null +++ b/src/test/moves/dragon_cheer.test.ts @@ -0,0 +1,100 @@ +import { BattlerIndex } from "#app/battle"; +import { Type } from "#app/data/type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Dragon Cheer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("double") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemyLevel(20) + .moveset([Moves.DRAGON_CHEER, Moves.TACKLE, Moves.SPLASH]); + }); + + it("increases the user's allies' critical hit ratio by one stage", async () => { + await game.classicMode.startBattle([Species.DRAGONAIR, Species.MAGIKARP]); + + const enemy = game.scene.getEnemyField()[0]; + + vi.spyOn(enemy, "getCritStage"); + + game.move.select(Moves.DRAGON_CHEER, 0); + game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + // After Tackle + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender + }, TIMEOUT); + + it("increases the user's Dragon-type allies' critical hit ratio by two stages", async () => { + await game.classicMode.startBattle([Species.MAGIKARP, Species.DRAGONAIR]); + + const enemy = game.scene.getEnemyField()[0]; + + vi.spyOn(enemy, "getCritStage"); + + game.move.select(Moves.DRAGON_CHEER, 0); + game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + // After Tackle + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getCritStage).toHaveReturnedWith(2); // getCritStage is called on defender + }, TIMEOUT); + + it("applies the effect based on the allies' type upon use of the move, and do not change if the allies' type changes later in battle", async () => { + await game.classicMode.startBattle([Species.DRAGONAIR, Species.MAGIKARP]); + + const magikarp = game.scene.getPlayerField()[1]; + const enemy = game.scene.getEnemyField()[0]; + + vi.spyOn(enemy, "getCritStage"); + + game.move.select(Moves.DRAGON_CHEER, 0); + game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + // After Tackle + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender + + await game.toNextTurn(); + + // Change Magikarp's type to Dragon + vi.spyOn(magikarp, "getTypes").mockReturnValue([Type.DRAGON]); + expect(magikarp.getTypes()).toEqual([Type.DRAGON]); + + game.move.select(Moves.SPLASH, 0); + game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender + }, TIMEOUT); +}); diff --git a/src/test/moves/dragon_rage.test.ts b/src/test/moves/dragon_rage.test.ts index 223635575ab..cab8c3f808b 100644 --- a/src/test/moves/dragon_rage.test.ts +++ b/src/test/moves/dragon_rage.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { Type } from "#app/data/type"; import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; @@ -8,7 +8,6 @@ import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -42,7 +41,7 @@ describe("Moves - Dragon Rage", () => { game.override.startingLevel(100); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.enemyPassiveAbility(Abilities.BALL_FETCH); game.override.enemyLevel(100); @@ -63,9 +62,8 @@ describe("Moves - Dragon Rage", () => { game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); it("ignores resistances", async () => { @@ -74,20 +72,18 @@ describe("Moves - Dragon Rage", () => { game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); - it("ignores stat changes", async () => { + it("ignores SPATK stat stages", async () => { game.override.disableCrits(); - partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2; + partyPokemon.setStatStage(Stat.SPATK, 2); game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); it("ignores stab", async () => { @@ -96,9 +92,8 @@ describe("Moves - Dragon Rage", () => { game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); it("ignores criticals", async () => { @@ -106,20 +101,18 @@ describe("Moves - Dragon Rage", () => { game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); - it("ignores damage modification from abilities such as ice scales", async () => { + it("ignores damage modification from abilities, for example ICE_SCALES", async () => { game.override.disableCrits(); game.override.enemyAbility(Abilities.ICE_SCALES); game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); it("ignores multi hit", async () => { @@ -128,8 +121,7 @@ describe("Moves - Dragon Rage", () => { game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); - const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; - expect(damageDealt).toBe(dragonRageDamage); + expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage); }); }); diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts index 362383e2fe3..e1af29b2db1 100644 --- a/src/test/moves/dragon_tail.test.ts +++ b/src/test/moves/dragon_tail.test.ts @@ -9,7 +9,6 @@ import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -32,7 +31,7 @@ describe("Moves - Dragon Tail", () => { game.override.battleType("single") .moveset([Moves.DRAGON_TAIL, Moves.SPLASH]) .enemySpecies(Species.WAILORD) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .startingLevel(5) .enemyLevel(5); @@ -82,7 +81,7 @@ describe("Moves - Dragon Tail", () => { test( "Double battles should proceed without crashing", async () => { - game.override.battleType("double").enemyMoveset(SPLASH_ONLY); + game.override.battleType("double").enemyMoveset(Moves.SPLASH); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]) .enemyAbility(Abilities.ROUGH_SKIN); await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); @@ -116,7 +115,7 @@ describe("Moves - Dragon Tail", () => { test( "Flee move redirection works", async () => { - game.override.battleType("double").enemyMoveset(SPLASH_ONLY); + game.override.battleType("double").enemyMoveset(Moves.SPLASH); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]); game.override.enemyAbility(Abilities.ROUGH_SKIN); await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); diff --git a/src/test/moves/effectiveness.test.ts b/src/test/moves/effectiveness.test.ts index af44586b69d..d1903c79844 100644 --- a/src/test/moves/effectiveness.test.ts +++ b/src/test/moves/effectiveness.test.ts @@ -1,23 +1,32 @@ import { allMoves } from "#app/data/move"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { TrainerSlot } from "#app/data/trainer-config"; +import { Type } from "#app/data/type"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import * as Messages from "#app/messages"; +import { TerastallizeModifier } from "#app/modifier/modifier"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; function testMoveEffectiveness(game: GameManager, move: Moves, targetSpecies: Species, - expected: number, targetAbility: Abilities = Abilities.BALL_FETCH): void { + expected: number, targetAbility: Abilities = Abilities.BALL_FETCH, teraType?: Type): void { // Suppress getPokemonNameWithAffix because it calls on a null battle spec vi.spyOn(Messages, "getPokemonNameWithAffix").mockReturnValue(""); game.override.enemyAbility(targetAbility); + + if (teraType !== undefined) { + game.override.enemyHeldItems([{ name:"TERA_SHARD", type: teraType }]); + } + const user = game.scene.addPlayerPokemon(getPokemonSpecies(Species.SNORLAX), 5); const target = game.scene.addEnemyPokemon(getPokemonSpecies(targetSpecies), 5, TrainerSlot.NONE); expect(target.getMoveEffectiveness(user, allMoves[move])).toBe(expected); + user.destroy(); + target.destroy(); } describe("Moves - Type Effectiveness", () => { @@ -29,6 +38,8 @@ describe("Moves - Type Effectiveness", () => { type: Phaser.HEADLESS, }); game = new GameManager(phaserGame); + TerastallizeModifier.prototype.apply = (args) => true; + game.override.ability(Abilities.BALL_FETCH); }); @@ -67,4 +78,30 @@ describe("Moves - Type Effectiveness", () => { it("Electric-type attacks are negated by Volt Absorb", () => testMoveEffectiveness(game, Moves.THUNDERBOLT, Species.GYARADOS, 0, Abilities.VOLT_ABSORB) ); + + it("Electric-type attacks are super-effective against Tera-Water Pokemon", + () => testMoveEffectiveness(game, Moves.THUNDERBOLT, Species.EXCADRILL, 2, Abilities.BALL_FETCH, Type.WATER) + ); + + it("Powder moves have no effect on Grass-type Pokemon", + () => testMoveEffectiveness(game, Moves.SLEEP_POWDER, Species.AMOONGUSS, 0) + ); + + it("Powder moves have no effect on Tera-Grass Pokemon", + () => testMoveEffectiveness(game, Moves.SLEEP_POWDER, Species.SNORLAX, 0, Abilities.BALL_FETCH, Type.GRASS) + ); + + it("Prankster-boosted status moves have no effect on Dark-type Pokemon", + () => { + game.override.ability(Abilities.PRANKSTER); + testMoveEffectiveness(game, Moves.BABY_DOLL_EYES, Species.MIGHTYENA, 0); + } + ); + + it("Prankster-boosted status moves have no effect on Tera-Dark Pokemon", + () => { + game.override.ability(Abilities.PRANKSTER); + testMoveEffectiveness(game, Moves.BABY_DOLL_EYES, Species.SNORLAX, 0, Abilities.BALL_FETCH, Type.DARK); + } + ); }); diff --git a/src/test/moves/fake_out.test.ts b/src/test/moves/fake_out.test.ts new file mode 100644 index 00000000000..04d6216b952 --- /dev/null +++ b/src/test/moves/fake_out.test.ts @@ -0,0 +1,81 @@ +import GameManager from "#app/test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Fake Out", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.CORVIKNIGHT) + .starterSpecies(Species.FEEBAS) + .moveset([Moves.FAKE_OUT, Moves.SPLASH]) + .enemyMoveset(Moves.SPLASH) + .disableCrits(); + }); + + it("can only be used on the first turn a pokemon is sent out", async() => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.FAKE_OUT); + await game.toNextTurn(); + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + const postTurnOneHp = enemy.hp; + + game.move.select(Moves.FAKE_OUT); + await game.toNextTurn(); + + expect(enemy.hp).toBe(postTurnOneHp); + + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + const newEnemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.FAKE_OUT); + await game.toNextTurn(); + + expect(newEnemy.hp).toBe(newEnemy.getMaxHp()); + }, 20000); + + it("can be used again if recalled and sent back out", async() => { + game.override.startingWave(4); + await game.classicMode.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.FAKE_OUT); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.move.select(Moves.FAKE_OUT); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); + }, 20000); +}); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index b2ff9e25dba..68ace42c2ec 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -1,10 +1,9 @@ -import { BattleStat } from "#app/data/battle-stat"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; @@ -35,13 +34,13 @@ describe("Moves - FILLET AWAY", () => { game.override.startingLevel(100); game.override.enemyLevel(100); game.override.moveset([Moves.FILLET_AWAY]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move) - test("Fillet Away raises the user's Attack, Special Attack, and Speed by two stages each, at the cost of 1/2 of its maximum HP", - async () => { + test("raises the user's ATK, SPATK, and SPD stat stages by 2 each, at the cost of 1/2 of its maximum HP", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -51,55 +50,55 @@ describe("Moves - FILLET AWAY", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(2); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(2); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(2); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(2); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2); }, TIMEOUT ); - test("Fillet Away will still take effect if one or more of the involved stats are not at max", - async () => { + test("still takes effect if one or more of the involved stat stages are not at max", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - //Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3 - leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPATK] = 3; + //Here - Stat.SPD -> 0 and Stat.SPATK -> 3 + leadPokemon.setStatStage(Stat.ATK, 6); + leadPokemon.setStatStage(Stat.SPATK, 3); game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(5); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(2); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(5); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2); }, TIMEOUT ); - test("Fillet Away fails if all stats involved are at max", - async () => { + test("fails if all stat stages involved are at max", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; - leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; + leadPokemon.setStatStage(Stat.ATK, 6); + leadPokemon.setStatStage(Stat.SPATK, 6); + leadPokemon.setStatStage(Stat.SPD, 6); game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(6); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6); }, TIMEOUT ); - test("Fillet Away fails if the user's health is less than 1/2", - async () => { + test("fails if the user's health is less than 1/2", + async() => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -110,9 +109,9 @@ describe("Moves - FILLET AWAY", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0); - expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(0); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(0); + expect(leadPokemon.getStatStage(Stat.SPD)).toBe(0); }, TIMEOUT ); }); diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index 51122b269b8..8689ce4079e 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { DamagePhase } from "#app/phases/damage-phase"; @@ -6,7 +6,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -38,7 +37,7 @@ describe("Moves - Fissure", () => { game.override.startingLevel(100); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyPassiveAbility(Abilities.BALL_FETCH); game.override.enemyLevel(100); @@ -52,7 +51,7 @@ describe("Moves - Fissure", () => { game.scene.clearEnemyHeldItemModifiers(); }); - it("ignores damage modification from abilities such as fur coat", async () => { + it("ignores damage modification from abilities, for example FUR_COAT", async () => { game.override.ability(Abilities.NO_GUARD); game.override.enemyAbility(Abilities.FUR_COAT); @@ -62,10 +61,10 @@ describe("Moves - Fissure", () => { expect(enemyPokemon.isFainted()).toBe(true); }); - it("ignores accuracy stat", async () => { + it("ignores user's ACC stat stage", async () => { vi.spyOn(partyPokemon, "getAccuracyMultiplier"); - enemyPokemon.summonData.battleStats[BattleStat.ACC] = -6; + partyPokemon.setStatStage(Stat.ACC, -6); game.move.select(Moves.FISSURE); @@ -75,10 +74,10 @@ describe("Moves - Fissure", () => { expect(partyPokemon.getAccuracyMultiplier).toHaveReturnedWith(1); }); - it("ignores evasion stat", async () => { + it("ignores target's EVA stat stage", async () => { vi.spyOn(partyPokemon, "getAccuracyMultiplier"); - enemyPokemon.summonData.battleStats[BattleStat.EVA] = 6; + enemyPokemon.setStatStage(Stat.EVA, 6); game.move.select(Moves.FISSURE); diff --git a/src/test/moves/flame_burst.test.ts b/src/test/moves/flame_burst.test.ts index 2777b8178b8..b2858af2b24 100644 --- a/src/test/moves/flame_burst.test.ts +++ b/src/test/moves/flame_burst.test.ts @@ -42,7 +42,7 @@ describe("Moves - Flame Burst", () => { game.override.startingWave(4); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH)); + game.override.enemyMoveset([Moves.SPLASH]); }); it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => { diff --git a/src/test/moves/flower_shield.test.ts b/src/test/moves/flower_shield.test.ts index b3e50219aec..f5fe8d532cc 100644 --- a/src/test/moves/flower_shield.test.ts +++ b/src/test/moves/flower_shield.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { SemiInvulnerableTag } from "#app/data/battler-tags"; import { Type } from "#app/data/type"; import { Biome } from "#app/enums/biome"; @@ -7,7 +7,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -31,27 +30,27 @@ describe("Moves - Flower Shield", () => { game.override.enemyAbility(Abilities.NONE); game.override.battleType("single"); game.override.moveset([Moves.FLOWER_SHIELD, Moves.SPLASH]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); - it("increases defense of all Grass-type Pokemon on the field by one stage - single battle", async () => { + it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - single battle", async () => { game.override.enemySpecies(Species.CHERRIM); await game.startBattle([Species.MAGIKARP]); const cherrim = game.scene.getEnemyPokemon()!; const magikarp = game.scene.getPlayerPokemon()!; - expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(magikarp.getStatStage(Stat.DEF)).toBe(0); + expect(cherrim.getStatStage(Stat.DEF)).toBe(0); game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); - expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(1); + expect(magikarp.getStatStage(Stat.DEF)).toBe(0); + expect(cherrim.getStatStage(Stat.DEF)).toBe(1); }); - it("increases defense of all Grass-type Pokemon on the field by one stage - double battle", async () => { + it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - double battle", async () => { game.override.enemySpecies(Species.MAGIKARP).startingBiome(Biome.GRASS).battleType("double"); await game.startBattle([Species.CHERRIM, Species.MAGIKARP]); @@ -60,21 +59,21 @@ describe("Moves - Flower Shield", () => { const grassPokemons = field.filter(p => p.getTypes().includes(Type.GRASS)); const nonGrassPokemons = field.filter(pokemon => !grassPokemons.includes(pokemon)); - grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); - nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); + grassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0)); + nonGrassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0)); game.move.select(Moves.FLOWER_SHIELD); game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); - grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(1)); - nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); + grassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(1)); + nonGrassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0)); }); /** * See semi-vulnerable state tags. {@linkcode SemiInvulnerableTag} */ - it("does not increase defense of a pokemon in semi-vulnerable state", async () => { + it("does not raise DEF stat stage for a Pokemon in semi-vulnerable state", async () => { game.override.enemySpecies(Species.PARAS); game.override.enemyMoveset([Moves.DIG, Moves.DIG, Moves.DIG, Moves.DIG]); game.override.enemyLevel(50); @@ -83,32 +82,32 @@ describe("Moves - Flower Shield", () => { const paras = game.scene.getEnemyPokemon()!; const cherrim = game.scene.getPlayerPokemon()!; - expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(paras.getStatStage(Stat.DEF)).toBe(0); + expect(cherrim.getStatStage(Stat.DEF)).toBe(0); expect(paras.getTag(SemiInvulnerableTag)).toBeUndefined; game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(paras.getTag(SemiInvulnerableTag)).toBeDefined(); - expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(1); + expect(paras.getStatStage(Stat.DEF)).toBe(0); + expect(cherrim.getStatStage(Stat.DEF)).toBe(1); }); - it("does nothing if there are no Grass-type pokemon on the field", async () => { + it("does nothing if there are no Grass-type Pokemon on the field", async () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.MAGIKARP]); const enemy = game.scene.getEnemyPokemon()!; const ally = game.scene.getPlayerPokemon()!; - expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(enemy.getStatStage(Stat.DEF)).toBe(0); + expect(ally.getStatStage(Stat.DEF)).toBe(0); game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); - expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(enemy.getStatStage(Stat.DEF)).toBe(0); + expect(ally.getStatStage(Stat.DEF)).toBe(0); }); }); diff --git a/src/test/moves/focus_punch.test.ts b/src/test/moves/focus_punch.test.ts index 99399623a1c..ca80c688169 100644 --- a/src/test/moves/focus_punch.test.ts +++ b/src/test/moves/focus_punch.test.ts @@ -7,7 +7,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -35,7 +34,7 @@ describe("Moves - Focus Punch", () => { .moveset([Moves.FOCUS_PUNCH]) .enemySpecies(Species.GROUDON) .enemyAbility(Abilities.INSOMNIA) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .startingLevel(100) .enemyLevel(100); }); @@ -68,7 +67,7 @@ describe("Moves - Focus Punch", () => { it( "should fail if the user is hit", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CHARIZARD]); @@ -95,7 +94,7 @@ describe("Moves - Focus Punch", () => { it( "should be cancelled if the user falls asleep mid-turn", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SPORE)); + game.override.enemyMoveset([Moves.SPORE]); await game.startBattle([Species.CHARIZARD]); @@ -123,7 +122,7 @@ describe("Moves - Focus Punch", () => { await game.startBattle([Species.CHARIZARD]); - game.forceOpponentToSwitch(); + game.forceEnemyToSwitch(); game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(TurnStartPhase); diff --git a/src/test/moves/follow_me.test.ts b/src/test/moves/follow_me.test.ts index d7ef199df3e..7d0c4fdb546 100644 --- a/src/test/moves/follow_me.test.ts +++ b/src/test/moves/follow_me.test.ts @@ -1,5 +1,5 @@ +import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; -import { Stat } from "#app/data/pokemon-stat"; import { Abilities } from "#app/enums/abilities"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; @@ -28,48 +28,55 @@ describe("Moves - Follow Me", () => { game = new GameManager(phaserGame); game.override.battleType("double"); game.override.starterSpecies(Species.AMOONGUSS); + game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.FOLLOW_ME, Moves.SPLASH]); }); test( "move should redirect enemy attacks to the user", async () => { - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - const playerStartingHp = playerPokemon.map(p => p.hp); - game.move.select(Moves.FOLLOW_ME); game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY); + + // Force both enemies to target the player Pokemon that did not use Follow Me + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to(TurnEndPhase, false); - expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]); - expect(playerPokemon[1].hp).toBe(playerStartingHp[1]); + expect(playerPokemon[0].hp).toBeLessThan(playerPokemon[0].getMaxHp()); + expect(playerPokemon[1].hp).toBe(playerPokemon[1].getMaxHp()); }, TIMEOUT ); test( "move should redirect enemy attacks to the first ally that uses it", async () => { - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - const playerStartingHp = playerPokemon.map(p => p.hp); - game.move.select(Moves.FOLLOW_ME); game.move.select(Moves.FOLLOW_ME, 1); + + // Each player is targeted by an enemy + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER); + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to(TurnEndPhase, false); - playerPokemon.sort((a, b) => a.getBattleStat(Stat.SPD) - b.getBattleStat(Stat.SPD)); + playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD)); - expect(playerPokemon[1].hp).toBeLessThan(playerStartingHp[1]); - expect(playerPokemon[0].hp).toBe(playerStartingHp[0]); + expect(playerPokemon[1].hp).toBeLessThan(playerPokemon[1].getMaxHp()); + expect(playerPokemon[0].hp).toBe(playerPokemon[0].getMaxHp()); }, TIMEOUT ); @@ -78,21 +85,23 @@ describe("Moves - Follow Me", () => { async () => { game.override.ability(Abilities.STALWART); game.override.moveset([Moves.QUICK_ATTACK]); - game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); + + // Target doesn't need to be specified if the move is self-targeted + await game.forceEnemyMove(Moves.FOLLOW_ME); + await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged - expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); - expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); }, TIMEOUT ); @@ -100,21 +109,22 @@ describe("Moves - Follow Me", () => { "move effect should be bypassed by Snipe Shot", async () => { game.override.moveset([Moves.SNIPE_SHOT]); - game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY); game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2); + + await game.forceEnemyMove(Moves.FOLLOW_ME); + await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged - expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); - expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); }, TIMEOUT ); }); diff --git a/src/test/moves/foresight.test.ts b/src/test/moves/foresight.test.ts index b856ec0f852..d58097691fd 100644 --- a/src/test/moves/foresight.test.ts +++ b/src/test/moves/foresight.test.ts @@ -4,7 +4,6 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - Foresight", () => { let phaserGame: Phaser.Game; @@ -25,7 +24,7 @@ describe("Moves - Foresight", () => { game.override .disableCrits() .enemySpecies(Species.GASTLY) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyLevel(5) .starterSpecies(Species.MAGIKARP) .moveset([Moves.FORESIGHT, Moves.QUICK_ATTACK, Moves.MACH_PUNCH]); @@ -55,7 +54,7 @@ describe("Moves - Foresight", () => { }); it("should ignore target's evasiveness boosts", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.MINIMIZE)); + game.override.enemyMoveset([Moves.MINIMIZE]); await game.startBattle(); const pokemon = game.scene.getPlayerPokemon()!; diff --git a/src/test/moves/freeze_dry.test.ts b/src/test/moves/freeze_dry.test.ts new file mode 100644 index 00000000000..ff9e2f07162 --- /dev/null +++ b/src/test/moves/freeze_dry.test.ts @@ -0,0 +1,106 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Freeze-Dry", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.FREEZE_DRY]); + }); + + it("should deal 2x damage to pure water types", async () => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2); + }, TIMEOUT); + + it("should deal 4x damage to water/flying types", async () => { + game.override.enemySpecies(Species.WINGULL); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4); + }, TIMEOUT); + + it("should deal 1x damage to water/fire types", async () => { + game.override.enemySpecies(Species.VOLCANION); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(1); + }, TIMEOUT); + + // enable if this is ever fixed (lol) + it.todo("should deal 2x damage to water types under Normalize", async () => { + game.override.ability(Abilities.NORMALIZE); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2); + }, TIMEOUT); + + // enable once Electrify is implemented (and the interaction is fixed, as above) + it.todo("should deal 2x damage to water types under Electrify", async () => { + game.override.enemyMoveset([Moves.ELECTRIFY]); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.FREEZE_DRY); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2); + }, TIMEOUT); +}); diff --git a/src/test/moves/freezy_frost.test.ts b/src/test/moves/freezy_frost.test.ts index 00d7104d373..ad5163dae48 100644 --- a/src/test/moves/freezy_frost.test.ts +++ b/src/test/moves/freezy_frost.test.ts @@ -1,82 +1,105 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { allMoves } from "#app/data/move"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Stat } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { allMoves } from "#app/data/move"; +import { CommandPhase } from "#app/phases/command-phase"; describe("Moves - Freezy Frost", () => { - describe("integration tests", () => { - let phaserGame: Phaser.Game; - let game: GameManager; + let phaserGame: Phaser.Game; + let game: GameManager; - beforeAll(() => { - phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); - }); + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); - beforeEach(() => { - game = new GameManager(phaserGame); + beforeEach(() => { + game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override + .battleType("single") + .enemySpecies(Species.RATTATA) + .enemyLevel(100) + .enemyMoveset([ Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL ]) + .enemyAbility(Abilities.BALL_FETCH) + .startingLevel(100) + .moveset([ Moves.FREEZY_FROST, Moves.HOWL, Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyLevel(100); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.enemyAbility(Abilities.NONE); + vi.spyOn(allMoves[ Moves.FREEZY_FROST ], "accuracy", "get").mockReturnValue(100); + }); - game.override.startingLevel(100); - game.override.moveset([Moves.FREEZY_FROST, Moves.SWORDS_DANCE, Moves.CHARM, Moves.SPLASH]); - vi.spyOn(allMoves[Moves.FREEZY_FROST], "accuracy", "get").mockReturnValue(100); - game.override.ability(Abilities.NONE); - }); - - it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, player uses Freezy Frost to clear all stat changes", { timeout: 10000 }, async () => { - await game.startBattle([Species.RATTATA]); + it( + "should clear stat changes of user and opponent", + async () => { + await game.classicMode.startBattle([ Species.SHUCKLE ]); const user = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.move.select(Moves.SWORDS_DANCE); - await game.phaseInterceptor.to(TurnInitPhase); + game.move.select(Moves.HOWL); + await game.toNextTurn(); - game.move.select(Moves.CHARM); - await game.phaseInterceptor.to(TurnInitPhase); - const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; - const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; - expect(userAtkBefore).toBe(2); - expect(enemyAtkBefore).toBe(-2); + expect(user.getStatStage(Stat.ATK)).toBe(1); + expect(enemy.getStatStage(Stat.ATK)).toBe(1); game.move.select(Moves.FREEZY_FROST); - await game.phaseInterceptor.to(TurnInitPhase); - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); + await game.toNextTurn(); + + expect(user.getStatStage(Stat.ATK)).toBe(0); + expect(enemy.getStatStage(Stat.ATK)).toBe(0); }); - it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, enemy uses Freezy Frost to clear all stat changes", { timeout: 10000 }, async () => { - game.override.enemyMoveset([Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST]); - await game.startBattle([Species.SHUCKLE]); // Shuckle for slower Swords Dance on first turn so Freezy Frost doesn't affect it. + it( + "should clear all stat changes even when enemy uses the move", + async () => { + game.override.enemyMoveset([ Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); // Shuckle for slower Howl on first turn so Freezy Frost doesn't affect it. const user = game.scene.getPlayerPokemon()!; - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.move.select(Moves.SWORDS_DANCE); - await game.phaseInterceptor.to(TurnInitPhase); + game.move.select(Moves.HOWL); + await game.toNextTurn(); - const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; - expect(userAtkBefore).toBe(2); + const userAtkBefore = user.getStatStage(Stat.ATK); + expect(userAtkBefore).toBe(1); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(MoveEndPhase); - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); + await game.toNextTurn(); + expect(user.getStatStage(Stat.ATK)).toBe(0); + }); + + it( + "should clear all stat changes in double battle", + async () => { + game.override.battleType("double"); + await game.classicMode.startBattle([ Species.SHUCKLE, Species.RATTATA ]); + const [ leftPlayer, rightPlayer ] = game.scene.getPlayerField(); + const [ leftOpp, rightOpp ] = game.scene.getEnemyField(); + + game.move.select(Moves.HOWL, 0); + await game.phaseInterceptor.to(CommandPhase); + game.move.select(Moves.SPLASH, 1); + await game.toNextTurn(); + + expect(leftPlayer.getStatStage(Stat.ATK)).toBe(1); + expect(rightPlayer.getStatStage(Stat.ATK)).toBe(1); + expect(leftOpp.getStatStage(Stat.ATK)).toBe(2); // Both enemies use Howl + expect(rightOpp.getStatStage(Stat.ATK)).toBe(2); + + game.move.select(Moves.FREEZY_FROST, 0, leftOpp.getBattlerIndex()); + await game.phaseInterceptor.to(CommandPhase); + game.move.select(Moves.SPLASH, 1); + await game.toNextTurn(); + + expect(leftPlayer.getStatStage(Stat.ATK)).toBe(0); + expect(rightPlayer.getStatStage(Stat.ATK)).toBe(0); + expect(leftOpp.getStatStage(Stat.ATK)).toBe(0); + expect(rightOpp.getStatStage(Stat.ATK)).toBe(0); }); - }); }); diff --git a/src/test/moves/fusion_flare.test.ts b/src/test/moves/fusion_flare.test.ts index 471f6a2ac7b..0a8f6f9115d 100644 --- a/src/test/moves/fusion_flare.test.ts +++ b/src/test/moves/fusion_flare.test.ts @@ -27,7 +27,7 @@ describe("Moves - Fusion Flare", () => { game.override.moveset([fusionFlare]); game.override.startingLevel(1); - game.override.enemySpecies(Species.RESHIRAM); + game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); game.override.battleType("single"); diff --git a/src/test/moves/fusion_flare_bolt.test.ts b/src/test/moves/fusion_flare_bolt.test.ts index ebef5148778..a8372fcaaab 100644 --- a/src/test/moves/fusion_flare_bolt.test.ts +++ b/src/test/moves/fusion_flare_bolt.test.ts @@ -1,6 +1,6 @@ +import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; import { allMoves } from "#app/data/move"; -import { Stat } from "#app/data/pokemon-stat"; import { DamagePhase } from "#app/phases/damage-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index 67fd3464cf9..cfc458a908f 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -4,7 +4,6 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -31,7 +30,7 @@ describe("Moves - Gastro Acid", () => { game.override.ability(Abilities.NONE); game.override.moveset([Moves.GASTRO_ACID, Moves.WATER_GUN, Moves.SPLASH, Moves.CORE_ENFORCER]); game.override.enemySpecies(Species.BIDOOF); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.WATER_ABSORB); }); diff --git a/src/test/moves/gigaton_hammer.test.ts b/src/test/moves/gigaton_hammer.test.ts new file mode 100644 index 00000000000..b0ab06fdeb5 --- /dev/null +++ b/src/test/moves/gigaton_hammer.test.ts @@ -0,0 +1,79 @@ +import { BattlerIndex } from "#app/battle"; +import GameManager from "#app/test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Gigaton Hammer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .starterSpecies(Species.FEEBAS) + .moveset([Moves.GIGATON_HAMMER]) + .startingLevel(10) + .enemyLevel(100) + .enemyMoveset(Moves.SPLASH) + .disableCrits(); + }); + + it("can't be used two turns in a row", async() => { + await game.classicMode.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.GIGATON_HAMMER); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.move.select(Moves.GIGATON_HAMMER); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBe(enemy2.getMaxHp()); + }, 20000); + + it("can be used again if recalled and sent back out", async() => { + game.override.startingWave(4); + await game.classicMode.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.GIGATON_HAMMER); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.move.select(Moves.GIGATON_HAMMER); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); + }, 20000); +}); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index 1eac3c32bb4..9eed6868432 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -1,13 +1,12 @@ import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; -import { DamagePhase } from "#app/phases/damage-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +const TIMEOUT = 20 * 1000; describe("Moves - Glaive Rush", () => { let phaserGame: Phaser.Game; @@ -25,131 +24,142 @@ describe("Moves - Glaive Rush", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.disableCrits(); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(Array(4).fill(Moves.GLAIVE_RUSH)); - game.override.starterSpecies(Species.KLINK); - game.override.ability(Abilities.UNNERVE); - game.override.passiveAbility(Abilities.FUR_COAT); - game.override.moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([Moves.GLAIVE_RUSH]) + .starterSpecies(Species.KLINK) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]); }); it("takes double damage from attacks", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; - vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0); game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); const damageDealt = 1000 - enemy.hp; - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); - }, 5000); // TODO: revert back to 20s + }, TIMEOUT); it("always gets hit by attacks", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; game.move.select(Moves.AVALANCHE); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.hp).toBeLessThan(1000); - }, 20000); + }, TIMEOUT); it("interacts properly with multi-lens", async () => { - game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]); - game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE)); - await game.startBattle(); + game.override + .startingHeldItems([{ name: "MULTI_LENS", count: 2 }]) + .enemyMoveset([Moves.AVALANCHE]); + await game.classicMode.startBattle(); + const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 1000; player.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; game.move.select(Moves.GLAIVE_RUSH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBeLessThan(1000); player.hp = 1000; game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(1000); - }, 20000); + }, TIMEOUT); it("secondary effects only last until next move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); - await game.startBattle(); + game.override.enemyMoveset([Moves.SHADOW_SNEAK]); + await game.classicMode.startBattle(); + const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 1000; player.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; game.move.select(Moves.GLAIVE_RUSH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(1000); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const damagedHp = player.hp; expect(player.hp).toBeLessThan(1000); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(damagedHp); - }, 20000); + }, TIMEOUT); it("secondary effects are removed upon switching", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); - game.override.starterSpecies(0); - await game.startBattle([Species.KLINK, Species.FEEBAS]); + game.override + .enemyMoveset([Moves.SHADOW_SNEAK]) + .starterSpecies(0); + await game.classicMode.startBattle([Species.KLINK, Species.FEEBAS]); + const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; game.move.select(Moves.GLAIVE_RUSH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(player.getMaxHp()); game.doSwitchPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); game.doSwitchPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(player.getMaxHp()); - }, 20000); + }, TIMEOUT); it("secondary effects don't activate if move fails", async () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]); - await game.startBattle(); + await game.classicMode.startBattle(); + const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 1000; player.hp = 1000; game.move.select(Moves.PROTECT); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(TurnEndPhase); - game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); + await game.phaseInterceptor.to("TurnEndPhase"); + game.override.enemyMoveset([Moves.SPLASH]); const damagedHP1 = 1000 - enemy.hp; enemy.hp = 1000; game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const damagedHP2 = 1000 - enemy.hp; expect(damagedHP2).toBeGreaterThanOrEqual((damagedHP1 * 2) - 1); - }, 20000); + }, TIMEOUT); }); diff --git a/src/test/moves/growth.test.ts b/src/test/moves/growth.test.ts index dfbf5406351..a66e4ec6719 100644 --- a/src/test/moves/growth.test.ts +++ b/src/test/moves/growth.test.ts @@ -1,14 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { Stat } from "#app/data/pokemon-stat"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Growth", () => { let phaserGame: Phaser.Game; @@ -26,31 +24,25 @@ describe("Moves - Growth", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.GROWTH; game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.MOXIE); game.override.ability(Abilities.INSOMNIA); - game.override.startingLevel(2000); - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.moveset([ Moves.GROWTH ]); + game.override.enemyMoveset(Moves.SPLASH); }); - it("GROWTH", async () => { - const moveToUse = Moves.GROWTH; + it("should raise SPATK stat stage by 1", async() => { await game.startBattle([ - Species.MIGHTYENA, - Species.MIGHTYENA, + Species.MIGHTYENA ]); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[Stat.SPATK]).toBe(0); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + const playerPokemon = game.scene.getPlayerPokemon()!; - game.move.select(moveToUse); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); + + game.move.select(Moves.GROWTH); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.SPATK]).toBe(1); + + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); }, 20000); }); diff --git a/src/test/moves/guard_split.test.ts b/src/test/moves/guard_split.test.ts new file mode 100644 index 00000000000..36be82ba5e4 --- /dev/null +++ b/src/test/moves/guard_split.test.ts @@ -0,0 +1,81 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; + +describe("Moves - Guard Split", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.NONE) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.GUARD_SPLIT ]) + .ability(Abilities.NONE); + }); + + it("should average the user's DEF and SPDEF stats with those of the target", async () => { + game.override.enemyMoveset(Moves.SPLASH); + await game.startBattle([ + Species.INDEEDEE + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgDef = Math.floor((player.getStat(Stat.DEF, false) + enemy.getStat(Stat.DEF, false)) / 2); + const avgSpDef = Math.floor((player.getStat(Stat.SPDEF, false) + enemy.getStat(Stat.SPDEF, false)) / 2); + + game.move.select(Moves.GUARD_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.DEF, false)).toBe(avgDef); + expect(enemy.getStat(Stat.DEF, false)).toBe(avgDef); + + expect(player.getStat(Stat.SPDEF, false)).toBe(avgSpDef); + expect(enemy.getStat(Stat.SPDEF, false)).toBe(avgSpDef); + }, 20000); + + it("should be idempotent", async () => { + game.override.enemyMoveset([Moves.GUARD_SPLIT]); + await game.startBattle([ + Species.INDEEDEE + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgDef = Math.floor((player.getStat(Stat.DEF, false) + enemy.getStat(Stat.DEF, false)) / 2); + const avgSpDef = Math.floor((player.getStat(Stat.SPDEF, false) + enemy.getStat(Stat.SPDEF, false)) / 2); + + game.move.select(Moves.GUARD_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + game.move.select(Moves.GUARD_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.DEF, false)).toBe(avgDef); + expect(enemy.getStat(Stat.DEF, false)).toBe(avgDef); + + expect(player.getStat(Stat.SPDEF, false)).toBe(avgSpDef); + expect(enemy.getStat(Stat.SPDEF, false)).toBe(avgSpDef); + }, 20000); +}); diff --git a/src/test/moves/guard_swap.test.ts b/src/test/moves/guard_swap.test.ts new file mode 100644 index 00000000000..a27afaaa7ba --- /dev/null +++ b/src/test/moves/guard_swap.test.ts @@ -0,0 +1,63 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; + +describe("Moves - Guard Swap", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([Moves.SHELL_SMASH]) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.GUARD_SWAP ]) + .ability(Abilities.NONE); + }); + + it("should swap the user's DEF AND SPDEF stat stages with the target's", async () => { + await game.startBattle([ + Species.INDEEDEE + ]); + + // Should start with no stat stages + const player = game.scene.getPlayerPokemon()!; + // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.GUARD_SWAP); + + await game.phaseInterceptor.to(MoveEndPhase); + + expect(player.getStatStage(Stat.DEF)).toBe(0); + expect(player.getStatStage(Stat.SPDEF)).toBe(0); + expect(enemy.getStatStage(Stat.DEF)).toBe(-1); + expect(enemy.getStatStage(Stat.SPDEF)).toBe(-1); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStatStage(Stat.DEF)).toBe(-1); + expect(player.getStatStage(Stat.SPDEF)).toBe(-1); + expect(enemy.getStatStage(Stat.DEF)).toBe(0); + expect(enemy.getStatStage(Stat.SPDEF)).toBe(0); + }, 20000); +}); diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts index 70c78490269..5d2e4e5b145 100644 --- a/src/test/moves/hard_press.test.ts +++ b/src/test/moves/hard_press.test.ts @@ -4,7 +4,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -30,7 +29,7 @@ describe("Moves - Hard Press", () => { game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.MUNCHLAX); game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.moveset([Moves.HARD_PRESS]); vi.spyOn(moveToCheck, "calculateBattlePower"); }); diff --git a/src/test/moves/haze.test.ts b/src/test/moves/haze.test.ts index 8a32a40cb32..211c1a41409 100644 --- a/src/test/moves/haze.test.ts +++ b/src/test/moves/haze.test.ts @@ -1,13 +1,11 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Haze", () => { describe("integration tests", () => { @@ -29,7 +27,7 @@ describe("Moves - Haze", () => { game.override.enemySpecies(Species.RATTATA); game.override.enemyLevel(100); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.NONE); game.override.startingLevel(100); @@ -37,44 +35,28 @@ describe("Moves - Haze", () => { game.override.ability(Abilities.NONE); }); - it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, player uses Haze to clear all stat changes", { timeout: 10000 }, async () => { + it("should reset all stat changes of all Pokemon on field", { timeout: 10000 }, async () => { await game.startBattle([Species.RATTATA]); const user = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); + + expect(user.getStatStage(Stat.ATK)).toBe(0); + expect(enemy.getStatStage(Stat.ATK)).toBe(0); game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnInitPhase); - const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; - const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; - expect(userAtkBefore).toBe(2); - expect(enemyAtkBefore).toBe(-2); + + expect(user.getStatStage(Stat.ATK)).toBe(2); + expect(enemy.getStatStage(Stat.ATK)).toBe(-2); game.move.select(Moves.HAZE); await game.phaseInterceptor.to(TurnInitPhase); - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - }); - it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, enemy uses Haze to clear all stat changes", { timeout: 10000 }, async () => { - game.override.enemyMoveset([Moves.HAZE, Moves.HAZE, Moves.HAZE, Moves.HAZE]); - await game.startBattle([Species.SHUCKLE]); // Shuckle for slower Swords Dance on first turn so Haze doesn't affect it. - const user = game.scene.getPlayerPokemon()!; - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - - game.move.select(Moves.SWORDS_DANCE); - await game.phaseInterceptor.to(TurnInitPhase); - - const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; - expect(userAtkBefore).toBe(2); - - game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(MoveEndPhase); - expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(user.getStatStage(Stat.ATK)).toBe(0); + expect(enemy.getStatStage(Stat.ATK)).toBe(0); }); }); }); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index 1280d8b429a..7aa2dbfec2b 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -32,7 +32,7 @@ describe("Moves - Hyper Beam", () => { game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); + game.override.enemyMoveset([Moves.SPLASH]); game.override.enemyLevel(100); game.override.moveset([Moves.HYPER_BEAM, Moves.TACKLE]); diff --git a/src/test/moves/jaw_lock.test.ts b/src/test/moves/jaw_lock.test.ts index 42f7a244977..75fd6f0ff32 100644 --- a/src/test/moves/jaw_lock.test.ts +++ b/src/test/moves/jaw_lock.test.ts @@ -6,7 +6,6 @@ import { FaintPhase } from "#app/phases/faint-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { SPLASH_ONLY } from "#app/test/utils/testUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -35,7 +34,7 @@ describe("Moves - Jaw Lock", () => { .battleType("single") .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .moveset([Moves.JAW_LOCK, Moves.SPLASH]) .startingLevel(100) .enemyLevel(100) @@ -153,7 +152,7 @@ describe("Moves - Jaw Lock", () => { it( "should not trap either pokemon if the target is protected", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.PROTECT)); + game.override.enemyMoveset([Moves.PROTECT]); await game.startBattle([Species.BULBASAUR]); diff --git a/src/test/moves/lash_out.test.ts b/src/test/moves/lash_out.test.ts new file mode 100644 index 00000000000..8c414832f36 --- /dev/null +++ b/src/test/moves/lash_out.test.ts @@ -0,0 +1,52 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Lash Out", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.FUR_COAT) + .enemyMoveset([Moves.GROWL]) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.LASH_OUT]); + + }); + + it("should deal double damage if the user's stat stages were lowered this turn", async () => { + vi.spyOn(allMoves[Moves.LASH_OUT], "calculateBattlePower"); + await game.classicMode.startBattle(); + + game.move.select(Moves.LASH_OUT); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(allMoves[Moves.LASH_OUT].calculateBattlePower).toHaveReturnedWith(150); + }, TIMEOUT); +}); diff --git a/src/test/moves/lucky_chant.test.ts b/src/test/moves/lucky_chant.test.ts index 7d5bfe02476..57e5ff80f1d 100644 --- a/src/test/moves/lucky_chant.test.ts +++ b/src/test/moves/lucky_chant.test.ts @@ -31,7 +31,7 @@ describe("Moves - Lucky Chant", () => { .moveset([Moves.LUCKY_CHANT, Moves.SPLASH, Moves.FOLLOW_ME]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) - .enemyMoveset(Array(4).fill(Moves.FLOWER_TRICK)) + .enemyMoveset([Moves.FLOWER_TRICK]) .startingLevel(100) .enemyLevel(100); }); @@ -87,7 +87,7 @@ describe("Moves - Lucky Chant", () => { it( "should prevent critical hits from field effects", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CHARIZARD]); diff --git a/src/test/moves/lunar_blessing.test.ts b/src/test/moves/lunar_blessing.test.ts index 73647716f06..6a104762f4d 100644 --- a/src/test/moves/lunar_blessing.test.ts +++ b/src/test/moves/lunar_blessing.test.ts @@ -1,10 +1,9 @@ -import { StatusEffect } from "#app/enums/status-effect.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; +import { StatusEffect } from "#app/enums/status-effect"; +import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -26,7 +25,7 @@ describe("Moves - Lunar Blessing", () => { game.override.battleType("double"); game.override.enemySpecies(Species.SHUCKLE); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([Moves.LUNAR_BLESSING, Moves.SPLASH]); diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 0af7763f175..5ac35168f92 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -1,13 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { Stat } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; const TIMEOUT = 20 * 1000; @@ -31,22 +30,22 @@ describe("Moves - Make It Rain", () => { game.override.moveset([Moves.MAKE_IT_RAIN, Moves.SPLASH]); game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.INSOMNIA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.startingLevel(100); game.override.enemyLevel(100); }); - it("should only reduce Sp. Atk. once in a double battle", async () => { + it("should only lower SPATK stat stage by 1 once in a double battle", async () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const playerPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.MAKE_IT_RAIN); game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEndPhase); - expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); }, TIMEOUT); it("should apply effects even if the target faints", async () => { @@ -60,10 +59,10 @@ describe("Moves - Make It Rain", () => { game.move.select(Moves.MAKE_IT_RAIN); - await game.phaseInterceptor.to(StatChangePhase); + await game.phaseInterceptor.to(StatStageChangePhase); expect(enemyPokemon.isFainted()).toBe(true); - expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); }, TIMEOUT); it("should reduce Sp. Atk. once after KOing two enemies", async () => { @@ -71,22 +70,22 @@ describe("Moves - Make It Rain", () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const playerPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyField(); game.move.select(Moves.MAKE_IT_RAIN); game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(StatChangePhase); + await game.phaseInterceptor.to(StatStageChangePhase); enemyPokemon.forEach(p => expect(p.isFainted()).toBe(true)); - expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); }, TIMEOUT); - it("should reduce Sp. Atk if it only hits the second target", async () => { + it("should lower SPATK stat stage by 1 if it only hits the second target", async () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const playerPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.MAKE_IT_RAIN); game.move.select(Moves.SPLASH, 1); @@ -96,6 +95,6 @@ describe("Moves - Make It Rain", () => { await game.phaseInterceptor.to(MoveEndPhase); - expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); }, TIMEOUT); }); diff --git a/src/test/moves/mat_block.test.ts b/src/test/moves/mat_block.test.ts index 29a97806242..b759f49bf98 100644 --- a/src/test/moves/mat_block.test.ts +++ b/src/test/moves/mat_block.test.ts @@ -1,13 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; const TIMEOUT = 20 * 1000; @@ -33,7 +33,7 @@ describe("Moves - Mat Block", () => { game.override.moveset([Moves.MAT_BLOCK, Moves.SPLASH]); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); @@ -62,7 +62,7 @@ describe("Moves - Mat Block", () => { test( "should not protect the user and allies from status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset([Moves.GROWL]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -76,7 +76,7 @@ describe("Moves - Mat Block", () => { await game.phaseInterceptor.to(BerryPhase, false); - leadPokemon.forEach(p => expect(p.summonData.battleStats[BattleStat.ATK]).toBe(-2)); + leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(-2)); }, TIMEOUT ); diff --git a/src/test/moves/miracle_eye.test.ts b/src/test/moves/miracle_eye.test.ts index d6b21a5dc2f..0528b509c82 100644 --- a/src/test/moves/miracle_eye.test.ts +++ b/src/test/moves/miracle_eye.test.ts @@ -5,7 +5,6 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - Miracle Eye", () => { let phaserGame: Phaser.Game; @@ -26,7 +25,7 @@ describe("Moves - Miracle Eye", () => { game.override .disableCrits() .enemySpecies(Species.UMBREON) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyLevel(5) .starterSpecies(Species.MAGIKARP) .moveset([Moves.MIRACLE_EYE, Moves.CONFUSION]); diff --git a/src/test/moves/multi_target.test.ts b/src/test/moves/multi_target.test.ts index 16ccd5519b1..5e830f23fc7 100644 --- a/src/test/moves/multi_target.test.ts +++ b/src/test/moves/multi_target.test.ts @@ -4,7 +4,6 @@ import { Species } from "#app/enums/species"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -32,7 +31,7 @@ describe("Multi-target damage reduction", () => { .enemyLevel(100) .startingLevel(100) .enemySpecies(Species.POLIWAG) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) .moveset([Moves.TACKLE, Moves.DAZZLING_GLEAM, Moves.EARTHQUAKE, Moves.SPLASH]) .ability(Abilities.BALL_FETCH); diff --git a/src/test/moves/obstruct.test.ts b/src/test/moves/obstruct.test.ts new file mode 100644 index 00000000000..539b11090de --- /dev/null +++ b/src/test/moves/obstruct.test.ts @@ -0,0 +1,71 @@ +import { Moves } from "#app/enums/moves"; +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Obstruct", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.OBSTRUCT]); + }); + + it("protects from contact damaging moves and lowers the opponent's defense by 2 stages", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("BerryPhase"); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + expect(player.isFullHp()).toBe(true); + expect(enemy.getStatStage(Stat.DEF)).toBe(-2); + }, TIMEOUT); + + it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN)); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("BerryPhase"); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + expect(player.isFullHp()).toBe(true); + expect(enemy.getStatStage(Stat.DEF)).toBe(0); + }, TIMEOUT); + + it("doesn't protect from status moves", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("BerryPhase"); + + const player = game.scene.getPlayerPokemon()!; + + expect(player.getStatStage(Stat.ATK)).toBe(-1); + }, TIMEOUT); +}); diff --git a/src/test/moves/octolock.test.ts b/src/test/moves/octolock.test.ts index 389e4a4c4cf..7618b08e9fc 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { TrappedTag } from "#app/data/battler-tags"; import { CommandPhase } from "#app/phases/command-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; @@ -7,73 +7,110 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Octolock", () => { - describe("integration tests", () => { - let phaserGame: Phaser.Game; - let game: GameManager; + let phaserGame: Phaser.Game; + let game: GameManager; - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - - game.override.battleType("single"); - - game.override.enemySpecies(Species.RATTATA); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.enemyAbility(Abilities.BALL_FETCH); - - game.override.startingLevel(2000); - game.override.moveset([Moves.OCTOLOCK, Moves.SPLASH]); - game.override.ability(Abilities.BALL_FETCH); - }); - - it("Reduces DEf and SPDEF by 1 each turn", { timeout: 10000 }, async () => { - await game.startBattle([Species.GRAPPLOCT]); - - const enemyPokemon = game.scene.getEnemyField(); - - // use Octolock and advance to init phase of next turn to check for stat changes - game.move.select(Moves.OCTOLOCK); - await game.phaseInterceptor.to(TurnInitPhase); - - expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-1); - expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-1); - - // take a second turn to make sure stat changes occur again - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.SPLASH); - - await game.phaseInterceptor.to(TurnInitPhase); - expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-2); - expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-2); - }); - - it("Traps the target pokemon", { timeout: 10000 }, async () => { - await game.startBattle([Species.GRAPPLOCT]); - - const enemyPokemon = game.scene.getEnemyField(); - - // before Octolock - enemy should not be trapped - expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeUndefined(); - - game.move.select(Moves.OCTOLOCK); - - // after Octolock - enemy should be trapped - await game.phaseInterceptor.to(MoveEndPhase); - expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeDefined(); + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, }); }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override.battleType("single") + .enemySpecies(Species.RATTATA) + .enemyMoveset(Moves.SPLASH) + .enemyAbility(Abilities.BALL_FETCH) + .startingLevel(2000) + .moveset([ Moves.OCTOLOCK, Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH); + }); + + it("lowers DEF and SPDEF stat stages of the target Pokemon by 1 each turn", { timeout: 10000 }, async () => { + await game.classicMode.startBattle([ Species.GRAPPLOCT ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-1); + + // take a second turn to make sure stat changes occur again + await game.phaseInterceptor.to(CommandPhase); + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to(TurnInitPhase); + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-2); + expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-2); + }); + + it("if target pokemon has BIG_PECKS, should only lower SPDEF stat stage by 1", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.BIG_PECKS); + await game.classicMode.startBattle([ Species.GRAPPLOCT ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-1); + }); + + it("if target pokemon has WHITE_SMOKE, should not reduce any stat stages", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.WHITE_SMOKE); + await game.classicMode.startBattle([ Species.GRAPPLOCT ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(0); + }); + + it("if target pokemon has CLEAR_BODY, should not reduce any stat stages", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.CLEAR_BODY); + await game.classicMode.startBattle([ Species.GRAPPLOCT ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(0); + }); + + it("traps the target pokemon", { timeout: 10000 }, async () => { + await game.classicMode.startBattle([ Species.GRAPPLOCT ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // before Octolock - enemy should not be trapped + expect(enemyPokemon.findTag(t => t instanceof TrappedTag)).toBeUndefined(); + + game.move.select(Moves.OCTOLOCK); + + // after Octolock - enemy should be trapped + await game.phaseInterceptor.to(MoveEndPhase); + expect(enemyPokemon.findTag(t => t instanceof TrappedTag)).toBeDefined(); + }); }); diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index 7c2ca3f334c..52cfaf98111 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -1,15 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { Stat } from "#enums/stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; const TIMEOUT = 20 * 1000; @@ -31,7 +30,7 @@ describe("Moves - Parting Shot", () => { game = new GameManager(phaserGame); game.override.battleType("single"); game.override.moveset([Moves.PARTING_SHOT, Moves.SPLASH]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.startingLevel(5); game.override.enemyLevel(5); @@ -51,9 +50,8 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -72,9 +70,8 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -108,16 +105,15 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-6); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-6); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6); // now parting shot should fail game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-6); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-6); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -128,7 +124,7 @@ describe("Moves - Parting Shot", () => { game.override .enemySpecies(Species.ALTARIA) .enemyAbility(Abilities.NONE) - .enemyMoveset(Array(4).fill(Moves.MIST)); + .enemyMoveset([Moves.MIST]); await game.startBattle([Species.SNORLAX, Species.MEOWTH]); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -137,9 +133,8 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -158,9 +153,8 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -176,9 +170,8 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = enemyPokemon.summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW); }, TIMEOUT ); @@ -199,9 +192,9 @@ describe("Moves - Parting Shot", () => { game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MEOWTH); }, TIMEOUT ); diff --git a/src/test/moves/power_shift.test.ts b/src/test/moves/power_shift.test.ts new file mode 100644 index 00000000000..3fda315193e --- /dev/null +++ b/src/test/moves/power_shift.test.ts @@ -0,0 +1,63 @@ +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Power Shift", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.POWER_SHIFT, Moves.BULK_UP]) + .battleType("single") + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("switches the user's raw Attack stat with its raw Defense stat", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + playerPokemon.setStat(Stat.ATK, 10, false); + playerPokemon.setStat(Stat.DEF, 20, false); + + game.move.select(Moves.BULK_UP); + + await game.phaseInterceptor.to("TurnEndPhase"); + + // Stat stages are increased by 1 + expect(playerPokemon.getStatStageMultiplier(Stat.ATK)).toBe(1.5); + expect(playerPokemon.getStatStageMultiplier(Stat.DEF)).toBe(1.5); + + await game.toNextTurn(); + + game.move.select(Moves.POWER_SHIFT); + + await game.phaseInterceptor.to("TurnEndPhase"); + + // Effective stats are calculated correctly + expect(playerPokemon.getEffectiveStat(Stat.ATK)).toBe(30); + expect(playerPokemon.getEffectiveStat(Stat.DEF)).toBe(15); + // Raw stats are swapped + expect(playerPokemon.getStat(Stat.ATK, false)).toBe(20); + expect(playerPokemon.getStat(Stat.DEF, false)).toBe(10); + }, TIMEOUT); +}); diff --git a/src/test/moves/power_split.test.ts b/src/test/moves/power_split.test.ts new file mode 100644 index 00000000000..aaf34541567 --- /dev/null +++ b/src/test/moves/power_split.test.ts @@ -0,0 +1,81 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; + +describe("Moves - Power Split", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.NONE) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.POWER_SPLIT ]) + .ability(Abilities.NONE); + }); + + it("should average the user's ATK and SPATK stats with those of the target", async () => { + game.override.enemyMoveset(Moves.SPLASH); + await game.startBattle([ + Species.INDEEDEE + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); + + game.move.select(Moves.POWER_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); + + expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + }, 20000); + + it("should be idempotent", async () => { + game.override.enemyMoveset([Moves.POWER_SPLIT]); + await game.startBattle([ + Species.INDEEDEE + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); + + game.move.select(Moves.POWER_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + game.move.select(Moves.POWER_SPLIT); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); + + expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + }, 20000); +}); diff --git a/src/test/moves/power_swap.test.ts b/src/test/moves/power_swap.test.ts new file mode 100644 index 00000000000..a3d4bfca19a --- /dev/null +++ b/src/test/moves/power_swap.test.ts @@ -0,0 +1,62 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; + +describe("Moves - Power Swap", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([Moves.SHELL_SMASH]) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.POWER_SWAP ]) + .ability(Abilities.NONE); + }); + + it("should swap the user's ATK AND SPATK stat stages with the target's", async () => { + await game.startBattle([ + Species.INDEEDEE + ]); + + // Should start with no stat stages + const player = game.scene.getPlayerPokemon()!; + // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF + const enemy = game.scene.getEnemyPokemon()!; + game.move.select(Moves.POWER_SWAP); + + await game.phaseInterceptor.to(MoveEndPhase); + + expect(player.getStatStage(Stat.ATK)).toBe(0); + expect(player.getStatStage(Stat.SPATK)).toBe(0); + expect(enemy.getStatStage(Stat.ATK)).toBe(2); + expect(enemy.getStatStage(Stat.SPATK)).toBe(2); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStatStage(Stat.ATK)).toBe(2); + expect(player.getStatStage(Stat.SPATK)).toBe(2); + expect(enemy.getStatStage(Stat.ATK)).toBe(0); + expect(enemy.getStatStage(Stat.SPATK)).toBe(0); + }, 20000); +}); diff --git a/src/test/moves/protect.test.ts b/src/test/moves/protect.test.ts index 3fd51f4bc93..24bbcbb9d34 100644 --- a/src/test/moves/protect.test.ts +++ b/src/test/moves/protect.test.ts @@ -1,13 +1,14 @@ -import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; -import { BattleStat } from "#app/data/battle-stat"; -import { allMoves } from "#app/data/move"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { allMoves } from "#app/data/move"; +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { BattlerIndex } from "#app/battle"; +import { MoveResult } from "#app/field/pokemon"; const TIMEOUT = 20 * 1000; @@ -34,7 +35,7 @@ describe("Moves - Protect", () => { game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.INSOMNIA); - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); game.override.startingLevel(100); game.override.enemyLevel(100); @@ -43,13 +44,13 @@ describe("Moves - Protect", () => { test( "should protect the user from attacks", async () => { - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.CHARIZARD]); const leadPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.PROTECT); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); }, TIMEOUT @@ -58,16 +59,16 @@ describe("Moves - Protect", () => { test( "should prevent secondary effects from the opponent's attack", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.CEASELESS_EDGE)); + game.override.enemyMoveset([Moves.CEASELESS_EDGE]); vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.CHARIZARD]); const leadPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.PROTECT); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeUndefined(); @@ -77,37 +78,57 @@ describe("Moves - Protect", () => { test( "should protect the user from status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.CHARM)); + game.override.enemyMoveset([Moves.CHARM]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.CHARIZARD]); const leadPokemon = game.scene.getPlayerPokemon()!; game.move.select(Moves.PROTECT); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); }, TIMEOUT ); test( "should stop subsequent hits of a multi-hit move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACHYON_CUTTER)); + game.override.enemyMoveset([Moves.TACHYON_CUTTER]); - await game.startBattle([Species.CHARIZARD]); + await game.classicMode.startBattle([Species.CHARIZARD]); const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; game.move.select(Moves.PROTECT); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.turnData.hitCount).toBe(1); }, TIMEOUT ); + + test( + "should fail if the user is the last to move in the turn", + async () => { + game.override.enemyMoveset([Moves.PROTECT]); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.PROTECT); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("BerryPhase", false); + + expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + expect(leadPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + }, TIMEOUT + ); }); diff --git a/src/test/moves/quick_guard.test.ts b/src/test/moves/quick_guard.test.ts index 26d9a74e9fd..9ab0fe1509c 100644 --- a/src/test/moves/quick_guard.test.ts +++ b/src/test/moves/quick_guard.test.ts @@ -1,12 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { BattlerIndex } from "#app/battle"; +import { MoveResult } from "#app/field/pokemon"; const TIMEOUT = 20 * 1000; @@ -32,7 +32,7 @@ describe("Moves - Quick Guard", () => { game.override.moveset([Moves.QUICK_GUARD, Moves.SPLASH, Moves.FOLLOW_ME]); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(Array(4).fill(Moves.QUICK_ATTACK)); + game.override.enemyMoveset([Moves.QUICK_ATTACK]); game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); @@ -42,19 +42,16 @@ describe("Moves - Quick Guard", () => { test( "should protect the user and allies from priority moves", async () => { - await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerField(); game.move.select(Moves.QUICK_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); + playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); }, TIMEOUT ); @@ -62,44 +59,60 @@ describe("Moves - Quick Guard", () => { "should protect the user and allies from Prankster-boosted moves", async () => { game.override.enemyAbility(Abilities.PRANKSTER); - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset([Moves.GROWL]); - await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerField(); game.move.select(Moves.QUICK_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.SPLASH, 1); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - leadPokemon.forEach(p => expect(p.summonData.battleStats[BattleStat.ATK]).toBe(0)); + playerPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); }, TIMEOUT ); test( "should stop subsequent hits of a multi-hit priority move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.WATER_SHURIKEN)); + game.override.enemyMoveset([Moves.WATER_SHURIKEN]); - await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); game.move.select(Moves.QUICK_GUARD); - - await game.phaseInterceptor.to(CommandPhase); - game.move.select(Moves.FOLLOW_ME, 1); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); + playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); enemyPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); } ); + + test( + "should fail if the user is the last to move in the turn", + async () => { + game.override.battleType("single"); + game.override.enemyMoveset([Moves.QUICK_GUARD]); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.QUICK_GUARD); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("BerryPhase", false); + + expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + }, TIMEOUT + ); }); diff --git a/src/test/moves/rage_powder.test.ts b/src/test/moves/rage_powder.test.ts index 3e78c6fe0c9..86bc48ef882 100644 --- a/src/test/moves/rage_powder.test.ts +++ b/src/test/moves/rage_powder.test.ts @@ -1,5 +1,4 @@ import { BattlerIndex } from "#app/battle"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -26,32 +25,31 @@ describe("Moves - Rage Powder", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("double"); - game.override.starterSpecies(Species.AMOONGUSS); game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.TACKLE, Moves.SPLASH]); }); test( "move effect should be bypassed by Grass type", async () => { - game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); - - await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.VENUSAUR]); const enemyPokemon = game.scene.getEnemyField(); - const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); - await game.phaseInterceptor.to(TurnEndPhase, false); + + await game.forceEnemyMove(Moves.RAGE_POWDER); + await game.forceEnemyMove(Moves.SPLASH); + + await game.phaseInterceptor.to("BerryPhase", false); // If redirection was bypassed, both enemies should be damaged - expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); - expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); }, TIMEOUT ); @@ -59,10 +57,9 @@ describe("Moves - Rage Powder", () => { "move effect should be bypassed by Overcoat", async () => { game.override.ability(Abilities.OVERCOAT); - game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); // Test with two non-Grass type player Pokemon - await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); @@ -70,7 +67,11 @@ describe("Moves - Rage Powder", () => { game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); - await game.phaseInterceptor.to(TurnEndPhase, false); + + await game.forceEnemyMove(Moves.RAGE_POWDER); + await game.forceEnemyMove(Moves.SPLASH); + + await game.phaseInterceptor.to("BerryPhase", false); // If redirection was bypassed, both enemies should be damaged expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); diff --git a/src/test/moves/retaliate.test.ts b/src/test/moves/retaliate.test.ts new file mode 100644 index 00000000000..62965fffba6 --- /dev/null +++ b/src/test/moves/retaliate.test.ts @@ -0,0 +1,49 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { Moves } from "#enums/moves"; +import { allMoves } from "#app/data/move"; + +describe("Moves - Retaliate", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + const retaliate = allMoves[Moves.RETALIATE]; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.SNORLAX) + .enemyMoveset([Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE]) + .enemyLevel(100) + .moveset([Moves.RETALIATE, Moves.SPLASH]) + .startingLevel(80) + .disableCrits(); + }); + + it("increases power if ally died previous turn", async () => { + vi.spyOn(retaliate, "calculateBattlePower"); + await game.startBattle([Species.ABRA, Species.COBALION]); + game.move.select(Moves.RETALIATE); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(retaliate.calculateBattlePower).toHaveLastReturnedWith(70); + game.doSelectPartyPokemon(1); + + await game.toNextTurn(); + game.move.select(Moves.RETALIATE); + await game.phaseInterceptor.to("MoveEffectPhase"); + expect(retaliate.calculateBattlePower).toHaveReturnedWith(140); + }); +}); diff --git a/src/test/moves/rollout.test.ts b/src/test/moves/rollout.test.ts index ddb0b22e642..c08535a61df 100644 --- a/src/test/moves/rollout.test.ts +++ b/src/test/moves/rollout.test.ts @@ -4,7 +4,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -32,7 +31,7 @@ describe("Moves - Rollout", () => { game.override.enemyAbility(Abilities.BALL_FETCH); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); it("should double it's dmg on sequential uses but reset after 5", async () => { diff --git a/src/test/moves/safeguard.test.ts b/src/test/moves/safeguard.test.ts new file mode 100644 index 00000000000..2caf698a73a --- /dev/null +++ b/src/test/moves/safeguard.test.ts @@ -0,0 +1,155 @@ +import { BattlerIndex } from "#app/battle"; +import { allAbilities, PostDefendContactApplyStatusEffectAbAttr } from "#app/data/ability"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import GameManager from "#app/test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Safeguard", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.DRATINI) + .enemyMoveset([Moves.SAFEGUARD]) + .enemyAbility(Abilities.BALL_FETCH) + .enemyLevel(5) + .starterSpecies(Species.DRATINI) + .moveset([Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH]) + .ability(Abilities.BALL_FETCH); + }); + + it("protects from damaging moves with additional effects", async () => { + await game.classicMode.startBattle(); + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.NUZZLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemy.status).toBeUndefined(); + }, TIMEOUT); + + it("protects from status moves", async () => { + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPORE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.status).toBeUndefined(); + }, TIMEOUT); + + it("protects from confusion", async () => { + game.override.moveset([Moves.CONFUSE_RAY]); + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.CONFUSE_RAY); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.summonData.tags).toEqual([]); + }, TIMEOUT); + + it("protects ally from status", async () => { + game.override.battleType("double"); + + await game.classicMode.startBattle(); + + game.move.select(Moves.SPORE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.NUZZLE, 1, BattlerIndex.ENEMY_2); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to("BerryPhase"); + + const enemyPokemon = game.scene.getEnemyField(); + + expect(enemyPokemon[0].status).toBeUndefined(); + expect(enemyPokemon[1].status).toBeUndefined(); + }, TIMEOUT); + + it("protects from Yawn", async () => { + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.YAWN); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.summonData.tags).toEqual([]); + }, TIMEOUT); + + it("doesn't protect from already existing Yawn", async () => { + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.YAWN); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP); + }, TIMEOUT); + + it("doesn't protect from self-inflicted via Rest or Flame Orb", async () => { + game.override.enemyHeldItems([{name: "FLAME_ORB"}]); + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + enemyPokemon.damageAndUpdate(1); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.BURN); + + game.override.enemyMoveset([Moves.REST]); + // Force the moveset to update mid-battle + // TODO: Remove after enemy AI rework is in + enemyPokemon.getMoveset(); + game.move.select(Moves.SPLASH); + enemyPokemon.damageAndUpdate(1); + await game.toNextTurn(); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP); + }, TIMEOUT); + + it("protects from ability-inflicted status", async () => { + game.override.ability(Abilities.STATIC); + vi.spyOn(allAbilities[Abilities.STATIC].getAttrs(PostDefendContactApplyStatusEffectAbAttr)[0], "chance", "get").mockReturnValue(100); + await game.classicMode.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + game.override.enemyMoveset([Moves.TACKLE]); + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(enemyPokemon.status).toBeUndefined(); + }, TIMEOUT); +}); diff --git a/src/test/moves/shell_trap.test.ts b/src/test/moves/shell_trap.test.ts index 4549a8b2b73..213b9c3fd0a 100644 --- a/src/test/moves/shell_trap.test.ts +++ b/src/test/moves/shell_trap.test.ts @@ -9,7 +9,6 @@ import { MovePhase } from "#app/phases/move-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -33,7 +32,7 @@ describe("Moves - Shell Trap", () => { .battleType("double") .moveset([Moves.SHELL_TRAP, Moves.SPLASH, Moves.BULLDOZE]) .enemySpecies(Species.SNORLAX) - .enemyMoveset(Array(4).fill(Moves.RAZOR_LEAF)) + .enemyMoveset([Moves.RAZOR_LEAF]) .startingLevel(100) .enemyLevel(100); @@ -67,7 +66,7 @@ describe("Moves - Shell Trap", () => { it( "should fail if the user is only hit by special attacks", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SWIFT)); + game.override.enemyMoveset([Moves.SWIFT]); await game.startBattle([Species.CHARIZARD, Species.TURTONATOR]); @@ -93,7 +92,7 @@ describe("Moves - Shell Trap", () => { it( "should fail if the user isn't hit with any attack", async () => { - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.CHARIZARD, Species.TURTONATOR]); @@ -119,7 +118,7 @@ describe("Moves - Shell Trap", () => { it( "should not activate from an ally's attack", async () => { - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); diff --git a/src/test/moves/speed_swap.test.ts b/src/test/moves/speed_swap.test.ts new file mode 100644 index 00000000000..179f1212394 --- /dev/null +++ b/src/test/moves/speed_swap.test.ts @@ -0,0 +1,53 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; + +describe("Moves - Speed Swap", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.NONE) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.SPEED_SWAP ]) + .ability(Abilities.NONE); + }); + + it("should swap the user's SPD and the target's SPD stats", async () => { + await game.startBattle([ + Species.INDEEDEE + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const playerSpd = player.getStat(Stat.SPD, false); + const enemySpd = enemy.getStat(Stat.SPD, false); + + game.move.select(Moves.SPEED_SWAP); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.SPD, false)).toBe(enemySpd); + expect(enemy.getStat(Stat.SPD, false)).toBe(playerSpd); + }, 20000); +}); diff --git a/src/test/moves/spikes.test.ts b/src/test/moves/spikes.test.ts index c4096111c6f..aa59912d802 100644 --- a/src/test/moves/spikes.test.ts +++ b/src/test/moves/spikes.test.ts @@ -1,4 +1,3 @@ -import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -23,93 +22,61 @@ describe("Moves - Spikes", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.scene.battleStyle = 1; - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyAbility(Abilities.HYDRATION); - game.override.enemyPassiveAbility(Abilities.HYDRATION); - game.override.ability(Abilities.HYDRATION); - game.override.passiveAbility(Abilities.HYDRATION); - game.override.startingWave(3); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - game.override.moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); }); - it("single - wild - stay on field - no damage", async () => { - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHp = game.scene.getParty()[0].hp; - expect(game.scene.getParty()[0].hp).toBe(initialHp); + it("should not damage the team that set them", async () => { + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); + game.move.select(Moves.SPLASH); await game.toNextTurn(); - expect(game.scene.getParty()[0].hp).toBe(initialHp); - }, 20000); - - it("single - wild - take some damage", async () => { - // player set spikes on the field and switch back to back - // opponent do splash for 2 turns - // nobody should take damage - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, false); - - const initialHp = game.scene.getParty()[0].hp; - game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase, false); game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase, false); + await game.toNextTurn(); - expect(game.scene.getParty()[0].hp).toBe(initialHp); + game.doSwitchPokemon(1); + await game.toNextTurn(); + + const player = game.scene.getParty()[0]; + expect(player.hp).toBe(player.getMaxHp()); }, 20000); - it("trainer - wild - force switch opponent - should take damage", async () => { + it("should damage opposing pokemon that are forced to switch in", async () => { game.override.startingWave(5); - // player set spikes on the field and do splash for 3 turns - // opponent do splash for 4 turns - // nobody should take damage - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); + game.move.select(Moves.ROAR); await game.toNextTurn(); - expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); + + const enemy = game.scene.getEnemyParty()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }, 20000); - it("trainer - wild - force switch by himself opponent - should take damage", async () => { + it("should damage opposing pokemon that choose to switch in", async () => { game.override.startingWave(5); - game.override.startingLevel(5000); - game.override.enemySpecies(0); - // turn 1: player set spikes, opponent do splash - // turn 2: player do splash, opponent switch pokemon - // opponent pokemon should trigger spikes and lose HP - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.forceOpponentToSwitch(); game.move.select(Moves.SPLASH); + game.forceEnemyToSwitch(); await game.toNextTurn(); - expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); + + const enemy = game.scene.getEnemyParty()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }, 20000); }); diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index ab47e65d653..acf7f01d991 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -1,22 +1,23 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; import { allMoves } from "#app/data/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { MoveResult, TurnMove } from "#app/field/pokemon"; -import { MovePhase } from "#app/phases/move-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Spit Up", () => { let phaserGame: Phaser.Game; let game: GameManager; + const spitUp = allMoves[Moves.SPIT_UP]; + beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); }); @@ -31,12 +32,14 @@ describe("Moves - Spit Up", () => { game.override.battleType("single"); game.override.enemySpecies(Species.RATTATA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.NONE); game.override.enemyLevel(2000); - game.override.moveset([Moves.SPIT_UP, Moves.SPIT_UP, Moves.SPIT_UP, Moves.SPIT_UP]); + game.override.moveset(new Array(4).fill(spitUp.id)); game.override.ability(Abilities.NONE); + + vi.spyOn(spitUp, "calculateBattlePower"); }); describe("consumes all stockpile stacks to deal damage (scaling with stacks)", () => { @@ -53,13 +56,11 @@ describe("Moves - Spit Up", () => { expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower); + expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); + expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); @@ -78,13 +79,11 @@ describe("Moves - Spit Up", () => { expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower); + expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); + expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); @@ -104,13 +103,11 @@ describe("Moves - Spit Up", () => { expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower); + expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); + expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); @@ -124,14 +121,12 @@ describe("Moves - Spit Up", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.FAIL }); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).not.toHaveBeenCalled(); + expect(spitUp.calculateBattlePower).not.toHaveBeenCalled(); }); describe("restores stat boosts granted by stacks", () => { @@ -144,22 +139,20 @@ describe("Moves - Spit Up", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(MovePhase); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1); + expect(pokemon.getStatStage(Stat.DEF)).toBe(1); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(1); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS }); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); + expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0); + expect(pokemon.getStatStage(Stat.DEF)).toBe(0); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); @@ -175,26 +168,19 @@ describe("Moves - Spit Up", () => { // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly stockpilingTag.statChangeCounts = { - [BattleStat.DEF]: -1, - [BattleStat.SPDEF]: 2, + [Stat.DEF]: -1, + [Stat.SPDEF]: 2, }; - expect(stockpilingTag.statChangeCounts).toMatchObject({ - [BattleStat.DEF]: -1, - [BattleStat.SPDEF]: 2, - }); - - vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS }); - expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); + expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(-2); + expect(pokemon.getStatStage(Stat.DEF)).toBe(1); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); diff --git a/src/test/moves/spotlight.test.ts b/src/test/moves/spotlight.test.ts index e5f4719d1d3..aef44369642 100644 --- a/src/test/moves/spotlight.test.ts +++ b/src/test/moves/spotlight.test.ts @@ -1,5 +1,4 @@ import { BattlerIndex } from "#app/battle"; -import { Stat } from "#app/data/pokemon-stat"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -31,52 +30,46 @@ describe("Moves - Spotlight", () => { game.override.startingLevel(100); game.override.enemyLevel(100); game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.SPLASH]); }); test( "move should redirect attacks to the target", async () => { - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY); game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); + + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to(TurnEndPhase, false); - expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); - expect(enemyPokemon[1].hp).toBe(enemyStartingHp[1]); + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); }, TIMEOUT ); test( "move should cause other redirection moves to fail", async () => { - game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - - await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); + await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - /** - * Spotlight will target the slower enemy. In this situation without Spotlight being used, - * the faster enemy would normally end up with the Center of Attention tag. - */ - enemyPokemon.sort((a, b) => b.getBattleStat(Stat.SPD) - a.getBattleStat(Stat.SPD)); - const spotTarget = enemyPokemon[1].getBattlerIndex(); - const attackTarget = enemyPokemon[0].getBattlerIndex(); + game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); - const enemyStartingHp = enemyPokemon.map(p => p.hp); + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.FOLLOW_ME); - game.move.select(Moves.SPOTLIGHT, 0, spotTarget); - game.move.select(Moves.QUICK_ATTACK, 1, attackTarget); - await game.phaseInterceptor.to(TurnEndPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); - expect(enemyPokemon[0].hp).toBe(enemyStartingHp[0]); + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); }, TIMEOUT ); }); diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index b1941b9f9b3..8e7a44d053b 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; import { MoveResult, TurnMove } from "#app/field/pokemon"; import { CommandPhase } from "#app/phases/command-phase"; @@ -7,7 +7,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -30,7 +29,7 @@ describe("Moves - Stockpile", () => { game.override.battleType("single"); game.override.enemySpecies(Species.RATTATA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.NONE); game.override.startingLevel(2000); @@ -38,7 +37,7 @@ describe("Moves - Stockpile", () => { game.override.ability(Abilities.NONE); }); - it("Gains a stockpile stack and increases DEF and SPDEF by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => { + it("gains a stockpile stack and raises user's DEF and SPDEF stat stages by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); const user = game.scene.getPlayerPokemon()!; @@ -47,8 +46,8 @@ describe("Moves - Stockpile", () => { // we just have to know that they're implemented as a BattlerTag. expect(user.getTag(StockpilingTag)).toBeUndefined(); - expect(user.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(0); + expect(user.getStatStage(Stat.DEF)).toBe(0); + expect(user.getStatStage(Stat.SPDEF)).toBe(0); // use Stockpile four times for (let i = 0; i < 4; i++) { @@ -60,18 +59,16 @@ describe("Moves - Stockpile", () => { await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTag = user.getTag(StockpilingTag)!; - const def = user.summonData.battleStats[BattleStat.DEF]; - const spdef = user.summonData.battleStats[BattleStat.SPDEF]; if (i < 3) { // first three uses should behave normally - expect(def).toBe(i + 1); - expect(spdef).toBe(i + 1); + expect(user.getStatStage(Stat.DEF)).toBe(i + 1); + expect(user.getStatStage(Stat.SPDEF)).toBe(i + 1); expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(i + 1); } else { // fourth should have failed - expect(def).toBe(3); - expect(spdef).toBe(3); + expect(user.getStatStage(Stat.DEF)).toBe(3); + expect(user.getStatStage(Stat.SPDEF)).toBe(3); expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(3); expect(user.getMoveHistory().at(-1)).toMatchObject({ result: MoveResult.FAIL, move: Moves.STOCKPILE }); @@ -79,17 +76,17 @@ describe("Moves - Stockpile", () => { } }); - it("Gains a stockpile stack even if DEF and SPDEF are at +6", { timeout: 10000 }, async () => { + it("gains a stockpile stack even if user's DEF and SPDEF stat stages are at +6", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); const user = game.scene.getPlayerPokemon()!; - user.summonData.battleStats[BattleStat.DEF] = 6; - user.summonData.battleStats[BattleStat.SPDEF] = 6; + user.setStatStage(Stat.DEF, 6); + user.setStatStage(Stat.SPDEF, 6); expect(user.getTag(StockpilingTag)).toBeUndefined(); - expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); - expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6); + expect(user.getStatStage(Stat.DEF)).toBe(6); + expect(user.getStatStage(Stat.SPDEF)).toBe(6); game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); @@ -97,8 +94,8 @@ describe("Moves - Stockpile", () => { const stockpilingTag = user.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(1); - expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); - expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6); + expect(user.getStatStage(Stat.DEF)).toBe(6); + expect(user.getStatStage(Stat.SPDEF)).toBe(6); // do it again, just for good measure await game.phaseInterceptor.to(CommandPhase); @@ -109,8 +106,8 @@ describe("Moves - Stockpile", () => { const stockpilingTagAgain = user.getTag(StockpilingTag)!; expect(stockpilingTagAgain).toBeDefined(); expect(stockpilingTagAgain.stockpiledCount).toBe(2); - expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); - expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6); + expect(user.getStatStage(Stat.DEF)).toBe(6); + expect(user.getStatStage(Stat.SPDEF)).toBe(6); }); }); }); diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index 202f25fee74..5a0e63e6e78 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { MoveResult, TurnMove } from "#app/field/pokemon"; @@ -8,7 +8,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -30,7 +29,7 @@ describe("Moves - Swallow", () => { game.override.battleType("single"); game.override.enemySpecies(Species.RATTATA); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.NONE); game.override.enemyLevel(2000); @@ -138,7 +137,7 @@ describe("Moves - Swallow", () => { expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.FAIL }); }); - describe("restores stat boosts granted by stacks", () => { + describe("restores stat stage boosts granted by stacks", () => { it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); @@ -151,20 +150,20 @@ describe("Moves - Swallow", () => { game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(MovePhase); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1); + expect(pokemon.getStatStage(Stat.DEF)).toBe(1); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(1); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.SUCCESS }); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(0); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0); + expect(pokemon.getStatStage(Stat.DEF)).toBe(0); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { + it("lower stat stages based on stored values (different boosts)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -175,22 +174,18 @@ describe("Moves - Swallow", () => { // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly stockpilingTag.statChangeCounts = { - [BattleStat.DEF]: -1, - [BattleStat.SPDEF]: 2, + [Stat.DEF]: -1, + [Stat.SPDEF]: 2, }; - expect(stockpilingTag.statChangeCounts).toMatchObject({ - [BattleStat.DEF]: -1, - [BattleStat.SPDEF]: 2, - }); - game.move.select(Moves.SWALLOW); + await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.SUCCESS }); - expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); - expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(-2); + expect(pokemon.getStatStage(Stat.DEF)).toBe(1); + expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2); expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); diff --git a/src/test/moves/tackle.test.ts b/src/test/moves/tackle.test.ts index 5eca9e344c8..b25c7524a1a 100644 --- a/src/test/moves/tackle.test.ts +++ b/src/test/moves/tackle.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#app/data/pokemon-stat"; +import { Stat } from "#enums/stat"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; diff --git a/src/test/moves/tail_whip.test.ts b/src/test/moves/tail_whip.test.ts index 0a999fe1920..5c83feb8a4e 100644 --- a/src/test/moves/tail_whip.test.ts +++ b/src/test/moves/tail_whip.test.ts @@ -1,12 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; describe("Moves - Tail whip", () => { @@ -31,23 +31,23 @@ describe("Moves - Tail whip", () => { game.override.enemyAbility(Abilities.INSOMNIA); game.override.ability(Abilities.INSOMNIA); game.override.startingLevel(2000); - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.moveset([ moveToUse ]); + game.override.enemyMoveset(Moves.SPLASH); }); - it("TAIL_WHIP", async () => { + it("should lower DEF stat stage by 1", async() => { const moveToUse = Moves.TAIL_WHIP; await game.startBattle([ Species.MIGHTYENA, Species.MIGHTYENA, ]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.DEF]).toBe(0); + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.DEF]).toBe(-1); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1); }, 20000); }); diff --git a/src/test/moves/tailwind.test.ts b/src/test/moves/tailwind.test.ts index 6b70122d08d..6a08cfe802f 100644 --- a/src/test/moves/tailwind.test.ts +++ b/src/test/moves/tailwind.test.ts @@ -1,11 +1,10 @@ +import { Stat } from "#enums/stat"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { Stat } from "#app/data/pokemon-stat"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -27,7 +26,7 @@ describe("Moves - Tailwind", () => { game = new GameManager(phaserGame); game.override.battleType("double"); game.override.moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); }); it("doubles the Speed stat of the Pokemons on its side", async () => { @@ -38,16 +37,16 @@ describe("Moves - Tailwind", () => { const magikarpSpd = magikarp.getStat(Stat.SPD); const meowthSpd = meowth.getStat(Stat.SPD); - expect(magikarp.getBattleStat(Stat.SPD)).equal(magikarpSpd); - expect(meowth.getBattleStat(Stat.SPD)).equal(meowthSpd); + expect(magikarp.getEffectiveStat(Stat.SPD)).equal(magikarpSpd); + expect(meowth.getEffectiveStat(Stat.SPD)).equal(meowthSpd); game.move.select(Moves.TAILWIND); game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); - expect(magikarp.getBattleStat(Stat.SPD)).toBe(magikarpSpd * 2); - expect(meowth.getBattleStat(Stat.SPD)).toBe(meowthSpd * 2); + expect(magikarp.getEffectiveStat(Stat.SPD)).toBe(magikarpSpd * 2); + expect(meowth.getEffectiveStat(Stat.SPD)).toBe(meowthSpd * 2); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); }); @@ -86,8 +85,8 @@ describe("Moves - Tailwind", () => { const enemySpd = enemy.getStat(Stat.SPD); - expect(ally.getBattleStat(Stat.SPD)).equal(allySpd); - expect(enemy.getBattleStat(Stat.SPD)).equal(enemySpd); + expect(ally.getEffectiveStat(Stat.SPD)).equal(allySpd); + expect(enemy.getEffectiveStat(Stat.SPD)).equal(enemySpd); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined(); @@ -95,8 +94,8 @@ describe("Moves - Tailwind", () => { await game.phaseInterceptor.to(TurnEndPhase); - expect(ally.getBattleStat(Stat.SPD)).toBe(allySpd * 2); - expect(enemy.getBattleStat(Stat.SPD)).equal(enemySpd); + expect(ally.getEffectiveStat(Stat.SPD)).toBe(allySpd * 2); + expect(enemy.getEffectiveStat(Stat.SPD)).equal(enemySpd); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined(); }); diff --git a/src/test/moves/tar_shot.test.ts b/src/test/moves/tar_shot.test.ts new file mode 100644 index 00000000000..2963f061fc6 --- /dev/null +++ b/src/test/moves/tar_shot.test.ts @@ -0,0 +1,132 @@ +import { BattlerIndex } from "#app/battle"; +import { Type } from "#app/data/type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Tar Shot", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.TANGELA) + .enemyLevel(1000) + .moveset([Moves.TAR_SHOT, Moves.FIRE_PUNCH]) + .disableCrits(); + }); + + it("lowers the target's Speed stat by one stage and doubles the effectiveness of Fire-type moves used on the target", async () => { + await game.classicMode.startBattle([Species.PIKACHU]); + + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.TAR_SHOT); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.SPD)).toBe(-1); + + await game.toNextTurn(); + + game.move.select(Moves.FIRE_PUNCH); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4); + }, TIMEOUT); + + it("will not double the effectiveness of Fire-type moves used on a target that is already under the effect of Tar Shot (but may still lower its Speed)", async () => { + await game.classicMode.startBattle([Species.PIKACHU]); + + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.TAR_SHOT); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.SPD)).toBe(-1); + + await game.toNextTurn(); + + game.move.select(Moves.TAR_SHOT); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.SPD)).toBe(-2); + + await game.toNextTurn(); + + game.move.select(Moves.FIRE_PUNCH); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4); + }, TIMEOUT); + + it("does not double the effectiveness of Fire-type moves against a Pokémon that is Terastallized", async () => { + game.override.enemyHeldItems([{ name: "TERA_SHARD", type: Type.GRASS }]).enemySpecies(Species.SPRIGATITO); + await game.classicMode.startBattle([Species.PIKACHU]); + + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.TAR_SHOT); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.SPD)).toBe(-1); + + await game.toNextTurn(); + + game.move.select(Moves.FIRE_PUNCH); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2); + }, TIMEOUT); + + it("doubles the effectiveness of Fire-type moves against a Pokémon that is already under the effects of Tar Shot before it Terastallized", async () => { + game.override.enemySpecies(Species.SPRIGATITO); + await game.classicMode.startBattle([Species.PIKACHU]); + + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "getMoveEffectiveness"); + + game.move.select(Moves.TAR_SHOT); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.SPD)).toBe(-1); + + await game.toNextTurn(); + + game.override.enemyHeldItems([{ name: "TERA_SHARD", type: Type.GRASS }]); + + game.move.select(Moves.FIRE_PUNCH); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4); + }, TIMEOUT); +}); diff --git a/src/test/moves/tera_blast.test.ts b/src/test/moves/tera_blast.test.ts index bd7df8403d1..55d61496297 100644 --- a/src/test/moves/tera_blast.test.ts +++ b/src/test/moves/tera_blast.test.ts @@ -1,16 +1,14 @@ import { BattlerIndex } from "#app/battle"; -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { allMoves } from "#app/data/move"; import { Type } from "#app/data/type"; import { Abilities } from "#app/enums/abilities"; -import { Stat } from "#app/enums/stat"; import { HitResult } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - Tera Blast", () => { let phaserGame: Phaser.Game; @@ -38,7 +36,7 @@ describe("Moves - Tera Blast", () => { .ability(Abilities.BALL_FETCH) .startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]) .enemySpecies(Species.MAGIKARP) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) .enemyLevel(20); @@ -112,7 +110,7 @@ describe("Moves - Tera Blast", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase"); - expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1); - expect(playerPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); }, 20000); }); diff --git a/src/test/moves/throat_chop.test.ts b/src/test/moves/throat_chop.test.ts new file mode 100644 index 00000000000..cb34b4bafff --- /dev/null +++ b/src/test/moves/throat_chop.test.ts @@ -0,0 +1,57 @@ +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; + +describe("Moves - Throat Chop", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset(Array(4).fill(Moves.GROWL)) + .battleType("single") + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Array(4).fill(Moves.THROAT_CHOP)) + .enemySpecies(Species.MAGIKARP); + }); + + it("prevents the target from using sound-based moves for two turns", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.GROWL); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + // First turn, move is interrupted + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemy.getStatStage(Stat.ATK)).toBe(0); + + // Second turn, struggle if no valid moves + await game.toNextTurn(); + + game.move.select(Moves.GROWL); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemy.isFullHp()).toBe(false); + }, TIMEOUT); +}); diff --git a/src/test/moves/thunder_wave.test.ts b/src/test/moves/thunder_wave.test.ts index 0c91be29714..7ad59518013 100644 --- a/src/test/moves/thunder_wave.test.ts +++ b/src/test/moves/thunder_wave.test.ts @@ -6,7 +6,6 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -30,7 +29,7 @@ describe("Moves - Thunder Wave", () => { .battleType("single") .starterSpecies(Species.PIKACHU) .moveset([Moves.THUNDER_WAVE]) - .enemyMoveset(SPLASH_ONLY); + .enemyMoveset(Moves.SPLASH); }); // References: https://bulbapedia.bulbagarden.net/wiki/Thunder_Wave_(move) diff --git a/src/test/moves/tidy_up.test.ts b/src/test/moves/tidy_up.test.ts index 1ef7933c114..255fe948447 100644 --- a/src/test/moves/tidy_up.test.ts +++ b/src/test/moves/tidy_up.test.ts @@ -1,4 +1,4 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; @@ -6,7 +6,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -30,7 +29,7 @@ describe("Moves - Tidy Up", () => { game.override.battleType("single"); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyMoveset(Moves.SPLASH); game.override.starterSpecies(Species.FEEBAS); game.override.ability(Abilities.BALL_FETCH); game.override.moveset([Moves.TIDY_UP]); @@ -60,7 +59,6 @@ describe("Moves - Tidy Up", () => { game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STEALTH_ROCK)).toBeUndefined(); - }, 20000); it("toxic spikes are cleared", async () => { @@ -73,7 +71,6 @@ describe("Moves - Tidy Up", () => { game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.TOXIC_SPIKES)).toBeUndefined(); - }, 20000); it("sticky webs are cleared", async () => { @@ -87,7 +84,6 @@ describe("Moves - Tidy Up", () => { game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STICKY_WEB)).toBeUndefined(); - }, 20000); it.skip("substitutes are cleared", async () => { @@ -101,22 +97,20 @@ describe("Moves - Tidy Up", () => { game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); // TODO: check for subs here once the move is implemented - }, 20000); it("user's stats are raised with no traps set", async () => { await game.startBattle(); - const player = game.scene.getPlayerPokemon()!.summonData.battleStats; - expect(player[BattleStat.ATK]).toBe(0); - expect(player[BattleStat.SPD]).toBe(0); + const playerPokemon = game.scene.getPlayerPokemon()!; + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0); game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(TurnEndPhase); - expect(player[BattleStat.ATK]).toBe(+1); - expect(player[BattleStat.SPD]).toBe(+1); - + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); + expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1); }, 20000); - }); diff --git a/src/test/moves/transform.test.ts b/src/test/moves/transform.test.ts new file mode 100644 index 00000000000..6686f1fc73b --- /dev/null +++ b/src/test/moves/transform.test.ts @@ -0,0 +1,100 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; + +// TODO: Add more tests once Transform is fully implemented +describe("Moves - Transform", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.MEW) + .enemyLevel(200) + .enemyAbility(Abilities.BEAST_BOOST) + .enemyPassiveAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .ability(Abilities.INTIMIDATE) + .moveset([ Moves.TRANSFORM ]); + }); + + it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { + await game.startBattle([ + Species.DITTO + ]); + + game.move.select(Moves.TRANSFORM); + await game.phaseInterceptor.to(TurnEndPhase); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(player.getAbility()).toBe(enemy.getAbility()); + expect(player.getGender()).toBe(enemy.getGender()); + + expect(player.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); + for (const s of EFFECTIVE_STATS) { + expect(player.getStat(s, false)).toBe(enemy.getStat(s, false)); + } + + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(enemy.getStatStage(s)); + } + + const playerMoveset = player.getMoveset(); + const enemyMoveset = player.getMoveset(); + + for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { + // TODO: Checks for 5 PP should be done here when that gets addressed + expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId); + } + + const playerTypes = player.getTypes(); + const enemyTypes = enemy.getTypes(); + + for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) { + expect(playerTypes[i]).toBe(enemyTypes[i]); + } + }, 20000); + + it("should copy in-battle overridden stats", async () => { + game.override.enemyMoveset([Moves.POWER_SPLIT]); + + await game.startBattle([ + Species.DITTO + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); + + game.move.select(Moves.TRANSFORM); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); + + expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + }); +}); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts index ae55302bb42..c4b6ae2497f 100644 --- a/src/test/moves/u_turn.test.ts +++ b/src/test/moves/u_turn.test.ts @@ -7,7 +7,6 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - U-turn", () => { let phaserGame: Phaser.Game; @@ -31,7 +30,7 @@ describe("Moves - U-turn", () => { .startingLevel(90) .startingWave(97) .moveset([Moves.U_TURN]) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .disableCrits(); }); diff --git a/src/test/moves/wide_guard.test.ts b/src/test/moves/wide_guard.test.ts index 616972de01b..b4e6e305539 100644 --- a/src/test/moves/wide_guard.test.ts +++ b/src/test/moves/wide_guard.test.ts @@ -1,12 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { CommandPhase } from "#app/phases/command-phase"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; const TIMEOUT = 20 * 1000; @@ -32,7 +32,7 @@ describe("Moves - Wide Guard", () => { game.override.moveset([Moves.WIDE_GUARD, Moves.SPLASH, Moves.SURF]); game.override.enemySpecies(Species.SNORLAX); - game.override.enemyMoveset(Array(4).fill(Moves.SWIFT)); + game.override.enemyMoveset([Moves.SWIFT]); game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); @@ -61,7 +61,7 @@ describe("Moves - Wide Guard", () => { test( "should protect the user and allies from multi-target status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset([Moves.GROWL]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -75,14 +75,14 @@ describe("Moves - Wide Guard", () => { await game.phaseInterceptor.to(BerryPhase, false); - leadPokemon.forEach(p => expect(p.summonData.battleStats[BattleStat.ATK]).toBe(0)); + leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); }, TIMEOUT ); test( "should not protect the user and allies from single-target moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.override.enemyMoveset([Moves.TACKLE]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -103,7 +103,7 @@ describe("Moves - Wide Guard", () => { test( "should protect the user from its ally's multi-target move", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); + game.override.enemyMoveset([Moves.SPLASH]); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); diff --git a/src/test/phases/learn-move-phase.test.ts b/src/test/phases/learn-move-phase.test.ts new file mode 100644 index 00000000000..60cdbee8570 --- /dev/null +++ b/src/test/phases/learn-move-phase.test.ts @@ -0,0 +1,47 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#enums/species"; +import { Moves } from "#enums/moves"; +import { LearnMovePhase } from "#app/phases/learn-move-phase"; + +describe("Learn Move Phase", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override.xpMultiplier(50); + }); + + it("If Pokemon has less than 4 moves, its newest move will be added to the lowest empty index", async () => { + game.override.moveset([Moves.SPLASH]); + await game.startBattle([Species.BULBASAUR]); + const pokemon = game.scene.getPlayerPokemon()!; + const newMovePos = pokemon?.getMoveset().length; + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to(LearnMovePhase); + const levelMove = pokemon.getLevelMoves(5)[0]; + const levelReq = levelMove[0]; + const levelMoveId = levelMove[1]; + expect(pokemon.level).toBeGreaterThanOrEqual(levelReq); + expect(pokemon?.getMoveset()[newMovePos]?.moveId).toBe(levelMoveId); + }); + + /** + * Future Tests: + * If a Pokemon has four moves, the user can specify an old move to be forgotten and a new move will take its place. + * If a Pokemon has four moves, the user can reject the new move, keeping the moveset the same. + */ +}); diff --git a/src/test/reload.test.ts b/src/test/reload.test.ts new file mode 100644 index 00000000000..a96a525ca2d --- /dev/null +++ b/src/test/reload.test.ts @@ -0,0 +1,136 @@ +import { Species } from "#app/enums/species"; +import { GameModes } from "#app/game-mode"; +import GameManager from "#test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { Moves } from "#app/enums/moves"; +import { Biome } from "#app/enums/biome"; + +describe("Reload", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + it("should not have RNG inconsistencies in a Classic run", async () => { + await game.classicMode.startBattle(); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies after a biome switch", async () => { + game.override + .startingWave(10) + .startingBiome(Biome.CAVE) // Will lead to biomes with randomly generated weather + .battleType("single") + .startingLevel(100) + .enemyLevel(1000) + .disableTrainerWaves() + .moveset([Moves.KOWTOW_CLEAVE]) + .enemyMoveset(Moves.SPLASH); + await game.dailyMode.startBattle(); + + // Transition from Daily Run Wave 10 to Wave 11 in order to trigger biome switch + game.move.select(Moves.KOWTOW_CLEAVE); + await game.phaseInterceptor.to("DamagePhase"); + await game.doKillOpponents(); + await game.toNextWave(); + expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase"); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies at a Daily run wild Pokemon fight", async () => { + await game.dailyMode.startBattle(); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies at a Daily run double battle", async () => { + game.override + .battleType("double"); + await game.dailyMode.startBattle(); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies at a Daily run Gym Leader fight", async () => { + game.override + .battleType("single") + .startingWave(40); + await game.dailyMode.startBattle(); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies at a Daily run regular trainer fight", async () => { + game.override + .battleType("single") + .startingWave(45); + await game.dailyMode.startBattle(); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); + + it("should not have RNG inconsistencies at a Daily run wave 50 Boss fight", async () => { + game.override + .battleType("single") + .startingWave(50); + await game.runToFinalBossEncounter([Species.BULBASAUR], GameModes.DAILY); + + const preReloadRngState = Phaser.Math.RND.state(); + + await game.reload.reloadSession(); + + const postReloadRngState = Phaser.Math.RND.state(); + + expect(preReloadRngState).toBe(postReloadRngState); + }, 20000); +}); diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index 8ef1ea16b4a..6d26ebfd6b3 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -53,9 +53,6 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.CYCLE_SHINY); - handler.processInput(Button.V); - handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -117,9 +114,6 @@ describe("UI - Starter select", () => { handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); handler.processInput(Button.CYCLE_GENDER); - handler.processInput(Button.CYCLE_SHINY); - handler.processInput(Button.V); - handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -184,9 +178,6 @@ describe("UI - Starter select", () => { handler.processInput(Button.CYCLE_GENDER); handler.processInput(Button.CYCLE_NATURE); handler.processInput(Button.CYCLE_ABILITY); - handler.processInput(Button.CYCLE_SHINY); - handler.processInput(Button.V); - handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -227,11 +218,12 @@ describe("UI - Starter select", () => { expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR); expect(game.scene.getParty()[0].shiny).toBe(true); expect(game.scene.getParty()[0].variant).toBe(2); + expect(game.scene.getParty()[0].gender).toBe(Gender.FEMALE); expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY); expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL); }, 20000); - it("Bulbasaur - shiny - variant 2 female lonely chlorophyl", async() => { + it("Bulbasaur - shiny - variant 2 female", async() => { await game.importData("src/test/utils/saves/everything.prsv"); const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => { const species = game.scene.gameData.dexData[key]; @@ -249,9 +241,6 @@ describe("UI - Starter select", () => { handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); handler.processInput(Button.CYCLE_GENDER); - handler.processInput(Button.CYCLE_SHINY); - handler.processInput(Button.V); - handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -313,6 +302,7 @@ describe("UI - Starter select", () => { handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); handler.processInput(Button.ACTION); + handler.processInput(Button.CYCLE_SHINY); game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); @@ -371,7 +361,7 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.V); handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); @@ -415,7 +405,7 @@ describe("UI - Starter select", () => { expect(game.scene.getParty()[0].variant).toBe(1); }, 20000); - it("Bulbasaur - shiny - variant 2", async() => { + it("Bulbasaur - shiny - variant 0", async() => { await game.importData("src/test/utils/saves/everything.prsv"); const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => { const species = game.scene.gameData.dexData[key]; @@ -432,8 +422,6 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.CYCLE_SHINY); - handler.processInput(Button.V); handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); @@ -474,7 +462,7 @@ describe("UI - Starter select", () => { expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR); expect(game.scene.getParty()[0].shiny).toBe(true); - expect(game.scene.getParty()[0].variant).toBe(2); + expect(game.scene.getParty()[0].variant).toBe(0); }, 20000); it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async() => { diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts index ccab02b82bf..726094b258a 100644 --- a/src/test/ui/type-hints.test.ts +++ b/src/test/ui/type-hints.test.ts @@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import MockText from "../utils/mocks/mocksContainer/mockText"; -import { SPLASH_ONLY } from "../utils/testUtils"; describe("UI - Type Hints", () => { let phaserGame: Phaser.Game; @@ -27,7 +26,7 @@ describe("UI - Type Hints", () => { beforeEach(async () => { game = new GameManager(phaserGame); game.settings.typeHints(true); //activate type hints - game.override.battleType("single").startingLevel(100).startingWave(1).enemyMoveset(SPLASH_ONLY); + game.override.battleType("single").startingLevel(100).startingWave(1).enemyMoveset(Moves.SPLASH); }); it("check immunity color", async () => { @@ -36,7 +35,7 @@ describe("UI - Type Hints", () => { .startingLevel(100) .startingWave(1) .enemySpecies(Species.FLORGES) - .enemyMoveset(SPLASH_ONLY) + .enemyMoveset(Moves.SPLASH) .moveset([Moves.DRAGON_CLAW]); game.settings.typeHints(true); //activate type hints diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 60d07065090..ade33aa1148 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -2,12 +2,16 @@ import { updateUserInfo } from "#app/account"; import { BattlerIndex } from "#app/battle"; import BattleScene from "#app/battle-scene"; import { BattleStyle } from "#app/enums/battle-style"; +import { Moves } from "#app/enums/moves"; +import { getMoveTargets } from "#app/data/move"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import Trainer from "#app/field/trainer"; import { GameModes, getGameMode } from "#app/game-mode"; import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import overrides from "#app/overrides"; import { CommandPhase } from "#app/phases/command-phase"; import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { FaintPhase } from "#app/phases/faint-phase"; import { LoginPhase } from "#app/phases/login-phase"; import { MovePhase } from "#app/phases/move-phase"; @@ -44,6 +48,8 @@ import { ChallengeModeHelper } from "./helpers/challengeModeHelper"; import { MoveHelper } from "./helpers/moveHelper"; import { OverridesHelper } from "./helpers/overridesHelper"; import { SettingsHelper } from "./helpers/settingsHelper"; +import { ReloadHelper } from "./helpers/reloadHelper"; +import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; /** * Class to manage the game state and transitions between phases. @@ -60,6 +66,7 @@ export default class GameManager { public readonly dailyMode: DailyModeHelper; public readonly challengeMode: ChallengeModeHelper; public readonly settings: SettingsHelper; + public readonly reload: ReloadHelper; /** * Creates an instance of GameManager. @@ -69,7 +76,7 @@ export default class GameManager { constructor(phaserGame: Phaser.Game, bypassLogin: boolean = true) { localStorage.clear(); ErrorInterceptor.getInstance().clear(); - BattleScene.prototype.randBattleSeedInt = (arg) => arg-1; + BattleScene.prototype.randBattleSeedInt = (range, min: number = 0) => min + range - 1; // This simulates a max roll this.gameWrapper = new GameWrapper(phaserGame, bypassLogin); this.scene = new BattleScene(); this.phaseInterceptor = new PhaseInterceptor(this.scene); @@ -81,6 +88,7 @@ export default class GameManager { this.dailyMode = new DailyModeHelper(this); this.challengeMode = new ChallengeModeHelper(this); this.settings = new SettingsHelper(this); + this.reload = new ReloadHelper(this); } /** @@ -138,7 +146,7 @@ export default class GameManager { this.scene.hpBarSpeed = 3; this.scene.enableTutorials = false; this.scene.gameData.gender = PlayerGender.MALE; // set initial player gender - + this.scene.battleStyle = this.settings.battleStyle; } /** @@ -148,28 +156,26 @@ export default class GameManager { * @param species * @param mode */ - async runToFinalBossEncounter(game: GameManager, species: Species[], mode: GameModes) { + async runToFinalBossEncounter(species: Species[], mode: GameModes) { console.log("===to final boss encounter==="); - await game.runToTitle(); + await this.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { - game.scene.gameMode = getGameMode(mode); - const starters = generateStarter(game.scene, species); - const selectStarterPhase = new SelectStarterPhase(game.scene); - game.scene.pushPhase(new EncounterPhase(game.scene, false)); + this.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.scene.gameMode = getGameMode(mode); + const starters = generateStarter(this.scene, species); + const selectStarterPhase = new SelectStarterPhase(this.scene); + this.scene.pushPhase(new EncounterPhase(this.scene, false)); selectStarterPhase.initBattle(starters); }); - game.onNextPrompt("EncounterPhase", Mode.MESSAGE, async () => { - // This will skip all entry dialogue (I can't figure out a way to sequentially handle the 8 chained messages via 1 prompt handler) - game.setMode(Mode.MESSAGE); - const encounterPhase = game.scene.getCurrentPhase() as EncounterPhase; + // This will consider all battle entry dialog as seens and skip them + vi.spyOn(this.scene.ui, "shouldSkipDialogue").mockReturnValue(true); - // No need to end phase, this will do it for you - encounterPhase.doEncounterCommon(false); - }); + if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) { + this.removeEnemyHeldItems(); + } - await game.phaseInterceptor.to(EncounterPhase, true); + await this.phaseInterceptor.to(EncounterPhase); console.log("===finished run to final boss encounter==="); } @@ -232,15 +238,42 @@ export default class GameManager { this.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler; handler.processInput(Button.CANCEL); - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase), true); + }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase), true); this.onNextPrompt("SelectModifierPhase", Mode.CONFIRM, () => { const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler; handler.processInput(Button.ACTION); - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase)); + }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase)); } - forceOpponentToSwitch() { + /** + * Forces the next enemy selecting a move to use the given move in its moveset against the + * given target (if applicable). + * @param moveId {@linkcode Moves} the move the enemy will use + * @param target {@linkcode BattlerIndex} the target on which the enemy will use the given move + */ + async forceEnemyMove(moveId: Moves, target?: BattlerIndex) { + // Wait for the next EnemyCommandPhase to start + await this.phaseInterceptor.to(EnemyCommandPhase, false); + const enemy = this.scene.getEnemyField()[(this.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()]; + const legalTargets = getMoveTargets(enemy, moveId); + + vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({ + move: moveId, + targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target)) + ? [target] + : enemy.getNextTargets(moveId) + }); + + /** + * Run the EnemyCommandPhase to completion. + * This allows this function to be called consecutively to + * force a move for each enemy in a double battle. + */ + await this.phaseInterceptor.to(EnemyCommandPhase); + } + + forceEnemyToSwitch() { const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores; Trainer.prototype.getPartyMemberMatchupScores = () => { Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore; @@ -381,7 +414,7 @@ export default class GameManager { } /** - * Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder} + * Intercepts `TurnStartPhase` and mocks the getSpeedOrder's return value {@linkcode TurnStartPhase.getSpeedOrder} * Used to modify the turn order. * @param {BattlerIndex[]} order The turn order to set * @example @@ -392,7 +425,7 @@ export default class GameManager { async setTurnOrder(order: BattlerIndex[]): Promise { await this.phaseInterceptor.to(TurnStartPhase, false); - vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); + vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); } /** diff --git a/src/test/utils/gameWrapper.ts b/src/test/utils/gameWrapper.ts index f3098fa9b71..1cf01a3a8c8 100644 --- a/src/test/utils/gameWrapper.ts +++ b/src/test/utils/gameWrapper.ts @@ -41,7 +41,7 @@ window.URL.createObjectURL = (blob: Blob) => { }); return null; }; -navigator.getGamepads = vi.fn().mockReturnValue([]); +navigator.getGamepads = () => []; global.fetch = vi.fn(MockFetch); Utils.setCookie(Utils.sessionIdKey, 'fake_token'); diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index cbae9d1ead1..12db3b5c1db 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -1,12 +1,12 @@ -import { StatusEffect } from "#app/data/status-effect.js"; +import { StatusEffect } from "#app/data/status-effect"; import { Weather, WeatherType } from "#app/data/weather"; -import { Abilities } from "#app/enums/abilities.js"; +import { Abilities } from "#app/enums/abilities"; import { Biome } from "#app/enums/biome"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; import * as GameMode from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode"; -import { ModifierOverride } from "#app/modifier/modifier-type.js"; +import { ModifierOverride } from "#app/modifier/modifier-type"; import Overrides from "#app/overrides"; import { vi } from "vitest"; import { GameManagerHelper } from "./gameManagerHelper"; @@ -49,6 +49,17 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the XP Multiplier + * @param value the XP multiplier to set + * @returns `this` + */ + xpMultiplier(value: number): this { + vi.spyOn(Overrides, "XP_MULTIPLIER_OVERRIDE", "get").mockReturnValue(value); + this.log(`XP Multiplier set to ${value}!`); + return this; + } + /** * Override the player (pokemon) starting held items * @param items the items to hold @@ -123,8 +134,11 @@ export class OverridesHelper extends GameManagerHelper { * @param moveset the {@linkcode Moves | moves}set to set * @returns this */ - moveset(moveset: Moves[]): this { + moveset(moveset: Moves | Moves[]): this { vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(moveset); + if (!Array.isArray(moveset)) { + moveset = [moveset]; + } const movesetStr = moveset.map((moveId) => Moves[moveId]).join(", "); this.log(`Player Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`); return this; @@ -242,8 +256,11 @@ export class OverridesHelper extends GameManagerHelper { * @param moveset the {@linkcode Moves | moves}set to set * @returns this */ - enemyMoveset(moveset: Moves[]): this { + enemyMoveset(moveset: Moves | Moves[]): this { vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(moveset); + if (!Array.isArray(moveset)) { + moveset = [moveset]; + } const movesetStr = moveset.map((moveId) => Moves[moveId]).join(", "); this.log(`Enemy Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`); return this; @@ -287,6 +304,31 @@ export class OverridesHelper extends GameManagerHelper { this.log("Temporarily unlocked the following content: ", unlockable); return this; } + + /** + * Override the items rolled at the end of a battle + * @param items the items to be rolled + * @returns this + */ + itemRewards(items: ModifierOverride[]) { + vi.spyOn(Overrides, "ITEM_REWARD_OVERRIDE", "get").mockReturnValue(items); + this.log("Item rewards set to:", items); + return this; + } + + /** + * Override the enemy (Pokemon) to have the given amount of health segments + * @param healthSegments the number of segments to give + * default: 0, the health segments will be handled like in the game based on wave, level and species + * 1: the Pokemon will not be a boss + * 2+: the Pokemon will be a boss with the given number of health segments + * @returns this + */ + enemyHealthSegments(healthSegments: number) { + vi.spyOn(Overrides, "OPP_HEALTH_SEGMENTS_OVERRIDE", "get").mockReturnValue(healthSegments); + this.log("Enemy Pokemon health segments set to:", healthSegments); + return this; + } private log(...params: any[]) { console.log("Overrides:", ...params); diff --git a/src/test/utils/helpers/reloadHelper.ts b/src/test/utils/helpers/reloadHelper.ts new file mode 100644 index 00000000000..c15347b08c9 --- /dev/null +++ b/src/test/utils/helpers/reloadHelper.ts @@ -0,0 +1,53 @@ +import { GameManagerHelper } from "./gameManagerHelper"; +import { TitlePhase } from "#app/phases/title-phase"; +import { Mode } from "#app/ui/ui"; +import { vi } from "vitest"; +import { BattleStyle } from "#app/enums/battle-style"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; + +/** + * Helper to allow reloading sessions in unit tests. + */ +export class ReloadHelper extends GameManagerHelper { + /** + * Simulate reloading the session from the title screen, until reaching the + * beginning of the first turn (equivalent to running `startBattle()`) for + * the reloaded session. + */ + async reloadSession() : Promise { + const scene = this.game.scene; + const sessionData = scene.gameData.getSessionSaveData(scene); + const titlePhase = new TitlePhase(scene); + + scene.clearPhaseQueue(); + + // Set the last saved session to the desired session data + vi.spyOn(scene.gameData, "getSession").mockReturnValue( + new Promise((resolve, reject) => { + resolve(sessionData); + }) + ); + scene.unshiftPhase(titlePhase); + this.game.endPhase(); // End the currently ongoing battle + + titlePhase.loadSaveSlot(-1); // Load the desired session data + this.game.phaseInterceptor.shift(); // Loading the save slot also ended TitlePhase, clean it up + + // Run through prompts for switching Pokemon, copied from classicModeHelper.ts + if (this.game.scene.battleStyle === BattleStyle.SWITCH) { + this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { + this.game.setMode(Mode.MESSAGE); + this.game.endPhase(); + }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase)); + + this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { + this.game.setMode(Mode.MESSAGE); + this.game.endPhase(); + }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase)); + } + + await this.game.phaseInterceptor.to(CommandPhase); + console.log("==================[New Turn]=================="); + } +} diff --git a/src/test/utils/helpers/settingsHelper.ts b/src/test/utils/helpers/settingsHelper.ts index 76ffafdbe10..8fca2a34d47 100644 --- a/src/test/utils/helpers/settingsHelper.ts +++ b/src/test/utils/helpers/settingsHelper.ts @@ -1,16 +1,30 @@ import { PlayerGender } from "#app/enums/player-gender"; +import { BattleStyle } from "#app/enums/battle-style"; import { GameManagerHelper } from "./gameManagerHelper"; /** * Helper to handle settings for tests */ export class SettingsHelper extends GameManagerHelper { + private _battleStyle: BattleStyle = BattleStyle.SET; + + get battleStyle(): BattleStyle { + return this._battleStyle; + } + + /** + * Change the battle style to Switch or Set mode (tests default to {@linkcode BattleStyle.SET}) + * @param mode {@linkcode BattleStyle.SWITCH} or {@linkcode BattleStyle.SET} + */ + set battleStyle(mode: BattleStyle.SWITCH | BattleStyle.SET) { + this._battleStyle = mode; + } /** * Disable/Enable type hints settings * @param enable true to enabled, false to disabled */ - typeHints(enable: boolean) { + typeHints(enable: boolean): void { this.game.scene.typeHints = enable; this.log(`Type Hints ${enable? "enabled" : "disabled"}` ); } diff --git a/src/test/utils/mocks/mockGameObjectCreator.ts b/src/test/utils/mocks/mockGameObjectCreator.ts index 19406a46923..27860be0cec 100644 --- a/src/test/utils/mocks/mockGameObjectCreator.ts +++ b/src/test/utils/mocks/mockGameObjectCreator.ts @@ -1,4 +1,3 @@ -import { vi } from "vitest"; import MockGraphics from "./mocksContainer/mockGraphics"; import MockTextureManager from "./mockTextureManager"; @@ -16,8 +15,8 @@ export class MockGameObjectCreator { rexTransitionImagePack() { return { - transit: vi.fn(), - once: vi.fn(), + transit: () => null, + once: () => null, }; } } diff --git a/src/test/utils/mocks/mockVideoGameObject.ts b/src/test/utils/mocks/mockVideoGameObject.ts index 96f03542bbc..d8155e23b6c 100644 --- a/src/test/utils/mocks/mockVideoGameObject.ts +++ b/src/test/utils/mocks/mockVideoGameObject.ts @@ -1,13 +1,12 @@ -import { vi } from "vitest"; import { MockGameObject } from "./mockGameObject"; /** Mocks video-related stuff */ export class MockVideoGameObject implements MockGameObject { constructor() {} - public play = vi.fn(); - public stop = vi.fn(() => this); - public setOrigin = vi.fn(); - public setScale = vi.fn(); - public setVisible = vi.fn(); + public play = () => null; + public stop = () => this; + public setOrigin = () => null; + public setScale = () => null; + public setVisible = () => null; } diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index 5babd9e71b2..e13cef0e43e 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -1,5 +1,4 @@ import MockTextureManager from "#test/utils/mocks/mockTextureManager"; -import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockContainer implements MockGameObject { @@ -52,9 +51,8 @@ export default class MockContainer implements MockGameObject { /// Sets the position of this Game Object to be a relative position from the source Game Object. } - setInteractive(hitArea?, callback?, dropZone?) { - /// Sets the InteractiveObject to be a drop zone for a drag and drop operation. - } + setInteractive = () => null; + setOrigin(x, y) { this.x = x; this.y = y; @@ -161,10 +159,9 @@ export default class MockContainer implements MockGameObject { // Moves this Game Object to be below the given Game Object in the display list. } - setName = vi.fn((name: string) => { + setName = (name: string) => { this.name = name; - // return this.phaserSprite.setName(name); - }); + }; bringToTop(obj) { // Brings this Game Object to the top of its parents display list. @@ -208,4 +205,5 @@ export default class MockContainer implements MockGameObject { return this.list; } + disableInteractive = () => null; } diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index 35cd2d5faab..83ec3951151 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -101,9 +101,7 @@ export default class MockSprite implements MockGameObject { return this.phaserSprite.stop(); } - setInteractive(hitArea, hitAreaCallback, dropZone) { - return null; - } + setInteractive = () => null; on(event, callback, source) { return this.phaserSprite.on(event, callback, source); diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index 6b9ecf083fd..5462056f1e5 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -1,5 +1,4 @@ import UI from "#app/ui/ui"; -import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockText implements MockGameObject { @@ -193,9 +192,11 @@ export default class MockText implements MockGameObject { }; } - setColor = vi.fn((color: string) => { + setColor = (color: string) => { this.color = color; - }); + }; + + setInteractive = () => null; setShadowColor(color) { // Sets the shadow color. @@ -221,9 +222,9 @@ export default class MockText implements MockGameObject { // return this.phaserText.setAlpha(alpha); } - setName = vi.fn((name: string) => { + setName = (name: string) => { this.name = name; - }); + }; setAlign(align) { // return this.phaserText.setAlign(align); diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index de65405abff..a89d1788be9 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -1,4 +1,6 @@ import { Phase } from "#app/phase"; +import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { BerryPhase } from "#app/phases/berry-phase"; import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; @@ -10,14 +12,16 @@ import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { EvolutionPhase } from "#app/phases/evolution-phase"; import { FaintPhase } from "#app/phases/faint-phase"; +import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { LevelCapPhase } from "#app/phases/level-cap-phase"; import { LoginPhase } from "#app/phases/login-phase"; import { MessagePhase } from "#app/phases/message-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MovePhase } from "#app/phases/move-phase"; import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PostSummonPhase } from "#app/phases/post-summon-phase"; import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; import { SelectGenderPhase } from "#app/phases/select-gender-phase"; @@ -26,7 +30,7 @@ import { SelectStarterPhase } from "#app/phases/select-starter-phase"; import { SelectTargetPhase } from "#app/phases/select-target-phase"; import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { SummonPhase } from "#app/phases/summon-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; @@ -37,7 +41,7 @@ import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { UnavailablePhase } from "#app/phases/unavailable-phase"; import { VictoryPhase } from "#app/phases/victory-phase"; -import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import { PartyHealPhase } from "#app/phases/party-heal-phase"; import UI, { Mode } from "#app/ui/ui"; export default class PhaseInterceptor { @@ -62,6 +66,7 @@ export default class PhaseInterceptor { [TitlePhase, this.startPhase], [SelectGenderPhase, this.startPhase], [EncounterPhase, this.startPhase], + [NewBiomeEncounterPhase, this.startPhase], [SelectStarterPhase, this.startPhase], [PostSummonPhase, this.startPhase], [SummonPhase, this.startPhase], @@ -85,8 +90,9 @@ export default class PhaseInterceptor { [NextEncounterPhase, this.startPhase], [NewBattlePhase, this.startPhase], [VictoryPhase, this.startPhase], + [LearnMovePhase, this.startPhase], [MoveEndPhase, this.startPhase], - [StatChangePhase, this.startPhase], + [StatStageChangePhase, this.startPhase], [ShinySparklePhase, this.startPhase], [SelectTargetPhase, this.startPhase], [UnavailablePhase, this.startPhase], @@ -96,6 +102,8 @@ export default class PhaseInterceptor { [PartyHealPhase, this.startPhase], [EvolutionPhase, this.startPhase], [EndEvolutionPhase, this.startPhase], + [LevelCapPhase, this.startPhase], + [AttemptRunPhase, this.startPhase], ]; private endBySetMode = [ @@ -237,6 +245,22 @@ export default class PhaseInterceptor { this.scene.shiftPhase(); } + /** + * Remove the current phase from the phase interceptor. + * + * Do not call this unless absolutely necessary. This function is intended + * for cleaning up the phase interceptor when, for whatever reason, a phase + * is manually ended without using the phase interceptor. + * + * @param shouldRun Whether or not the current scene should also be run. + */ + shift(shouldRun: boolean = false) : void { + this.onHold.shift(); + if (shouldRun) { + this.scene.shiftPhase(); + } + } + /** * Method to initialize phases and their corresponding methods. */ diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index 378c3d23fde..b922fc9c61c 100644 --- a/src/test/utils/testUtils.ts +++ b/src/test/utils/testUtils.ts @@ -1,10 +1,6 @@ -import { Moves } from "#app/enums/moves.js"; import i18next, { type ParseKeys } from "i18next"; import { vi } from "vitest"; -/** Ready to use array of Moves.SPLASH x4 */ -export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]; - /** * Sets up the i18next mock. * Includes a i18next.t mocked implementation only returning the raw key (`(key) => key`) diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index eaa987c1a66..bf806cd053a 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -1,4 +1,3 @@ -import "#test/fontFace.setup"; import "vitest-canvas-mock"; import { initLoggedInUser } from "#app/account"; diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 18de67bfa86..9bfa3bdf54a 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -1,35 +1,39 @@ -import BattleScene from "#app/battle-scene.js"; -import { TextStyle, addTextObject } from "#app/ui/text.js"; +import BattleScene from "#app/battle-scene"; +import { TextStyle, addTextObject } from "#app/ui/text"; +import i18next from "i18next"; export enum EventType { - SHINY + SHINY, + GENERIC } -interface TimedEvent { - name: string; - eventType: EventType; - shinyMultiplier?: number; - startDate: Date; - endDate: Date; - bannerFilename?: string +interface EventBanner { + bannerKey?: string; + xPosition?: number; + yPosition?: number; + scale?: number; + availableLangs?: string[]; +} + +interface TimedEvent extends EventBanner { + name: string; + eventType: EventType; + shinyMultiplier?: number; + startDate: Date; + endDate: Date; } const timedEvents: TimedEvent[] = [ { - name: "Pride Update", - eventType: EventType.SHINY, - shinyMultiplier: 2, - startDate: new Date(Date.UTC(2024, 5, 14, 0)), - endDate: new Date(Date.UTC(2024, 5, 23, 0)), - bannerFilename: "pride-update" - }, - { - name: "August Variant Update", - eventType: EventType.SHINY, - shinyMultiplier: 2, - startDate: new Date(Date.UTC(2024, 7, 16, 0)), - endDate: new Date(Date.UTC(2024, 7, 22, 0)), - bannerFilename: "august-variant-update" + name: "Egg Skip Update", + eventType: EventType.GENERIC, + startDate: new Date(Date.UTC(2024, 8, 8, 0)), + endDate: new Date(Date.UTC(2024, 8, 12, 0)), + bannerKey: "egg-update", + xPosition: 19, + yPosition: 120, + scale: 0.21, + availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es", "pt-BR", "zh-CN"] } ]; @@ -67,7 +71,7 @@ export class TimedEventManager { } getEventBannerFilename(): string { - return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerFilename!; // TODO: is this bang correct? + return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerKey!; // TODO: is this bang correct? } } @@ -85,38 +89,37 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { } setup() { - console.log(this.event?.bannerFilename); - this.banner = new Phaser.GameObjects.Image(this.scene, 29, 64, this.event!.bannerFilename!); // TODO: are the bangs correct here? - this.banner.setName("img-event-banner"); - this.banner.setOrigin(0.08, -0.35); - this.banner.setScale(0.18); - // this.bannerShadow = new Phaser.GameObjects.Rectangle( - // this.scene, - // this.banner.x - 2, - // this.banner.y + 2, - // this.banner.width, - // this.banner.height, - // 0x484848 - // ); - // this.bannerShadow.setName("rect-event-banner-shadow"); - // this.bannerShadow.setScale(0.07); - // this.bannerShadow.setAlpha(0.5); - // this.bannerShadow.setOrigin(0,0); - this.eventTimerText = addTextObject( - this.scene, - this.banner.x + 8, - this.banner.y + 100, - this.timeToGo(this.event!.endDate), // TODO: is the bang correct here? - TextStyle.WINDOW - ); - this.eventTimerText.setName("text-event-timer"); - this.eventTimerText.setScale(0.15); - this.eventTimerText.setOrigin(0, 0); + const lang = i18next.resolvedLanguage; + if (this.event && this.event.bannerKey) { + let key = this.event.bannerKey; + if (lang && this.event.availableLangs && this.event.availableLangs.length > 0) { + if (this.event.availableLangs.includes(lang)) { + key += "_"+lang; + } else { + key += "_en"; + } + } + console.log(this.event.bannerKey); + this.banner = new Phaser.GameObjects.Image(this.scene, this.event.xPosition ?? 29, this.event.yPosition ?? 64, key); + this.banner.setName("img-event-banner"); + this.banner.setOrigin(0.08, -0.35); + this.banner.setScale(this.event.scale ?? 0.18); + if (this.event.eventType !== EventType.GENERIC) { + this.eventTimerText = addTextObject( + this.scene, + this.banner.x + 8, + this.banner.y + 100, + this.timeToGo(this.event.endDate), + TextStyle.WINDOW + ); + this.eventTimerText.setName("text-event-timer"); + this.eventTimerText.setScale(0.15); + this.eventTimerText.setOrigin(0, 0); - this.add([ - this.eventTimerText, - // this.bannerShadow, - this.banner]); + this.add(this.eventTimerText); + } + this.add(this.banner); + } } show() { @@ -157,6 +160,8 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { } updateCountdown() { - this.eventTimerText.setText(this.timeToGo(this.event!.endDate)); // TODO: is the bang correct here? + if (this.event && this.event.eventType !== EventType.GENERIC) { + this.eventTimerText.setText(this.timeToGo(this.event.endDate)); + } } } diff --git a/src/ui/ability-bar.ts b/src/ui/ability-bar.ts index b8259af9f3d..a924d545852 100644 --- a/src/ui/ability-bar.ts +++ b/src/ui/ability-bar.ts @@ -1,4 +1,4 @@ -import { getPokemonNameWithAffix } from "#app/messages.js"; +import { getPokemonNameWithAffix } from "#app/messages"; import BattleScene from "../battle-scene"; import Pokemon from "../field/pokemon"; import { TextStyle, addTextObject } from "./text"; diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index eb4316dc24b..605b8c538a9 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -74,7 +74,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.headerText = addTextObject(this.scene, 0, 0, "", TextStyle.SETTINGS_LABEL); this.headerText.setOrigin(0, 0); this.headerText.setPositionRelative(this.headerBg, 8, 4); - this.headerActionButton = new Phaser.GameObjects.Sprite(this.scene, 0, 0, "keyboard", "SPACE.png"); + this.headerActionButton = new Phaser.GameObjects.Sprite(this.scene, 0, 0, "keyboard", "ACTION.png"); this.headerActionButton.setOrigin(0, 0); this.headerActionButton.setPositionRelative(this.headerBg, 236, 6); this.headerActionText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW, {fontSize:"60px"}); diff --git a/src/ui/admin-ui-handler.ts b/src/ui/admin-ui-handler.ts index 5c91a36ab64..c48138853fc 100644 --- a/src/ui/admin-ui-handler.ts +++ b/src/ui/admin-ui-handler.ts @@ -1,9 +1,9 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { ModalConfig } from "./modal-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; import { FormModalUiHandler } from "./form-modal-ui-handler"; -import { Button } from "#app/enums/buttons.js"; +import { Button } from "#app/enums/buttons"; export default class AdminUiHandler extends FormModalUiHandler { diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index 882c15d3d8c..42a2396e665 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -1,10 +1,10 @@ import { addTextObject, TextStyle } from "./text"; -import BattleScene from "#app/battle-scene.js"; -import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag.js"; -import { WeatherType } from "#app/data/weather.js"; -import { TerrainType } from "#app/data/terrain.js"; +import BattleScene from "#app/battle-scene"; +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { WeatherType } from "#app/data/weather"; +import { TerrainType } from "#app/data/terrain"; import { addWindow, WindowVariant } from "./ui-theme"; -import { ArenaEvent, ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena.js"; +import { ArenaEvent, ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import { BattleSceneEventType, TurnEndEvent } from "../events/battle-scene"; import { ArenaTagType } from "#enums/arena-tag-type"; import TimeOfDayWidget from "./time-of-day-widget"; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 04691886d9c..332fe5f65b9 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -6,7 +6,7 @@ import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; -import { CommandPhase } from "#app/phases/command-phase.js"; +import { CommandPhase } from "#app/phases/command-phase"; export default class BallUiHandler extends UiHandler { private pokeballSelectContainer: Phaser.GameObjects.Container; diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index 6204fa2f928..fe561b76c9f 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -1,13 +1,13 @@ import { default as Pokemon } from "../field/pokemon"; import { addTextObject, TextStyle } from "./text"; import * as Utils from "../utils"; -import BattleScene from "#app/battle-scene.js"; -import Move from "#app/data/move.js"; +import BattleScene from "#app/battle-scene"; +import Move from "#app/data/move"; import { BattleSceneEventType, BerryUsedEvent, MoveUsedEvent } from "../events/battle-scene"; import { BerryType } from "#enums/berry-type"; import { Moves } from "#enums/moves"; import { UiTheme } from "#enums/ui-theme"; -import { getPokemonNameWithAffix } from "#app/messages.js"; +import { getPokemonNameWithAffix } from "#app/messages"; /** Container for info about a {@linkcode Move} */ interface MoveInfo { diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 11b807e8ab7..c7b82dc826e 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -7,7 +7,7 @@ import { StatusEffect } from "../data/status-effect"; import BattleScene from "../battle-scene"; import { Type, getTypeRgb } from "../data/type"; import { getVariantTint } from "#app/data/variant"; -import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#enums/stat"; import BattleFlyout from "./battle-flyout"; import { WindowVariant, addWindow } from "./ui-theme"; import i18next from "i18next"; @@ -30,7 +30,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { private lastLevelExp: integer; private lastLevel: integer; private lastLevelCapped: boolean; - private lastBattleStats: string; + private lastStats: string; private box: Phaser.GameObjects.Sprite; private nameText: Phaser.GameObjects.Text; @@ -68,9 +68,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container { public flyoutMenu?: BattleFlyout; - private battleStatOrder: BattleStat[]; - private battleStatOrderPlayer = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD]; - private battleStatOrderEnemy = [BattleStat.HP, BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD]; + private statOrder: Stat[]; + private readonly statOrderPlayer = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD ]; + private readonly statOrderEnemy = [ Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD ]; constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) { super(scene, x, y); @@ -229,9 +229,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5; const paddingX = this.player ? 4 : 2; const statOverflow = this.player ? 1 : 0; - this.battleStatOrder = this.player ? this.battleStatOrderPlayer : this.battleStatOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order + this.statOrder = this.player ? this.statOrderPlayer : this.statOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order - this.battleStatOrder.map((s, i) => { + this.statOrder.map((s, i) => { // we do a check for i > statOverflow to see when the stat labels go onto the next column // For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0 // For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1 @@ -239,25 +239,25 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis let statY: number; // this will be the y-axis placement for the labels - if (this.battleStatOrder[i] === BattleStat.SPD || this.battleStatOrder[i] === BattleStat.HP) { + if (this.statOrder[i] === Stat.SPD || this.statOrder[i] === Stat.HP) { statY = baseY + 5; } else { statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us } - const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", BattleStat[s]); + const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", Stat[s]); statLabel.setName("icon_stat_label_" + i.toString()); statLabel.setOrigin(0, 0); statLabels.push(statLabel); this.statValuesContainer.add(statLabel); - const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.battleStatOrder[i] !== BattleStat.HP ? "3" : "empty"); + const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.statOrder[i] !== Stat.HP ? "3" : "empty"); statNumber.setName("icon_stat_number_" + i.toString()); statNumber.setOrigin(0, 0); this.statNumbers.push(statNumber); this.statValuesContainer.add(statNumber); - if (this.battleStatOrder[i] === BattleStat.HP) { + if (this.statOrder[i] === Stat.HP) { statLabel.setVisible(false); statNumber.setVisible(false); } @@ -323,7 +323,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN); this.teraIcon.on("pointerover", () => { if (this.lastTeraType !== Type.UNKNOWN) { - (this.scene as BattleScene).ui.showTooltip("", `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`); + (this.scene as BattleScene).ui.showTooltip("", i18next.t("fightUiHandler:teraHover", {type: i18next.t(`pokemonInfo:Type.${Type[this.lastTeraType]}`) })); } }); this.teraIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); @@ -433,10 +433,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.statValuesContainer.setPosition(8, 7); } - const battleStats = this.battleStatOrder.map(() => 0); + const stats = this.statOrder.map(() => 0); - this.lastBattleStats = battleStats.join(""); - this.updateBattleStats(battleStats); + this.lastStats = stats.join(""); + this.updateStats(stats); } getTextureName(): string { @@ -650,14 +650,12 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.lastLevel = pokemon.level; } - const battleStats = pokemon.summonData - ? pokemon.summonData.battleStats - : this.battleStatOrder.map(() => 0); - const battleStatsStr = battleStats.join(""); + const stats = pokemon.getStatStages(); + const statsStr = stats.join(""); - if (this.lastBattleStats !== battleStatsStr) { - this.updateBattleStats(battleStats); - this.lastBattleStats = battleStatsStr; + if (this.lastStats !== statsStr) { + this.updateStats(stats); + this.lastStats = statsStr; } this.shinyIcon.setVisible(pokemon.isShiny()); @@ -769,10 +767,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } } - updateBattleStats(battleStats: integer[]): void { - this.battleStatOrder.map((s, i) => { - if (s !== BattleStat.HP) { - this.statNumbers[i].setFrame(battleStats[s].toString()); + updateStats(stats: integer[]): void { + this.statOrder.map((s, i) => { + if (s !== Stat.HP) { + this.statNumbers[i].setFrame(stats[s - 1].toString()); } }); } diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 86f8d9e01a8..9a694d50b29 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -1,13 +1,12 @@ import BattleScene from "../battle-scene"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Mode } from "./ui"; -import * as Utils from "../utils"; import MessageUiHandler from "./message-ui-handler"; -import { getStatName, Stat } from "../data/pokemon-stat"; import { addWindow } from "./ui-theme"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import {Button} from "#enums/buttons"; import i18next from "i18next"; +import { Stat, PERMANENT_STATS, getStatKey } from "#app/enums/stat"; export default class BattleMessageUiHandler extends MessageUiHandler { private levelUpStatsContainer: Phaser.GameObjects.Container; @@ -98,11 +97,11 @@ export default class BattleMessageUiHandler extends MessageUiHandler { this.levelUpStatsContainer = levelUpStatsContainer; const levelUpStatsLabelsContent = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 73, -94, "", TextStyle.WINDOW, { maxLines: 6 }); + levelUpStatsLabelsContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); let levelUpStatsLabelText = ""; - const stats = Utils.getEnumValues(Stat); - for (const s of stats) { - levelUpStatsLabelText += `${getStatName(s)}\n`; + for (const s of PERMANENT_STATS) { + levelUpStatsLabelText += `${i18next.t(getStatKey(s))}\n`; } levelUpStatsLabelsContent.text = levelUpStatsLabelText; levelUpStatsLabelsContent.x -= levelUpStatsLabelsContent.displayWidth; @@ -114,11 +113,13 @@ export default class BattleMessageUiHandler extends MessageUiHandler { levelUpStatsContainer.add(levelUpStatsLabelsContent); const levelUpStatsIncrContent = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 50, -94, "+\n+\n+\n+\n+\n+", TextStyle.WINDOW, { maxLines: 6 }); + levelUpStatsIncrContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); levelUpStatsContainer.add(levelUpStatsIncrContent); this.levelUpStatsIncrContent = levelUpStatsIncrContent; const levelUpStatsValuesContent = addBBCodeTextObject(this.scene, (this.scene.game.canvas.width / 6) - 7, -94, "", TextStyle.WINDOW, { maxLines: 6, lineSpacing: 5}); + levelUpStatsValuesContent.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); levelUpStatsValuesContent.setOrigin(1, 0); levelUpStatsValuesContent.setAlign("right"); levelUpStatsContainer.add(levelUpStatsValuesContent); @@ -176,8 +177,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } const newStats = (this.scene as BattleScene).getParty()[partyMemberIndex].stats; let levelUpStatsValuesText = ""; - const stats = Utils.getEnumValues(Stat); - for (const s of stats) { + for (const s of PERMANENT_STATS) { levelUpStatsValuesText += `${showTotals ? newStats[s] : newStats[s] - prevStats[s]}\n`; } this.levelUpStatsValuesContent.text = levelUpStatsValuesText; @@ -199,10 +199,9 @@ export default class BattleMessageUiHandler extends MessageUiHandler { return new Promise(resolve => { this.scene.executeWithSeedOffset(() => { let levelUpStatsValuesText = ""; - const stats = Utils.getEnumValues(Stat); const shownStats = this.getTopIvs(ivs, shownIvsCount); - for (const s of stats) { - levelUpStatsValuesText += `${shownStats.indexOf(s) > -1 ? this.getIvDescriptor(ivs[s], s, pokemonId) : "???"}\n`; + for (const s of PERMANENT_STATS) { + levelUpStatsValuesText += `${shownStats.includes(s) ? this.getIvDescriptor(ivs[s], s, pokemonId) : "???"}\n`; } this.levelUpStatsValuesContent.text = levelUpStatsValuesText; this.levelUpStatsIncrContent.setVisible(false); @@ -217,26 +216,16 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] { - const stats = Utils.getEnumValues(Stat); let shownStats: Stat[] = []; if (shownIvsCount < 6) { - const statsPool = stats.slice(0); + const statsPool = PERMANENT_STATS.slice(); + // Sort the stats from highest to lowest iv + statsPool.sort((s1, s2) => ivs[s2] - ivs[s1]); for (let i = 0; i < shownIvsCount; i++) { - let shownStat: Stat | null = null; - let highestIv = -1; - statsPool.map(s => { - if (ivs[s] > highestIv) { - shownStat = s as Stat; - highestIv = ivs[s]; - } - }); - if (shownStat !== null && shownStat !== undefined) { - shownStats.push(shownStat); - statsPool.splice(statsPool.indexOf(shownStat), 1); - } + shownStats.push(statsPool[i]); } } else { - shownStats = stats; + shownStats = PERMANENT_STATS.slice(); } return shownStats; } diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index f1ba0da6c51..e08736d2b70 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -5,13 +5,13 @@ import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; import i18next from "i18next"; -import { Challenge } from "#app/data/challenge.js"; +import { Challenge } from "#app/data/challenge"; import * as Utils from "../utils"; -import { Challenges } from "#app/enums/challenges.js"; +import { Challenges } from "#app/enums/challenges"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { Color, ShadowColor } from "#app/enums/color.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; +import { Color, ShadowColor } from "#app/enums/color"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; /** * Handles all the UI for choosing optional challenges. @@ -33,7 +33,10 @@ export default class GameChallengesUiHandler extends UiHandler { private cursorObj: Phaser.GameObjects.NineSlice | null; + private startBg: Phaser.GameObjects.NineSlice; private startCursor: Phaser.GameObjects.NineSlice; + private startText: Phaser.GameObjects.Text; + private hasSelectedChallenge: boolean; private optionsWidth: number; @@ -104,20 +107,20 @@ export default class GameChallengesUiHandler extends UiHandler { this.descriptionText.setShadow(4, 5, ShadowColor.ORANGE); this.descriptionText.setOrigin(0, 0); - const startBg = addWindow(this.scene, 0, 0, descriptionBg.width, 24); - startBg.setName("window-start-bg"); - startBg.setOrigin(0, 0); - startBg.setPositionRelative(descriptionBg, 0, descriptionBg.height); + this.startBg = addWindow(this.scene, 0, 0, descriptionBg.width, 24); + this.startBg.setName("window-start-bg"); + this.startBg.setOrigin(0, 0); + this.startBg.setPositionRelative(descriptionBg, 0, descriptionBg.height); - const startText = addTextObject(this.scene, 0, 0, i18next.t("common:start"), TextStyle.SETTINGS_LABEL); - startText.setName("text-start"); - startText.setOrigin(0, 0); - startText.setPositionRelative(startBg, (startBg.width - startText.displayWidth) / 2, 4); + this.startText = addTextObject(this.scene, 0, 0, i18next.t("challenges:noneSelected"), TextStyle.SETTINGS_LABEL); + this.startText.setName("text-start"); + this.startText.setOrigin(0, 0); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, descriptionBg.width - 8, 16, 1, 1, 1, 1); this.startCursor.setName("9s-start-cursor"); this.startCursor.setOrigin(0, 0); - this.startCursor.setPositionRelative(startBg, 4, 3); + this.startCursor.setPositionRelative(this.startBg, 4, 3); this.startCursor.setVisible(false); this.valuesContainer = this.scene.add.container(0, 0); @@ -157,8 +160,8 @@ export default class GameChallengesUiHandler extends UiHandler { this.challengesContainer.add(this.optionsBg); this.challengesContainer.add(descriptionBg); this.challengesContainer.add(this.descriptionText); - this.challengesContainer.add(startBg); - this.challengesContainer.add(startText); + this.challengesContainer.add(this.startBg); + this.challengesContainer.add(this.startText); this.challengesContainer.add(this.startCursor); this.challengesContainer.add(this.valuesContainer); @@ -216,6 +219,21 @@ export default class GameChallengesUiHandler extends UiHandler { this.monoTypeValue.setVisible(false); } + // This checks if a challenge has been selected by the user and updates the text/its opacity accordingly. + this.hasSelectedChallenge = this.scene.gameMode.challenges.some(c => c.value !== 0); + if (this.hasSelectedChallenge) { + + this.startText.setText(i18next.t("common:start")); + this.startText.setAlpha(1); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); + } else { + + this.startText.setText(i18next.t("challenges:noneSelected")); + this.startText.setAlpha(0.5); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); + } + this.challengesContainer.update(); + // const totalDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getDifficulty(), 0); // const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); // this.difficultyText.text = `${totalDifficulty}` + (totalMinDifficulty ? `/${totalMinDifficulty}` : ""); @@ -227,6 +245,8 @@ export default class GameChallengesUiHandler extends UiHandler { this.startCursor.setVisible(false); this.challengesContainer.setVisible(true); + // Should always be false at the start + this.hasSelectedChallenge = this.scene.gameMode.challenges.some(c => c.value !== 0); this.setCursor(0); this.initLabels(); @@ -257,6 +277,7 @@ export default class GameChallengesUiHandler extends UiHandler { if (button === Button.CANCEL) { if (this.startCursor.visible) { + // If the user presses cancel when the start cursor has been activated, the game deactivates the start cursor and allows typical challenge selection behavior this.startCursor.setVisible(false); this.cursorObj?.setVisible(true); } else { @@ -266,83 +287,82 @@ export default class GameChallengesUiHandler extends UiHandler { } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { - if (this.startCursor.visible) { - const totalDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getDifficulty(), 0); - const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); - if (totalDifficulty >= totalMinDifficulty) { + if (this.hasSelectedChallenge) { + if (this.startCursor.visible) { this.scene.unshiftPhase(new SelectStarterPhase(this.scene)); this.scene.getCurrentPhase()?.end(); - success = true; } else { - success = false; + this.startCursor.setVisible(true); + this.cursorObj?.setVisible(false); } - } else { - this.startCursor.setVisible(true); - this.cursorObj?.setVisible(false); success = true; + } else { + success = false; } } else { - switch (button) { - case Button.UP: - if (this.cursor === 0) { - if (this.scrollCursor === 0) { - // When at the top of the menu and pressing UP, move to the bottommost item. - if (this.scene.gameMode.challenges.length > rowsToDisplay) { // If there are more than 9 challenges, scroll to the bottom - // First, set the cursor to the last visible element, preparing for the scroll to the end. - const successA = this.setCursor(rowsToDisplay - 1); - // Then, adjust the scroll to display the bottommost elements of the menu. - const successB = this.setScrollCursor(this.scene.gameMode.challenges.length - rowsToDisplay); - success = successA && successB; // success is just there to play the little validation sound effect - } else { // If there are 9 or less challenges, just move to the bottom one - success = this.setCursor(this.scene.gameMode.challenges.length - 1); + if (this.cursorObj?.visible && !this.startCursor.visible) { + switch (button) { + case Button.UP: + if (this.cursor === 0) { + if (this.scrollCursor === 0) { + // When at the top of the menu and pressing UP, move to the bottommost item. + if (this.scene.gameMode.challenges.length > rowsToDisplay) { // If there are more than 9 challenges, scroll to the bottom + // First, set the cursor to the last visible element, preparing for the scroll to the end. + const successA = this.setCursor(rowsToDisplay - 1); + // Then, adjust the scroll to display the bottommost elements of the menu. + const successB = this.setScrollCursor(this.scene.gameMode.challenges.length - rowsToDisplay); + success = successA && successB; // success is just there to play the little validation sound effect + } else { // If there are 9 or less challenges, just move to the bottom one + success = this.setCursor(this.scene.gameMode.challenges.length - 1); + } + } else { + success = this.setScrollCursor(this.scrollCursor - 1); } } else { - success = this.setScrollCursor(this.scrollCursor - 1); + success = this.setCursor(this.cursor - 1); } - } else { - success = this.setCursor(this.cursor - 1); - } - if (success) { - this.updateText(); - } - break; - case Button.DOWN: - if (this.cursor === rowsToDisplay - 1) { - if (this.scrollCursor < this.scene.gameMode.challenges.length - rowsToDisplay) { - // When at the bottom and pressing DOWN, scroll if possible. - success = this.setScrollCursor(this.scrollCursor + 1); + if (success) { + this.updateText(); + } + break; + case Button.DOWN: + if (this.cursor === rowsToDisplay - 1) { + if (this.scrollCursor < this.scene.gameMode.challenges.length - rowsToDisplay) { + // When at the bottom and pressing DOWN, scroll if possible. + success = this.setScrollCursor(this.scrollCursor + 1); + } else { + // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item. + // First, set the cursor to the first visible element, preparing for the scroll to the top. + const successA = this.setCursor(0); + // Then, adjust the scroll to display the topmost elements of the menu. + const successB = this.setScrollCursor(0); + success = successA && successB; // success is just there to play the little validation sound effect + } + } else if (this.scene.gameMode.challenges.length < rowsToDisplay && this.cursor === this.scene.gameMode.challenges.length - 1) { + // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item. + success = this.setCursor(0); } else { - // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item. - // First, set the cursor to the first visible element, preparing for the scroll to the top. - const successA = this.setCursor(0); - // Then, adjust the scroll to display the topmost elements of the menu. - const successB = this.setScrollCursor(0); - success = successA && successB; // success is just there to play the little validation sound effect + success = this.setCursor(this.cursor + 1); } - } else if (this.scene.gameMode.challenges.length < rowsToDisplay && this.cursor === this.scene.gameMode.challenges.length - 1) { - // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item. - success = this.setCursor(0); - } else { - success = this.setCursor(this.cursor + 1); + if (success) { + this.updateText(); + } + break; + case Button.LEFT: + // Moves the option cursor left, if possible. + success = this.getActiveChallenge().decreaseValue(); + if (success) { + this.updateText(); + } + break; + case Button.RIGHT: + // Moves the option cursor right, if possible. + success = this.getActiveChallenge().increaseValue(); + if (success) { + this.updateText(); + } + break; } - if (success) { - this.updateText(); - } - break; - case Button.LEFT: - // Moves the option cursor left, if possible. - success = this.getActiveChallenge().decreaseValue(); - if (success) { - this.updateText(); - } - break; - case Button.RIGHT: - // Moves the option cursor right, if possible. - success = this.getActiveChallenge().increaseValue(); - if (success) { - this.updateText(); - } - break; } } @@ -350,7 +370,6 @@ export default class GameChallengesUiHandler extends UiHandler { if (success) { ui.playSelect(); } - return success; } diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index b22ea5d20fc..764e71a8c3f 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -5,8 +5,8 @@ import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import i18next from "i18next"; import {Button} from "#enums/buttons"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { CommandPhase } from "#app/phases/command-phase"; export enum Command { FIGHT = 0, @@ -68,7 +68,11 @@ export default class CommandUiHandler extends UiHandler { messageHandler.movesWindowContainer.setVisible(false); messageHandler.message.setWordWrapWidth(1110); messageHandler.showText(i18next.t("commandUiHandler:actionMessage", {pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon())}), 0); - this.setCursor(this.getCursor()); + if (this.getCursor() === Command.POKEMON) { + this.setCursor(Command.FIGHT); + } else { + this.setCursor(this.getCursor()); + } return true; } @@ -85,7 +89,7 @@ export default class CommandUiHandler extends UiHandler { if (button === Button.ACTION) { switch (cursor) { // Fight - case 0: + case Command.FIGHT: if ((this.scene.getCurrentPhase() as CommandPhase).checkFightOverride()) { return true; } @@ -93,17 +97,17 @@ export default class CommandUiHandler extends UiHandler { success = true; break; // Ball - case 1: + case Command.BALL: ui.setModeWithoutClear(Mode.BALL); success = true; break; // Pokemon - case 2: + case Command.POKEMON: ui.setMode(Mode.PARTY, PartyUiMode.SWITCH, (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, PartyUiHandler.FilterNonFainted); success = true; break; // Run - case 3: + case Command.RUN: (this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0); success = true; break; diff --git a/src/ui/dropdown.ts b/src/ui/dropdown.ts index 1fef7259108..4e1235c211d 100644 --- a/src/ui/dropdown.ts +++ b/src/ui/dropdown.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene.js"; -import { SceneBase } from "#app/scene-base.js"; +import BattleScene from "#app/battle-scene"; +import { SceneBase } from "#app/scene-base"; import { addTextObject, TextStyle } from "./text"; import { addWindow, WindowVariant } from "./ui-theme"; import i18next from "i18next"; @@ -23,6 +23,14 @@ export enum SortDirection { DESC = 1 } +export enum SortCriteria { + NUMBER = 0, + COST = 1, + CANDY = 2, + IV = 3, + NAME = 4 +} + export class DropDownLabel { public state: DropDownState; public text: string; diff --git a/src/ui/egg-counter-container.ts b/src/ui/egg-counter-container.ts index a688b8b7efc..21cebf5d97e 100644 --- a/src/ui/egg-counter-container.ts +++ b/src/ui/egg-counter-container.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { addWindow } from "./ui-theme"; import { addTextObject, TextStyle } from "./text"; -import { EggCountChangedEvent, EggEventType } from "#app/events/egg.js"; +import { EggCountChangedEvent, EggEventType } from "#app/events/egg"; import EggHatchSceneHandler from "./egg-hatch-scene-handler"; /** diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 733873b974e..7b01ef7a3a6 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -2,7 +2,7 @@ import BattleScene from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import {Button} from "#enums/buttons"; -import { EggHatchPhase } from "#app/phases/egg-hatch-phase.js"; +import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; export default class EggHatchSceneHandler extends UiHandler { public eggHatchContainer: Phaser.GameObjects.Container; diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts new file mode 100644 index 00000000000..99fbccb4257 --- /dev/null +++ b/src/ui/egg-summary-ui-handler.ts @@ -0,0 +1,327 @@ +import BattleScene from "../battle-scene"; +import { Mode } from "./ui"; +import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; +import MessageUiHandler from "./message-ui-handler"; +import { getEggTierForSpecies } from "../data/egg"; +import {Button} from "#enums/buttons"; +import { Gender } from "#app/data/gender"; +import { getVariantTint } from "#app/data/variant"; +import { EggTier } from "#app/enums/egg-type"; +import PokemonHatchInfoContainer from "./pokemon-hatch-info-container"; +import { EggSummaryPhase } from "#app/phases/egg-summary-phase"; +import { DexAttr } from "#app/system/game-data"; +import { EggHatchData } from "#app/data/egg-hatch-data"; + +const iconContainerX = 115; +const iconContainerY = 9; +const numCols = 11; +const iconSize = 18; + +/** + * UI Handler for the egg summary. + * Handles navigation and display of each pokemon as a list + * Also handles display of the pokemon-hatch-info-container + */ +export default class EggSummaryUiHandler extends MessageUiHandler { + /** holds all elements in the scene */ + private eggHatchContainer: Phaser.GameObjects.Container; + /** holds the icon containers and info container */ + private summaryContainer: Phaser.GameObjects.Container; + /** container for the mini pokemon sprites */ + private pokemonIconSpritesContainer: Phaser.GameObjects.Container; + /** container for the icons displayed on top of the mini pokemon sprites (e.g. shiny, HA capsule) */ + private pokemonIconsContainer: Phaser.GameObjects.Container; + /** container for the elements displayed behind the mini pokemon sprites (e.g. egg rarity bg) */ + private pokemonBackgroundContainer: Phaser.GameObjects.Container; + /** hatch info container that displays the current pokemon / hatch (main element on left hand side) */ + private infoContainer: PokemonHatchInfoContainer; + /** handles jumping animations for the pokemon sprite icons */ + private iconAnimHandler: PokemonIconAnimHandler; + private eggHatchBg: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image; + private eggHatchData: EggHatchData[]; + + + /** + * Allows subscribers to listen for events + * + * Current Events: + * - {@linkcode EggEventType.EGG_COUNT_CHANGED} {@linkcode EggCountChangedEvent} + */ + public readonly eventTarget: EventTarget = new EventTarget(); + + constructor(scene: BattleScene) { + super(scene, Mode.EGG_HATCH_SUMMARY); + } + + + setup() { + const ui = this.getUi(); + + this.summaryContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6); + this.summaryContainer.setVisible(false); + ui.add(this.summaryContainer); + + this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6); + this.eggHatchContainer.setVisible(false); + ui.add(this.eggHatchContainer); + + this.iconAnimHandler = new PokemonIconAnimHandler(); + this.iconAnimHandler.setup(this.scene); + + this.eggHatchBg = this.scene.add.image(0, 0, "egg_summary_bg"); + this.eggHatchBg.setOrigin(0, 0); + this.eggHatchContainer.add(this.eggHatchBg); + + this.cursorObj = this.scene.add.image(0, 0, "select_cursor"); + this.cursorObj.setOrigin(0, 0); + this.summaryContainer.add(this.cursorObj); + + this.pokemonIconSpritesContainer = this.scene.add.container(iconContainerX, iconContainerY); + this.pokemonIconsContainer = this.scene.add.container(iconContainerX, iconContainerY); + this.pokemonBackgroundContainer = this.scene.add.container(iconContainerX, iconContainerY); + this.summaryContainer.add(this.pokemonBackgroundContainer); + this.summaryContainer.add(this.pokemonIconSpritesContainer); + this.summaryContainer.add(this.pokemonIconsContainer); + + this.infoContainer = new PokemonHatchInfoContainer(this.scene, this.summaryContainer); + this.infoContainer.setup(); + this.infoContainer.changeToEggSummaryLayout(); + this.infoContainer.setVisible(true); + this.summaryContainer.add(this.infoContainer); + + this.cursor = -1; + } + + clear() { + super.clear(); + this.cursor = -1; + this.summaryContainer.setVisible(false); + this.pokemonIconSpritesContainer.removeAll(true); + this.pokemonIconsContainer.removeAll(true); + this.pokemonBackgroundContainer.removeAll(true); + this.eggHatchBg.setVisible(false); + this.getUi().hideTooltip(); + + // Note: Questions on garbage collection go to @frutescens + const activeKeys = this.scene.getActiveKeys(); + // Removing unnecessary sprites from animation manager + const animKeys = Object.keys(this.scene.anims["anims"]["entries"]); + animKeys.forEach(key => { + if (key.startsWith("pkmn__") && !activeKeys.includes(key)) { + this.scene.anims.remove(key); + } + }); + // Removing unnecessary cries from audio cache + const audioKeys = Object.keys(this.scene.cache.audio.entries.entries); + audioKeys.forEach(key => { + if (key.startsWith("cry/") && !activeKeys.includes(key)) { + delete this.scene.cache.audio.entries.entries[key]; + } + }); + // Clears eggHatchData in EggSummaryUiHandler + this.eggHatchData.length = 0; + // Removes Pokemon icons in EggSummaryUiHandler + this.iconAnimHandler.removeAll(); + } + + /** + * @param args EggHatchData[][] + * args[0]: list of EggHatchData for each egg/pokemon hatched + */ + show(args: EggHatchData[][]): boolean { + super.show(args); + if (args.length >= 1) { + // sort the egg hatch data by egg tier then by species number (then by order hatched) + this.eggHatchData = args[0].sort(function sortHatchData(a: EggHatchData, b: EggHatchData) { + const speciesA = a.pokemon.species; + const speciesB = b.pokemon.species; + if (getEggTierForSpecies(speciesA) < getEggTierForSpecies(speciesB)) { + return -1; + } else if (getEggTierForSpecies(speciesA) > getEggTierForSpecies(speciesB)) { + return 1; + } else { + if (speciesA.speciesId < speciesB.speciesId) { + return -1; + } else if (speciesA.speciesId > speciesB.speciesId) { + return 1; + } else { + return 0; + } + } + } + + ); + } + + this.getUi().bringToTop(this.summaryContainer); + this.summaryContainer.setVisible(true); + this.eggHatchContainer.setVisible(true); + this.pokemonIconsContainer.setVisible(true); + this.eggHatchBg.setVisible(true); + this.infoContainer.hideDisplayPokemon(); + + this.eggHatchData.forEach( (value: EggHatchData, i: number) => { + const x = (i % numCols) * iconSize; + const y = Math.floor(i / numCols) * iconSize; + + const displayPokemon = value.pokemon; + const offset = 2; + const rightSideX = 12; + + const rarityBg = this.scene.add.image(x + 2, y + 5, "passive_bg"); + rarityBg.setOrigin(0, 0); + rarityBg.setScale(0.75); + rarityBg.setVisible(true); + this.pokemonBackgroundContainer.add(rarityBg); + + // set tint for passive bg + switch (getEggTierForSpecies(displayPokemon.species)) { + case EggTier.COMMON: + rarityBg.setVisible(false); + break; + case EggTier.GREAT: + rarityBg.setTint(0xabafff); + break; + case EggTier.ULTRA: + rarityBg.setTint(0xffffaa); + break; + case EggTier.MASTER: + rarityBg.setTint(0xdfffaf); + break; + } + const species = displayPokemon.species; + const female = displayPokemon.gender === Gender.FEMALE; + const formIndex = displayPokemon.formIndex; + const variant = displayPokemon.variant; + const isShiny = displayPokemon.shiny; + + // set pokemon icon (and replace with base sprite if there is a mismatch) + const pokemonIcon = this.scene.add.sprite(x - offset, y + offset, species.getIconAtlasKey(formIndex, isShiny, variant)); + pokemonIcon.setScale(0.5); + pokemonIcon.setOrigin(0, 0); + pokemonIcon.setFrame(species.getIconId(female, formIndex, isShiny, variant)); + + if (pokemonIcon.frame.name !== species.getIconId(female, formIndex, isShiny, variant)) { + console.log(`${species.name}'s variant icon does not exist. Replacing with default.`); + pokemonIcon.setTexture(species.getIconAtlasKey(formIndex, false, variant)); + pokemonIcon.setFrame(species.getIconId(female, formIndex, false, variant)); + } + this.pokemonIconSpritesContainer.add(pokemonIcon); + + const shinyIcon = this.scene.add.image(x + rightSideX, y + offset, "shiny_star_small"); + shinyIcon.setOrigin(0, 0); + shinyIcon.setScale(0.5); + shinyIcon.setVisible(displayPokemon.shiny); + shinyIcon.setTint(getVariantTint(displayPokemon.variant)); + this.pokemonIconsContainer.add(shinyIcon); + + const haIcon = this.scene.add.image(x + rightSideX, y + offset * 4, "ha_capsule"); + haIcon.setOrigin(0, 0); + haIcon.setScale(0.5); + haIcon.setVisible(displayPokemon.abilityIndex === 2); + this.pokemonIconsContainer.add(haIcon); + + const dexEntry = value.dexEntryBeforeUpdate; + const caughtAttr = dexEntry.caughtAttr; + const newShiny = BigInt(1 << (displayPokemon.shiny ? 1 : 0)); + const newVariant = BigInt(1 << (displayPokemon.variant + 4)); + const newShinyOrVariant = ((newShiny & caughtAttr) === BigInt(0)) || ((newVariant & caughtAttr) === BigInt(0)); + const newForm = (BigInt(1 << displayPokemon.formIndex) * DexAttr.DEFAULT_FORM & caughtAttr) === BigInt(0); + + const pokeballIcon = this.scene.add.image(x + rightSideX, y + offset * 7, "icon_owned"); + pokeballIcon.setOrigin(0, 0); + pokeballIcon.setScale(0.5); + pokeballIcon.setVisible(!caughtAttr || newForm); + this.pokemonIconsContainer.add(pokeballIcon); + + const eggMoveIcon = this.scene.add.image(x, y + offset, "icon_egg_move"); + eggMoveIcon.setOrigin(0, 0); + eggMoveIcon.setScale(0.5); + eggMoveIcon.setVisible(value.eggMoveUnlocked); + this.pokemonIconsContainer.add(eggMoveIcon); + + // add animation to the Pokemon sprite for new unlocks (new catch, new shiny or new form) + if (!caughtAttr || newShinyOrVariant || newForm) { + this.iconAnimHandler.addOrUpdate(pokemonIcon, PokemonIconAnimMode.PASSIVE); + } else { + this.iconAnimHandler.addOrUpdate(pokemonIcon, PokemonIconAnimMode.NONE); + } + }); + + this.setCursor(0); + this.scene.playSoundWithoutBgm("evolution_fanfare"); + return true; + } + + processInput(button: Button): boolean { + const ui = this.getUi(); + + let success = false; + const error = false; + if (button === Button.CANCEL) { + const phase = this.scene.getCurrentPhase(); + if (phase instanceof EggSummaryPhase) { + phase.end(); + } + success = true; + } else { + const count = this.eggHatchData.length; + const rows = Math.ceil(count / numCols); + const row = Math.floor(this.cursor / numCols); + switch (button) { + case Button.UP: + if (row) { + success = this.setCursor(this.cursor - numCols); + } + break; + case Button.DOWN: + if (row < rows - 2 || (row < rows - 1 && this.cursor % numCols <= (count - 1) % numCols)) { + success = this.setCursor(this.cursor + numCols); + } + break; + case Button.LEFT: + if (this.cursor % numCols) { + success = this.setCursor(this.cursor - 1); + } + break; + case Button.RIGHT: + if (this.cursor % numCols < (row < rows - 1 ? 10 : (count - 1) % numCols)) { + success = this.setCursor(this.cursor + 1); + } + break; + } + } + + if (success) { + ui.playSelect(); + } else if (error) { + ui.playError(); + } + + return success || error; + } + + setCursor(cursor: number): boolean { + let changed = false; + + const lastCursor = this.cursor; + + changed = super.setCursor(cursor); + + if (changed) { + this.cursorObj.setPosition(iconContainerX - 1 + iconSize * (cursor % numCols), iconContainerY + 1 + iconSize * Math.floor(cursor / numCols)); + + if (lastCursor > -1) { + this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE); + } + this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE); + + this.infoContainer.showHatchInfo(this.eggHatchData[cursor]); + + } + + return changed; + } + +} diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 0beaddbb517..60db9d19eef 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -5,11 +5,11 @@ import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; -import { MoveCategory } from "#app/data/move.js"; +import { MoveCategory } from "#app/data/move"; import i18next from "i18next"; import {Button} from "#enums/buttons"; -import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; +import { CommandPhase } from "#app/phases/command-phase"; export default class FightUiHandler extends UiHandler { public static readonly MOVES_CONTAINER_NAME = "moves"; @@ -95,9 +95,13 @@ export default class FightUiHandler extends UiHandler { messageHandler.bg.setVisible(false); messageHandler.commandWindow.setVisible(false); messageHandler.movesWindowContainer.setVisible(true); - this.setCursor(this.getCursor()); + const pokemon = (this.scene.getCurrentPhase() as CommandPhase).getPokemon(); + if (pokemon.battleSummonData.turnCount <= 1) { + this.setCursor(0); + } else { + this.setCursor(this.getCursor()); + } this.displayMoves(); - return true; } diff --git a/src/ui/filter-bar.ts b/src/ui/filter-bar.ts index 31d7c562da2..aa0f575d398 100644 --- a/src/ui/filter-bar.ts +++ b/src/ui/filter-bar.ts @@ -1,4 +1,4 @@ -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { DropDown, DropDownType } from "./dropdown"; import { StarterContainer } from "./starter-container"; import { addTextObject, getTextColor, TextStyle } from "./text"; diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 8c4ea5f6768..331154263ad 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -60,7 +60,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { const inputBg = addWindow(this.scene, 0, 0, 80, 16, false, false, 0, 0, WindowVariant.XTHIN); const isPassword = field.includes(i18next.t("menu:password")) || field.includes(i18next.t("menu:confirmPassword")); - const input = addTextInputObject(this.scene, 4, -2, 440, 116, TextStyle.TOOLTIP_CONTENT, { type: isPassword ? "password" : "text", maxLength: isPassword ? 64 : 18 }); + const input = addTextInputObject(this.scene, 4, -2, 440, 116, TextStyle.TOOLTIP_CONTENT, { type: isPassword ? "password" : "text", maxLength: isPassword ? 64 : 20 }); input.setOrigin(0, 0); inputContainer.add(inputBg); diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 5f4cb889574..631b2e50b02 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -3,14 +3,31 @@ import { ModalConfig } from "./modal-ui-handler"; import * as Utils from "../utils"; import { Mode } from "./ui"; import i18next from "i18next"; -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { addTextObject, TextStyle } from "./text"; import { addWindow } from "./ui-theme"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; + +interface BuildInteractableImageOpts { + scale?: number; + x?: number; + y?: number; + origin?: { x: number; y: number }; +} export default class LoginFormUiHandler extends FormModalUiHandler { + private readonly ERR_USERNAME: string = "invalid username"; + private readonly ERR_PASSWORD: string = "invalid password"; + private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist"; + private readonly ERR_PASSWORD_MATCH: string = "password doesn't match"; + private readonly ERR_NO_SAVES: string = "No save files found"; + private readonly ERR_TOO_MANY_SAVES: string = "Too many save files found"; + private googleImage: Phaser.GameObjects.Image; private discordImage: Phaser.GameObjects.Image; + private usernameInfoImage: Phaser.GameObjects.Image; private externalPartyContainer: Phaser.GameObjects.Container; + private infoContainer: Phaser.GameObjects.Container; private externalPartyBg: Phaser.GameObjects.NineSlice; private externalPartyTitle: Phaser.GameObjects.Text; constructor(scene: BattleScene, mode: Mode | null = null) { @@ -18,8 +35,23 @@ export default class LoginFormUiHandler extends FormModalUiHandler { } setup(): void { - super.setup(); + this.buildExternalPartyContainer(); + + this.infoContainer = this.scene.add.container(0, 0); + + this.usernameInfoImage = this.buildInteractableImage("settings_icon", "username-info-icon", { + x: 20, + scale: 0.5 + }); + + this.infoContainer.add(this.usernameInfoImage); + this.getUi().add(this.infoContainer); + this.infoContainer.setVisible(false); + this.infoContainer.disableInteractive(); + } + + private buildExternalPartyContainer() { this.externalPartyContainer = this.scene.add.container(0, 0); this.externalPartyContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 12, this.scene.game.canvas.height / 12), Phaser.Geom.Rectangle.Contains); this.externalPartyTitle = addTextObject(this.scene, 0, 4, "", TextStyle.SETTINGS_LABEL); @@ -28,31 +60,8 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.externalPartyContainer.add(this.externalPartyBg); this.externalPartyContainer.add(this.externalPartyTitle); - const googleImage = this.scene.add.image(0, 0, "google"); - googleImage.setOrigin(0, 0); - googleImage.setScale(0.07); - googleImage.setInteractive(); - googleImage.setName("google-icon"); - googleImage.on("pointerdown", () => { - const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/google/callback`); - const googleId = import.meta.env.VITE_GOOGLE_CLIENT_ID; - const googleUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${googleId}&redirect_uri=${redirectUri}&response_type=code&scope=openid`; - window.open(googleUrl, "_self"); - }); - this.googleImage = googleImage; - - const discordImage = this.scene.add.image(20, 0, "discord"); - discordImage.setOrigin(0, 0); - discordImage.setScale(0.07); - discordImage.setInteractive(); - discordImage.setName("discord-icon"); - discordImage.on("pointerdown", () => { - const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`); - const discordId = import.meta.env.VITE_DISCORD_CLIENT_ID; - const discordUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordId}&redirect_uri=${redirectUri}&response_type=code&scope=identify&prompt=none`; - window.open(discordUrl, "_self"); - }); - this.discordImage = discordImage; + this.googleImage = this.buildInteractableImage("google", "google-icon"); + this.discordImage = this.buildInteractableImage("discord", "discord-icon"); this.externalPartyContainer.add(this.googleImage); this.externalPartyContainer.add(this.discordImage); @@ -62,51 +71,54 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.externalPartyContainer.setVisible(false); } - getModalTitle(config?: ModalConfig): string { + override getModalTitle(_config?: ModalConfig): string { return i18next.t("menu:login"); } - getFields(config?: ModalConfig): string[] { + override getFields(_config?: ModalConfig): string[] { return [ i18next.t("menu:username"), i18next.t("menu:password") ]; } - getWidth(config?: ModalConfig): number { + override getWidth(_config?: ModalConfig): number { return 160; } - getMargin(config?: ModalConfig): [number, number, number, number] { + override getMargin(_config?: ModalConfig): [number, number, number, number] { return [ 0, 0, 48, 0 ]; } - getButtonLabels(config?: ModalConfig): string[] { + override getButtonLabels(_config?: ModalConfig): string[] { return [ i18next.t("menu:login"), i18next.t("menu:register")]; } - getReadableErrorMessage(error: string): string { + override getReadableErrorMessage(error: string): string { const colonIndex = error?.indexOf(":"); if (colonIndex > 0) { error = error.slice(0, colonIndex); } switch (error) { - case "invalid username": + case this.ERR_USERNAME: return i18next.t("menu:invalidLoginUsername"); - case "invalid password": + case this.ERR_PASSWORD: return i18next.t("menu:invalidLoginPassword"); - case "account doesn't exist": + case this.ERR_ACCOUNT_EXIST: return i18next.t("menu:accountNonExistent"); - case "password doesn't match": + case this.ERR_PASSWORD_MATCH: return i18next.t("menu:unmatchingPassword"); + case this.ERR_NO_SAVES: + return i18next.t("menu:noSaves"); + case this.ERR_TOO_MANY_SAVES: + return i18next.t("menu:tooManySaves"); } return super.getReadableErrorMessage(error); } - show(args: any[]): boolean { + override show(args: any[]): boolean { if (super.show(args)) { - this.processExternalProvider(); - const config = args[0] as ModalConfig; + this.processExternalProvider(config); const originalLoginAction = this.submitAction; this.submitAction = (_) => { // Prevent overlapping overrides on action modification @@ -143,25 +155,81 @@ export default class LoginFormUiHandler extends FormModalUiHandler { return false; } - clear() { + override clear() { super.clear(); this.externalPartyContainer.setVisible(false); + this.infoContainer.setVisible(false); + this.setMouseCursorStyle("default"); //reset cursor - this.discordImage.off("pointerdown"); - this.googleImage.off("pointerdown"); + [this.discordImage, this.googleImage, this.usernameInfoImage].forEach((img) => img.off("pointerdown")); } - processExternalProvider() : void { + private processExternalProvider(config: ModalConfig) : void { this.externalPartyTitle.setText(i18next.t("menu:orUse") ?? ""); this.externalPartyTitle.setX(20+this.externalPartyTitle.text.length); this.externalPartyTitle.setVisible(true); this.externalPartyContainer.setPositionRelative(this.modalContainer, 175, 0); this.externalPartyContainer.setVisible(true); - this.externalPartyBg.setSize(this.externalPartyTitle.text.length+50, this.modalBg.height); + this.externalPartyBg.setSize(this.externalPartyTitle.text.length + 50, this.modalBg.height); this.getUi().moveTo(this.externalPartyContainer, this.getUi().length - 1); this.googleImage.setPosition(this.externalPartyBg.width/3.1, this.externalPartyBg.height-60); this.discordImage.setPosition(this.externalPartyBg.width/3.1, this.externalPartyBg.height-40); + this.infoContainer.setPosition(5, -76); + this.infoContainer.setVisible(true); + this.getUi().moveTo(this.infoContainer, this.getUi().length - 1); + this.usernameInfoImage.setPositionRelative(this.infoContainer, 0, 0); + + this.discordImage.on("pointerdown", () => { + const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`); + const discordId = import.meta.env.VITE_DISCORD_CLIENT_ID; + const discordUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordId}&redirect_uri=${redirectUri}&response_type=code&scope=identify&prompt=none`; + window.open(discordUrl, "_self"); + }); + + this.googleImage.on("pointerdown", () => { + const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/google/callback`); + const googleId = import.meta.env.VITE_GOOGLE_CLIENT_ID; + const googleUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${googleId}&redirect_uri=${redirectUri}&response_type=code&scope=openid`; + window.open(googleUrl, "_self"); + }); + + const onFail = error => { + this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + this.scene.ui.setModeForceTransition(Mode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); + this.scene.ui.playError(); + }; + + this.usernameInfoImage.on("pointerdown", () => { + const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage + const keyToFind = "data_"; + const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0); + if (dataKeys.length > 0 && dataKeys.length <= 2) { + const options: OptionSelectItem[] = []; + for (let i = 0; i < dataKeys.length; i++) { + options.push({ + label: dataKeys[i].replace(keyToFind, ""), + handler: () => { + this.scene.ui.revertMode(); + this.infoContainer.disableInteractive(); + return true; + } + }); + } + this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { + options: options, + delay: 1000 + }); + this.infoContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width, this.scene.game.canvas.height), Phaser.Geom.Rectangle.Contains); + } else { + if (dataKeys.length > 2) { + return onFail(this.ERR_TOO_MANY_SAVES); + } else { + return onFail(this.ERR_NO_SAVES); + } + } + }); + this.externalPartyContainer.setAlpha(0); this.scene.tweens.add({ targets: this.externalPartyContainer, @@ -170,5 +238,31 @@ export default class LoginFormUiHandler extends FormModalUiHandler { y: "-=24", alpha: 1 }); + + this.infoContainer.setAlpha(0); + this.scene.tweens.add({ + targets: this.infoContainer, + duration: Utils.fixedInt(1000), + ease: "Sine.easeInOut", + y: "-=24", + alpha: 1 + }); + } + + private buildInteractableImage(texture: string, name: string, opts: BuildInteractableImageOpts = {}) { + const { + scale = 0.07, + x = 0, + y = 0, + origin = { x: 0, y: 0 } + } = opts; + const img = this.scene.add.image(x, y, texture); + img.setName(name); + img.setOrigin(origin.x, origin.y); + img.setScale(scale); + img.setInteractive(); + this.addInteractionHoverEffect(img); + + return img; } } diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 6349d219827..b8c3cfd1364 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -31,6 +31,7 @@ let wikiUrl = "https://wiki.pokerogue.net/start"; const discordUrl = "https://discord.gg/uWpTfdKG49"; const githubUrl = "https://github.com/pagefaultgames/pokerogue"; const redditUrl = "https://www.reddit.com/r/pokerogue"; +const donateUrl = "https://github.com/sponsors/patapancakes"; export default class MenuUiHandler extends MessageUiHandler { private readonly textPadding = 8; @@ -369,7 +370,16 @@ export default class MenuUiHandler extends MessageUiHandler { return true; }, keepOpen: true - }]; + }, + { + label: i18next.t("menuUiHandler:donate"), + handler: () => { + window.open(donateUrl, "_blank")?.focus(); + return true; + }, + keepOpen: true + } + ]; if (!bypassLogin && loggedInUser?.hasAdminRole) { communityOptions.push({ label: "Admin", @@ -450,6 +460,7 @@ export default class MenuUiHandler extends MessageUiHandler { } } } + this.showText("", 0); switch (adjustedCursor) { case MenuOptions.GAME_SETTINGS: ui.setOverlayMode(Mode.SETTINGS); @@ -538,15 +549,28 @@ export default class MenuUiHandler extends MessageUiHandler { case MenuOptions.SAVE_AND_QUIT: if (this.scene.currentBattle) { success = true; + const doSaveQuit = () => { + ui.setMode(Mode.LOADING, { + buttonActions: [], fadeOut: () => + this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => { + + this.scene.reset(true); + }) + }); + }; if (this.scene.currentBattle.turn > 1) { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { - ui.setOverlayMode(Mode.CONFIRM, () => this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true)), () => { + if (!this.active) { + this.showText("", 0); + return; + } + ui.setOverlayMode(Mode.CONFIRM, doSaveQuit, () => { ui.revertMode(); - ui.showText("", 0); + this.showText("", 0); }, false, -98); }); } else { - this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true)); + doSaveQuit(); } } else { error = true; @@ -555,19 +579,25 @@ export default class MenuUiHandler extends MessageUiHandler { case MenuOptions.LOG_OUT: success = true; const doLogout = () => { - Utils.apiFetch("account/logout", true).then(res => { - if (!res.ok) { - console.error(`Log out failed (${res.status}: ${res.statusText})`); - } - Utils.removeCookie(Utils.sessionIdKey); - updateUserInfo().then(() => this.scene.reset(true, true)); + ui.setMode(Mode.LOADING, { + buttonActions: [], fadeOut: () => Utils.apiFetch("account/logout", true).then(res => { + if (!res.ok) { + console.error(`Log out failed (${res.status}: ${res.statusText})`); + } + Utils.removeCookie(Utils.sessionIdKey); + updateUserInfo().then(() => this.scene.reset(true, true)); + }) }); }; if (this.scene.currentBattle) { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { + if (!this.active) { + this.showText("", 0); + return; + } ui.setOverlayMode(Mode.CONFIRM, doLogout, () => { ui.revertMode(); - ui.showText("", 0); + this.showText("", 0); }, false, -98); }); } else { diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index cecdacc1eb9..80a39d7bf7f 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -57,32 +57,57 @@ export abstract class ModalUiHandler extends UiHandler { const buttonLabels = this.getButtonLabels(); - const buttonTopMargin = this.getButtonTopMargin(); - for (const label of buttonLabels) { - const buttonLabel = addTextObject(this.scene, 0, 8, label, TextStyle.TOOLTIP_CONTENT); - buttonLabel.setOrigin(0.5, 0.5); - - const buttonBg = addWindow(this.scene, 0, 0, buttonLabel.getBounds().width + 8, 16, false, false, 0, 0, WindowVariant.THIN); - buttonBg.setOrigin(0.5, 0); - buttonBg.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonBg.width, buttonBg.height), Phaser.Geom.Rectangle.Contains); - - const buttonContainer = this.scene.add.container(0, buttonTopMargin); - - this.buttonBgs.push(buttonBg); - this.buttonContainers.push(buttonContainer); - - buttonContainer.add(buttonBg); - buttonContainer.add(buttonLabel); - this.modalContainer.add(buttonContainer); + this.addButton(label); } this.modalContainer.setVisible(false); } + private addButton(label: string) { + const buttonTopMargin = this.getButtonTopMargin(); + const buttonLabel = addTextObject(this.scene, 0, 8, label, TextStyle.TOOLTIP_CONTENT); + buttonLabel.setOrigin(0.5, 0.5); + + const buttonBg = addWindow(this.scene, 0, 0, buttonLabel.getBounds().width + 8, 16, false, false, 0, 0, WindowVariant.THIN); + buttonBg.setOrigin(0.5, 0); + buttonBg.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonBg.width, buttonBg.height), Phaser.Geom.Rectangle.Contains); + + const buttonContainer = this.scene.add.container(0, buttonTopMargin); + + this.buttonBgs.push(buttonBg); + this.buttonContainers.push(buttonContainer); + + buttonContainer.add(buttonBg); + buttonContainer.add(buttonLabel); + + this.addInteractionHoverEffect(buttonBg); + + this.modalContainer.add(buttonContainer); + } + show(args: any[]): boolean { if (args.length >= 1 && "buttonActions" in args[0]) { super.show(args); + if (args[0].hasOwnProperty("fadeOut") && typeof args[0].fadeOut === "function") { + const [ marginTop, marginRight, marginBottom, marginLeft ] = this.getMargin(); + + const overlay = this.scene.add.rectangle(( this.getWidth() + marginLeft + marginRight) / 2, (this.getHeight() + marginTop + marginBottom) / 2, this.scene.game.canvas.width / 6, this.scene.game.canvas.height /6, 0); + overlay.setOrigin(0.5, 0.5); + overlay.setName("rect-ui-overlay-modal"); + overlay.setAlpha(0); + + this.modalContainer.add(overlay); + this.modalContainer.moveTo(overlay, 0); + + this.scene.tweens.add({ + targets: overlay, + alpha: 1, + duration: 250, + ease: "Sine.easeOut", + onComplete: args[0].fadeOut + }); + } const config = args[0] as ModalConfig; @@ -135,4 +160,20 @@ export abstract class ModalUiHandler extends UiHandler { this.buttonBgs.map(bg => bg.off("pointerdown")); } + + /** + * Adds a hover effect to a game object which changes the cursor to a `pointer` and tints it slighly + * @param gameObject the game object to add hover events/effects to + */ + protected addInteractionHoverEffect(gameObject: Phaser.GameObjects.Image | Phaser.GameObjects.NineSlice | Phaser.GameObjects.Sprite) { + gameObject.on("pointerover", () => { + this.setMouseCursorStyle("pointer"); + gameObject.setTint(0xbbbbbb); + }); + + gameObject.on("pointerout", () => { + this.setMouseCursorStyle("default"); + gameObject.clearTint(); + }); + } } diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 61fa9622dc2..d21cc3a6167 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -258,6 +258,9 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { if (this.scene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { this.setRowCursor(0); this.setCursor(2); + } else if ((this.scene.shopCursorTarget === ShopCursorTarget.SHOP) && this.scene.gameMode.hasNoShop) { + this.setRowCursor(ShopCursorTarget.REWARDS); + this.setCursor(0); } else { this.setRowCursor(this.scene.shopCursorTarget); this.setCursor(0); diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 859e95a39b6..77010f84528 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -58,6 +58,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem // set up the description; wordWrap uses true pixels, unaffected by any scaling, while other values are affected this.desc = addTextObject(scene, (options?.onSide && !options?.right ? EFF_WIDTH : 0) + BORDER, (options?.top ? EFF_HEIGHT : 0) + BORDER - 2, "", TextStyle.BATTLE_INFO, { wordWrap: { width: (width - (BORDER - 2) * 2 - (options?.onSide ? EFF_WIDTH : 0)) * GLOBAL_SCALE } }); + this.desc.setLineSpacing(i18next.resolvedLanguage === "ja" ? 25 : 5); // limit the text rendering, required for scrolling later on const maskPointOrigin = { diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index e7c4069c16e..9e025dbe086 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -15,15 +15,15 @@ import { addWindow } from "./ui-theme"; import { SpeciesFormChangeItemTrigger, FormChangeItem } from "../data/pokemon-forms"; import { getVariantTint } from "#app/data/variant"; import {Button} from "#enums/buttons"; -import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; +import { applyChallenges, ChallengeType } from "#app/data/challenge"; import MoveInfoOverlay from "./move-info-overlay"; import i18next from "i18next"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { CommandPhase } from "#app/phases/command-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); @@ -166,6 +166,8 @@ export default class PartyUiHandler extends MessageUiHandler { private iconAnimHandler: PokemonIconAnimHandler; + private blockInput: boolean; + private static FilterAll = (_pokemon: PlayerPokemon) => null; public static FilterNonFainted = (pokemon: PlayerPokemon) => { @@ -309,7 +311,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.partyContainer.setVisible(true); this.partyBg.setTexture(`party_bg${this.scene.currentBattle.double ? "_double" : ""}`); this.populatePartySlots(); - this.setCursor(this.cursor < 6 ? this.cursor : 0); + this.setCursor(0); return true; } @@ -317,7 +319,7 @@ export default class PartyUiHandler extends MessageUiHandler { processInput(button: Button): boolean { const ui = this.getUi(); - if (this.pendingPrompt) { + if (this.pendingPrompt || this.blockInput) { return false; } @@ -485,7 +487,9 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); if (this.cursor >= this.scene.currentBattle.getBattlerCount() || !pokemon.isAllowedInBattle()) { + this.blockInput = true; this.showText(i18next.t("partyUiHandler:releaseConfirmation", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.blockInput = false; ui.setModeWithoutClear(Mode.CONFIRM, () => { ui.setMode(Mode.PARTY); this.doRelease(this.cursor); @@ -1317,16 +1321,13 @@ class PartySlot extends Phaser.GameObjects.Container { this.slotHpOverlay.setVisible(false); this.slotHpText.setVisible(false); let slotTmText: string; - switch (true) { - case (this.pokemon.compatibleTms.indexOf(tmMoveId) === -1): - slotTmText = i18next.t("partyUiHandler:notAble"); - break; - case (this.pokemon.getMoveset().filter(m => m?.moveId === tmMoveId).length > 0): + + if (this.pokemon.getMoveset().filter(m => m?.moveId === tmMoveId).length > 0) { slotTmText = i18next.t("partyUiHandler:learned"); - break; - default: + } else if (this.pokemon.compatibleTms.indexOf(tmMoveId) === -1) { + slotTmText = i18next.t("partyUiHandler:notAble"); + } else { slotTmText = i18next.t("partyUiHandler:able"); - break; } this.slotDescriptionLabel.setText(slotTmText); diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts new file mode 100644 index 00000000000..f8a9adced36 --- /dev/null +++ b/src/ui/pokemon-hatch-info-container.ts @@ -0,0 +1,189 @@ +import PokemonInfoContainer from "./pokemon-info-container"; +import BattleScene from "../battle-scene"; +import { Gender } from "../data/gender"; +import { Type } from "../data/type"; +import * as Utils from "../utils"; +import { TextStyle, addTextObject } from "./text"; +import { speciesEggMoves } from "#app/data/egg-moves"; +import { allMoves } from "#app/data/move"; +import { Species } from "#app/enums/species"; +import { getEggTierForSpecies } from "#app/data/egg"; +import { starterColors } from "../battle-scene"; +import { argbFromRgba } from "@material/material-color-utilities"; +import { EggHatchData } from "#app/data/egg-hatch-data"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; + +/** + * Class for the hatch info summary of each pokemon + * Holds an info container as well as an additional egg sprite, name, egg moves and main sprite + */ +export default class PokemonHatchInfoContainer extends PokemonInfoContainer { + private currentPokemonSprite: Phaser.GameObjects.Sprite; + private pokemonNumberText: Phaser.GameObjects.Text; + private pokemonNameText: Phaser.GameObjects.Text; + private pokemonEggMovesContainer: Phaser.GameObjects.Container; + private pokemonEggMoveContainers: Phaser.GameObjects.Container[]; + private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[]; + private pokemonEggMoveLabels: Phaser.GameObjects.Text[]; + private pokemonHatchedIcon : Phaser.GameObjects.Sprite; + private pokemonListContainer: Phaser.GameObjects.Container; + private pokemonCandyIcon: Phaser.GameObjects.Sprite; + private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite; + private pokemonCandyCountText: Phaser.GameObjects.Text; + + constructor(scene: BattleScene, listContainer : Phaser.GameObjects.Container, x: number = 115, y: number = 9,) { + super(scene, x, y); + this.pokemonListContainer = listContainer; + + } + setup(): void { + super.setup(); + super.changeToEggSummaryLayout(); + + this.currentPokemonSprite = this.scene.add.sprite(54, 80, "pkmn__sub"); + this.currentPokemonSprite.setScale(0.8); + this.currentPokemonSprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true }); + this.pokemonListContainer.add(this.currentPokemonSprite); + + // setup name and number + this.pokemonNumberText = addTextObject(this.scene, 80, 107.5, "0000", TextStyle.SUMMARY, {fontSize: 74}); + this.pokemonNumberText.setOrigin(0, 0); + this.pokemonListContainer.add(this.pokemonNumberText); + + this.pokemonNameText = addTextObject(this.scene, 7, 107.5, "", TextStyle.SUMMARY, {fontSize: 74}); + this.pokemonNameText.setOrigin(0, 0); + this.pokemonListContainer.add(this.pokemonNameText); + + // setup egg icon and candy count + this.pokemonHatchedIcon = this.scene.add.sprite(-5, 90, "egg_icons"); + this.pokemonHatchedIcon.setOrigin(0, 0.2); + this.pokemonHatchedIcon.setScale(0.8); + this.pokemonListContainer.add(this.pokemonHatchedIcon); + + this.pokemonCandyIcon = this.scene.add.sprite(4.5, 40, "candy"); + this.pokemonCandyIcon.setScale(0.5); + this.pokemonCandyIcon.setOrigin(0, 0); + this.pokemonListContainer.add(this.pokemonCandyIcon); + + this.pokemonCandyOverlayIcon = this.scene.add.sprite(4.5, 40, "candy_overlay"); + this.pokemonCandyOverlayIcon.setScale(0.5); + this.pokemonCandyOverlayIcon.setOrigin(0, 0); + this.pokemonListContainer.add(this.pokemonCandyOverlayIcon); + + this.pokemonCandyCountText = addTextObject(this.scene, 14, 40, "x0", TextStyle.SUMMARY, { fontSize: "56px" }); + this.pokemonCandyCountText.setOrigin(0, 0); + this.pokemonListContainer.add(this.pokemonCandyCountText); + + // setup egg moves + this.pokemonEggMoveContainers = []; + this.pokemonEggMoveBgs = []; + this.pokemonEggMoveLabels = []; + this.pokemonEggMovesContainer = this.scene.add.container(0, 200); + this.pokemonEggMovesContainer.setVisible(false); + this.pokemonEggMovesContainer.setScale(0.5); + + for (let m = 0; m < 4; m++) { + const eggMoveContainer = this.scene.add.container(0, 0 + 6 * m); + + const eggMoveBg = this.scene.add.nineslice(70, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2); + eggMoveBg.setOrigin(1, 0); + + const eggMoveLabel = addTextObject(this.scene, 70 -eggMoveBg.width / 2, 0, "???", TextStyle.PARTY); + eggMoveLabel.setOrigin(0.5, 0); + + this.pokemonEggMoveBgs.push(eggMoveBg); + this.pokemonEggMoveLabels.push(eggMoveLabel); + + eggMoveContainer.add(eggMoveBg); + eggMoveContainer.add(eggMoveLabel); + eggMoveContainer.setScale(0.44); + + this.pokemonEggMoveContainers.push(eggMoveContainer); + + this.pokemonEggMovesContainer.add(eggMoveContainer); + } + + super.add(this.pokemonEggMoveContainers); + + } + + /** + * Disable the sprite (and replace with substitute) + */ + hideDisplayPokemon() { + this.currentPokemonSprite.setVisible(false); + } + + /** + * Display a given pokemon sprite with animations + * assumes the specific pokemon sprite has already been loaded + */ + displayPokemon(pokemon: PlayerPokemon) { + const species = pokemon.species; + const female = pokemon.gender === Gender.FEMALE; + const formIndex = pokemon.formIndex; + const shiny = pokemon.shiny; + const variant = pokemon.variant; + this.currentPokemonSprite.setVisible(false); + species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => { + + getPokemonSpeciesForm(species.speciesId, pokemon.formIndex).cry(this.scene); + this.currentPokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant)); + this.currentPokemonSprite.setPipelineData("shiny", shiny); + this.currentPokemonSprite.setPipelineData("variant", variant); + this.currentPokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant)); + this.currentPokemonSprite.setVisible(true); + }); + } + + /** + * Updates the info container with the appropriate dex data and starter entry from the hatchInfo + * Also updates the displayed name, number, egg moves and main animated sprite for the pokemon + * @param hatchInfo The EggHatchData of the pokemon / new hatch to show + */ + showHatchInfo(hatchInfo: EggHatchData) { + this.pokemonEggMovesContainer.setVisible(true); + + const pokemon = hatchInfo.pokemon; + const species = pokemon.species; + this.displayPokemon(pokemon); + + super.show(pokemon, false, 1, hatchInfo.getDex(), hatchInfo.getStarterEntry(), true); + const colorScheme = starterColors[species.speciesId]; + + this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); + this.pokemonCandyIcon.setVisible(true); + this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.pokemonCandyOverlayIcon.setVisible(true); + this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); + this.pokemonCandyCountText.setVisible(true); + + this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4)); + this.pokemonNameText.setText(species.name); + + const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId); + + for (let em = 0; em < 4; em++) { + const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null; + const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em); + this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase()); + + this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???"); + if (!(eggMove && hatchInfo.starterDataEntryBeforeUpdate.eggMoves & Math.pow(2, em)) && eggMoveUnlocked) { + this.pokemonEggMoveLabels[em].setText("(+) " + eggMove.name); + } + } + + // will always have at least one egg move + this.pokemonEggMovesContainer.setVisible(true); + + if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) { + this.pokemonHatchedIcon.setFrame("manaphy"); + } else { + this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species)); + } + + } + +} diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index edb85ecff7a..3c54e529d43 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -6,7 +6,7 @@ import { getNatureName } from "../data/nature"; import { Type } from "../data/type"; import Pokemon from "../field/pokemon"; import i18next from "i18next"; -import { DexAttr } from "../system/game-data"; +import { DexAttr, DexEntry, StarterDataEntry } from "../system/game-data"; import * as Utils from "../utils"; import ConfirmUiHandler from "./confirm-ui-handler"; import { StatsContainer } from "./stats-container"; @@ -24,7 +24,7 @@ const languageSettings: { [key: string]: LanguageSetting } = { infoContainerTextSize: "64px" }, "de": { - infoContainerTextSize: "64px" + infoContainerTextSize: "64px", }, "es": { infoContainerTextSize: "64px" @@ -63,6 +63,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { private pokemonMovesContainers: Phaser.GameObjects.Container[]; private pokemonMoveBgs: Phaser.GameObjects.NineSlice[]; private pokemonMoveLabels: Phaser.GameObjects.Text[]; + private infoBg; private numCharsBeforeCutoff = 16; @@ -83,9 +84,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct? const textSettings = languageSettings[langSettingKey]; - const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); - infoBg.setOrigin(0.5, 0.5); - infoBg.setName("window-info-bg"); + this.infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); + this.infoBg.setOrigin(0.5, 0.5); + this.infoBg.setName("window-info-bg"); this.pokemonMovesContainer = this.scene.add.container(6, 14); this.pokemonMovesContainer.setName("pkmn-moves"); @@ -133,7 +134,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.statsContainer = new StatsContainer(this.scene, -48, -64, true); - this.add(infoBg); + this.add(this.infoBg); this.add(this.statsContainer); // The position should be set per language @@ -207,9 +208,16 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.setVisible(false); } - show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1): Promise { + show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1, dexEntry?: DexEntry, starterEntry?: StarterDataEntry, eggInfo = false): Promise { return new Promise(resolve => { - const caughtAttr = BigInt(pokemon.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr); + if (!dexEntry) { + dexEntry = pokemon.scene.gameData.dexData[pokemon.species.speciesId]; + } + if (!starterEntry) { + starterEntry = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()]; + } + + const caughtAttr = BigInt(dexEntry.caughtAttr); if (pokemon.gender > Gender.GENDERLESS) { this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender)); this.pokemonGenderText.setColor(getGenderColor(pokemon.gender)); @@ -254,7 +262,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonFormText.disableInteractive(); } - const abilityTextStyle = pokemon.abilityIndex === (pokemon.species.ability2 ? 2 : 1) ? TextStyle.MONEY : TextStyle.WINDOW; + const abilityTextStyle = pokemon.abilityIndex === 2 ? TextStyle.MONEY : TextStyle.WINDOW; this.pokemonAbilityText.setText(pokemon.getAbility(true).name); this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, this.scene.uiTheme)); this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, this.scene.uiTheme)); @@ -268,7 +276,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex; const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex; - const rootFormHasHiddenAbility = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr & opponentPokemonAbilityAttr; + const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr; if (!rootFormHasHiddenAbility) { this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); @@ -280,7 +288,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme)); - const dexNatures = pokemon.scene.gameData.dexData[pokemon.species.speciesId].natureAttr; + const dexNatures = dexEntry.natureAttr; const newNature = 1 << (pokemon.nature + 1); if (!(dexNatures & newNature)) { @@ -324,31 +332,31 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } const starterSpeciesId = pokemon.species.getRootSpeciesId(); - const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr - ? this.scene.gameData.dexData[starterSpeciesId].ivs - : null; + const originalIvs: integer[] | null = eggInfo ? (dexEntry.caughtAttr ? dexEntry.ivs : null) : (this.scene.gameData.dexData[starterSpeciesId].caughtAttr + ? this.scene.gameData.dexData[starterSpeciesId].ivs : null); this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct? - - this.scene.tweens.add({ - targets: this, - duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), - ease: "Cubic.easeInOut", - x: this.initialX - this.infoWindowWidth, - onComplete: () => { - resolve(); - } - }); - - if (showMoves) { + if (!eggInfo) { this.scene.tweens.add({ - delay: Utils.fixedInt(Math.floor(325 / speedMultiplier)), - targets: this.pokemonMovesContainer, - duration: Utils.fixedInt(Math.floor(325 / speedMultiplier)), + targets: this, + duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), ease: "Cubic.easeInOut", - x: this.movesContainerInitialX - 57, - onComplete: () => resolve() + x: this.initialX - this.infoWindowWidth, + onComplete: () => { + resolve(); + } }); + + if (showMoves) { + this.scene.tweens.add({ + delay: Utils.fixedInt(Math.floor(325 / speedMultiplier)), + targets: this.pokemonMovesContainer, + duration: Utils.fixedInt(Math.floor(325 / speedMultiplier)), + ease: "Cubic.easeInOut", + x: this.movesContainerInitialX - 57, + onComplete: () => resolve() + }); + } } for (let m = 0; m < 4; m++) { @@ -364,6 +372,36 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { }); } + changeToEggSummaryLayout() { + // The position should be set per language (and shifted for new layout) + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct? + const textSettings = languageSettings[langSettingKey]; + + const eggLabelTextOffset = 43; + const infoContainerLabelXPos = (textSettings?.infoContainerLabelXPos || -18) + eggLabelTextOffset; + const infoContainerTextXPos = (textSettings?.infoContainerTextXPos || -14) + eggLabelTextOffset; + + this.x = this.initialX - this.infoWindowWidth; + + this.pokemonGenderText.setPosition(89, -2); + this.pokemonGenderNewText.setPosition(79, -2); + this.pokemonShinyIcon.setPosition(82, 87); + this.pokemonShinyNewIcon.setPosition(72, 87); + + this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 152); + this.pokemonFormText.setPosition(infoContainerTextXPos, 152); + this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 110); + this.pokemonAbilityText.setPosition(infoContainerTextXPos, 110); + this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 125); + this.pokemonNatureText.setPosition(infoContainerTextXPos, 125); + + this.statsContainer.setScale(0.7); + this.statsContainer.setPosition(30, -3); + this.infoBg.setVisible(false); + this.pokemonMovesContainer.setVisible(false); + } + makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise { const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth; return new Promise(resolve => { diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index 733aab79b05..0c4b54ac723 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -5,6 +5,20 @@ import { Mode } from "./ui"; import { TextStyle, addTextObject } from "./text"; import i18next from "i18next"; + +interface LanguageSetting { + inputFieldFontSize?: string, + warningMessageFontSize?: string, + errorMessageFontSize?: string, +} + +const languageSettings: { [key: string]: LanguageSetting } = { + "es":{ + inputFieldFontSize: "50px", + errorMessageFontSize: "40px", + } +}; + export default class RegistrationFormUiHandler extends FormModalUiHandler { getModalTitle(config?: ModalConfig): string { return i18next.t("menu:register"); @@ -50,7 +64,17 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { setup(): void { super.setup(); - const label = addTextObject(this.scene, 10, 87, i18next.t("menu:registrationAgeWarning"), TextStyle.TOOLTIP_CONTENT, { fontSize: "42px" }); + this.modalContainer.list.forEach((child: Phaser.GameObjects.GameObject) => { + if (child instanceof Phaser.GameObjects.Text && child !== this.titleText) { + const inputFieldFontSize = languageSettings[i18next.resolvedLanguage!]?.inputFieldFontSize; + if (inputFieldFontSize) { + child.setFontSize(inputFieldFontSize); + } + } + }); + + const warningMessageFontSize = languageSettings[i18next.resolvedLanguage!]?.warningMessageFontSize ?? "42px"; + const label = addTextObject(this.scene, 10, 87, i18next.t("menu:registrationAgeWarning"), TextStyle.TOOLTIP_CONTENT, { fontSize: warningMessageFontSize}); this.modalContainer.add(label); } @@ -68,6 +92,10 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { const onFail = error => { this.scene.ui.setMode(Mode.REGISTRATION_FORM, Object.assign(config, { errorMessage: error?.trim() })); this.scene.ui.playError(); + const errorMessageFontSize = languageSettings[i18next.resolvedLanguage!]?.errorMessageFontSize; + if (errorMessageFontSize) { + this.errorMessage.setFontSize(errorMessageFontSize); + } }; if (!this.inputs[0].text) { return onFail(i18next.t("menu:emptyUsername")); diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 33885509344..078177cafb1 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -1,7 +1,7 @@ import { FormModalUiHandler } from "./form-modal-ui-handler"; import { ModalConfig } from "./modal-ui-handler"; import i18next from "i18next"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import { PlayerPokemon } from "#app/field/pokemon"; export default class RenameFormUiHandler extends FormModalUiHandler { getModalTitle(config?: ModalConfig): string { diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 25210277edc..b7ad5f5adec 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -13,14 +13,15 @@ import { BattleType } from "../battle"; import { TrainerVariant } from "../field/trainer"; import { Challenges } from "#enums/challenges"; import { getLuckString, getLuckTextTint } from "../modifier/modifier-type"; -import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle.js"; +import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; import { Type, getTypeRgb } from "../data/type"; +import { TypeColor, TypeShadow } from "#app/enums/color"; import { getNatureStatMultiplier, getNatureName } from "../data/nature"; import { getVariantTint } from "#app/data/variant"; -import { PokemonHeldItemModifier, TerastallizeModifier } from "../modifier/modifier"; -import {modifierSortFunc} from "../modifier/modifier"; +import * as Modifier from "../modifier/modifier"; import { Species } from "#enums/species"; import { PlayerGender } from "#enums/player-gender"; +import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; /** * RunInfoUiMode indicates possible overlays of RunInfoUiHandler. @@ -67,7 +68,7 @@ export default class RunInfoUiHandler extends UiHandler { override async setup() { this.runContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1); // The import of the modifiersModule is loaded here to sidestep async/await issues. - this.modifiersModule = await import("../modifier/modifier"); + this.modifiersModule = Modifier; this.runContainer.setVisible(false); } @@ -151,7 +152,13 @@ export default class RunInfoUiHandler extends UiHandler { const headerBgCoords = headerBg.getTopRight(); const abilityButtonContainer = this.scene.add.container(0, 0); const abilityButtonText = addTextObject(this.scene, 8, 0, i18next.t("runHistory:viewHeldItems"), TextStyle.WINDOW, {fontSize:"34px"}); - const abilityButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 2, "keyboard", "E.png"); + const gamepadType = this.getUi().getGamepadType(); + let abilityButtonElement: Phaser.GameObjects.Sprite; + if (gamepadType === "touch") { + abilityButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 2, "keyboard", "E.png"); + } else { + abilityButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 2, gamepadType, this.scene.inputController?.getIconForLatestInputRecorded(SettingKeyboard.Button_Cycle_Ability)); + } abilityButtonContainer.add([abilityButtonText, abilityButtonElement]); abilityButtonContainer.setPosition(headerBgCoords.x - abilityButtonText.displayWidth - abilityButtonElement.displayWidth - 8, 10); this.runContainer.add(abilityButtonContainer); @@ -180,11 +187,19 @@ export default class RunInfoUiHandler extends UiHandler { if (this.isVictory) { const hallofFameInstructionContainer = this.scene.add.container(0, 0); const shinyButtonText = addTextObject(this.scene, 8, 0, i18next.t("runHistory:viewHallOfFame"), TextStyle.WINDOW, {fontSize:"65px"}); - const shinyButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 4, "keyboard", "R.png"); + const formButtonText = addTextObject(this.scene, 8, 12, i18next.t("runHistory:viewEndingSplash"), TextStyle.WINDOW, {fontSize:"65px"}); + const gamepadType = this.getUi().getGamepadType(); + let shinyButtonElement: Phaser.GameObjects.Sprite; + let formButtonElement: Phaser.GameObjects.Sprite; + if (gamepadType === "touch") { + shinyButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 4, "keyboard", "R.png"); + formButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 16, "keyboard", "F.png"); + } else { + shinyButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 4, gamepadType, this.scene.inputController?.getIconForLatestInputRecorded(SettingKeyboard.Button_Cycle_Shiny)); + formButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 16, gamepadType, this.scene.inputController?.getIconForLatestInputRecorded(SettingKeyboard.Button_Cycle_Form)); + } hallofFameInstructionContainer.add([shinyButtonText, shinyButtonElement]); - const formButtonText = addTextObject(this.scene, 8, 12, i18next.t("runHistory:viewEndingSplash"), TextStyle.WINDOW, {fontSize:"65px"}); - const formButtonElement = new Phaser.GameObjects.Sprite(this.scene, 0, 16, "keyboard", "F.png"); hallofFameInstructionContainer.add([formButtonText, formButtonElement]); hallofFameInstructionContainer.setPosition(12, 25); @@ -302,7 +317,7 @@ export default class RunInfoUiHandler extends UiHandler { const teraPokemon = {}; this.runInfo.enemyModifiers.forEach((m) => { const modifier = m.toModifier(this.scene, this.modifiersModule[m.className]); - if (modifier instanceof TerastallizeModifier) { + if (modifier instanceof Modifier.TerastallizeModifier) { const teraDetails = modifier?.getArgs(); const pkmnId = teraDetails[0]; teraPokemon[pkmnId] = teraDetails[1]; @@ -373,15 +388,16 @@ export default class RunInfoUiHandler extends UiHandler { break; case GameModes.CHALLENGE: modeText.appendText(`${i18next.t("gameMode:challenge")}`, false); - modeText.appendText(`\t\t${i18next.t("runHistory:challengeRules")}: `); + modeText.appendText(`${i18next.t("runHistory:challengeRules")}: `); + modeText.setWrapMode(1); // wrap by word + modeText.setWrapWidth(500); const rules: string[] = this.challengeParser(); if (rules) { for (let i = 0; i < rules.length; i++) { - const newline = i > 0 && i%2 === 0; if (i > 0) { - modeText.appendText(" + ", newline); + modeText.appendText(" + ", false); } - modeText.appendText(rules[i], newline); + modeText.appendText(rules[i], false); } } break; @@ -432,7 +448,7 @@ export default class RunInfoUiHandler extends UiHandler { modifierIconsContainer.setScale(0.45); for (const m of this.runInfo.modifiers) { const modifier = m.toModifier(this.scene, this.modifiersModule[m.className]); - if (modifier instanceof PokemonHeldItemModifier) { + if (modifier instanceof Modifier.PokemonHeldItemModifier) { continue; } const icon = modifier?.getIcon(this.scene, false); @@ -470,14 +486,18 @@ export default class RunInfoUiHandler extends UiHandler { rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`)); break; case Challenges.SINGLE_TYPE: - rules.push(i18next.t(`pokemonInfo:Type.${Type[this.runInfo.challenges[i].value-1]}` as const)); + const typeRule = Type[this.runInfo.challenges[i].value-1]; + const typeTextColor = `[color=${TypeColor[typeRule]}]`; + const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`; + const typeText = typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)!+"[/color]"+"[/shadow]"; + rules.push(typeText); break; case Challenges.FRESH_START: rules.push(i18next.t("challenges:freshStart.name")); break; case Challenges.INVERSE_BATTLE: // - rules.push(i18next.t("challenges:inverseBattle.shortName").split("").reverse().join("")); + rules.push(i18next.t("challenges:inverseBattle.shortName")); break; } } @@ -521,7 +541,9 @@ export default class RunInfoUiHandler extends UiHandler { // Contains Name, Level + Nature, Ability, Passive const pokeInfoTextContainer = this.scene.add.container(-85, 3.5); const textContainerFontSize = "34px"; - const pNature = getNatureName(pokemon.nature); + // This checks if the Pokemon's nature has been overwritten during the run and displays the change accurately + const pNature = pokemon.getNature(); + const pNatureName = getNatureName(pNature); const pName = pokemon.getNameToRender(); //With the exception of Korean/Traditional Chinese/Simplified Chinese, the code shortens the terms for ability and passive to their first letter. //These languages are exempted because they are already short enough. @@ -537,7 +559,7 @@ export default class RunInfoUiHandler extends UiHandler { // Japanese is set to a greater line spacing of 35px in addBBCodeTextObject() if lineSpacing < 12. const lineSpacing = (i18next.resolvedLanguage === "ja") ? 12 : 3; const pokeInfoText = addBBCodeTextObject(this.scene, 0, 0, pName, TextStyle.SUMMARY, {fontSize: textContainerFontSize, lineSpacing: lineSpacing}); - pokeInfoText.appendText(`${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatFancyLargeNumber(pokemon.level, 1)} - ${pNature}`); + pokeInfoText.appendText(`${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatFancyLargeNumber(pokemon.level, 1)} - ${pNatureName}`); pokeInfoText.appendText(pAbilityInfo); pokeInfoText.appendText(pPassiveInfo); pokeInfoTextContainer.add(pokeInfoText); @@ -548,7 +570,7 @@ export default class RunInfoUiHandler extends UiHandler { const pStats : string[]= []; pokemon.stats.forEach((element) => pStats.push(Utils.formatFancyLargeNumber(element, 1))); for (let i = 0; i < pStats.length; i++) { - const isMult = getNatureStatMultiplier(pokemon.nature, i); + const isMult = getNatureStatMultiplier(pNature, i); pStats[i] = (isMult < 1) ? pStats[i] + "[color=#40c8f8]↓[/color]" : pStats[i]; pStats[i] = (isMult > 1) ? pStats[i] + "[color=#f89890]↑[/color]" : pStats[i]; } @@ -628,18 +650,18 @@ export default class RunInfoUiHandler extends UiHandler { // Pokemon Held Items - not displayed by default // Endless/Endless Spliced have a different scale because Pokemon tend to accumulate more items in these runs. const heldItemsScale = (this.runInfo.gameMode === GameModes.SPLICED_ENDLESS || this.runInfo.gameMode === GameModes.ENDLESS) ? 0.25 : 0.5; - const heldItemsContainer = this.scene.add.container(-82, 6); - const heldItemsList : PokemonHeldItemModifier[] = []; + const heldItemsContainer = this.scene.add.container(-82, 2); + const heldItemsList : Modifier.PokemonHeldItemModifier[] = []; if (this.runInfo.modifiers.length) { for (const m of this.runInfo.modifiers) { const modifier = m.toModifier(this.scene, this.modifiersModule[m.className]); - if (modifier instanceof PokemonHeldItemModifier && modifier.pokemonId === pokemon.id) { + if (modifier instanceof Modifier.PokemonHeldItemModifier && modifier.pokemonId === pokemon.id) { modifier.stackCount = m["stackCount"]; heldItemsList.push(modifier); } } if (heldItemsList.length > 0) { - (heldItemsList as PokemonHeldItemModifier[]).sort(modifierSortFunc); + (heldItemsList as Modifier.PokemonHeldItemModifier[]).sort(Modifier.modifierSortFunc); let row = 0; for (const [index, item] of heldItemsList.entries()) { if ( index > 36 ) { @@ -648,6 +670,9 @@ export default class RunInfoUiHandler extends UiHandler { break; } const itemIcon = item?.getIcon(this.scene, true); + if (item?.stackCount < item?.getMaxHeldItemCount(pokemon) && itemIcon.list[1] instanceof Phaser.GameObjects.BitmapText) { + itemIcon.list[1].clearTint(); + } itemIcon.setScale(heldItemsScale); itemIcon.setPosition((index%19) * 10, row * 10); heldItemsContainer.add(itemIcon); @@ -866,10 +891,12 @@ export default class RunInfoUiHandler extends UiHandler { } break; case Button.CYCLE_ABILITY: - if (this.partyVisibility) { - this.showParty(false); - } else { - this.showParty(true); + if (this.runInfo.modifiers.length !== 0) { + if (this.partyVisibility) { + this.showParty(false); + } else { + this.showParty(true); + } } break; } diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index e6ab0d3b3c3..89b20322a68 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -2,7 +2,7 @@ import i18next from "i18next"; import BattleScene from "../battle-scene"; import { Button } from "#enums/buttons"; import { GameMode } from "../game-mode"; -import { PokemonHeldItemModifier } from "../modifier/modifier"; +import * as Modifier from "../modifier/modifier"; import { SessionSaveData } from "../system/game-data"; import PokemonData from "../system/pokemon-data"; import * as Utils from "../utils"; @@ -306,14 +306,12 @@ class SessionSlot extends Phaser.GameObjects.Container { this.add(pokemonIconsContainer); - const modifiersModule = await import("../modifier/modifier"); - const modifierIconsContainer = this.scene.add.container(148, 30); modifierIconsContainer.setScale(0.5); let visibleModifierIndex = 0; for (const m of data.modifiers) { - const modifier = m.toModifier(this.scene, modifiersModule[m.className]); - if (modifier instanceof PokemonHeldItemModifier) { + const modifier = m.toModifier(this.scene, Modifier[m.className]); + if (modifier instanceof Modifier.PokemonHeldItemModifier) { continue; } const icon = modifier?.getIcon(this.scene, false); diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index f21f2ebcc9d..570377eab43 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -5,7 +5,7 @@ import { Mode } from "../ui"; import UiHandler from "../ui-handler"; import { addWindow } from "../ui-theme"; import {Button} from "#enums/buttons"; -import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler.js"; +import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler"; import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu"; import { Setting, SettingKeys, SettingType } from "#app/system/settings/settings"; import i18next from "i18next"; diff --git a/src/ui/settings/move-touch-controls-handler.ts b/src/ui/settings/move-touch-controls-handler.ts index cf56bf47d3b..cff68fa523d 100644 --- a/src/ui/settings/move-touch-controls-handler.ts +++ b/src/ui/settings/move-touch-controls-handler.ts @@ -1,5 +1,5 @@ -import TouchControl from "#app/touch-controls.js"; -import UI from "#app/ui/ui.js"; +import TouchControl from "#app/touch-controls"; +import UI from "#app/ui/ui"; import { Scene } from "phaser"; export const TOUCH_CONTROL_POSITIONS_LANDSCAPE = "touchControlPositionsLandscape"; diff --git a/src/ui/settings/navigationMenu.ts b/src/ui/settings/navigationMenu.ts index 370b6f67c76..7d7761b7b69 100644 --- a/src/ui/settings/navigationMenu.ts +++ b/src/ui/settings/navigationMenu.ts @@ -1,6 +1,6 @@ import BattleScene from "#app/battle-scene"; import {Mode} from "#app/ui/ui"; -import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler.js"; +import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler"; import {addTextObject, setTextStyle, TextStyle} from "#app/ui/text"; import {addWindow} from "#app/ui/ui-theme"; import {Button} from "#enums/buttons"; diff --git a/src/ui/settings/settings-audio-ui-handler.ts b/src/ui/settings/settings-audio-ui-handler.ts index 4a895fc3170..86c6a9bce40 100644 --- a/src/ui/settings/settings-audio-ui-handler.ts +++ b/src/ui/settings/settings-audio-ui-handler.ts @@ -1,6 +1,6 @@ import BattleScene from "../../battle-scene"; import { Mode } from "../ui"; -"#app/inputs-controller.js"; +"#app/inputs-controller"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { SettingType } from "#app/system/settings/settings"; diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts index 25dda6e9bfb..3d602c50a78 100644 --- a/src/ui/settings/settings-display-ui-handler.ts +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -1,6 +1,6 @@ import BattleScene from "../../battle-scene"; import { Mode } from "../ui"; -"#app/inputs-controller.js"; +"#app/inputs-controller"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { SettingKeys, SettingType } from "#app/system/settings/settings"; diff --git a/src/ui/settings/settings-gamepad-ui-handler.ts b/src/ui/settings/settings-gamepad-ui-handler.ts index 902d7eff34e..63a9d2ab23b 100644 --- a/src/ui/settings/settings-gamepad-ui-handler.ts +++ b/src/ui/settings/settings-gamepad-ui-handler.ts @@ -12,7 +12,7 @@ import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; import pad_dualshock from "#app/configs/inputs/pad_dualshock"; import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES"; import {InterfaceConfig} from "#app/inputs-controller"; -import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler.js"; +import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; import {Device} from "#enums/devices"; import {truncateString} from "#app/utils"; import i18next from "i18next"; diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts index dc6de8c90dc..7e020034bc6 100644 --- a/src/ui/settings/settings-keyboard-ui-handler.ts +++ b/src/ui/settings/settings-keyboard-ui-handler.ts @@ -9,7 +9,7 @@ import { settingKeyboardOptions } from "#app/system/settings/settings-keyboard"; import {reverseValueToKeySetting, truncateString} from "#app/utils"; -import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler.js"; +import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; import {InterfaceConfig} from "#app/inputs-controller"; import {addTextObject, TextStyle} from "#app/ui/text"; import {deleteBind} from "#app/configs/inputs/configHandler"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 267a62104e3..d99c25bc612 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -28,23 +28,24 @@ import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; import { Egg } from "#app/data/egg"; import Overrides from "#app/overrides"; -import {SettingKeyboard} from "#app/system/settings/settings-keyboard"; -import {Passive as PassiveAttr} from "#enums/passive"; +import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { Passive as PassiveAttr } from "#enums/passive"; import * as Challenge from "../data/challenge"; import MoveInfoOverlay from "./move-info-overlay"; -import { getEggTierForSpecies } from "#app/data/egg.js"; +import { getEggTierForSpecies } from "#app/data/egg"; import { Device } from "#enums/devices"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import {Button} from "#enums/buttons"; -import { EggSourceType } from "#app/enums/egg-source-types.js"; +import { Button } from "#enums/buttons"; +import { EggSourceType } from "#app/enums/egg-source-types"; import AwaitableUiHandler from "./awaitable-ui-handler"; -import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType } from "./dropdown"; +import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "./dropdown"; import { StarterContainer } from "./starter-container"; import { DropDownColumn, FilterBar } from "./filter-bar"; import { ScrollBar } from "./scroll-bar"; -import { SelectChallengePhase } from "#app/phases/select-challenge-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; +import { SelectChallengePhase } from "#app/phases/select-challenge-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { Abilities } from "#app/enums/abilities"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -262,7 +263,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private pokemonHatchedIcon : Phaser.GameObjects.Sprite; private pokemonHatchedCountText: Phaser.GameObjects.Text; private pokemonShinyIcon: Phaser.GameObjects.Sprite; + private pokemonPassiveDisabledIcon: Phaser.GameObjects.Sprite; + private pokemonPassiveLockedIcon: Phaser.GameObjects.Sprite; + private activeTooltip: "ABILITY" | "PASSIVE" | "CANDY" | undefined; private instructionsContainer: Phaser.GameObjects.Container; private filterInstructionsContainer: Phaser.GameObjects.Container; private shinyIconElement: Phaser.GameObjects.Sprite; @@ -501,11 +505,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // sort filter const sortOptions = [ - new DropDownOption(this.scene, 0, new DropDownLabel(i18next.t("filterBar:sortByNumber"), undefined, DropDownState.ON)), - new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("filterBar:sortByCost"))), - new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("filterBar:sortByCandies"))), - new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("filterBar:sortByIVs"))), - new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("filterBar:sortByName"))) + new DropDownOption(this.scene, SortCriteria.NUMBER, new DropDownLabel(i18next.t("filterBar:sortByNumber"), undefined, DropDownState.ON)), + new DropDownOption(this.scene, SortCriteria.COST, new DropDownLabel(i18next.t("filterBar:sortByCost"))), + new DropDownOption(this.scene, SortCriteria.CANDY, new DropDownLabel(i18next.t("filterBar:sortByCandies"))), + new DropDownOption(this.scene, SortCriteria.IV, new DropDownLabel(i18next.t("filterBar:sortByIVs"))), + new DropDownOption(this.scene, SortCriteria.NAME, new DropDownLabel(i18next.t("filterBar:sortByName"))) ]; this.filterBar.addFilter(DropDownColumn.SORT, i18next.t("filterBar:sortFilter"), new DropDown(this.scene, 0, 0, sortOptions, this.updateStarters, DropDownType.SINGLE)); this.filterBarContainer.add(this.filterBar); @@ -558,10 +562,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127 + starterInfoYOffset, i18next.t("starterSelectUiHandler:ability"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonAbilityLabelText.setOrigin(0, 0); this.pokemonAbilityLabelText.setVisible(false); + this.starterSelectContainer.add(this.pokemonAbilityLabelText); this.pokemonAbilityText = addTextObject(this.scene, starterInfoXPos, 127 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonAbilityText.setOrigin(0, 0); + this.pokemonAbilityText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); + this.starterSelectContainer.add(this.pokemonAbilityText); this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136 + starterInfoYOffset, i18next.t("starterSelectUiHandler:passive"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); @@ -571,8 +578,21 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonPassiveText = addTextObject(this.scene, starterInfoXPos, 136 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonPassiveText.setOrigin(0, 0); + this.pokemonPassiveText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); this.starterSelectContainer.add(this.pokemonPassiveText); + this.pokemonPassiveDisabledIcon = this.scene.add.sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_stop"); + this.pokemonPassiveDisabledIcon.setOrigin(0, 0.5); + this.pokemonPassiveDisabledIcon.setScale(0.35); + this.pokemonPassiveDisabledIcon.setVisible(false); + this.starterSelectContainer.add(this.pokemonPassiveDisabledIcon); + + this.pokemonPassiveLockedIcon = this.scene.add.sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_lock"); + this.pokemonPassiveLockedIcon.setOrigin(0, 0.5); + this.pokemonPassiveLockedIcon.setScale(0.42, 0.38); + this.pokemonPassiveLockedIcon.setVisible(false); + this.starterSelectContainer.add(this.pokemonPassiveLockedIcon); + this.pokemonNatureLabelText = addTextObject(this.scene, 6, 145 + starterInfoYOffset, i18next.t("starterSelectUiHandler:nature"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonNatureLabelText.setOrigin(0, 0); this.pokemonNatureLabelText.setVisible(false); @@ -733,7 +753,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonShinyIcon = this.scene.add.sprite(14, 76, "shiny_icons"); this.pokemonShinyIcon.setOrigin(0.15, 0.2); this.pokemonShinyIcon.setScale(1); - this.pokemonCaughtHatchedContainer.add ((this.pokemonShinyIcon)); + this.pokemonCaughtHatchedContainer.add(this.pokemonShinyIcon); this.pokemonHatchedCountText = addTextObject(this.scene, 24, 19, "0", TextStyle.SUMMARY_ALT); this.pokemonHatchedCountText.setOrigin(0, 0); @@ -1200,6 +1220,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } + /** + * Update the display of candy upgrade icons or animations for the given StarterContainer + * @param starterContainer the container for the Pokemon to update + */ + updateCandyUpgradeDisplay(starterContainer: StarterContainer) { + if (this.isUpgradeIconEnabled() ) { + this.setUpgradeIcon(starterContainer); + } + if (this.isUpgradeAnimationEnabled()) { + this.setUpgradeAnimation(starterContainer.icon, this.lastSpecies, true); + } + } + /** * Processes an {@linkcode CandyUpgradeNotificationChangedEvent} sent when the corresponding setting changes * @param event {@linkcode Event} sent by the callback @@ -1604,7 +1637,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); } - const candyCount = starterData.candyCount; + const passiveAttr = starterData.passiveAttr; if (passiveAttr & PassiveAttr.UNLOCKED) { // this is for enabling and disabling the passive if (!(passiveAttr & PassiveAttr.ENABLED)) { @@ -1685,8 +1718,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return true; } }); - const showUseCandies = () => { // this lets you use your candies + + // Purchases with Candy + const candyCount = starterData.candyCount; + const showUseCandies = () => { const options: any[] = []; // TODO: add proper type + + // Unlock passive option if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); options.push({ @@ -1704,18 +1742,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); ui.setMode(Mode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, undefined); + this.setSpeciesDetails(this.lastSpecies); + this.scene.playSound("se/buy"); - // if starterContainer exists, update the passive background + // update the passive background and icon/animation for available upgrade if (starterContainer) { - // Update the candy upgrade display - if (this.isUpgradeIconEnabled() ) { - this.setUpgradeIcon(starterContainer); - } - if (this.isUpgradeAnimationEnabled()) { - this.setUpgradeAnimation(starterContainer.icon, this.lastSpecies, true); - } - + this.updateCandyUpgradeDisplay(starterContainer); starterContainer.starterPassiveBgs.setVisible(!!this.scene.gameData.starterData[this.lastSpecies.speciesId].passiveAttr); } return true; @@ -1726,6 +1758,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { itemArgs: starterColors[this.lastSpecies.speciesId] }); } + + // Reduce cost option const valueReduction = starterData.valueReduction; if (valueReduction < valueReductionMax) { const reductionCost = getValueReductionCandyCounts(speciesStarters[this.lastSpecies.speciesId])[valueReduction]; @@ -1747,19 +1781,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(Mode.STARTER_SELECT); this.scene.playSound("se/buy"); - // if starterContainer exists, update the value reduction background + // update the value label and icon/animation for available upgrade if (starterContainer) { this.updateStarterValueLabel(starterContainer); - - // If the notification setting is set to 'On', update the candy upgrade display - if (this.scene.candyUpgradeNotification === 2) { - if (this.isUpgradeIconEnabled() ) { - this.setUpgradeIcon(starterContainer); - } - if (this.isUpgradeAnimationEnabled()) { - this.setUpgradeAnimation(starterContainer.icon, this.lastSpecies, true); - } - } + this.updateCandyUpgradeDisplay(starterContainer); } return true; } @@ -1792,6 +1817,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(Mode.STARTER_SELECT); this.scene.playSound("se/buy"); + // update the icon/animation for available upgrade + if (starterContainer) { + this.updateCandyUpgradeDisplay(starterContainer); + } + return true; } return false; @@ -1838,10 +1868,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switch (button) { case Button.CYCLE_SHINY: if (this.canCycleShiny) { - const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : props.variant; - starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true; - this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : newVariant, undefined, undefined); + starterAttributes.shiny = starterAttributes.shiny !== undefined ? !starterAttributes.shiny : false; + if (starterAttributes.shiny) { + // Change to shiny, we need to get the proper default variant + const newProps = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId)); + const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : newProps.variant; + this.setSpeciesDetails(this.lastSpecies, true, undefined, undefined, newVariant, undefined, undefined); + this.scene.playSound("se/sparkle"); // Set the variant label to the shiny tint const tint = getVariantTint(newVariant); @@ -1849,6 +1883,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonShinyIcon.setTint(tint); this.pokemonShinyIcon.setVisible(true); } else { + this.setSpeciesDetails(this.lastSpecies, false, undefined, undefined, 0, undefined, undefined); this.pokemonShinyIcon.setVisible(false); success = true; } @@ -1901,6 +1936,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } while (newAbilityIndex !== this.abilityCursor); starterAttributes.ability = newAbilityIndex; // store the selected ability + + const { visible: tooltipVisible } = this.scene.ui.getTooltip(); + + if (tooltipVisible && this.activeTooltip === "ABILITY") { + const newAbility = allAbilities[this.lastSpecies.getAbility(newAbilityIndex)]; + this.scene.ui.editTooltip(`${newAbility.name}`, `${newAbility.description}`); + } + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, newAbilityIndex, undefined); success = true; } @@ -2363,6 +2406,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // First, ensure you have the caught attributes for the species else default to bigint 0 const caughtAttr = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0); const starterData = this.scene.gameData.starterData[container.species.speciesId]; + const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId); // Gen filter const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation); @@ -2398,7 +2442,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.ON) { return isPassiveUnlocked; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.EXCLUDE) { - return !isPassiveUnlocked; + return isStarterProgressable && !isPassiveUnlocked; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.UNLOCKABLE) { return isPassiveUnlockable; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.OFF) { @@ -2413,7 +2457,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.ON) { return isCostReduced; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.EXCLUDE) { - return !isCostReduced; + return isStarterProgressable && !isCostReduced; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.UNLOCKABLE) { return isCostReductionUnlockable; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.OFF) { @@ -2450,12 +2494,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }); // HA Filter + const speciesHasHiddenAbility = container.species.abilityHidden !== container.species.ability1 && container.species.abilityHidden !== Abilities.NONE; const hasHA = starterData.abilityAttr & AbilityAttr.ABILITY_HIDDEN; const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => { if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) { return hasHA; } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.EXCLUDE) { - return !hasHA; + return speciesHasHiddenAbility && !hasHA; } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.OFF) { return true; } @@ -2467,7 +2512,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (misc.val === "EGG" && misc.state === DropDownState.ON) { return isEggPurchasable; } else if (misc.val === "EGG" && misc.state === DropDownState.EXCLUDE) { - return !isEggPurchasable; + return isStarterProgressable && !isEggPurchasable; } else if (misc.val === "EGG" && misc.state === DropDownState.OFF) { return true; } @@ -2498,19 +2543,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switch (sort.val) { default: break; - case 0: + case SortCriteria.NUMBER: return (a.species.speciesId - b.species.speciesId) * -sort.dir; - case 1: + case SortCriteria.COST: return (a.cost - b.cost) * -sort.dir; - case 2: + case SortCriteria.CANDY: const candyCountA = this.scene.gameData.starterData[a.species.speciesId].candyCount; const candyCountB = this.scene.gameData.starterData[b.species.speciesId].candyCount; return (candyCountA - candyCountB) * -sort.dir; - case 3: + case SortCriteria.IV: const avgIVsA = this.scene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0) / this.scene.gameData.dexData[a.species.speciesId].ivs.length; const avgIVsB = this.scene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0) / this.scene.gameData.dexData[b.species.speciesId].ivs.length; return (avgIVsA - avgIVsB) * -sort.dir; - case 4: + case SortCriteria.NAME: return a.species.name.localeCompare(b.species.name) * -sort.dir; } return 0; @@ -2665,12 +2710,30 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } + getFriendship(speciesId: number) { + let currentFriendship = this.scene.gameData.starterData[speciesId].friendship; + if (!currentFriendship || currentFriendship === undefined) { + currentFriendship = 0; + } + + const friendshipCap = getStarterValueFriendshipCap(speciesStarters[speciesId]); + + return { currentFriendship, friendshipCap }; + } + setSpecies(species: PokemonSpecies | null) { this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n; this.abilityCursor = species ? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0; + if (!species && this.scene.ui.getTooltip().visible) { + this.scene.ui.hideTooltip(); + } + + this.pokemonAbilityText.off("pointerover"); + this.pokemonPassiveText.off("pointerover"); + const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null; if (starterAttributes?.nature) { @@ -2785,17 +2848,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonHatchedIcon.setVisible(true); this.pokemonHatchedCountText.setVisible(true); - let currentFriendship = this.scene.gameData.starterData[this.lastSpecies.speciesId].friendship; - if (!currentFriendship || currentFriendship === undefined) { - currentFriendship = 0; - } - - const friendshipCap = getStarterValueFriendshipCap(speciesStarters[this.lastSpecies.speciesId]); + const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); const candyCropY = 16 - (16 * (currentFriendship / friendshipCap)); if (this.pokemonCandyDarknessOverlay.visible) { - this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true)); - this.pokemonCandyDarknessOverlay.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); + this.pokemonCandyDarknessOverlay.on("pointerover", () => { + this.scene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true); + this.activeTooltip = "CANDY"; + }); + this.pokemonCandyDarknessOverlay.on("pointerout", () => { + this.scene.ui.hideTooltip(); + this.activeTooltip = undefined; + }); } this.pokemonCandyDarknessOverlay.setCrop(0, 0, 16, candyCropY); @@ -2841,7 +2905,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const speciesForm = getPokemonSpeciesForm(species.speciesId, props.formIndex); - this.setTypeIcons(speciesForm.type1, speciesForm!.type2!); // TODO: are those bangs correct? + this.setTypeIcons(speciesForm.type1, speciesForm.type2); this.pokemonSprite.clearTint(); if (this.pokerusSpecies.includes(species)) { @@ -2910,6 +2974,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.abilityCursor = -1; this.natureCursor = -1; + if (this.activeTooltip === "CANDY") { + const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); + this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`); + } + if (species?.forms?.find(f => f.formKey === "female")) { if (female !== undefined) { formIndex = female ? 1 : 0; @@ -2932,6 +3001,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } this.pokemonSprite.setVisible(false); + this.pokemonPassiveLabelText.setVisible(false); + this.pokemonPassiveText.setVisible(false); + this.pokemonPassiveDisabledIcon.setVisible(false); + this.pokemonPassiveLockedIcon.setVisible(false); if (this.assetLoadCancelled) { this.assetLoadCancelled.value = true; @@ -3055,17 +3128,72 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (dexEntry.caughtAttr) { - const ability = this.lastSpecies.getAbility(abilityIndex!); // TODO: is this bang correct? - this.pokemonAbilityText.setText(allAbilities[ability].name); + const ability = allAbilities[this.lastSpecies.getAbility(abilityIndex!)]; // TODO: is this bang correct? + this.pokemonAbilityText.setText(ability.name); const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1); this.pokemonAbilityText.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD)); this.pokemonAbilityText.setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true)); const passiveAttr = this.scene.gameData.starterData[species.speciesId].passiveAttr; - this.pokemonPassiveText.setText(passiveAttr & PassiveAttr.UNLOCKED ? passiveAttr & PassiveAttr.ENABLED ? allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name : i18next.t("starterSelectUiHandler:disabled") : i18next.t("starterSelectUiHandler:locked")); - this.pokemonPassiveText.setColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY)); - this.pokemonPassiveText.setShadowColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY, true)); + const passiveAbility = allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]]; + + if (this.pokemonAbilityText.visible) { + if (this.activeTooltip === "ABILITY") { + this.scene.ui.editTooltip(`${ability.name}`, `${ability.description}`); + } + + this.pokemonAbilityText.on("pointerover", () => { + this.scene.ui.showTooltip(`${ability.name}`, `${ability.description}`, true); + this.activeTooltip = "ABILITY"; + }); + this.pokemonAbilityText.on("pointerout", () => { + this.scene.ui.hideTooltip(); + this.activeTooltip = undefined; + }); + } + + if (passiveAbility) { + const isUnlocked = !!(passiveAttr & PassiveAttr.UNLOCKED); + const isEnabled = !!(passiveAttr & PassiveAttr.ENABLED); + + const textStyle = isUnlocked && isEnabled ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY; + const textAlpha = isUnlocked && isEnabled ? 1 : 0.5; + + this.pokemonPassiveLabelText.setVisible(true); + this.pokemonPassiveLabelText.setColor(this.getTextColor(TextStyle.SUMMARY_ALT)); + this.pokemonPassiveLabelText.setShadowColor(this.getTextColor(TextStyle.SUMMARY_ALT, true)); + this.pokemonPassiveText.setVisible(true); + this.pokemonPassiveText.setText(passiveAbility.name); + this.pokemonPassiveText.setColor(this.getTextColor(textStyle)); + this.pokemonPassiveText.setAlpha(textAlpha); + this.pokemonPassiveText.setShadowColor(this.getTextColor(textStyle, true)); + + if (this.activeTooltip === "PASSIVE") { + this.scene.ui.editTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`); + } + + if (this.pokemonPassiveText.visible) { + this.pokemonPassiveText.on("pointerover", () => { + this.scene.ui.showTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`, true); + this.activeTooltip = "PASSIVE"; + }); + this.pokemonPassiveText.on("pointerout", () => { + this.scene.ui.hideTooltip(); + this.activeTooltip = undefined; + }); + } + + const iconPosition = { + x: this.pokemonPassiveText.x + this.pokemonPassiveText.displayWidth + 1, + y: this.pokemonPassiveText.y + this.pokemonPassiveText.displayHeight / 2 + }; + this.pokemonPassiveDisabledIcon.setVisible(isUnlocked && !isEnabled); + this.pokemonPassiveDisabledIcon.setPosition(iconPosition.x, iconPosition.y); + this.pokemonPassiveLockedIcon.setVisible(!isUnlocked); + this.pokemonPassiveLockedIcon.setPosition(iconPosition.x, iconPosition.y); + + } this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false, this.scene.uiTheme)); @@ -3114,13 +3242,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonFormText.setText(formText ? i18next.t(`pokemonForm:${speciesName}${formText}`) : ""); } - this.setTypeIcons(speciesForm.type1, speciesForm.type2!); // TODO: is this bang correct? + this.setTypeIcons(speciesForm.type1, speciesForm.type2); } else { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - // @ts-ignore - this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? + this.setTypeIcons(null, null); } } else { this.shinyOverlay.setVisible(false); @@ -3130,8 +3257,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - // @ts-ignore - this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? + this.setTypeIcons(null, null); } if (!this.starterMoveset) { @@ -3164,7 +3290,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.updateInstructions(); } - setTypeIcons(type1: Type, type2: Type): void { + setTypeIcons(type1: Type | null, type2: Type | null): void { if (type1 !== null) { this.type1Icon.setVisible(true); this.type1Icon.setFrame(Type[type1].toLowerCase()); @@ -3441,23 +3567,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler { props += DexAttr.MALE; } /* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences. - * If they're not there, it checks the caughtAttr for shiny only (i.e. SHINY === true && NON_SHINY === false) + * If they're not there, it enables shiny state by default if any shiny was caught */ - if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && (caughtAttr & DexAttr.NON_SHINY) === 0n)) { + if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)) { props += DexAttr.SHINY; - if (this.starterPreferences[speciesId]?.variant) { + if (this.starterPreferences[speciesId]?.variant !== undefined) { props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT; } else { /* This calculates the correct variant if there's no starter preferences for it. - * This gets the lowest tier variant that you've caught (in line with other mechanics) and adds it to the temp props + * This gets the highest tier variant that you've caught and adds it to the temp props */ - if ((caughtAttr & DexAttr.DEFAULT_VARIANT) > 0) { - props += DexAttr.DEFAULT_VARIANT; - } - if ((caughtAttr & DexAttr.VARIANT_2) > 0) { - props += DexAttr.VARIANT_2; - } else if ((caughtAttr & DexAttr.VARIANT_3) > 0) { + if ((caughtAttr & DexAttr.VARIANT_3) > 0) { props += DexAttr.VARIANT_3; + } else if ((caughtAttr & DexAttr.VARIANT_2) > 0) { + props += DexAttr.VARIANT_2; + } else { + props += DexAttr.DEFAULT_VARIANT; } } } else { diff --git a/src/ui/stats-container.ts b/src/ui/stats-container.ts index 2bd7099a2c5..c6e0ea3a71c 100644 --- a/src/ui/stats-container.ts +++ b/src/ui/stats-container.ts @@ -1,7 +1,8 @@ import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; import BattleScene from "../battle-scene"; -import { Stat, getStatName } from "../data/pokemon-stat"; import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; +import { PERMANENT_STATS, getStatKey } from "#app/enums/stat"; +import i18next from "i18next"; const ivChartSize = 24; const ivChartStatCoordMultipliers = [[0, -1], [0.825, -0.5], [0.825, 0.5], [-0.825, -0.5], [-0.825, 0.5], [0, 1]]; @@ -53,16 +54,16 @@ export class StatsContainer extends Phaser.GameObjects.Container { this.ivStatValueTexts = []; - new Array(6).fill(null).map((_, i: integer) => { - const statLabel = addTextObject(this.scene, ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[i][0] * 1.325, ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[i][1] * 1.325 - 4 + ivLabelOffset[i], getStatName(i as Stat), TextStyle.TOOLTIP_CONTENT); + for (const s of PERMANENT_STATS) { + const statLabel = addTextObject(this.scene, ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[s][0] * 1.325, ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[s][1] * 1.325 - 4 + ivLabelOffset[s], i18next.t(getStatKey(s)), TextStyle.TOOLTIP_CONTENT); statLabel.setOrigin(0.5); - this.ivStatValueTexts[i] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT); - this.ivStatValueTexts[i].setOrigin(0.5); + this.ivStatValueTexts[s] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT); + this.ivStatValueTexts[s].setOrigin(0.5); this.add(statLabel); - this.add(this.ivStatValueTexts[i]); - }); + this.add(this.ivStatValueTexts[s]); + } } updateIvs(ivs: integer[], originalIvs?: integer[]): void { diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index ea7b798f2bf..8ae72f08edd 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -11,7 +11,6 @@ import Move, { MoveCategory } from "../data/move"; import { getPokeballAtlasKey } from "../data/pokeball"; import { getGenderColor, getGenderSymbol } from "../data/gender"; import { getLevelRelExp, getLevelTotalExp } from "../data/exp"; -import { Stat, getStatName } from "../data/pokemon-stat"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { StatusEffect } from "../data/status-effect"; import { getBiomeName } from "../data/biomes"; @@ -19,10 +18,11 @@ import { Nature, getNatureName, getNatureStatMultiplier } from "../data/nature"; import { loggedInUser } from "../account"; import { Variant, getVariantTint } from "#app/data/variant"; import {Button} from "#enums/buttons"; -import { Ability } from "../data/ability.js"; +import { Ability } from "../data/ability"; import i18next from "i18next"; import {modifierSortFunc} from "../modifier/modifier"; import { PlayerGender } from "#enums/player-gender"; +import { Stat, PERMANENT_STATS, getStatKey } from "#app/enums/stat"; enum Page { PROFILE, @@ -836,10 +836,8 @@ export default class SummaryUiHandler extends UiHandler { const statsContainer = this.scene.add.container(0, -pageBg.height); pageContainer.add(statsContainer); - const stats = Utils.getEnumValues(Stat) as Stat[]; - - stats.forEach((stat, s) => { - const statName = getStatName(stat); + PERMANENT_STATS.forEach((stat, s) => { + const statName = i18next.t(getStatKey(stat)); const rowIndex = s % 3; const colIndex = Math.floor(s / 3); @@ -850,7 +848,7 @@ export default class SummaryUiHandler extends UiHandler { statsContainer.add(statLabel); const statValueText = stat !== Stat.HP - ? Utils.formatStat(this.pokemon?.stats[s]!) // TODO: is this bang correct? + ? Utils.formatStat(this.pokemon?.getStat(stat)!) // TODO: is this bang correct? : `${Utils.formatStat(this.pokemon?.hp!, true)}/${Utils.formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct? const statValue = addTextObject(this.scene, 120 + 88 * colIndex, 56 + 16 * rowIndex, statValueText, TextStyle.WINDOW_ALT); diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index 5d2f184e679..ea80a6e524a 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -1,5 +1,5 @@ import * as Utils from "../utils"; -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { EaseType } from "#enums/ease-type"; import { TimeOfDay } from "#enums/time-of-day"; diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 3c25ed34d61..67a4f7260e6 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -5,7 +5,7 @@ import * as Utils from "../utils"; import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { getBattleCountSplashMessage, getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; -import { TimedEventDisplay } from "#app/timed-event-manager.js"; +import { TimedEventDisplay } from "#app/timed-event-manager"; export default class TitleUiHandler extends OptionSelectUiHandler { private titleContainer: Phaser.GameObjects.Container; diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index 94625efaa75..d9f0a876b71 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -52,6 +52,15 @@ export default abstract class UiHandler { return changed; } + /** + * Changes the style of the mouse cursor. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} + * @param cursorStyle cursor style to apply + */ + protected setMouseCursorStyle(cursorStyle: "pointer" | "default") { + this.scene.input.manager.canvas.style.cursor = cursorStyle; + } + clear() { this.active = false; } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 7108a8dd480..82b3ee6b4fa 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -49,8 +49,10 @@ import RenameFormUiHandler from "./rename-form-ui-handler"; import AdminUiHandler from "./admin-ui-handler"; import RunHistoryUiHandler from "./run-history-ui-handler"; import RunInfoUiHandler from "./run-info-ui-handler"; +import EggSummaryUiHandler from "./egg-summary-ui-handler"; import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler"; import AutoCompleteUiHandler from "./autocomplete-ui-handler"; +import { Device } from "#enums/devices"; export enum Mode { MESSAGE, @@ -66,6 +68,7 @@ export enum Mode { STARTER_SELECT, EVOLUTION_SCENE, EGG_HATCH_SCENE, + EGG_HATCH_SUMMARY, CONFIRM, OPTION_SELECT, MENU, @@ -171,6 +174,7 @@ export default class UI extends Phaser.GameObjects.Container { new StarterSelectUiHandler(scene), new EvolutionSceneHandler(scene), new EggHatchSceneHandler(scene), + new EggSummaryUiHandler(scene), new ConfirmUiHandler(scene), new OptionSelectUiHandler(scene), new MenuUiHandler(scene), @@ -241,7 +245,7 @@ export default class UI extends Phaser.GameObjects.Container { this.tooltipContent = addTextObject(this.scene, 6, 16, "", TextStyle.TOOLTIP_CONTENT); this.tooltipContent.setName("text-tooltip-content"); - this.tooltipContent.setWordWrapWidth(696); + this.tooltipContent.setWordWrapWidth(850); this.tooltipContainer.add(this.tooltipBg); this.tooltipContainer.add(this.tooltipTitle); @@ -286,6 +290,12 @@ export default class UI extends Phaser.GameObjects.Container { return handler.processInput(button); } + showTextPromise(text: string, callbackDelay: number = 0, prompt: boolean = true, promptDelay?: integer | null): Promise { + return new Promise(resolve => { + this.showText(text ?? "", null, () => resolve(), callbackDelay, prompt, promptDelay); + }); + } + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null): void { if (prompt && text.indexOf("$") > -1) { const messagePages = text.split(/\$/g).map(m => m.trim()); @@ -317,10 +327,11 @@ export default class UI extends Phaser.GameObjects.Container { if (i18next.exists(keyOrText) ) { const i18nKey = keyOrText; hasi18n = true; + text = i18next.t(i18nKey, { context: genderStr }); // override text with translation // Skip dialogue if the player has enabled the option and the dialogue has been already seen - if (battleScene.skipSeenDialogues && battleScene.gameData.getSeenDialogues()[i18nKey] === true) { + if (this.shouldSkipDialogue(i18nKey)) { console.log(`Dialogue ${i18nKey} skipped`); callback(); return; @@ -358,14 +369,13 @@ export default class UI extends Phaser.GameObjects.Container { return false; } + getTooltip(): { visible: boolean; title: string; content: string } { + return { visible: this.tooltipContainer.visible, title: this.tooltipTitle.text, content: this.tooltipContent.text }; + } + showTooltip(title: string, content: string, overlap?: boolean): void { this.tooltipContainer.setVisible(true); - this.tooltipTitle.setText(title || ""); - const wrappedContent = this.tooltipContent.runWordWrap(content); - this.tooltipContent.setText(wrappedContent); - this.tooltipContent.y = title ? 16 : 4; - this.tooltipBg.width = Math.min(Math.max(this.tooltipTitle.displayWidth, this.tooltipContent.displayWidth) + 12, 684); - this.tooltipBg.height = (title ? 31 : 19) + 10.5 * (wrappedContent.split("\n").length - 1); + this.editTooltip(title, content); if (overlap) { (this.scene as BattleScene).uiContainer.moveAbove(this.tooltipContainer, this); } else { @@ -373,6 +383,15 @@ export default class UI extends Phaser.GameObjects.Container { } } + editTooltip(title: string, content: string): void { + this.tooltipTitle.setText(title || ""); + const wrappedContent = this.tooltipContent.runWordWrap(content); + this.tooltipContent.setText(wrappedContent); + this.tooltipContent.y = title ? 16 : 4; + this.tooltipBg.width = Math.min(Math.max(this.tooltipTitle.displayWidth, this.tooltipContent.displayWidth) + 12, 838); + this.tooltipBg.height = (title ? 31 : 19) + 10.5 * (wrappedContent.split("\n").length - 1); + } + hideTooltip(): void { this.tooltipContainer.setVisible(false); this.tooltipTitle.clearTint(); @@ -380,8 +399,12 @@ export default class UI extends Phaser.GameObjects.Container { update(): void { if (this.tooltipContainer.visible) { - const reverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; - this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer!.y / 6 + 2); // TODO: are these bangs correct? + const xReverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; + const yReverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.y >= this.scene.game.canvas.height - this.tooltipBg.height * 6 - 12; + this.tooltipContainer.setPosition( + !xReverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2, + !yReverse ? this.scene.game.input.mousePointer!.y / 6 + 2 : this.scene.game.input.mousePointer!.y / 6 - this.tooltipBg.height - 2, + ); } } @@ -556,4 +579,20 @@ export default class UI extends Phaser.GameObjects.Container { public getModeChain(): Mode[] { return this.modeChain; } + + /** + * getGamepadType - returns the type of gamepad being used + * inputMethod could be "keyboard" or "touch" or "gamepad" + * if inputMethod is "keyboard" or "touch", then the inputMethod is returned + * if inputMethod is "gamepad", then the gamepad type is returned it could be "xbox" or "dualshock" + * @returns gamepad type + */ + public getGamepadType(): string { + const scene = this.scene as BattleScene; + if (scene.inputMethod === "gamepad") { + return scene.inputController.getConfig(scene.inputController.selectedDevice[Device.GAMEPAD]).padType; + } else { + return scene.inputMethod; + } + } } diff --git a/src/utils.ts b/src/utils.ts index 173ea25b17c..7decf9bb4c0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import i18next from "i18next"; import { MoneyFormat } from "#enums/money-format"; +import i18next from "i18next"; export const MissingTextureKey = "__MISSING"; @@ -82,6 +82,12 @@ export function randInt(range: integer, min: integer = 0): integer { return Math.floor(Math.random() * range) + min; } +/** + * Generates a random number using the global seed, or the current battle's seed if called via `Battle.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) + */ export function randSeedInt(range: integer, min: integer = 0): integer { if (range <= 1) { return min; @@ -449,6 +455,26 @@ export function rgbaToInt(rgba: integer[]): integer { return (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3]; } +/** + * Provided valid HSV values, calculates and stitches together a string of that + * HSV color's corresponding hex code. + * + * Sourced from {@link https://stackoverflow.com/a/44134328}. + * @param h Hue in degrees, must be in a range of [0, 360] + * @param s Saturation percentage, must be in a range of [0, 1] + * @param l Ligthness percentage, must be in a range of [0, 1] + * @returns a string of the corresponding color hex code with a "#" prefix + */ +export function hslToHex(h: number, s: number, l: number): string { + const a = s * Math.min(l, 1 - l); + const f = (n: number) => { + const k = (n + h / 30) % 12; + const rgb = l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1)); + return Math.round(rgb * 255).toString(16).padStart(2, "0"); + }; + return `#${f(0)}${f(8)}${f(4)}`; +} + /*This function returns true if the current lang is available for some functions If the lang is not in the function, it usually means that lang is going to use the default english version This function is used in: @@ -583,3 +609,14 @@ export function toDmgValue(value: number, minValue: number = 1) { export function getLocalizedSpriteKey(baseKey: string) { return `${baseKey}${verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`; } + +/** + * Check if a number is **inclusive** between two numbers + * @param num the number to check + * @param min the minimum value (included) + * @param max the maximum value (included) + * @returns true if number is **inclusive** between min and max + */ +export function isBetween(num: number, min: number, max: number): boolean { + return num >= min && num <= max; +} diff --git a/vite.config.ts b/vite.config.ts index 8fa7d799f27..1fd85e2572f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,6 +10,7 @@ export const defaultConfig: UserConfig = { clearScreen: false, appType: "mpa", build: { + chunkSizeWarningLimit: 10000, minify: 'esbuild', sourcemap: false, rollupOptions: { diff --git a/vitest.config.ts b/vitest.config.ts index d1827103807..54462675704 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,41 +1,42 @@ -import { defineProject } from 'vitest/config'; -import { defaultConfig } from './vite.config'; +import { defineProject } from "vitest/config"; +import { defaultConfig } from "./vite.config"; export default defineProject(({ mode }) => ({ - ...defaultConfig, - test: { - name: "main", - include: ["./src/test/**/*.{test,spec}.ts"], - exclude: ["./src/test/pre.test.ts"], - setupFiles: ['./src/test/vitest.setup.ts'], - server: { - deps: { - inline: ['vitest-canvas-mock'], - optimizer: { - web: { - include: ['vitest-canvas-mock'], - } - } - } - }, - environment: 'jsdom' as const, - environmentOptions: { - jsdom: { - resources: 'usable', - }, - }, - threads: false, - trace: true, - restoreMocks: true, - watch: false, - coverage: { - provider: 'istanbul' as const, - reportsDirectory: 'coverage' as const, - reporters: ['text-summary', 'html'], - }, - }, - esbuild: { - pure: mode === 'production' ? [ 'console.log' ] : [], - keepNames: true, - }, -})) + ...defaultConfig, + test: { + setupFiles: ["./src/test/fontFace.setup.ts", "./src/test/vitest.setup.ts"], + server: { + deps: { + inline: ["vitest-canvas-mock"], + //@ts-ignore + optimizer: { + web: { + include: ["vitest-canvas-mock"], + }, + }, + }, + }, + environment: "jsdom" as const, + environmentOptions: { + jsdom: { + resources: "usable", + }, + }, + threads: false, + trace: true, + restoreMocks: true, + watch: false, + coverage: { + provider: "istanbul" as const, + reportsDirectory: "coverage" as const, + reporters: ["text-summary", "html"], + }, + name: "main", + include: ["./src/test/**/*.{test,spec}.ts"], + exclude: ["./src/test/pre.test.ts"], + }, + esbuild: { + pure: mode === "production" ? ["console.log"] : [], + keepNames: true, + }, +}));